robustness improvements for -O gen-C++ generation of lambdas / "when"s

This commit is contained in:
Vern Paxson 2024-08-13 14:45:33 -07:00
parent 0ca2f9a8b2
commit 207b82ae4b
6 changed files with 66 additions and 35 deletions

View file

@ -385,6 +385,10 @@ private:
std::string LocalName(const ID* l) const;
std::string LocalName(const IDPtr& l) const { return LocalName(l.get()); }
// The same, but for a capture.
std::string CaptureName(const ID* l) const;
std::string CaptureName(const IDPtr& l) const { return CaptureName(l.get()); }
// Returns a canonicalized name, with various non-alphanumeric
// characters stripped or transformed, and guaranteed not to
// conflict with C++ keywords.
@ -585,8 +589,11 @@ private:
// Maps function names to events relevant to them.
std::unordered_map<std::string, std::vector<std::string>> body_events;
// Full type of the function we're currently compiling.
FuncTypePtr func_type;
// Return type of the function we're currently compiling.
TypePtr ret_type = nullptr;
TypePtr ret_type;
// Internal name of the function we're currently compiling.
std::string body_name;
@ -697,6 +704,8 @@ private:
void GenValueSwitchStmt(const Expr* e, const case_list* cases);
void GenWhenStmt(const WhenStmt* w);
void GenWhenStmt(const WhenInfo* wi, const std::string& when_lambda, const Location* loc,
std::vector<std::string> local_aggrs);
void GenForStmt(const ForStmt* f);
void GenForOverTable(const ExprPtr& tbl, const IDPtr& value_var, const IDPList* loop_vars);
void GenForOverVector(const ExprPtr& tbl, const IDPtr& value_var, const IDPList* loop_vars);
@ -771,6 +780,7 @@ private:
std::string GenSizeExpr(const Expr* e, GenType gt);
std::string GenScheduleExpr(const Expr* e);
std::string GenLambdaExpr(const Expr* e);
std::string GenLambdaExpr(const Expr* e, std::string capture_args);
std::string GenIsExpr(const Expr* e, GenType gt);
std::string GenArithCoerceExpr(const Expr* e, GenType gt);

View file

@ -31,7 +31,7 @@ void CPPCompile::DeclareLambda(const LambdaExpr* l, const ProfileFunc* pf) {
auto& ids = l->OuterIDs();
for ( auto id : ids )
lambda_names[id] = LocalName(id);
lambda_names[id] = CaptureName(id);
CreateFunction(l_id->GetType<FuncType>(), pf, lname, body, 0, l, FUNC_FLAVOR_FUNCTION);
}
@ -40,7 +40,12 @@ void CPPCompile::CreateFunction(const FuncTypePtr& ft, const ProfileFunc* pf, co
int priority, const LambdaExpr* l, FunctionFlavor flavor) {
const auto& yt = ft->Yield();
in_hook = flavor == FUNC_FLAVOR_HOOK;
const IDPList* lambda_ids = l ? &l->OuterIDs() : nullptr;
IDPList effective_lambda_ids;
if ( l )
effective_lambda_ids = l->OuterIDs();
const IDPList* lambda_ids = l ? &effective_lambda_ids : nullptr;
string args = BindArgs(ft, lambda_ids);
@ -313,7 +318,7 @@ void CPPCompile::GatherParamTypes(vector<string>& p_types, const FuncTypePtr& ft
auto tn = FullTypeName(t);
// Allow the captures to be modified.
p_types.emplace_back(string(tn) + "& ");
p_types.emplace_back(string(tn) + "&");
}
}
@ -328,18 +333,16 @@ void CPPCompile::GatherParamNames(vector<string>& p_names, const FuncTypePtr& ft
if ( param_id ) {
if ( t->Tag() == TYPE_ANY && param_id->GetType()->Tag() != TYPE_ANY )
// We'll need to translate the parameter
// from its current representation to
// type "any".
// We'll need to translate the parameter from its current
// representation to type "any".
p_names.emplace_back(string("any_param__CPP_") + Fmt(i));
else
p_names.emplace_back(LocalName(param_id));
}
else
// Parameters that are unused don't wind up in the
// ProfileFunc. Rather than dig their name out of
// the function's declaration, we explicitly name
// them to reflect that they're unused.
// Parameters that are unused don't wind up in the ProfileFunc.
// Rather than dig their name out of the function's declaration,
// we explicitly name them to reflect that they're unused.
p_names.emplace_back(string("unused_param__CPP_") + Fmt(i));
}

