extensive rewrite of generation & execution of run-time initialization

This commit is contained in:
Vern Paxson 2021-11-07 17:00:19 -08:00
parent bc3bf4ea6c
commit e1a760e674
26 changed files with 3459 additions and 1580 deletions

View file

@ -22,7 +22,7 @@ void CPPCompile::DeclareFunc(const FuncInfo& func)
const auto& body = func.Body();
auto priority = func.Priority();
DeclareSubclass(f->GetType(), pf, fname, body, priority, nullptr, f->Flavor());
CreateFunction(f->GetType(), pf, fname, body, priority, nullptr, f->Flavor());
if ( f->GetBodies().size() == 1 )
compiled_simple_funcs[f->Name()] = fname;
@ -40,17 +40,88 @@ void CPPCompile::DeclareLambda(const LambdaExpr* l, const ProfileFunc* pf)
for ( auto id : ids )
lambda_names[id] = LocalName(id);
DeclareSubclass(l_id->GetType<FuncType>(), pf, lname, body, 0, l, FUNC_FLAVOR_FUNCTION);
CreateFunction(l_id->GetType<FuncType>(), pf, lname, body, 0, l, FUNC_FLAVOR_FUNCTION);
}
void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname,
const StmtPtr& body, int priority, const LambdaExpr* l,
FunctionFlavor flavor)
void CPPCompile::CreateFunction(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname,
const StmtPtr& body, 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;
string args = BindArgs(ft, lambda_ids);
auto yt_decl = in_hook ? "bool" : FullTypeName(yt);
vector<string> p_types;
GatherParamTypes(p_types, ft, lambda_ids, pf);
string cast = string(yt_decl) + "(*)(";
for ( auto& pt : p_types )
cast += pt + ", ";
cast += string("Frame*)");
// We need to distinguish between hooks and non-hooks that happen
// to have matching type signatures. They'll be equivalent if they
// have identical cast's. To keep them separate, we cheat and
// make hook casts different, string-wise, without altering their
// semantics.
if ( in_hook )
cast += " ";
func_index[fname] = cast;
if ( casting_index.count(cast) == 0 )
{
casting_index[cast] = func_casting_glue.size();
DispatchInfo di;
di.cast = cast;
di.args = args;
di.is_hook = in_hook;
di.yield = yt;
func_casting_glue.emplace_back(di);
}
if ( lambda_ids )
{
DeclareSubclass(ft, pf, fname, args, lambda_ids);
BuildLambda(ft, pf, fname, body, l, lambda_ids);
EndBlock(true);
}
else
{
Emit("static %s %s(%s);", yt_decl, fname, ParamDecl(ft, lambda_ids, pf));
// Track this function as known to have been compiled.
// We don't track lambda bodies as compiled because they
// can't be instantiated directly without also supplying
// the captures. In principle we could make an exception
// for lambdas that don't take any arguments, but that
// seems potentially more confusing than beneficial.
compiled_funcs.emplace(fname);
auto loc_f = script_specific_filename(body);
cf_locs[fname] = loc_f;
}
auto h = pf->HashVal();
body_hashes[fname] = h;
body_priorities[fname] = priority;
body_names.emplace(body.get(), fname);
total_hash = merge_p_hashes(total_hash, h);
}
void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname,
const string& args, const IDPList* lambda_ids)
{
const auto& yt = ft->Yield();
auto yt_decl = in_hook ? "bool" : FullTypeName(yt);
NL();
@ -76,8 +147,7 @@ void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, c
}
}
Emit("%s_cl(const char* name%s) : CPPStmt(name)%s { }", fname, addl_args.c_str(),
inits.c_str());
Emit("%s_cl(const char* name%s) : CPPStmt(name)%s { }", fname, addl_args, inits);
// An additional constructor just used to generate place-holder
// instances, due to the mis-design that lambdas are identified
@ -92,7 +162,7 @@ void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, c
if ( in_hook )
{
Emit("if ( ! %s(%s) )", fname, BindArgs(ft, lambda_ids));
Emit("if ( ! %s(%s) )", fname, args);
StartBlock();
Emit("flow = FLOW_BREAK;");
EndBlock();
@ -100,42 +170,36 @@ void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, c
}
else if ( IsNativeType(yt) )
GenInvokeBody(fname, yt, BindArgs(ft, lambda_ids));
GenInvokeBody(fname, yt, args);
else
Emit("return %s(%s);", fname, BindArgs(ft, lambda_ids));
Emit("return %s(%s);", fname, args);
EndBlock();
}
if ( lambda_ids )
BuildLambda(ft, pf, fname, body, l, lambda_ids);
else
{
// Track this function as known to have been compiled.
// We don't track lambda bodies as compiled because they
// can't be instantiated directly without also supplying
// the captures. In principle we could make an exception
// for lambdas that don't take any arguments, but that
// seems potentially more confusing than beneficial.
compiled_funcs.emplace(fname);
auto loc_f = script_specific_filename(body);
cf_locs[fname] = loc_f;
// Some guidance for those looking through the generated code.
Emit("// compiled body for: %s", loc_f);
}
EndBlock(true);
auto h = pf->HashVal();
body_hashes[fname] = h;
body_priorities[fname] = priority;
body_names.emplace(body.get(), fname);
names_to_bodies.emplace(fname, body.get());
total_hash = merge_p_hashes(total_hash, h);
void CPPCompile::DeclareDynCPPStmt()
{
Emit("// A version of CPPStmt that manages a function pointer and");
Emit("// dynamically casts it to a given type to call it via Exec().");
Emit("// We will later generate a custom Exec method to support this");
Emit("// dispatch. All of this is ugly, and only needed because clang");
Emit("// goes nuts (super slow) in the face of thousands of templates");
Emit("// in a given context (initializers, or a function body).");
Emit("class CPPDynStmt : public CPPStmt");
Emit("\t{");
Emit("public:");
Emit("\tCPPDynStmt(const char* _name, void* _func, int _type_signature) : CPPStmt(_name), "
"func(_func), type_signature(_type_signature) { }");
Emit("\tValPtr Exec(Frame* f, StmtFlowType& flow) override final;");
Emit("private:");
Emit("\t// The function to call in Exec().");
Emit("\tvoid* func;");
Emit("\t// Used via a switch in the dynamically-generated Exec() method");
Emit("\t// to cast func to the write type, and to call it with the");
Emit("\t// right arguments pulled out of the frame.");
Emit("\tint type_signature;");
Emit("\t};");
}
void CPPCompile::BuildLambda(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname,
@ -146,28 +210,17 @@ void CPPCompile::BuildLambda(const FuncTypePtr& ft, const ProfileFunc* pf, const
{
auto name = lambda_names[id];
auto tn = FullTypeName(id->GetType());
Emit("%s %s;", tn, name.c_str());
Emit("%s %s;", tn, name);
}
// Generate initialization to create and register the lambda.
auto literal_name = string("\"") + l->Name() + "\"";
auto instantiate = string("make_intrusive<") + fname + "_cl>(" + literal_name + ")";
auto h = pf->HashVal();
auto nl = lambda_ids->length();
bool has_captures = nl > 0;
int nl = lambda_ids->length();
auto h = Fmt(pf->HashVal());
auto has_captures = nl > 0 ? "true" : "false";
auto l_init = string("register_lambda__CPP(") + instantiate + ", " + h + ", \"" + l->Name() +
"\", " + GenTypeName(ft) + ", " + has_captures + ");";
AddInit(l, l_init);
NoteInitDependency(l, TypeRep(ft));
// Make the lambda's body's initialization depend on the lambda's
// initialization. That way GenFuncVarInits() can generate
// initializations with the assurance that the associated body
// hashes will have been registered.
AddInit(body.get());
NoteInitDependency(body.get(), l);
auto gi = make_shared<LambdaRegistrationInfo>(this, l->Name(), ft, fname + "_cl", h,
has_captures);
lambda_reg_info->AddInstance(gi);
// Generate method to extract the lambda captures from a deserialized
// Frame object.
@ -237,17 +290,71 @@ string CPPCompile::BindArgs(const FuncTypePtr& ft, const IDPList* lambda_ids)
string CPPCompile::ParamDecl(const FuncTypePtr& ft, const IDPList* lambda_ids,
const ProfileFunc* pf)
{
const auto& params = ft->Params();
int n = params->NumFields();
vector<string> p_types;
vector<string> p_names;
GatherParamTypes(p_types, ft, lambda_ids, pf);
GatherParamNames(p_names, ft, lambda_ids, pf);
ASSERT(p_types.size() == p_names.size());
string decl;
for ( auto i = 0U; i < p_types.size(); ++i )
decl += p_types[i] + " " + p_names[i] + ", ";
// Add in the declaration of the frame.
return decl + "Frame* f__CPP";
}
void CPPCompile::GatherParamTypes(vector<string>& p_types, const FuncTypePtr& ft,
const IDPList* lambda_ids, const ProfileFunc* pf)
{
const auto& params = ft->Params();
int n = params->NumFields();
for ( auto i = 0; i < n; ++i )
{
const auto& t = params->GetFieldType(i);
auto tn = FullTypeName(t);
auto param_id = FindParam(i, pf);
string fn;
if ( IsNativeType(t) )
// Native types are always pass-by-value.
p_types.emplace_back(tn);
else
{
if ( param_id && pf->Assignees().count(param_id) > 0 )
// We modify the parameter.
p_types.emplace_back(tn);
else
// Not modified, so pass by const reference.
p_types.emplace_back(string("const ") + tn + "&");
}
}
if ( lambda_ids )
// Add the captures as additional parameters.
for ( auto& id : *lambda_ids )
{
const auto& t = id->GetType();
auto tn = FullTypeName(t);
// Allow the captures to be modified.
p_types.emplace_back(string(tn) + "& ");
}
}
void CPPCompile::GatherParamNames(vector<string>& p_names, const FuncTypePtr& ft,
const IDPList* lambda_ids, const ProfileFunc* pf)
{
const auto& params = ft->Params();
int n = params->NumFields();
for ( auto i = 0; i < n; ++i )
{
const auto& t = params->GetFieldType(i);
auto param_id = FindParam(i, pf);
if ( param_id )
{
@ -255,50 +362,22 @@ string CPPCompile::ParamDecl(const FuncTypePtr& ft, const IDPList* lambda_ids,
// We'll need to translate the parameter
// from its current representation to
// type "any".
fn = string("any_param__CPP_") + Fmt(i);
p_names.emplace_back(string("any_param__CPP_") + Fmt(i));
else
fn = LocalName(param_id);
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.
fn = string("unused_param__CPP_") + Fmt(i);
if ( IsNativeType(t) )
// Native types are always pass-by-value.
decl = decl + tn + " " + fn;
else
{
if ( param_id && pf->Assignees().count(param_id) > 0 )
// We modify the parameter.
decl = decl + tn + " " + fn;
else
// Not modified, so pass by const reference.
decl = decl + "const " + tn + "& " + fn;
}
decl += ", ";
// 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));
}
if ( lambda_ids )
{
// Add the captures as additional parameters.
for ( auto& id : *lambda_ids )
{
auto name = lambda_names[id];
const auto& t = id->GetType();
auto tn = FullTypeName(t);
// Allow the captures to be modified.
decl = decl + tn + "& " + name + ", ";
}
}
// Add in the declaration of the frame.
return decl + "Frame* f__CPP";
p_names.emplace_back(lambda_names[id]);
}
const ID* CPPCompile::FindParam(int i, const ProfileFunc* pf)