From 86d3c932b7b925eaa2385e77432c4923a8d7f3ea Mon Sep 17 00:00:00 2001 From: Vern Paxson Date: Mon, 8 Apr 2024 18:20:05 -0400 Subject: [PATCH] make same_expr() functionality accessible as a utility --- src/script_opt/Reduce.cc | 395 +++++++++++++++++++++------------------ src/script_opt/Reduce.h | 27 +-- 2 files changed, 215 insertions(+), 207 deletions(-) diff --git a/src/script_opt/Reduce.cc b/src/script_opt/Reduce.cc index 648a6393da..14a9198f26 100644 --- a/src/script_opt/Reduce.cc +++ b/src/script_opt/Reduce.cc @@ -3,12 +3,218 @@ #include "zeek/script_opt/Reduce.h" #include "zeek/script_opt/CSE.h" -#include "zeek/script_opt/ExprOptInfo.h" +#include "zeek/script_opt/Expr.h" #include "zeek/script_opt/StmtOptInfo.h" #include "zeek/script_opt/TempVar.h" namespace zeek::detail { +// 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.) + +static bool same_val(const Val* v1, const Val* v2) { + if ( is_atomic_val(v1) ) + return same_atomic_val(v1, v2); + else + return v1 == v2; +} + +static bool same_expr(const Expr* e1, const Expr* e2, bool check_defs); + +// Returns true if op1 and op2 represent the same operand. If check_defs +// is true then this factors in the reaching definitions available at +// their usages. + +static bool same_op(const Expr* op1, const Expr* op2, bool check_defs) { + 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 definitions. + 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; + + if ( ! check_defs ) + return true; + + auto e_stmt_1 = op1->GetOptInfo()->stmt_num; + auto e_stmt_2 = op2->GetOptInfo()->stmt_num; + + auto def_1 = op1_id->GetOptInfo()->DefinitionBefore(e_stmt_1); + auto def_2 = op2_id->GetOptInfo()->DefinitionBefore(e_stmt_2); + + return def_1 == def_2 && def_1 != NO_DEF; + } + + 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 same_val(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 ( ! same_expr(op1_l[i], op2_l[i], check_defs) ) + return false; + + return true; + } + + reporter->InternalError("bad singleton tag"); + return false; +} + +static bool same_op(const ExprPtr& op1, const ExprPtr& op2, bool check_defs) { + return same_op(op1.get(), op2.get(), check_defs); +} + +static bool same_expr(const Expr* e1, const Expr* e2, bool check_defs) { + 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 same_op(e1, e2, check_defs); + + case EXPR_REF: return same_expr(e1->GetOp1(), e2->GetOp1()); + + 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::same_expr"); + + case EXPR_ANY_INDEX: { + auto a1 = static_cast(e1); + auto a2 = static_cast(e2); + + if ( a1->Index() != a2->Index() ) + return false; + + return same_op(a1->GetOp1(), a2->GetOp1(), check_defs); + } + + case EXPR_FIELD: { + auto f1 = e1->AsFieldExpr(); + auto f2 = e2->AsFieldExpr(); + + if ( f1->Field() != f2->Field() ) + return false; + + return same_op(f1->GetOp1(), f2->GetOp1(), check_defs); + } + + case EXPR_HAS_FIELD: { + auto f1 = e1->AsHasFieldExpr(); + auto f2 = e2->AsHasFieldExpr(); + + if ( f1->Field() != f2->Field() ) + return false; + + return same_op(f1->GetOp1(), f2->GetOp1(), check_defs); + } + + 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 ( ! same_expr(l1[i], l2[i], check_defs) ) + 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 same_expr(c1->Args(), c2->Args(), check_defs); + } + + case EXPR_LAMBDA: return false; + + case EXPR_IS: { + if ( ! same_op(e1->GetOp1(), e2->GetOp1(), check_defs) ) + 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::same_expr"); + + if ( ! same_op(e1->GetOp1(), e2->GetOp1(), check_defs) ) + return false; + + if ( e1->GetOp2() && ! same_op(e1->GetOp2(), e2->GetOp2(), check_defs) ) + return false; + + if ( e1->GetOp3() && ! same_op(e1->GetOp3(), e2->GetOp3(), check_defs) ) + return false; + + return true; + } +} + +bool same_expr(const ExprPtr& e1, const ExprPtr& e2) { return same_expr(e1.get(), e2.get(), false); } + Reducer::Reducer(const ScriptFuncPtr& func, std::shared_ptr _pf, std::shared_ptr _pfs) : pf(std::move(_pf)), pfs(std::move(_pfs)) { auto& ft = func->GetType(); @@ -191,13 +397,6 @@ void Reducer::PopInlineBlock() { block_locals.pop_back(); } -bool Reducer::SameVal(const Val* v1, const Val* v2) const { - if ( is_atomic_val(v1) ) - return same_atomic_val(v1, v2); - else - return v1 == v2; -} - ExprPtr Reducer::NewVarUsage(IDPtr var, const Expr* orig) { auto var_usage = make_intrusive(var); BindExprToCurrStmt(var_usage); @@ -215,184 +414,6 @@ void Reducer::BindStmtToCurrStmt(const StmtPtr& s) { s->SetLocationInfo(curr_stmt->GetLocationInfo()); } -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 definitions. - 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 e_stmt_1 = op1->GetOptInfo()->stmt_num; - auto e_stmt_2 = op2->GetOptInfo()->stmt_num; - - auto def_1 = op1_id->GetOptInfo()->DefinitionBefore(e_stmt_1); - auto def_2 = op2_id->GetOptInfo()->DefinitionBefore(e_stmt_2); - - return def_1 == def_2 && def_1 != NO_DEF; - } - - 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; - } - - reporter->InternalError("bad singleton tag"); - return false; -} - -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& lhs_tmp) { for ( const auto& et_i : expr_temps ) { if ( et_i->Alias() || ! et_i->IsActive() || et_i == lhs_tmp ) @@ -402,7 +423,7 @@ IDPtr Reducer::FindExprTmp(const Expr* rhs, const Expr* a, const std::shared_ptr auto et_i_expr = et_i->RHS(); - if ( SameExpr(rhs, et_i_expr) ) { + if ( same_expr(rhs, et_i_expr, true) ) { // We have an apt candidate. Make sure its value // always makes it here. auto id = et_i->Id().get(); diff --git a/src/script_opt/Reduce.h b/src/script_opt/Reduce.h index 2bb1036445..a0c59c532a 100644 --- a/src/script_opt/Reduce.h +++ b/src/script_opt/Reduce.h @@ -7,7 +7,6 @@ namespace zeek::detail { -class Expr; class TempVar; class Reducer { @@ -162,12 +161,6 @@ public: 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" will be a replacement for // the "orig" expression. Returns the replacement expression // (which is is just a NameExpr referring to "var"). @@ -176,19 +169,6 @@ protected: void BindExprToCurrStmt(const ExprPtr& e); void BindStmtToCurrStmt(const StmtPtr& s); - // 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" @@ -318,4 +298,11 @@ extern bool checking_reduction; // Used to report a non-reduced expression. extern bool NonReduced(const Expr* perp); +// True if e1 and e2 reflect identical expressions, meaning that it's okay +// to use 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.) +extern bool same_expr(const ExprPtr& e1, const ExprPtr& e2); + } // namespace zeek::detail