View file

@ -639,13 +639,15 @@ string CPPCompile::GenScheduleExpr(const Expr* e) {
}
string CPPCompile::GenLambdaExpr(const Expr* e) {
auto l = static_cast<const LambdaExpr*>(e);
auto& body = l->Ingredients()->Body();
return GenLambdaExpr(e, GenLambdaClone(l, false));
}
string CPPCompile::GenLambdaExpr(const Expr* e, string capture_args) {
auto l = static_cast<const LambdaExpr*>(e);
auto name = Canonicalize(l->Name().c_str()) + "_lb_cl";
auto cl_args = string("\"") + name + "\"";
if ( l->OuterIDs().size() > 0 )
cl_args = cl_args + GenLambdaClone(l, false);
auto cl_args = string("\"") + name + "\"" + std::move(capture_args);
auto body = string("make_intrusive<") + name + ">(" + cl_args + ")";
auto func = string("make_intrusive<CPPLambdaFunc>(\"") + l->Name() + "\", cast_intrusive<FuncType>(" +
GenTypeName(l->GetType()) + "), " + body + ")";
@ -1175,7 +1177,7 @@ string CPPCompile::GenLambdaClone(const LambdaExpr* l, bool all_deep) {
for ( const auto& id : ids ) {
const auto& id_t = id->GetType();
auto arg = LocalName(id);
auto arg = CaptureName(id);
if ( captures && ! IsNativeType(id_t) ) {
for ( const auto& c : *captures )
@ -1183,7 +1185,7 @@ string CPPCompile::GenLambdaClone(const LambdaExpr* l, bool all_deep) {
arg = string("cast_intrusive<") + TypeName(id_t) + ">(" + arg + "->Clone())";
}
cl_args = cl_args + ", " + arg;
cl_args += ", " + arg;
}
return cl_args;

View file

@ -77,6 +77,7 @@ protected:
// Methods related to sending lambdas via Broker.
std::optional<BrokerData> SerializeCaptures() const override;
void SetCaptures(Frame* f) override;
void SetCapturesVec(std::unique_ptr<std::vector<ZVal>> _captures_vec) { captures_vec = std::move(_captures_vec); }
FuncPtr DoClone() override;

View file

@ -38,12 +38,18 @@ void CPPCompile::GenInvokeBody(const string& call, const TypePtr& t) {
void CPPCompile::DefineBody(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname, const StmtPtr& body,
const IDPList* lambda_ids, FunctionFlavor flavor) {
IDPList l_ids;
if ( lambda_ids )
l_ids = *lambda_ids;
locals.clear();
params.clear();
body_name = fname;
func_type = ft;
ret_type = ft->Yield();
in_hook = flavor == FUNC_FLAVOR_HOOK;
auto ret_type_str = in_hook ? "bool" : FullTypeName(ret_type);
@ -52,7 +58,7 @@ void CPPCompile::DefineBody(const FuncTypePtr& ft, const ProfileFunc* pf, const
NL();
Emit("%s %s(%s)", ret_type_str, fname, ParamDecl(ft, lambda_ids, pf));
Emit("%s %s(%s)", ret_type_str, fname, ParamDecl(ft, &l_ids, pf));
StartBlock();
@ -64,7 +70,7 @@ void CPPCompile::DefineBody(const FuncTypePtr& ft, const ProfileFunc* pf, const
InitializeEvents(pf);
// Create the local variables.
DeclareLocals(pf, lambda_ids);
DeclareLocals(pf, &l_ids);
GenStmt(body);
@ -135,11 +141,12 @@ void CPPCompile::InitializeEvents(const ProfileFunc* pf) {
}
void CPPCompile::DeclareLocals(const ProfileFunc* pf, const IDPList* lambda_ids) {
// It's handy to have a set of the lambda captures rather than a list.
IDSet lambda_set;
// We track captures by their names rather than their ID*'s because the
// latter can be inconsistent when inlining.
set<string> capture_names;
if ( lambda_ids )
for ( auto li : *lambda_ids )
lambda_set.insert(li);
capture_names.insert(CaptureName(li));
const auto& ls = pf->Locals();
@ -149,11 +156,11 @@ void CPPCompile::DeclareLocals(const ProfileFunc* pf, const IDPList* lambda_ids)
for ( const auto& l : ls ) {
auto ln = LocalName(l);
auto cn = CaptureName(l);
if ( lambda_set.count(l) > 0 )
// No need to declare these, they're passed in as
// parameters.
ln = lambda_names[l];
if ( capture_names.count(cn) > 0 )
// No need to declare these, they're passed in as parameters.
ln = cn;
else if ( params.count(l) == 0 ) { // Not a parameter, so must be a local.
Emit("%s %s;", FullTypeName(l->GetType()), ln);

View file

@ -305,15 +305,22 @@ void CPPCompile::GenValueSwitchStmt(const Expr* e, const case_list* cases) {
void CPPCompile::GenWhenStmt(const WhenStmt* w) {
auto wi = w->Info();
auto wl = wi->Lambda();
if ( ! wl )
reporter->FatalError("cannot compile deprecated \"when\" statement");
vector<string> local_aggrs;
for ( auto& l : wi->WhenExprLocals() )
if ( IsAggr(l->GetType()) )
local_aggrs.push_back(IDNameStr(l.get()));
auto when_lambda = GenExpr(wi->Lambda(), GEN_NATIVE);
GenWhenStmt(wi.get(), when_lambda, w->GetLocationInfo(), std::move(local_aggrs));
}
void CPPCompile::GenWhenStmt(const WhenInfo* wi, const std::string& when_lambda, const Location* loc,
vector<string> local_aggrs) {
auto is_return = wi->IsReturn() ? "true" : "false";
auto timeout = wi->TimeoutExpr();
auto timeout_val = timeout ? GenExpr(timeout, GEN_NATIVE) : "-1.0";
auto loc = w->GetLocationInfo();
Emit("{ // begin a new scope for internal variables");
@ -331,17 +338,18 @@ void CPPCompile::GenWhenStmt(const WhenStmt* w) {
NL();
Emit("std::vector<ValPtr> CPP__local_aggrs;");
for ( auto& l : wi->WhenExprLocals() )
if ( IsAggr(l->GetType()) )
Emit("CPP__local_aggrs.emplace_back(%s);", IDNameStr(l.get()));
for ( auto& la : local_aggrs )
Emit("CPP__local_aggrs.emplace_back(%s);", la);
Emit("CPP__wi->Instantiate(%s);", GenExpr(wi->Lambda(), GEN_NATIVE));
Emit("CPP__wi->Instantiate(%s);", when_lambda);
// We need a new frame for the trigger to unambiguously associate
// with, in case we're called multiple times with our existing frame.
Emit("auto new_frame = make_intrusive<Frame>(0, nullptr, nullptr);");
Emit("auto curr_t = f__CPP->GetTrigger();");
Emit("auto curr_assoc = f__CPP->GetTriggerAssoc();");
if ( ! ret_type || ret_type->Tag() == TYPE_VOID )
Emit("// Note, the following works even if curr_t is nil.");
Emit("new_frame->SetTrigger({NewRef{}, curr_t});");
Emit("new_frame->SetTriggerAssoc(curr_assoc);");