mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
methods implementing AST optimization (aliasing, constant propagation, CSE)
This commit is contained in:
parent
34ccd3e417
commit
77f04935a8
2 changed files with 720 additions and 74 deletions
|
@ -127,65 +127,569 @@ bool Reducer::SameVal(const Val* v1, const Val* v2) const
|
|||
return v1 == v2;
|
||||
}
|
||||
|
||||
IDPtr Reducer::GenTemporary(const TypePtr& t, ExprPtr rhs)
|
||||
ExprPtr Reducer::NewVarUsage(IDPtr var, const DefPoints* dps, const Expr* orig)
|
||||
{
|
||||
if ( Optimizing() )
|
||||
reporter->InternalError("Generating a new temporary while optimizing");
|
||||
if ( ! dps )
|
||||
reporter->InternalError("null defpoints in NewVarUsage");
|
||||
|
||||
auto temp = new TempVar(temps.length(), t, rhs);
|
||||
IDPtr temp_id = install_ID(temp->Name(), "<internal>", false, false);
|
||||
auto var_usage = make_intrusive<NameExpr>(var);
|
||||
SetDefPoints(var_usage.get(), dps);
|
||||
TrackExprReplacement(orig, var_usage.get());
|
||||
|
||||
temp->SetID(temp_id);
|
||||
temp_id->SetType(t);
|
||||
|
||||
temps.append(temp);
|
||||
ids_to_temps[temp_id.get()] = temp;
|
||||
|
||||
return temp_id;
|
||||
return var_usage;
|
||||
}
|
||||
|
||||
IDPtr Reducer::FindNewLocal(const IDPtr& id)
|
||||
const DefPoints* Reducer::GetDefPoints(const NameExpr* var)
|
||||
{
|
||||
auto mapping = orig_to_new_locals.find(id.get());
|
||||
auto dps = FindDefPoints(var);
|
||||
|
||||
if ( mapping != orig_to_new_locals.end() )
|
||||
return mapping->second;
|
||||
if ( ! dps )
|
||||
{
|
||||
auto id = var->Id();
|
||||
auto di = mgr->GetConstID_DI(id);
|
||||
auto rds = mgr->GetPreMaxRDs(GetRDLookupObj(var));
|
||||
|
||||
return GenLocal(id);
|
||||
dps = rds->GetDefPoints(di);
|
||||
|
||||
SetDefPoints(var, dps);
|
||||
}
|
||||
|
||||
IDPtr Reducer::GenLocal(const IDPtr& orig)
|
||||
{
|
||||
if ( Optimizing() )
|
||||
reporter->InternalError("Generating a new local while optimizing");
|
||||
|
||||
char buf[8192];
|
||||
int n = new_locals.size();
|
||||
snprintf(buf, sizeof buf, "%s.%d", orig->Name(), n);
|
||||
|
||||
IDPtr local_id = install_ID(buf, "<internal>", false, false);
|
||||
local_id->SetType(orig->GetType());
|
||||
local_id->SetAttrs(orig->GetAttrs());
|
||||
|
||||
new_locals.insert(local_id.get());
|
||||
orig_to_new_locals[orig.get()] = local_id;
|
||||
|
||||
return local_id;
|
||||
return dps;
|
||||
}
|
||||
|
||||
bool Reducer::IsNewLocal(const ID* id) const
|
||||
const DefPoints* Reducer::FindDefPoints(const NameExpr* var) const
|
||||
{
|
||||
ID* non_const_ID = (ID*) id; // I don't get why C++ requires this
|
||||
return new_locals.count(non_const_ID) != 0;
|
||||
}
|
||||
|
||||
TempVar* Reducer::FindTemporary(const ID* id) const
|
||||
{
|
||||
auto tmp = ids_to_temps.find(id);
|
||||
if ( tmp == ids_to_temps.end() )
|
||||
auto dps = var_usage_to_DPs.find(var);
|
||||
if ( dps == var_usage_to_DPs.end() )
|
||||
return nullptr;
|
||||
else
|
||||
return tmp->second;
|
||||
return dps->second;
|
||||
}
|
||||
|
||||
void Reducer::SetDefPoints(const NameExpr* var, const DefPoints* dps)
|
||||
{
|
||||
var_usage_to_DPs[var] = dps;
|
||||
}
|
||||
|
||||
bool Reducer::SameOp(const Expr* op1, const Expr* op2)
|
||||
{
|
||||
if ( op1 == op2 )
|
||||
return true;
|
||||
|
||||
if ( op1->Tag() != op2->Tag() )
|
||||
return false;
|
||||
|
||||
if ( op1->Tag() == EXPR_NAME )
|
||||
{
|
||||
// Needs to be both the same identifier and in contexts
|
||||
// where the identifier has the same definition points.
|
||||
auto op1_n = op1->AsNameExpr();
|
||||
auto op2_n = op2->AsNameExpr();
|
||||
|
||||
auto op1_id = op1_n->Id();
|
||||
auto op2_id = op2_n->Id();
|
||||
|
||||
if ( op1_id != op2_id )
|
||||
return false;
|
||||
|
||||
auto op1_dps = GetDefPoints(op1_n);
|
||||
auto op2_dps = GetDefPoints(op2_n);
|
||||
|
||||
return same_DPs(op1_dps, op2_dps);
|
||||
}
|
||||
|
||||
else if ( op1->Tag() == EXPR_CONST )
|
||||
{
|
||||
auto op1_c = op1->AsConstExpr();
|
||||
auto op2_c = op2->AsConstExpr();
|
||||
|
||||
auto op1_v = op1_c->Value();
|
||||
auto op2_v = op2_c->Value();
|
||||
|
||||
return SameVal(op1_v, op2_v);
|
||||
}
|
||||
|
||||
else if ( op1->Tag() == EXPR_LIST )
|
||||
{
|
||||
auto op1_l = op1->AsListExpr()->Exprs();
|
||||
auto op2_l = op2->AsListExpr()->Exprs();
|
||||
|
||||
if ( op1_l.length() != op2_l.length() )
|
||||
return false;
|
||||
|
||||
for ( auto i = 0; i < op1_l.length(); ++i )
|
||||
if ( ! SameExpr(op1_l[i], op2_l[i]) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
else
|
||||
reporter->InternalError("bad singleton tag");
|
||||
}
|
||||
|
||||
bool Reducer::SameExpr(const Expr* e1, const Expr* e2)
|
||||
{
|
||||
if ( e1 == e2 )
|
||||
return true;
|
||||
|
||||
if ( e1->Tag() != e2->Tag() )
|
||||
return false;
|
||||
|
||||
if ( ! same_type(e1->GetType(), e2->GetType()) )
|
||||
return false;
|
||||
|
||||
switch ( e1->Tag() ) {
|
||||
case EXPR_NAME:
|
||||
case EXPR_CONST:
|
||||
return SameOp(e1, e2);
|
||||
|
||||
case EXPR_CLONE:
|
||||
case EXPR_RECORD_CONSTRUCTOR:
|
||||
case EXPR_TABLE_CONSTRUCTOR:
|
||||
case EXPR_SET_CONSTRUCTOR:
|
||||
case EXPR_VECTOR_CONSTRUCTOR:
|
||||
case EXPR_EVENT:
|
||||
case EXPR_SCHEDULE:
|
||||
// These always generate a new value.
|
||||
return false;
|
||||
|
||||
case EXPR_INCR:
|
||||
case EXPR_DECR:
|
||||
case EXPR_AND_AND:
|
||||
case EXPR_OR_OR:
|
||||
case EXPR_ASSIGN:
|
||||
case EXPR_FIELD_ASSIGN:
|
||||
case EXPR_INDEX_SLICE_ASSIGN:
|
||||
// All of these should have been translated into something
|
||||
// else.
|
||||
reporter->InternalError("Unexpected tag in Reducer::SameExpr");
|
||||
|
||||
case EXPR_ANY_INDEX:
|
||||
{
|
||||
auto a1 = e1->AsAnyIndexExpr();
|
||||
auto a2 = e2->AsAnyIndexExpr();
|
||||
|
||||
if ( a1->Index() != a2->Index() )
|
||||
return false;
|
||||
|
||||
return SameOp(a1->GetOp1(), a2->GetOp1());
|
||||
}
|
||||
|
||||
case EXPR_FIELD:
|
||||
{
|
||||
auto f1 = e1->AsFieldExpr();
|
||||
auto f2 = e2->AsFieldExpr();
|
||||
|
||||
if ( f1->Field() != f2->Field() )
|
||||
return false;
|
||||
|
||||
return SameOp(f1->GetOp1(), f2->GetOp1());
|
||||
}
|
||||
|
||||
case EXPR_HAS_FIELD:
|
||||
{
|
||||
auto f1 = e1->AsHasFieldExpr();
|
||||
auto f2 = e2->AsHasFieldExpr();
|
||||
|
||||
if ( f1->Field() != f2->Field() )
|
||||
return false;
|
||||
|
||||
return SameOp(f1->GetOp1(), f2->GetOp1());
|
||||
}
|
||||
|
||||
case EXPR_LIST:
|
||||
{
|
||||
auto l1 = e1->AsListExpr()->Exprs();
|
||||
auto l2 = e2->AsListExpr()->Exprs();
|
||||
|
||||
ASSERT(l1.length() == l2.length());
|
||||
|
||||
for ( int i = 0; i < l1.length(); ++i )
|
||||
if ( ! SameExpr(l1[i], l2[i]) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case EXPR_CALL:
|
||||
{
|
||||
auto c1 = e1->AsCallExpr();
|
||||
auto c2 = e2->AsCallExpr();
|
||||
auto f1 = c1->Func();
|
||||
auto f2 = c2->Func();
|
||||
|
||||
if ( f1 != f2 )
|
||||
return false;
|
||||
|
||||
if ( ! f1->IsPure() )
|
||||
return false;
|
||||
|
||||
return SameExpr(c1->Args(), c2->Args());
|
||||
}
|
||||
|
||||
case EXPR_LAMBDA:
|
||||
return false;
|
||||
|
||||
case EXPR_IS:
|
||||
{
|
||||
if ( ! SameOp(e1->GetOp1(), e2->GetOp1()) )
|
||||
return false;
|
||||
|
||||
auto i1 = e1->AsIsExpr();
|
||||
auto i2 = e2->AsIsExpr();
|
||||
|
||||
return same_type(i1->TestType(), i2->TestType());
|
||||
}
|
||||
|
||||
default:
|
||||
if ( ! e1->GetOp1() )
|
||||
reporter->InternalError("Bad default in Reducer::SameExpr");
|
||||
|
||||
if ( ! SameOp(e1->GetOp1(), e2->GetOp1()) )
|
||||
return false;
|
||||
|
||||
if ( e1->GetOp2() && ! SameOp(e1->GetOp2(), e2->GetOp2()) )
|
||||
return false;
|
||||
|
||||
if ( e1->GetOp3() && ! SameOp(e1->GetOp3(), e2->GetOp3()) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
IDPtr Reducer::FindExprTmp(const Expr* rhs, const Expr* a,
|
||||
const std::shared_ptr<const TempVar>& lhs_tmp)
|
||||
{
|
||||
for ( const auto& et_i : expr_temps )
|
||||
{
|
||||
if ( et_i->Alias() || ! et_i->IsActive() || et_i == lhs_tmp )
|
||||
// This can happen due to re-reduction while
|
||||
// optimizing.
|
||||
continue;
|
||||
|
||||
auto et_i_expr = et_i->RHS();
|
||||
|
||||
if ( SameExpr(rhs, et_i_expr) )
|
||||
{
|
||||
// We have an apt candidate. Make sure its value
|
||||
// always makes it here.
|
||||
auto id = et_i->Id().get();
|
||||
|
||||
// We use 'a' in the following rather than rhs
|
||||
// because the RHS can get rewritten (for example,
|
||||
// due to folding) after we generate RDs, and
|
||||
// thus might not have any.
|
||||
if ( ! mgr->HasSinglePreMinRD(a, id) )
|
||||
// The temporary's value isn't guaranteed
|
||||
// to make it here.
|
||||
continue;
|
||||
|
||||
// Make sure there aren't ambiguities due to
|
||||
// possible modifications to aggregates.
|
||||
if ( ! ExprValid(id, et_i_expr, a) )
|
||||
continue;
|
||||
|
||||
return et_i->Id();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Reducer::ExprValid(const ID* id, const Expr* e1, const Expr* e2) const
|
||||
{
|
||||
// Here are the considerations for expression validity.
|
||||
//
|
||||
// * None of the operands used in the given expression can
|
||||
// have been assigned.
|
||||
//
|
||||
// * If the expression yields an aggregate, or one of the
|
||||
// operands in the expression is an aggregate, then there
|
||||
// must not be any assignments to aggregates of the same
|
||||
// type(s). This is to deal with possible aliases.
|
||||
//
|
||||
// * Same goes to modifications of aggregates via "add" or "delete".
|
||||
//
|
||||
// * No propagation of expressions that are based on aggregates
|
||||
// across function calls.
|
||||
//
|
||||
// * No propagation of expressions that are based on globals
|
||||
// across calls.
|
||||
|
||||
// Tracks which ID's are germane for our analysis.
|
||||
std::vector<const ID*> ids;
|
||||
|
||||
ids.push_back(id);
|
||||
|
||||
// Identify variables involved in the expression.
|
||||
CheckIDs(e1->GetOp1().get(), ids);
|
||||
CheckIDs(e1->GetOp2().get(), ids);
|
||||
CheckIDs(e1->GetOp3().get(), ids);
|
||||
|
||||
if ( e1->Tag() == EXPR_NAME )
|
||||
ids.push_back(e1->AsNameExpr()->Id());
|
||||
|
||||
CSE_ValidityChecker vc(ids, e1, e2);
|
||||
reduction_root->Traverse(&vc);
|
||||
|
||||
return vc.IsValid();
|
||||
}
|
||||
|
||||
void Reducer::CheckIDs(const Expr* e, std::vector<const ID*>& ids) const
|
||||
{
|
||||
if ( ! e )
|
||||
return;
|
||||
|
||||
if ( e->Tag() == EXPR_LIST )
|
||||
{
|
||||
const auto& e_l = e->AsListExpr()->Exprs();
|
||||
for ( auto i = 0; i < e_l.length(); ++i )
|
||||
CheckIDs(e_l[i], ids);
|
||||
}
|
||||
|
||||
else if ( e->Tag() == EXPR_NAME )
|
||||
ids.push_back(e->AsNameExpr()->Id());
|
||||
}
|
||||
|
||||
bool Reducer::IsCSE(const AssignExpr* a, const NameExpr* lhs, const Expr* rhs)
|
||||
{
|
||||
auto a_max_rds = mgr->GetPostMaxRDs(GetRDLookupObj(a));
|
||||
|
||||
auto lhs_id = lhs->Id();
|
||||
auto lhs_tmp = FindTemporary(lhs_id); // nil if LHS not a temporary
|
||||
auto rhs_tmp = FindExprTmp(rhs, a, lhs_tmp);
|
||||
|
||||
ExprPtr new_rhs;
|
||||
if ( rhs_tmp )
|
||||
{ // We already have a temporary
|
||||
auto tmp_di = mgr->GetConstID_DI(rhs_tmp.get());
|
||||
auto dps = a_max_rds->GetDefPoints(tmp_di);
|
||||
new_rhs = NewVarUsage(rhs_tmp, dps, rhs);
|
||||
rhs = new_rhs.get();
|
||||
}
|
||||
|
||||
if ( lhs_tmp )
|
||||
{
|
||||
if ( rhs->Tag() == EXPR_CONST )
|
||||
{ // mark temporary as just being a constant
|
||||
lhs_tmp->SetConst(rhs->AsConstExpr());
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( rhs->Tag() == EXPR_NAME )
|
||||
{
|
||||
auto rhs_id = rhs->AsNameExpr()->IdPtr();
|
||||
auto rhs_tmp_var = FindTemporary(rhs_id.get());
|
||||
|
||||
if ( rhs_tmp_var && rhs_tmp_var->Const() )
|
||||
{ // temporary can be replaced with constant
|
||||
lhs_tmp->SetConst(rhs_tmp_var->Const());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Treat the LHS as either an alias for the RHS,
|
||||
// or as a constant if the RHS is a constant in
|
||||
// this context.
|
||||
auto rhs_di = mgr->GetConstID_DI(rhs_id.get());
|
||||
auto dps = a_max_rds->GetDefPoints(rhs_di);
|
||||
|
||||
auto rhs_const = CheckForConst(rhs_id, dps);
|
||||
if ( rhs_const )
|
||||
lhs_tmp->SetConst(rhs_const);
|
||||
else
|
||||
lhs_tmp->SetAlias(rhs_id, dps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Track where we define the temporary.
|
||||
auto lhs_di = mgr->GetConstID_DI(lhs_id);
|
||||
auto dps = a_max_rds->GetDefPoints(lhs_di);
|
||||
if ( lhs_tmp->DPs() && ! same_DPs(lhs_tmp->DPs(), dps) )
|
||||
reporter->InternalError("double DPs for temporary");
|
||||
|
||||
lhs_tmp->SetDPs(dps);
|
||||
SetDefPoints(lhs, dps);
|
||||
|
||||
expr_temps.emplace_back(lhs_tmp);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const ConstExpr* Reducer::CheckForConst(const IDPtr& id,
|
||||
const DefPoints* dps) const
|
||||
{
|
||||
if ( ! dps || dps->length() == 0 )
|
||||
// This can happen for access to uninitialized values.
|
||||
return nullptr;
|
||||
|
||||
if ( dps->length() != 1 )
|
||||
// Multiple definitions of the variable reach to this
|
||||
// location. In theory we could check whether they *all*
|
||||
// provide the same constant, but that hardly seems likely.
|
||||
return nullptr;
|
||||
|
||||
// Identifier has a unique definition.
|
||||
auto dp = (*dps)[0];
|
||||
const Expr* e = nullptr;
|
||||
|
||||
if ( dp.Tag() == STMT_DEF )
|
||||
{
|
||||
auto s = dp.StmtVal();
|
||||
|
||||
if ( s->Tag() == STMT_CATCH_RETURN )
|
||||
{
|
||||
// Change 's' to refer to the associated assignment
|
||||
// statement, if any.
|
||||
auto cr = s->AsCatchReturnStmt();
|
||||
s = cr->AssignStmt().get();
|
||||
|
||||
if ( ! s )
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( s->Tag() != STMT_EXPR )
|
||||
// Defined in a statement other than an assignment.
|
||||
return nullptr;
|
||||
|
||||
e = s->AsExprStmt()->StmtExpr();
|
||||
}
|
||||
|
||||
else if ( dp.Tag() == EXPR_DEF )
|
||||
e = dp.ExprVal();
|
||||
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
if ( e->Tag() != EXPR_ASSIGN )
|
||||
// Not sure why this would happen, other than EXPR_APPEND_TO,
|
||||
// but in any case not an expression we can mine for a
|
||||
// constant.
|
||||
return nullptr;
|
||||
|
||||
auto rhs = e->GetOp2();
|
||||
|
||||
if ( rhs->Tag() != EXPR_CONST )
|
||||
return nullptr;
|
||||
|
||||
return rhs->AsConstExpr();
|
||||
}
|
||||
|
||||
void Reducer::TrackExprReplacement(const Expr* orig, const Expr* e)
|
||||
{
|
||||
new_expr_to_orig[e] = orig;
|
||||
}
|
||||
|
||||
const Obj* Reducer::GetRDLookupObj(const Expr* e) const
|
||||
{
|
||||
auto orig_e = new_expr_to_orig.find(e);
|
||||
if ( orig_e == new_expr_to_orig.end() )
|
||||
return e;
|
||||
else
|
||||
return orig_e->second;
|
||||
}
|
||||
|
||||
ExprPtr Reducer::OptExpr(Expr* e)
|
||||
{
|
||||
StmtPtr opt_stmts;
|
||||
auto opt_e = e->Reduce(this, opt_stmts);
|
||||
|
||||
if ( opt_stmts )
|
||||
reporter->InternalError("Generating new statements while optimizing");
|
||||
|
||||
if ( opt_e->Tag() == EXPR_NAME )
|
||||
return UpdateExpr(opt_e);
|
||||
|
||||
return opt_e;
|
||||
}
|
||||
|
||||
ExprPtr Reducer::UpdateExpr(ExprPtr e)
|
||||
{
|
||||
if ( e->Tag() != EXPR_NAME )
|
||||
return OptExpr(e);
|
||||
|
||||
auto n = e->AsNameExpr();
|
||||
auto id = n->Id();
|
||||
|
||||
if ( id->IsGlobal() )
|
||||
return e;
|
||||
|
||||
auto tmp_var = FindTemporary(id);
|
||||
if ( ! tmp_var )
|
||||
{
|
||||
auto max_rds = mgr->GetPreMaxRDs(GetRDLookupObj(n));
|
||||
|
||||
IDPtr id_ptr = {NewRef{}, id};
|
||||
auto di = mgr->GetConstID_DI(id);
|
||||
auto dps = max_rds->GetDefPoints(di);
|
||||
|
||||
auto is_const = CheckForConst(id_ptr, dps);
|
||||
if ( is_const )
|
||||
{
|
||||
// Remember this variable as one whose value
|
||||
// we used for constant propagation. That
|
||||
// ensures we can subsequently not complain
|
||||
// about it being assigned but not used (though
|
||||
// we can still omit the assignment).
|
||||
constant_vars.insert(id);
|
||||
return make_intrusive<ConstExpr>(is_const->ValuePtr());
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
if ( tmp_var->Const() )
|
||||
return make_intrusive<ConstExpr>(tmp_var->Const()->ValuePtr());
|
||||
|
||||
auto alias = tmp_var->Alias();
|
||||
if ( alias )
|
||||
{
|
||||
// Make sure that the definition points for the
|
||||
// alias here are the same as when the alias
|
||||
// was created.
|
||||
auto alias_tmp = FindTemporary(alias.get());
|
||||
|
||||
if ( alias_tmp )
|
||||
{
|
||||
while ( alias_tmp->Alias() )
|
||||
{
|
||||
// Alias chains can occur due to
|
||||
// re-reduction while optimizing.
|
||||
auto a_id = alias_tmp->Id();
|
||||
if ( a_id == id )
|
||||
return e;
|
||||
|
||||
alias_tmp = FindTemporary(alias_tmp->Id().get());
|
||||
}
|
||||
|
||||
// Temporaries always have only one definition point,
|
||||
// so no need to check for consistency.
|
||||
auto new_usage = NewVarUsage(alias, alias_tmp->DPs(), e.get());
|
||||
return new_usage;
|
||||
}
|
||||
|
||||
auto e_max_rds = mgr->GetPreMaxRDs(GetRDLookupObj(e.get()));
|
||||
auto alias_di = mgr->GetConstID_DI(alias.get());
|
||||
auto alias_dps = e_max_rds->GetDefPoints(alias_di);
|
||||
|
||||
if ( same_DPs(alias_dps, tmp_var->DPs()) )
|
||||
return NewVarUsage(alias, alias_dps, e.get());
|
||||
else
|
||||
{
|
||||
printf("DPs differ: %s\n", obj_desc(e.get()).c_str());
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
auto rhs = tmp_var->RHS();
|
||||
if ( rhs->Tag() != EXPR_CONST )
|
||||
return e;
|
||||
|
||||
auto c = rhs->AsConstExpr();
|
||||
return make_intrusive<ConstExpr>(c->ValuePtr());
|
||||
}
|
||||
|
||||
StmtPtr Reducer::MergeStmts(const NameExpr* lhs, ExprPtr rhs, Stmt* succ_stmt)
|
||||
|
@ -247,9 +751,71 @@ StmtPtr Reducer::MergeStmts(const NameExpr* lhs, ExprPtr rhs, Stmt* succ_stmt)
|
|||
return make_intrusive<ExprStmt>(merge_e);
|
||||
}
|
||||
|
||||
void Reducer::TrackExprReplacement(const Expr* orig, const Expr* e)
|
||||
IDPtr Reducer::GenTemporary(const TypePtr& t, ExprPtr rhs)
|
||||
{
|
||||
new_expr_to_orig[e] = orig;
|
||||
if ( Optimizing() )
|
||||
reporter->InternalError("Generating a new temporary while optimizing");
|
||||
|
||||
if ( omitted_stmts.size() > 0 )
|
||||
reporter->InternalError("Generating a new temporary while pruning statements");
|
||||
|
||||
auto temp = std::make_shared<TempVar>(temps.size(), t, rhs);
|
||||
IDPtr temp_id = install_ID(temp->Name(), "<internal>", false, false);
|
||||
|
||||
temp->SetID(temp_id);
|
||||
temp_id->SetType(t);
|
||||
|
||||
temps.push_back(temp);
|
||||
ids_to_temps[temp_id.get()] = temp;
|
||||
|
||||
return temp_id;
|
||||
}
|
||||
|
||||
IDPtr Reducer::FindNewLocal(const IDPtr& id)
|
||||
{
|
||||
auto mapping = orig_to_new_locals.find(id.get());
|
||||
|
||||
if ( mapping != orig_to_new_locals.end() )
|
||||
return mapping->second;
|
||||
|
||||
return GenLocal(id);
|
||||
}
|
||||
|
||||
IDPtr Reducer::GenLocal(const IDPtr& orig)
|
||||
{
|
||||
if ( Optimizing() )
|
||||
reporter->InternalError("Generating a new local while optimizing");
|
||||
|
||||
if ( omitted_stmts.size() > 0 )
|
||||
reporter->InternalError("Generating a new local while pruning statements");
|
||||
|
||||
char buf[8192];
|
||||
int n = new_locals.size();
|
||||
snprintf(buf, sizeof buf, "%s.%d", orig->Name(), n);
|
||||
|
||||
IDPtr local_id = install_ID(buf, "<internal>", false, false);
|
||||
local_id->SetType(orig->GetType());
|
||||
local_id->SetAttrs(orig->GetAttrs());
|
||||
|
||||
new_locals.insert(local_id.get());
|
||||
orig_to_new_locals[orig.get()] = local_id;
|
||||
|
||||
return local_id;
|
||||
}
|
||||
|
||||
bool Reducer::IsNewLocal(const ID* id) const
|
||||
{
|
||||
ID* non_const_ID = (ID*) id; // I don't get why C++ requires this
|
||||
return new_locals.count(non_const_ID) != 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<TempVar> Reducer::FindTemporary(const ID* id) const
|
||||
{
|
||||
auto tmp = ids_to_temps.find(id);
|
||||
if ( tmp == ids_to_temps.end() )
|
||||
return nullptr;
|
||||
else
|
||||
return tmp->second;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ public:
|
|||
|
||||
StmtPtr Reduce(StmtPtr s)
|
||||
{
|
||||
reduction_root = s;
|
||||
return s->Reduce(this);
|
||||
}
|
||||
|
||||
|
@ -63,7 +64,7 @@ public:
|
|||
void PushBifurcation() { ++bifurcation_level; }
|
||||
void PopBifurcation() { --bifurcation_level; }
|
||||
|
||||
int NumTemps() const { return temps.length(); }
|
||||
int NumTemps() const { return temps.size(); }
|
||||
|
||||
// True if this name already reflects the replacement.
|
||||
bool IsNewLocal(const NameExpr* n) const
|
||||
|
@ -118,45 +119,94 @@ public:
|
|||
replaced_stmts.clear();
|
||||
}
|
||||
|
||||
// NOT YET IMPLEMENTED, SO CURRENTLY A STUB:
|
||||
// Given the LHS and RHS of an assignment, returns true
|
||||
// if the RHS is a common subexpression (meaning that the
|
||||
// current assignment statement should be deleted). In
|
||||
// that case, has the side effect of associating an alias
|
||||
// for the LHS with the temporary holding the equivalent RHS.
|
||||
// for the LHS with the temporary variable that holds the
|
||||
// equivalent RHS.
|
||||
//
|
||||
// Assumes reduction (including alias propagation) has
|
||||
// already been applied.
|
||||
bool IsCSE(const AssignExpr* a, const NameExpr* lhs, const Expr* rhs)
|
||||
{ return false; }
|
||||
bool IsCSE(const AssignExpr* a, const NameExpr* lhs, const Expr* rhs);
|
||||
|
||||
// Given an lhs=rhs statement followed by succ_stmt, returns
|
||||
// a (new) merge of the two if they're of the form tmp=rhs, var=tmp;
|
||||
// otherwise, nil.
|
||||
StmtPtr MergeStmts(const NameExpr* lhs, ExprPtr rhs, Stmt* succ_stmt);
|
||||
|
||||
// The following two methods will, in the future, update expressions
|
||||
// with optimized versions. They are distinct because the first
|
||||
// one (meant for calls in a Stmt reduction context) will also Reduce
|
||||
// the expression, whereas the second one (meant for calls in an Expr
|
||||
// context) does not, to avoid circularity.
|
||||
//
|
||||
// For now, they are stubs.
|
||||
//
|
||||
// These two are used for use in optimizing expressions that appear in
|
||||
// a Stmt context.
|
||||
ExprPtr OptExpr(Expr* e) { return {NewRef{}, e}; }
|
||||
ExprPtr OptExpr(ExprPtr e) { return e; }
|
||||
// This one for expressions appearing in an Expr context.
|
||||
ExprPtr UpdateExpr(ExprPtr e) { return e; }
|
||||
// Update expressions with optimized versions. They are distinct
|
||||
// because the first two (meant for calls in a Stmt reduction
|
||||
// context) will also Reduce the expression, whereas the last
|
||||
// one (meant for calls in an Expr context) does not, to avoid
|
||||
// circularity.
|
||||
ExprPtr OptExpr(Expr* e);
|
||||
ExprPtr OptExpr(ExprPtr e)
|
||||
{ return OptExpr(e.get()); }
|
||||
|
||||
const Scope* FuncScope() const { return scope; }
|
||||
// This one for expressions appearing in an Expr context.
|
||||
ExprPtr UpdateExpr(ExprPtr e);
|
||||
|
||||
protected:
|
||||
// True if two Val's refer to the same underlying value. We gauge
|
||||
// this conservatively (i.e., for complicated values we just return
|
||||
// false, even if with a lot of work we could establish that they
|
||||
// are in fact equivalent.)
|
||||
bool SameVal(const Val* v1, const Val* v2) const;
|
||||
|
||||
// Track that the variable "var", which has the given set of
|
||||
// definition points, will be a replacement for the "orig"
|
||||
// expression. Returns the replacement expression (which is
|
||||
// is just a NameExpr referring to "var").
|
||||
ExprPtr NewVarUsage(IDPtr var, const DefPoints* dps, const Expr* orig);
|
||||
|
||||
// Returns the definition points associated with "var". If none
|
||||
// exist in our cache, then populates the cache.
|
||||
const DefPoints* GetDefPoints(const NameExpr* var);
|
||||
|
||||
// Retrieve the definition points associated in our cache with the
|
||||
// given variable, if any.
|
||||
const DefPoints* FindDefPoints(const NameExpr* var) const;
|
||||
|
||||
// Adds a mapping in our cache of the given variable to the given
|
||||
// set of definition points.
|
||||
void SetDefPoints(const NameExpr* var, const DefPoints* dps);
|
||||
|
||||
// Returns true if op1 and op2 represent the same operand, given
|
||||
// the reaching definitions available at their usages (e1 and e2).
|
||||
bool SameOp(const Expr* op1, const Expr* op2);
|
||||
bool SameOp(const ExprPtr& op1, const ExprPtr& op2)
|
||||
{ return SameOp(op1.get(), op2.get()); }
|
||||
|
||||
// True if e1 and e2 reflect identical expressions in the context
|
||||
// of using a value computed for one of them in lieu of computing
|
||||
// the other. (Thus, for example, two record construction expressions
|
||||
// are never equivalent even if they both specify exactly the same
|
||||
// record elements, because each invocation of the expression produces
|
||||
// a distinct value.)
|
||||
bool SameExpr(const Expr* e1, const Expr* e2);
|
||||
|
||||
// Finds a temporary, if any, whose RHS matches the given "rhs", using
|
||||
// the reaching defs associated with the assignment "a". The context
|
||||
// is that "rhs" is currently being assigned to temporary "lhs_tmp"
|
||||
// (nil if the assignment isn't to a temporary), and we're wondering
|
||||
// whether we can skip that assignment because we already have the
|
||||
// exact same value available in a previously assigned temporary.
|
||||
IDPtr FindExprTmp(const Expr* rhs, const Expr* a,
|
||||
const std::shared_ptr<const TempVar>& lhs_tmp);
|
||||
|
||||
// Tests whether an expression computed at e1 (and assigned to "id")
|
||||
// remains valid for substitution at e2.
|
||||
bool ExprValid(const ID* id, const Expr* e1, const Expr* e2) const;
|
||||
|
||||
// Inspects the given expression for identifiers, adding any
|
||||
// observed to the given vector. Assumes reduced form, so only
|
||||
// NameExpr's and ListExpr's are of interest - does not traverse
|
||||
// into compound expressions.
|
||||
void CheckIDs(const Expr* e, std::vector<const ID*>& ids) const;
|
||||
|
||||
IDPtr GenTemporary(const TypePtr& t, ExprPtr rhs);
|
||||
TempVar* FindTemporary(const ID* id) const;
|
||||
std::shared_ptr<TempVar> FindTemporary(const ID* id) const;
|
||||
|
||||
// Retrieve the identifier corresponding to the new local for
|
||||
// the given expression. Creates the local if necessary.
|
||||
|
@ -170,27 +220,48 @@ protected:
|
|||
// for the current function.
|
||||
IDPtr GenLocal(const IDPtr& orig);
|
||||
|
||||
// This is the heart of constant propagation. Given an identifier
|
||||
// and a set of definition points for it, if its value is constant
|
||||
// then returns the corresponding ConstExpr with the value.
|
||||
const ConstExpr* CheckForConst(const IDPtr& id,
|
||||
const DefPoints* dps) const;
|
||||
|
||||
// Track that we're replacing instances of "orig" with a new
|
||||
// expression. This allows us to locate the RDs associated
|
||||
// with "orig" in the context of the new expression, without
|
||||
// requiring an additional RD propagation pass.
|
||||
void TrackExprReplacement(const Expr* orig, const Expr* e);
|
||||
|
||||
Scope* scope;
|
||||
PList<TempVar> temps;
|
||||
// Returns the object we should use to look up RD's associated
|
||||
// with 'e'. (This isn't necessarily 'e' itself because we
|
||||
// may have decided to replace it with a different expression,
|
||||
// per TrackExprReplacement().)
|
||||
const Obj* GetRDLookupObj(const Expr* e) const;
|
||||
|
||||
// Tracks the temporary variables created during the reduction/
|
||||
// optimization process.
|
||||
std::vector<std::shared_ptr<TempVar>> temps;
|
||||
|
||||
// Temps for which we've processed their associated expression
|
||||
// (and they didn't wind up being aliases).
|
||||
PList<TempVar> expr_temps;
|
||||
std::vector<std::shared_ptr<const TempVar>> expr_temps;
|
||||
|
||||
// Let's us go from an identifier to an associated temporary
|
||||
// Lets us go from an identifier to an associated temporary
|
||||
// variable, if it corresponds to one.
|
||||
std::unordered_map<const ID*, TempVar*> ids_to_temps;
|
||||
std::unordered_map<const ID*, std::shared_ptr<TempVar>> ids_to_temps;
|
||||
|
||||
// Local variables created during reduction/optimization.
|
||||
std::unordered_set<ID*> new_locals;
|
||||
|
||||
// Mapping of original identifiers to new locals. Used to
|
||||
// rename local variables when inlining.
|
||||
std::unordered_map<const ID*, IDPtr> orig_to_new_locals;
|
||||
|
||||
// Which statements to elide from the AST (because optimization
|
||||
// has determined they're no longer needed).
|
||||
std::unordered_set<const Stmt*> omitted_stmts;
|
||||
|
||||
// Maps statements to replacements constructed during optimization.
|
||||
std::unordered_map<const Stmt*, StmtPtr> replaced_stmts;
|
||||
|
||||
// Tracks whether we're inside an inline block, and if so then
|
||||
|
@ -203,6 +274,12 @@ protected:
|
|||
// exponentially.
|
||||
int bifurcation_level = 0;
|
||||
|
||||
// For a given usage of a variable's value, return the definition
|
||||
// points associated with its use at that point. We use this
|
||||
// both as a cache (populating it every time we do a more laborious
|
||||
// lookup), and proactively when creating new references to variables.
|
||||
std::unordered_map<const NameExpr*, const DefPoints*> var_usage_to_DPs;
|
||||
|
||||
// Tracks which (non-temporary) variables had constant
|
||||
// values used for constant propagation.
|
||||
std::unordered_set<const ID*> constant_vars;
|
||||
|
@ -212,6 +289,9 @@ protected:
|
|||
// with the usage.
|
||||
std::unordered_map<const Expr*, const Expr*> new_expr_to_orig;
|
||||
|
||||
// Statement at which the current reduction started.
|
||||
StmtPtr reduction_root = nullptr;
|
||||
|
||||
const DefSetsMgr* mgr = nullptr;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue