mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
initial analysis working
This commit is contained in:
parent
db018253fe
commit
1fdd1f72c0
4 changed files with 275 additions and 46 deletions
|
@ -1762,7 +1762,7 @@ WhenInfo::WhenInfo(bool arg_is_return) : is_return(arg_is_return) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WhenInfo::BuildProfile() {
|
void WhenInfo::BuildProfile() {
|
||||||
ProfileFunc cond_pf(cond.get());
|
ProfileFunc cond_pf(cond.get(), false);
|
||||||
|
|
||||||
auto when_expr_locals_set = cond_pf.Locals();
|
auto when_expr_locals_set = cond_pf.Locals();
|
||||||
when_expr_globals = cond_pf.AllGlobals();
|
when_expr_globals = cond_pf.AllGlobals();
|
||||||
|
|
|
@ -67,7 +67,7 @@ void CPPCompile::Compile(bool report_uncompilable) {
|
||||||
accessed_globals.insert(g.get());
|
accessed_globals.insert(g.get());
|
||||||
|
|
||||||
for ( const auto& i_e : g->GetOptInfo()->GetInitExprs() ) {
|
for ( const auto& i_e : g->GetOptInfo()->GetInitExprs() ) {
|
||||||
auto pf = std::make_shared<ProfileFunc>(i_e.get());
|
auto pf = std::make_shared<ProfileFunc>(i_e.get(), true);
|
||||||
for ( auto& t : pf->OrderedTypes() ) {
|
for ( auto& t : pf->OrderedTypes() ) {
|
||||||
(void)pfs->HashType(t);
|
(void)pfs->HashType(t);
|
||||||
rep_types.insert(TypeRep(t));
|
rep_types.insert(TypeRep(t));
|
||||||
|
|
|
@ -63,7 +63,7 @@ ProfileFunc::ProfileFunc(const Stmt* s, bool _abs_rec_fields) {
|
||||||
s->Traverse(this);
|
s->Traverse(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileFunc::ProfileFunc(const Expr* e, bool _abs_rec_fields) {
|
ProfileFunc::ProfileFunc(const Expr* e, bool _is_init_expr, bool _abs_rec_fields) : is_init_expr(_is_init_expr) {
|
||||||
profiled_expr = e;
|
profiled_expr = e;
|
||||||
|
|
||||||
abs_rec_fields = _abs_rec_fields;
|
abs_rec_fields = _abs_rec_fields;
|
||||||
|
@ -121,7 +121,8 @@ TraversalCode ProfileFunc::PreStmt(const Stmt* s) {
|
||||||
|
|
||||||
// Don't traverse further into the statement, since we
|
// Don't traverse further into the statement, since we
|
||||||
// don't want to view the identifiers as locals unless
|
// don't want to view the identifiers as locals unless
|
||||||
// they're also used elsewhere.
|
// they're also used elsewhere, and for aggregates we don't
|
||||||
|
// want to treat these as creating aliases or modifications.
|
||||||
return TC_ABORTSTMT;
|
return TC_ABORTSTMT;
|
||||||
|
|
||||||
case STMT_WHEN: {
|
case STMT_WHEN: {
|
||||||
|
@ -139,11 +140,19 @@ TraversalCode ProfileFunc::PreStmt(const Stmt* s) {
|
||||||
auto loop_vars = sf->LoopVars();
|
auto loop_vars = sf->LoopVars();
|
||||||
auto value_var = sf->ValueVar();
|
auto value_var = sf->ValueVar();
|
||||||
|
|
||||||
for ( auto id : *loop_vars )
|
for ( auto id : *loop_vars ) {
|
||||||
locals.insert(id);
|
locals.insert(id);
|
||||||
|
if ( IsAggr(id->GetType()) )
|
||||||
|
TrackAggrIDMod(id);
|
||||||
|
}
|
||||||
|
|
||||||
if ( value_var )
|
if ( value_var ) {
|
||||||
locals.insert(value_var.get());
|
locals.insert(value_var.get());
|
||||||
|
if ( IsAggr(value_var->GetType()) )
|
||||||
|
TrackAggrIDMod(value_var.get());
|
||||||
|
}
|
||||||
|
// Next node is the aggregate we're looping over.
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case STMT_SWITCH: {
|
case STMT_SWITCH: {
|
||||||
|
@ -162,11 +171,13 @@ TraversalCode ProfileFunc::PreStmt(const Stmt* s) {
|
||||||
auto idl = c->TypeCases();
|
auto idl = c->TypeCases();
|
||||||
if ( idl ) {
|
if ( idl ) {
|
||||||
for ( auto id : *idl )
|
for ( auto id : *idl )
|
||||||
// Make sure it's not a placeholder
|
// Make sure it's not a placeholder identifier, used
|
||||||
// identifier, used when there's
|
// when there's no explicit one.
|
||||||
// no explicit one.
|
if ( id->Name() ) {
|
||||||
if ( id->Name() )
|
|
||||||
locals.insert(id);
|
locals.insert(id);
|
||||||
|
if ( IsAggr(id->GetType()) )
|
||||||
|
TrackAggrIDMod(id);
|
||||||
|
}
|
||||||
|
|
||||||
is_type_switch = true;
|
is_type_switch = true;
|
||||||
}
|
}
|
||||||
|
@ -178,6 +189,8 @@ TraversalCode ProfileFunc::PreStmt(const Stmt* s) {
|
||||||
expr_switches.insert(sw);
|
expr_switches.insert(sw);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case STMT_PRINT: aggr_id_in_next_node_is_not_potential_alias = true; break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +200,15 @@ TraversalCode ProfileFunc::PreStmt(const Stmt* s) {
|
||||||
TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
exprs.emplace_back(NewRef{}, const_cast<Expr*>(e));
|
exprs.emplace_back(NewRef{}, const_cast<Expr*>(e));
|
||||||
|
|
||||||
|
auto do_aliasing = ! aggr_id_in_next_node_is_not_potential_alias;
|
||||||
|
|
||||||
|
// After processing this node, we revert to the default (unless in
|
||||||
|
// the processing we turn this back on) ... except for EXPR_REF
|
||||||
|
// nodes, for which what we want to target is their operand, not
|
||||||
|
// the node itself.
|
||||||
|
if ( e->Tag() != EXPR_REF )
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = false;
|
||||||
|
|
||||||
TrackType(e->GetType());
|
TrackType(e->GetType());
|
||||||
|
|
||||||
switch ( e->Tag() ) {
|
switch ( e->Tag() ) {
|
||||||
|
@ -199,7 +221,11 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
// Turns out that NameExpr's can be constructed using a
|
// Turns out that NameExpr's can be constructed using a
|
||||||
// different Type* than that of the identifier itself,
|
// different Type* than that of the identifier itself,
|
||||||
// so be sure we track the latter too.
|
// so be sure we track the latter too.
|
||||||
TrackType(id->GetType());
|
auto t = id->GetType();
|
||||||
|
TrackType(t);
|
||||||
|
|
||||||
|
if ( id->IsGlobal() && do_aliasing && IsAggr(t) )
|
||||||
|
aggr_id_has_potential_aliases.insert(id);
|
||||||
|
|
||||||
if ( id->IsGlobal() ) {
|
if ( id->IsGlobal() ) {
|
||||||
PreID(id);
|
PreID(id);
|
||||||
|
@ -220,23 +246,30 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
auto fn = e->AsFieldExpr()->FieldName();
|
auto fn = e->AsFieldExpr()->FieldName();
|
||||||
addl_hashes.push_back(p_hash(fn));
|
addl_hashes.push_back(p_hash(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXPR_HAS_FIELD:
|
case EXPR_HAS_FIELD: {
|
||||||
|
auto fe = e->AsHasFieldExpr();
|
||||||
if ( abs_rec_fields ) {
|
if ( abs_rec_fields ) {
|
||||||
auto f = e->AsHasFieldExpr()->Field();
|
auto f = fe->Field();
|
||||||
addl_hashes.push_back(std::hash<int>{}(f));
|
addl_hashes.push_back(std::hash<int>{}(f));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto fn = e->AsHasFieldExpr()->FieldName();
|
auto fn = fe->FieldName();
|
||||||
addl_hashes.push_back(std::hash<std::string>{}(fn));
|
addl_hashes.push_back(std::hash<std::string>{}(fn));
|
||||||
}
|
}
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case EXPR_INDEX: {
|
case EXPR_INDEX: {
|
||||||
auto lhs_t = e->GetOp1()->GetType();
|
auto lhs = e->GetOp1();
|
||||||
|
auto lhs_t = lhs->GetType();
|
||||||
if ( lhs_t->Tag() == TYPE_TABLE )
|
if ( lhs_t->Tag() == TYPE_TABLE )
|
||||||
tbl_refs.insert(lhs_t.get());
|
tbl_type_refs.insert(lhs_t.get());
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case EXPR_INCR:
|
case EXPR_INCR:
|
||||||
|
@ -283,8 +316,17 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
// inside a when clause.
|
// inside a when clause.
|
||||||
when_locals.insert(id);
|
when_locals.insert(id);
|
||||||
}
|
}
|
||||||
else if ( IsAggr(lhs_t->Tag()) )
|
if ( IsAggr(lhs_t->Tag()) ) {
|
||||||
aggr_mods.insert(lhs_t.get());
|
if ( is_init_expr )
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
else {
|
||||||
|
TrackAggrIDMod(lhs);
|
||||||
|
if ( ! is_assign ) {
|
||||||
|
aggr_type_mods.insert(lhs_t.get());
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case EXPR_INDEX: {
|
case EXPR_INDEX: {
|
||||||
|
@ -296,9 +338,9 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
// rather a's type. However, for any of the others,
|
// rather a's type. However, for any of the others,
|
||||||
// e.g. "a[b] -= aggr" it is a[b]'s type.
|
// e.g. "a[b] -= aggr" it is a[b]'s type.
|
||||||
if ( is_assign )
|
if ( is_assign )
|
||||||
aggr_mods.insert(lhs_aggr_t.get());
|
aggr_type_mods.insert(lhs_aggr_t.get());
|
||||||
else
|
else
|
||||||
aggr_mods.insert(lhs_t.get());
|
aggr_type_mods.insert(lhs_t.get());
|
||||||
|
|
||||||
if ( lhs_aggr_t->Tag() == TYPE_TABLE ) {
|
if ( lhs_aggr_t->Tag() == TYPE_TABLE ) {
|
||||||
// We don't want the default recursion into the
|
// We don't want the default recursion into the
|
||||||
|
@ -306,7 +348,11 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
// table modification as a reference instead. So
|
// table modification as a reference instead. So
|
||||||
// do it manually. Given that, we need to do the
|
// do it manually. Given that, we need to do the
|
||||||
// expression's RHS manually too.
|
// expression's RHS manually too.
|
||||||
lhs->GetOp1()->Traverse(this);
|
if ( lhs_aggr->Tag() == EXPR_NAME ) {
|
||||||
|
TrackAggrIDMod(lhs_aggr);
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
}
|
||||||
|
lhs_aggr->Traverse(this);
|
||||||
lhs->GetOp2()->Traverse(this);
|
lhs->GetOp2()->Traverse(this);
|
||||||
|
|
||||||
auto rhs = e->GetOp2();
|
auto rhs = e->GetOp2();
|
||||||
|
@ -317,27 +363,38 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case EXPR_FIELD: aggr_mods.insert(lhs_t.get()); break;
|
case EXPR_FIELD:
|
||||||
|
aggr_type_mods.insert(lhs_t.get());
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case EXPR_LIST: {
|
case EXPR_LIST:
|
||||||
for ( auto id : lhs->AsListExpr()->Exprs() ) {
|
for ( auto id : lhs->AsListExpr()->Exprs() ) {
|
||||||
auto id_t = id->GetType();
|
auto id_t = id->GetType();
|
||||||
if ( IsAggr(id_t->Tag()) )
|
if ( IsAggr(id_t->Tag()) )
|
||||||
aggr_mods.insert(id_t.get());
|
aggr_type_mods.insert(id_t.get());
|
||||||
}
|
}
|
||||||
} break;
|
break;
|
||||||
|
|
||||||
default: reporter->InternalError("bad expression in ProfileFunc: %s", obj_desc(e).c_str());
|
default: reporter->InternalError("bad expression in ProfileFunc: %s", obj_desc(e).c_str());
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case EXPR_LIST:
|
||||||
|
for ( auto e_i : e->AsListExpr()->Exprs() ) {
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = ! do_aliasing;
|
||||||
|
e_i->Traverse(this);
|
||||||
|
}
|
||||||
|
return TC_ABORTSTMT;
|
||||||
|
|
||||||
case EXPR_AGGR_ADD:
|
case EXPR_AGGR_ADD:
|
||||||
case EXPR_AGGR_DEL: {
|
case EXPR_AGGR_DEL: {
|
||||||
auto lhs = e->GetOp1();
|
auto op = e->GetOp1();
|
||||||
if ( lhs )
|
auto aggr = op->GetOp1() ? op->GetOp1() : op;
|
||||||
aggr_mods.insert(lhs->GetType().get());
|
aggr_type_mods.insert(aggr->GetType().get());
|
||||||
else
|
if ( aggr->Tag() == EXPR_NAME )
|
||||||
aggr_mods.insert(e->GetType().get());
|
TrackAggrIDMod(aggr);
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case EXPR_CALL: {
|
case EXPR_CALL: {
|
||||||
|
@ -476,6 +533,20 @@ TraversalCode ProfileFunc::PreExpr(const Expr* e) {
|
||||||
type_aliases[orig_type].insert(res_type);
|
type_aliases[orig_type].insert(res_type);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case EXPR_CLONE:
|
||||||
|
case EXPR_SIZE:
|
||||||
|
case EXPR_IS: aggr_id_in_next_node_is_not_potential_alias = true; break;
|
||||||
|
|
||||||
|
case EXPR_IN:
|
||||||
|
// This one we need to traverse manually, so we can avoid
|
||||||
|
// viewing an aggregate appearing in either operand as potentially
|
||||||
|
// creating an alias.
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
e->GetOp1()->Traverse(this);
|
||||||
|
aggr_id_in_next_node_is_not_potential_alias = true;
|
||||||
|
e->GetOp2()->Traverse(this);
|
||||||
|
return TC_ABORTSTMT;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,6 +621,13 @@ void ProfileFunc::TrackAssignment(const ID* id) {
|
||||||
non_local_assignees.insert(id);
|
non_local_assignees.insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProfileFunc::TrackAggrIDMod(const Expr* e) { TrackAggrIDMod(e->AsNameExpr()->Id()); }
|
||||||
|
|
||||||
|
void ProfileFunc::TrackAggrIDMod(const ID* id) {
|
||||||
|
if ( id->IsGlobal() )
|
||||||
|
aggr_id_mods.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
void ProfileFunc::CheckRecordConstructor(TypePtr t) {
|
void ProfileFunc::CheckRecordConstructor(TypePtr t) {
|
||||||
auto rt = cast_intrusive<RecordType>(t);
|
auto rt = cast_intrusive<RecordType>(t);
|
||||||
for ( auto td : *rt->Types() )
|
for ( auto td : *rt->Types() )
|
||||||
|
@ -618,6 +696,10 @@ ProfileFuncs::ProfileFuncs(std::vector<FuncInfo>& funcs, is_compilable_pred pred
|
||||||
// Now that we have everything profiled, we can proceed to analyses
|
// Now that we have everything profiled, we can proceed to analyses
|
||||||
// that require full global information.
|
// that require full global information.
|
||||||
ComputeSideEffects();
|
ComputeSideEffects();
|
||||||
|
|
||||||
|
// Finally, analyze aggregate globals for those that we can confidently
|
||||||
|
// deem constant.
|
||||||
|
FindConstGlobalAggrs();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfileFuncs::IsTableWithDefaultAggr(const Type* t) {
|
bool ProfileFuncs::IsTableWithDefaultAggr(const Type* t) {
|
||||||
|
@ -709,7 +791,7 @@ void ProfileFuncs::MergeInProfile(ProfileFunc* pf) {
|
||||||
auto& init_exprs = g->GetOptInfo()->GetInitExprs();
|
auto& init_exprs = g->GetOptInfo()->GetInitExprs();
|
||||||
for ( const auto& i_e : init_exprs )
|
for ( const auto& i_e : init_exprs )
|
||||||
if ( i_e ) {
|
if ( i_e ) {
|
||||||
pending_exprs.push_back(i_e.get());
|
pending_exprs.push_back({i_e.get(), true});
|
||||||
|
|
||||||
if ( i_e->Tag() == EXPR_LAMBDA )
|
if ( i_e->Tag() == EXPR_LAMBDA )
|
||||||
lambdas.insert(i_e->AsLambdaExpr());
|
lambdas.insert(i_e->AsLambdaExpr());
|
||||||
|
@ -725,10 +807,14 @@ void ProfileFuncs::MergeInProfile(ProfileFunc* pf) {
|
||||||
script_calls.insert(pf->ScriptCalls().begin(), pf->ScriptCalls().end());
|
script_calls.insert(pf->ScriptCalls().begin(), pf->ScriptCalls().end());
|
||||||
BiF_globals.insert(pf->BiFGlobals().begin(), pf->BiFGlobals().end());
|
BiF_globals.insert(pf->BiFGlobals().begin(), pf->BiFGlobals().end());
|
||||||
events.insert(pf->Events().begin(), pf->Events().end());
|
events.insert(pf->Events().begin(), pf->Events().end());
|
||||||
|
aggr_type_mods.insert(pf->AggrTypeMods().begin(), pf->AggrTypeMods().end());
|
||||||
|
aggr_id_has_potential_aliases.insert(pf->AggrIDHasPotentialAliases().begin(),
|
||||||
|
pf->AggrIDHasPotentialAliases().end());
|
||||||
|
aggr_id_mods.insert(pf->AggrIDMods().begin(), pf->AggrIDMods().end());
|
||||||
|
|
||||||
for ( auto& i : pf->Lambdas() ) {
|
for ( auto& i : pf->Lambdas() ) {
|
||||||
lambdas.insert(i);
|
lambdas.insert(i);
|
||||||
pending_exprs.push_back(i);
|
pending_exprs.push_back({i, false});
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto& a : pf->ConstructorAttrs() )
|
for ( auto& a : pf->ConstructorAttrs() )
|
||||||
|
@ -813,8 +899,8 @@ void ProfileFuncs::DrainPendingExprs() {
|
||||||
auto pe = pending_exprs;
|
auto pe = pending_exprs;
|
||||||
pending_exprs.clear();
|
pending_exprs.clear();
|
||||||
|
|
||||||
for ( auto e : pe ) {
|
for ( auto [e, is_init] : pe ) {
|
||||||
auto pf = std::make_shared<ProfileFunc>(e, full_record_hashes);
|
auto pf = std::make_shared<ProfileFunc>(e, is_init, full_record_hashes);
|
||||||
|
|
||||||
expr_profs[e] = pf;
|
expr_profs[e] = pf;
|
||||||
MergeInProfile(pf.get());
|
MergeInProfile(pf.get());
|
||||||
|
@ -1089,7 +1175,7 @@ void ProfileFuncs::AnalyzeAttrs(const Attributes* attrs, const Type* t) {
|
||||||
if ( ! e )
|
if ( ! e )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pending_exprs.push_back(e.get());
|
pending_exprs.push_back({e.get(), false});
|
||||||
|
|
||||||
auto prev_ea = expr_attrs.find(a.get());
|
auto prev_ea = expr_attrs.find(a.get());
|
||||||
if ( prev_ea == expr_attrs.end() )
|
if ( prev_ea == expr_attrs.end() )
|
||||||
|
@ -1210,7 +1296,7 @@ bool ProfileFuncs::DefinitelyHasNoSideEffects(const ExprPtr& e) const {
|
||||||
|
|
||||||
const auto& pf = ep->second;
|
const auto& pf = ep->second;
|
||||||
|
|
||||||
if ( ! pf->NonLocalAssignees().empty() || ! pf->TableRefs().empty() || ! pf->AggrMods().empty() ||
|
if ( ! pf->NonLocalAssignees().empty() || ! pf->TableTypeRefs().empty() || ! pf->AggrTypeMods().empty() ||
|
||||||
! pf->ScriptCalls().empty() )
|
! pf->ScriptCalls().empty() )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1322,11 +1408,11 @@ bool ProfileFuncs::AssessSideEffects(const ProfileFunc* pf, IDSet& non_local_ids
|
||||||
// Not enough information yet to know all of the side effects.
|
// Not enough information yet to know all of the side effects.
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for ( auto& tr : pf->TableRefs() )
|
for ( auto& tr : pf->TableTypeRefs() )
|
||||||
if ( ! AssessAggrEffects(SideEffectsOp::READ, tr, nla, mod_aggrs, is_unknown) )
|
if ( ! AssessAggrEffects(SideEffectsOp::READ, tr, nla, mod_aggrs, is_unknown) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for ( auto& tm : pf->AggrMods() ) {
|
for ( auto& tm : pf->AggrTypeMods() ) {
|
||||||
if ( tm->Tag() == TYPE_TABLE && ! AssessAggrEffects(SideEffectsOp::WRITE, tm, nla, mod_aggrs, is_unknown) )
|
if ( tm->Tag() == TYPE_TABLE && ! AssessAggrEffects(SideEffectsOp::WRITE, tm, nla, mod_aggrs, is_unknown) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1445,6 +1531,74 @@ std::shared_ptr<SideEffectsOp> ProfileFuncs::GetCallSideEffects(const ScriptFunc
|
||||||
return seo;
|
return seo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProfileFuncs::AggrTypePotentiallyModified(const TypePtr& t, std::unordered_set<const Type*> seen) const {
|
||||||
|
if ( ! IsAggr(t) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( seen.count(t.get()) > 0 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( aggr_type_mods.count(t.get()) > 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Look for any sub-aggregates that themselves are potentially modified.
|
||||||
|
seen.insert(t.get());
|
||||||
|
|
||||||
|
switch ( t->Tag() ) {
|
||||||
|
case TYPE_RECORD: {
|
||||||
|
auto rt = cast_intrusive<RecordType>(t);
|
||||||
|
for ( const auto& td : *rt->Types() )
|
||||||
|
if ( AggrTypePotentiallyModified(td->type, seen) )
|
||||||
|
return true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case TYPE_VECTOR: {
|
||||||
|
const auto& yt = cast_intrusive<VectorType>(t)->Yield();
|
||||||
|
if ( AggrTypePotentiallyModified(yt, seen) )
|
||||||
|
return true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case TYPE_TABLE: {
|
||||||
|
const auto& tt = cast_intrusive<TableType>(t);
|
||||||
|
const auto& tt_i = tt->GetIndices();
|
||||||
|
for ( const auto& t : tt_i->GetTypes() )
|
||||||
|
if ( AggrTypePotentiallyModified(t, seen) )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto& yt = tt->Yield();
|
||||||
|
if ( yt && AggrTypePotentiallyModified(yt, seen) )
|
||||||
|
return true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
reporter->InternalError("bad aggregate type in ProfileFuncs::AggrTypePotentiallyModified: %s",
|
||||||
|
obj_desc(t).c_str());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfileFuncs::FindConstGlobalAggrs() {
|
||||||
|
for ( auto& g : Globals() ) {
|
||||||
|
auto& gt = g->GetType();
|
||||||
|
|
||||||
|
if ( ! IsAggr(gt) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( aggr_id_has_potential_aliases.count(g) > 0 || aggr_id_mods.count(g) > 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( g->GetAttr(ATTR_DEFAULT_INSERT) )
|
||||||
|
// A simple lookup can modify the aggregate.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::unordered_set<const Type*> seen;
|
||||||
|
if ( AggrTypePotentiallyModified(gt, seen) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const_aggr_globals.insert(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We associate modules with filenames, and take the first one we see.
|
// We associate modules with filenames, and take the first one we see.
|
||||||
static std::unordered_map<std::string, std::string> filename_module;
|
static std::unordered_map<std::string, std::string> filename_module;
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,14 @@ public:
|
||||||
|
|
||||||
// Constructors for profiling an AST statement expression. These exist
|
// Constructors for profiling an AST statement expression. These exist
|
||||||
// to support (1) profiling lambda expressions and loop bodies, and
|
// to support (1) profiling lambda expressions and loop bodies, and
|
||||||
// (2) traversing attribute expressions (such as &default=expr)
|
// (2) traversing attribute expressions (such as &default=expr) and
|
||||||
// to discover what components they include.
|
// global initializations to discover what components they include.
|
||||||
ProfileFunc(const Stmt* body, bool abs_rec_fields = false);
|
ProfileFunc(const Stmt* body, bool abs_rec_fields = false);
|
||||||
ProfileFunc(const Expr* func, bool abs_rec_fields = false);
|
|
||||||
|
// The second argument is true if the expression is an initialization
|
||||||
|
// of a global. That enables us to disambiguate whether the global
|
||||||
|
// is ever re-assigned.
|
||||||
|
ProfileFunc(const Expr* e, bool is_init_expr, bool abs_rec_fields = false);
|
||||||
|
|
||||||
// Returns the function, body, or expression profiled. Each can be
|
// Returns the function, body, or expression profiled. Each can be
|
||||||
// null depending on the constructor used.
|
// null depending on the constructor used.
|
||||||
|
@ -100,8 +104,10 @@ public:
|
||||||
const IDSet& Params() const { return params; }
|
const IDSet& Params() const { return params; }
|
||||||
const std::unordered_map<const ID*, int>& Assignees() const { return assignees; }
|
const std::unordered_map<const ID*, int>& Assignees() const { return assignees; }
|
||||||
const IDSet& NonLocalAssignees() const { return non_local_assignees; }
|
const IDSet& NonLocalAssignees() const { return non_local_assignees; }
|
||||||
const auto& TableRefs() const { return tbl_refs; }
|
const auto& TableTypeRefs() const { return tbl_type_refs; }
|
||||||
const auto& AggrMods() const { return aggr_mods; }
|
const auto& AggrTypeMods() const { return aggr_type_mods; }
|
||||||
|
const auto& AggrIDHasPotentialAliases() const { return aggr_id_has_potential_aliases; }
|
||||||
|
const auto& AggrIDMods() const { return aggr_id_mods; }
|
||||||
const IDSet& Inits() const { return inits; }
|
const IDSet& Inits() const { return inits; }
|
||||||
const std::vector<StmtPtr>& Stmts() const { return stmts; }
|
const std::vector<StmtPtr>& Stmts() const { return stmts; }
|
||||||
const std::vector<ExprPtr>& Exprs() const { return exprs; }
|
const std::vector<ExprPtr>& Exprs() const { return exprs; }
|
||||||
|
@ -158,6 +164,10 @@ protected:
|
||||||
// Take note of an assignment to an identifier.
|
// Take note of an assignment to an identifier.
|
||||||
void TrackAssignment(const ID* id);
|
void TrackAssignment(const ID* id);
|
||||||
|
|
||||||
|
void TrackAggrIDMod(ExprPtr e) { TrackAggrIDMod(e.get()); }
|
||||||
|
void TrackAggrIDMod(const Expr* e);
|
||||||
|
void TrackAggrIDMod(const ID* id);
|
||||||
|
|
||||||
// Extracts attributes of a record type used in a constructor (or implicit
|
// Extracts attributes of a record type used in a constructor (or implicit
|
||||||
// initialization, or coercion, which does an implicit construction).
|
// initialization, or coercion, which does an implicit construction).
|
||||||
void CheckRecordConstructor(TypePtr t);
|
void CheckRecordConstructor(TypePtr t);
|
||||||
|
@ -204,10 +214,39 @@ protected:
|
||||||
IDSet non_local_assignees;
|
IDSet non_local_assignees;
|
||||||
|
|
||||||
// TableType's that are used in table references (i.e., index operations).
|
// TableType's that are used in table references (i.e., index operations).
|
||||||
TypeSet tbl_refs;
|
TypeSet tbl_type_refs;
|
||||||
|
|
||||||
// Types corresponding to aggregates that are modified.
|
// Types corresponding to aggregates that are modified.
|
||||||
TypeSet aggr_mods;
|
TypeSet aggr_type_mods;
|
||||||
|
|
||||||
|
// Identifiers with aggregate types with potential aliases. These occur
|
||||||
|
// for some types of direct references to the aggregate, where its entire
|
||||||
|
// value is provided, not just an element of it. So for a record 'r',
|
||||||
|
// "do_my_work(r)" or "x = r" or "r = x" would qualify (for both of these
|
||||||
|
// last two, the assignment means that x and r will be aliases at a
|
||||||
|
// certain point). However, "r$foo" does not qualify, nor does "|r|"
|
||||||
|
// or "copy(r)".
|
||||||
|
//
|
||||||
|
// We want to track these because an identifier that does _not_ have any
|
||||||
|
// potential aliases can be optimized in certain circumstances (for
|
||||||
|
// example, treating it as a constant if it never has any modifications,
|
||||||
|
// either).
|
||||||
|
//
|
||||||
|
// Note that we only track these for globals.
|
||||||
|
IDSet aggr_id_has_potential_aliases;
|
||||||
|
|
||||||
|
// Identifiers with aggregate types that are modified (i.e., changes
|
||||||
|
// are made to the aggregate). Note that this doesn't include changes
|
||||||
|
// to sub-aggregates inside the aggregate. So for a record 'r',
|
||||||
|
// "++r$cnt" would qualify, as would "r = x", but not "++r$stats$cnt".
|
||||||
|
// For a table 't', "t[ind] = val" would qualify, as would "delete t[ind]".
|
||||||
|
//
|
||||||
|
// These are useful to track because an identifier that has no potential
|
||||||
|
// aliases (see above), no modifications, and no sub-aggregates of a type
|
||||||
|
// that's modified (per aggr_type_mods) can be treated as constant.
|
||||||
|
//
|
||||||
|
// Note that we only track these for globals.
|
||||||
|
IDSet aggr_id_mods;
|
||||||
|
|
||||||
// Same for locals seen in initializations, so we can find,
|
// Same for locals seen in initializations, so we can find,
|
||||||
// for example, unused aggregates.
|
// for example, unused aggregates.
|
||||||
|
@ -302,6 +341,18 @@ protected:
|
||||||
// Whether we should treat record field accesses as absolute
|
// Whether we should treat record field accesses as absolute
|
||||||
// (integer offset) or relative (name-based).
|
// (integer offset) or relative (name-based).
|
||||||
bool abs_rec_fields;
|
bool abs_rec_fields;
|
||||||
|
|
||||||
|
// Whether we're profiling the initialization of a global. If so, then
|
||||||
|
// we don't treat it as modifying the global.
|
||||||
|
bool is_init_expr = false;
|
||||||
|
|
||||||
|
// Whether the next node visited by AST traversal should not treat
|
||||||
|
// the appearance of an aggregate identifier as potentially creating
|
||||||
|
// an alias for that identifier. We use this somewhat awkward notion
|
||||||
|
// because it turns out to simplify the logic needed to tell apart
|
||||||
|
// instances of "safe" identifier appearances (not creating aliases)
|
||||||
|
// versus the frequent case that they do in fact create an alias.
|
||||||
|
bool aggr_id_in_next_node_is_not_potential_alias = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Describes an operation for which some forms of access can lead to state
|
// Describes an operation for which some forms of access can lead to state
|
||||||
|
@ -371,6 +422,7 @@ public:
|
||||||
// the (non-skipped) functions in "funcs". See the comments for
|
// the (non-skipped) functions in "funcs". See the comments for
|
||||||
// the associated member variables for documentation.
|
// the associated member variables for documentation.
|
||||||
const IDSet& Globals() const { return globals; }
|
const IDSet& Globals() const { return globals; }
|
||||||
|
const auto& ConstAggrGlobals() const { return const_aggr_globals; }
|
||||||
const IDSet& AllGlobals() const { return all_globals; }
|
const IDSet& AllGlobals() const { return all_globals; }
|
||||||
const std::unordered_set<const ConstExpr*>& Constants() const { return constants; }
|
const std::unordered_set<const ConstExpr*>& Constants() const { return constants; }
|
||||||
const std::vector<const Type*>& MainTypes() const { return main_types; }
|
const std::vector<const Type*>& MainTypes() const { return main_types; }
|
||||||
|
@ -380,6 +432,9 @@ public:
|
||||||
const std::unordered_set<const LambdaExpr*>& Lambdas() const { return lambdas; }
|
const std::unordered_set<const LambdaExpr*>& Lambdas() const { return lambdas; }
|
||||||
const std::unordered_set<std::string>& Events() const { return events; }
|
const std::unordered_set<std::string>& Events() const { return events; }
|
||||||
const auto& ExprAttrs() const { return expr_attrs; }
|
const auto& ExprAttrs() const { return expr_attrs; }
|
||||||
|
const auto& AggrTypeMods() const { return aggr_type_mods; }
|
||||||
|
const auto& AggrIDHasPotentialAliases() const { return aggr_id_has_potential_aliases; }
|
||||||
|
const auto& AggrIDMods() const { return aggr_id_mods; }
|
||||||
|
|
||||||
const auto& FuncProfs() const { return func_profs; }
|
const auto& FuncProfs() const { return func_profs; }
|
||||||
|
|
||||||
|
@ -505,10 +560,22 @@ protected:
|
||||||
// as a signal so that this method can also be used during that analysis.
|
// as a signal so that this method can also be used during that analysis.
|
||||||
std::shared_ptr<SideEffectsOp> GetCallSideEffects(const ScriptFunc* f);
|
std::shared_ptr<SideEffectsOp> GetCallSideEffects(const ScriptFunc* f);
|
||||||
|
|
||||||
|
// Determine which global aggregates can be safely treated as constants.
|
||||||
|
void FindConstGlobalAggrs();
|
||||||
|
|
||||||
|
// True if a given aggregate type is potentially modified, which includes
|
||||||
|
// whether any of its sub-aggregates might be modified. The second
|
||||||
|
// argument is to prevent infinite recursion for recursive types.
|
||||||
|
bool AggrTypePotentiallyModified(const TypePtr& t, std::unordered_set<const Type*> seen) const;
|
||||||
|
|
||||||
// Globals seen across the functions, other than those solely seen
|
// Globals seen across the functions, other than those solely seen
|
||||||
// as the function being called in a call.
|
// as the function being called in a call.
|
||||||
IDSet globals;
|
IDSet globals;
|
||||||
|
|
||||||
|
// Which of the above globals are aggregates that can be safely assumed
|
||||||
|
// to be constant (they have no modifications and no potential aliases).
|
||||||
|
IDSet const_aggr_globals;
|
||||||
|
|
||||||
// Same, but also includes globals only seen as called functions.
|
// Same, but also includes globals only seen as called functions.
|
||||||
IDSet all_globals;
|
IDSet all_globals;
|
||||||
|
|
||||||
|
@ -570,6 +637,12 @@ protected:
|
||||||
// shared across multiple distinct (though compatible) types.
|
// shared across multiple distinct (though compatible) types.
|
||||||
std::unordered_map<const Attr*, std::vector<const Type*>> expr_attrs;
|
std::unordered_map<const Attr*, std::vector<const Type*>> expr_attrs;
|
||||||
|
|
||||||
|
// The sets of all aggregate types or identifiers that are modified or
|
||||||
|
// have potential aliases. See ProfileFunc for particulars.
|
||||||
|
TypeSet aggr_type_mods;
|
||||||
|
IDSet aggr_id_has_potential_aliases;
|
||||||
|
IDSet aggr_id_mods;
|
||||||
|
|
||||||
// Tracks whether a given TableType has a &default that returns an
|
// Tracks whether a given TableType has a &default that returns an
|
||||||
// aggregate. Expressions involving indexing tables with such types
|
// aggregate. Expressions involving indexing tables with such types
|
||||||
// cannot be optimized out using CSE because each returned value is
|
// cannot be optimized out using CSE because each returned value is
|
||||||
|
@ -615,7 +688,9 @@ protected:
|
||||||
|
|
||||||
// Expressions that we've discovered that we need to further profile.
|
// Expressions that we've discovered that we need to further profile.
|
||||||
// These can arise for example due to lambdas or record attributes.
|
// These can arise for example due to lambdas or record attributes.
|
||||||
std::vector<const Expr*> pending_exprs;
|
// The second argument is true if the expression appears in the context
|
||||||
|
// of an initialization of a global, false otherwise.
|
||||||
|
std::vector<std::pair<const Expr*, bool>> pending_exprs;
|
||||||
|
|
||||||
// Whether to compute new hashes for the FuncInfo entries. If the FuncInfo
|
// Whether to compute new hashes for the FuncInfo entries. If the FuncInfo
|
||||||
// doesn't have a hash, it will always be computed.
|
// doesn't have a hash, it will always be computed.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue