From 10e80dfcd35630a68fca684f38686a09a03b5533 Mon Sep 17 00:00:00 2001 From: Vern Paxson Date: Sun, 10 Jan 2021 14:04:01 -0800 Subject: [PATCH] reductions of expressions in ASTs - code compiles but doesn't yet link --- src/Expr.cc | 294 +++-- src/Expr.h | 508 +++++++- src/Stmt.h | 76 ++ src/StmtBase.h | 42 +- src/script_opt/Expr.cc | 2314 ++++++++++++++++++++++++++++++++++++- src/script_opt/Reduce.h | 186 +++ src/script_opt/TempVar.cc | 4 +- src/script_opt/TempVar.h | 4 +- 8 files changed, 3264 insertions(+), 164 deletions(-) create mode 100644 src/script_opt/Reduce.h diff --git a/src/Expr.cc b/src/Expr.cc index c9cbd10e34..18638f29ad 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -41,6 +41,10 @@ const char* expr_name(BroExprTag t) "coerce", "record_coerce", "table_coerce", "vector_coerce", "sizeof", "cast", "is", "[:]=", "inline()", + "[]=", "$=", + "vec+=", + "to_any_coerce", "from_any_coerce", + "any[]", "nop", }; @@ -95,12 +99,24 @@ NameExpr* Expr::AsNameExpr() return (NameExpr*) this; } +NameExprPtr Expr::AsNameExprPtr() + { + CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name) + return {NewRef{}, (NameExpr*) this}; + } + const ConstExpr* Expr::AsConstExpr() const { CHECK_TAG(tag, EXPR_CONST, "ExprVal::AsConstExpr", expr_name) return (const ConstExpr*) this; } +ConstExprPtr Expr::AsConstExprPtr() + { + CHECK_TAG(tag, EXPR_CONST, "ExprVal::AsConstExpr", expr_name) + return {NewRef{}, (ConstExpr*) this}; + } + const CallExpr* Expr::AsCallExpr() const { CHECK_TAG(tag, EXPR_CALL, "ExprVal::AsCallExpr", expr_name) @@ -143,6 +159,12 @@ EventExprPtr Expr::AsEventExprPtr() return {NewRef{}, (EventExpr*) this}; } +RefExprPtr Expr::AsRefExprPtr() + { + CHECK_TAG(tag, EXPR_REF, "ExprVal::AsRefExpr", expr_name) + return {NewRef{}, (RefExpr*) this}; + } + bool Expr::CanAdd() const { return false; @@ -182,6 +204,132 @@ void Expr::Assign(Frame* /* f */, ValPtr /* v */) Internal("Expr::Assign called"); } +void Expr::AssignToIndex(ValPtr v1, ValPtr v2, ValPtr v3) const + { + bool iterators_invalidated; + + auto error_msg = assign_to_index(v1, v2, v3, iterators_invalidated); + + if ( iterators_invalidated ) + { + ODesc d; + Describe(&d); + reporter->PushLocation(GetLocationInfo()); + reporter->Warning("possible loop/iterator invalidation caused by expression: %s", d.Description()); + reporter->PopLocation(); + } + + if ( error_msg ) + RuntimeErrorWithCallStack(error_msg); + } + +static int get_slice_index(int idx, int len) + { + if ( abs(idx) > len ) + idx = idx > 0 ? len : 0; // Clamp maximum positive/negative indices. + else if ( idx < 0 ) + idx += len; // Map to a positive index. + + return idx; + } + +const char* assign_to_index(ValPtr v1, ValPtr v2, ValPtr v3, + bool& iterators_invalidated) + { + iterators_invalidated = false; + + if ( ! v1 || ! v2 || ! v3 ) + return nullptr; + + // Hold an extra reference to 'arg_v' in case the ownership transfer + // to the table/vector goes wrong and we still want to obtain + // diagnostic info from the original value after the assignment + // already unref'd. + auto v_extra = v3; + + switch ( v1->GetType()->Tag() ) { + case TYPE_VECTOR: + { + const ListVal* lv = v2->AsListVal(); + VectorVal* v1_vect = v1->AsVectorVal(); + + if ( lv->Length() > 1 ) + { + auto len = v1_vect->Size(); + bro_int_t first = get_slice_index(lv->Idx(0)->CoerceToInt(), len); + bro_int_t last = get_slice_index(lv->Idx(1)->CoerceToInt(), len); + + // Remove the elements from the vector within the slice. + for ( auto idx = first; idx < last; idx++ ) + v1_vect->Remove(first); + + // Insert the new elements starting at the first + // position. + + VectorVal* v_vect = v3->AsVectorVal(); + + for ( auto idx = 0u; idx < v_vect->Size(); + idx++, first++ ) + v1_vect->Insert(first, v_vect->At(idx)); + } + + else if ( ! v1_vect->Assign(lv->Idx(0)->CoerceToUnsigned(), std::move(v3)) ) + { + v3 = std::move(v_extra); + + if ( v3 ) + { + ODesc d; + v3->Describe(&d); + const auto& vt = v3->GetType(); + auto vtt = vt->Tag(); + std::string tn = vtt == TYPE_RECORD ? + vt->GetName() : type_name(vtt); + return util::fmt("vector index assignment failed for invalid type '%s', value: %s", + tn.data(), d.Description()); + } + else + return "assignment failed with null value"; + } + break; + } + + case TYPE_TABLE: + { + if ( ! v1->AsTableVal()->Assign(std::move(v2), std::move(v3), true, &iterators_invalidated) ) + { + v3 = std::move(v_extra); + + if ( v3 ) + { + ODesc d; + v3->Describe(&d); + const auto& vt = v3->GetType(); + auto vtt = vt->Tag(); + std::string tn = vtt == TYPE_RECORD ? + vt->GetName() : type_name(vtt); + return util::fmt("table index assignment failed for invalid type '%s', value: %s", + tn.data(), d.Description()); + } + else + return "assignment failed with null value"; + } + + break; + } + + case TYPE_STRING: + return "assignment via string index accessor not allowed"; + break; + + default: + return "bad index expression type in assignment"; + break; + } + + return nullptr; + } + TypePtr Expr::InitType() const { return type; @@ -312,6 +460,12 @@ NameExpr::NameExpr(IDPtr arg_id, bool const_init) h->SetUsed(); } +// This isn't in-lined to avoid needing to pull in ID.h. +IDPtr NameExpr::IdPtr() + { + return id; + } + ValPtr NameExpr::Eval(Frame* f) const { ValPtr v; @@ -436,7 +590,14 @@ ValPtr UnaryExpr::Eval(Frame* f) const if ( ! v ) return nullptr; - if ( is_vector(v) && Tag() != EXPR_IS && Tag() != EXPR_CAST ) + if ( is_vector(v) && Tag() != EXPR_IS && Tag() != EXPR_CAST && + // The following allows passing vectors-by-reference to + // functions that use vector-of-any for generic vector + // manipulation ... + Tag() != EXPR_TO_ANY_COERCE && + // ... and the following to avoid vectorizing operations + // on vector-of-any's + Tag() != EXPR_FROM_ANY_COERCE ) { VectorVal* v_op = v->AsVectorVal(); VectorTypePtr out_t; @@ -457,9 +618,7 @@ ValPtr UnaryExpr::Eval(Frame* f) const return result; } else - { return Fold(v.get()); - } } bool UnaryExpr::IsPure() const @@ -2129,7 +2288,8 @@ void RefExpr::Assign(Frame* f, ValPtr v) AssignExpr::AssignExpr(ExprPtr arg_op1, ExprPtr arg_op2, bool arg_is_init, ValPtr arg_val, - const AttributesPtr& attrs) + const AttributesPtr& attrs, + bool typecheck) : BinaryExpr(EXPR_ASSIGN, arg_is_init ? std::move(arg_op1) : arg_op1->MakeLvalue(), std::move(arg_op2)) @@ -2152,9 +2312,10 @@ AssignExpr::AssignExpr(ExprPtr arg_op1, return; } - // We discard the status from TypeCheck since it has already - // generated error messages. - (void) TypeCheck(attrs); + if ( typecheck ) + // We discard the status from TypeCheck since it has already + // generated error messages. + (void) TypeCheck(attrs); val = std::move(arg_val); @@ -2754,16 +2915,6 @@ ValPtr IndexExpr::Eval(Frame* f) const return Fold(v1.get(), v2.get()); } -static int get_slice_index(int idx, int len) - { - if ( abs(idx) > len ) - idx = idx > 0 ? len : 0; // Clamp maximum positive/negative indices. - else if ( idx < 0 ) - idx += len; // Map to a positive index. - - return idx; - } - ValPtr IndexExpr::Fold(Val* v1, Val* v2) const { if ( IsError() ) @@ -2855,105 +3006,9 @@ void IndexExpr::Assign(Frame* f, ValPtr v) return; auto v1 = op1->Eval(f); - - if ( ! v1 ) - return; - auto v2 = op2->Eval(f); - if ( ! v1 || ! v2 ) - return; - - // Hold an extra reference to 'arg_v' in case the ownership transfer to - // the table/vector goes wrong and we still want to obtain diagnostic info - // from the original value after the assignment already unref'd. - auto v_extra = v; - - switch ( v1->GetType()->Tag() ) { - case TYPE_VECTOR: - { - const ListVal* lv = v2->AsListVal(); - VectorVal* v1_vect = v1->AsVectorVal(); - - if ( lv->Length() > 1 ) - { - auto len = v1_vect->Size(); - bro_int_t first = get_slice_index(lv->Idx(0)->CoerceToInt(), len); - bro_int_t last = get_slice_index(lv->Idx(1)->CoerceToInt(), len); - - // Remove the elements from the vector within the slice - for ( auto idx = first; idx < last; idx++ ) - v1_vect->Remove(first); - - // Insert the new elements starting at the first position - VectorVal* v_vect = v->AsVectorVal(); - - for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ ) - v1_vect->Insert(first, v_vect->At(idx)); - } - else if ( ! v1_vect->Assign(lv->Idx(0)->CoerceToUnsigned(), std::move(v)) ) - { - v = std::move(v_extra); - - if ( v ) - { - ODesc d; - v->Describe(&d); - const auto& vt = v->GetType(); - auto vtt = vt->Tag(); - std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt); - RuntimeErrorWithCallStack(util::fmt( - "vector index assignment failed for invalid type '%s', value: %s", - tn.data(), d.Description())); - } - else - RuntimeErrorWithCallStack("assignment failed with null value"); - } - break; - } - - case TYPE_TABLE: - { - bool iterators_invalidated = false; - - if ( ! v1->AsTableVal()->Assign(std::move(v2), std::move(v), true, &iterators_invalidated) ) - { - v = std::move(v_extra); - - if ( v ) - { - ODesc d; - v->Describe(&d); - const auto& vt = v->GetType(); - auto vtt = vt->Tag(); - std::string tn = vtt == TYPE_RECORD ? vt->GetName() : type_name(vtt); - RuntimeErrorWithCallStack(util::fmt( - "table index assignment failed for invalid type '%s', value: %s", - tn.data(), d.Description())); - } - else - RuntimeErrorWithCallStack("assignment failed with null value"); - } - - if ( iterators_invalidated ) - { - ODesc d; - Describe(&d); - reporter->PushLocation(GetLocationInfo()); - reporter->Warning("possible loop/iterator invalidation caused by expression: %s", d.Description()); - reporter->PopLocation(); - } - } - break; - - case TYPE_STRING: - RuntimeErrorWithCallStack("assignment via string index accessor not allowed"); - break; - - default: - RuntimeErrorWithCallStack("bad index expression type in assignment"); - break; - } + AssignToIndex(v1, v2, v); } void IndexExpr::ExprDescribe(ODesc* d) const @@ -5168,7 +5223,16 @@ bool check_and_promote_args(ListExpr* const args, RecordType* types) return false; } - def_elements.push_front(def_attr->GetExpr().get()); + // Don't use the default expression directly, as + // doing so will wind up sharing its code across + // different invocations that use the default + // argument. That works okay for the interpreter, + // but if we transform the code we want that done + // separately for each instance, rather than + // one instance inheriting the transformed version + // from another. + auto e = def_attr->GetExpr(); + def_elements.push_front(e->Duplicate().release()); } for ( const auto& elem : def_elements ) diff --git a/src/Expr.h b/src/Expr.h index 396b1107a4..c4f1dc58a5 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -68,6 +68,15 @@ enum BroExprTag : int { EXPR_IS, EXPR_INDEX_SLICE_ASSIGN, EXPR_INLINE, + + // The following types of expressions are only created for + // ASTs transformed to reduced form; they aren't germane for + // ASTs produced by parsing .zeek script files. + EXPR_INDEX_ASSIGN, EXPR_FIELD_LHS_ASSIGN, + EXPR_APPEND_TO, + EXPR_TO_ANY_COERCE, EXPR_FROM_ANY_COERCE, + EXPR_ANY_INDEX, + EXPR_NOP, #define NUM_EXPRS (int(EXPR_NOP) + 1) @@ -75,18 +84,27 @@ enum BroExprTag : int { extern const char* expr_name(BroExprTag t); -class ListExpr; -class NameExpr; -class ConstExpr; -class IndexExpr; class AssignExpr; class CallExpr; +class ConstExpr; class EventExpr; -class Stmt; +class FieldAssignExpr; +class FieldExpr; +class ForExpr; +class IndexExpr; +class ListExpr; +class NameExpr; +class RefExpr; class Expr; -using ExprPtr = IntrusivePtr; +using CallExprPtr = IntrusivePtr; +using ConstExprPtr = IntrusivePtr; using EventExprPtr = IntrusivePtr; +using ExprPtr = IntrusivePtr; +using NameExprPtr = IntrusivePtr; +using RefExprPtr = IntrusivePtr; + +class Stmt; using StmtPtr = IntrusivePtr; class Expr : public Obj { @@ -182,13 +200,17 @@ public: ctype* As ## ctype (); \ IntrusivePtr As ## ctype ## Ptr (); + ZEEK_EXPR_ACCESSOR_DECLS(AssignExpr) + ZEEK_EXPR_ACCESSOR_DECLS(CallExpr) + ZEEK_EXPR_ACCESSOR_DECLS(ConstExpr) + ZEEK_EXPR_ACCESSOR_DECLS(EventExpr) + ZEEK_EXPR_ACCESSOR_DECLS(FieldAssignExpr) + ZEEK_EXPR_ACCESSOR_DECLS(FieldExpr) + ZEEK_EXPR_ACCESSOR_DECLS(ForExpr) + ZEEK_EXPR_ACCESSOR_DECLS(IndexExpr) ZEEK_EXPR_ACCESSOR_DECLS(ListExpr) ZEEK_EXPR_ACCESSOR_DECLS(NameExpr) - ZEEK_EXPR_ACCESSOR_DECLS(ConstExpr) - ZEEK_EXPR_ACCESSOR_DECLS(CallExpr) - ZEEK_EXPR_ACCESSOR_DECLS(AssignExpr) - ZEEK_EXPR_ACCESSOR_DECLS(IndexExpr) - ZEEK_EXPR_ACCESSOR_DECLS(EventExpr) + ZEEK_EXPR_ACCESSOR_DECLS(RefExpr) void Describe(ODesc* d) const override final; @@ -200,6 +222,117 @@ public: // Recursively traverses the AST to inline eligible function calls. virtual ExprPtr Inline(Inliner* inl) { return ThisPtr(); } + // True if the expression can serve as an operand to a reduced + // expression. + bool IsSingleton(Reducer* r) const + { + return (tag == EXPR_NAME && IsReduced(r)) || tag == EXPR_CONST; + } + + // True if the expression has no side effects, false otherwise. + virtual bool HasNoSideEffects() const { return IsPure(); } + + // True if the expression is in fully reduced form: a singleton + // or an assignment to an operator with singleton operands. + virtual bool IsReduced(Reducer* c) const; + + // True if the expression's operands are singletons. + virtual bool HasReducedOps(Reducer* c) const; + + // True if (a) the expression has at least one operand, and (b) all + // of its operands are constant. + bool HasConstantOps() const + { + return GetOp1() && GetOp1()->IsConst() && + (! GetOp2() || + (GetOp2()->IsConst() && + (! GetOp3() || GetOp3()->IsConst()))); + } + + // True if the expression is reduced to a form that can be + // used in a conditional. + bool IsReducedConditional(Reducer* c) const; + + // True if the expression is reduced to a form that can be + // used in a field assignment. + bool IsReducedFieldAssignment(Reducer* c) const; + + // True if this expression can be the RHS for a field assignment. + bool IsFieldAssignable(const Expr* e) const; + + // True if the expression will transform to one of another type + // upon reduction, for non-constant operands. "Transform" means + // something beyond assignment to a temporary. Necessary so that + // we know to fully reduce such expressions if they're the RHS + // of an assignment. + virtual bool WillTransform(Reducer* c) const { return false; } + + // The same, but for the expression when used in a conditional context. + virtual bool WillTransformInConditional(Reducer* c) const + { return false; } + + // Returns the current expression transformed into "new_me". + // Takes a bare pointer for "new_me" since often it's newly + // allocated. + ExprPtr TransformMe(Expr* new_me, Reducer* c, StmtPtr& red_stmt); + ExprPtr TransformMe(ExprPtr new_me, Reducer* c, StmtPtr& red_stmt) + { return TransformMe(new_me.get(), c, red_stmt); } + + // Returns a set of predecessor statements in red_stmt (which might + // be nil if no reduction necessary), and the reduced version of + // the expression, suitable for replacing previous uses. The + // second version always yields a singleton suitable for use + // as an operand. The first version does this too except + // for assignment statements; thus, its form is not guarantee + // suitable for use as an operand. + virtual ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt); + virtual ExprPtr ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) + { return Reduce(c, red_stmt); } + + // Reduces the expression to one whose operands are singletons. + // Returns a predecessor statement (which might be a StmtList), if any. + virtual StmtPtr ReduceToSingletons(Reducer* c); + + // Reduces the expression to one that can appear as a conditional. + ExprPtr ReduceToConditional(Reducer* c, StmtPtr& red_stmt); + + // Reduces the expression to one that can appear as a field + // assignment. + ExprPtr ReduceToFieldAssignment(Reducer* c, StmtPtr& red_stmt); + + // Helper function for factoring out complexities related to + // index-based assignment. + void AssignToIndex(ValPtr v1, ValPtr v2, ValPtr v3) const; + + // Returns a new expression corresponding to a temporary + // that's been assigned to the given expression via red_stmt. + ExprPtr AssignToTemporary(ExprPtr e, Reducer* c, StmtPtr& red_stmt); + // Same but for this expression. + ExprPtr AssignToTemporary(Reducer* c, StmtPtr& red_stmt) + { return AssignToTemporary(ThisPtr(), c, red_stmt); } + + // If the expression always evaluates to the same value, returns + // that value. Otherwise, returns nullptr. + virtual ValPtr FoldVal() const { return nullptr; } + + // Returns a Val or a constant Expr corresponding to zero. + ValPtr MakeZero(TypeTag t) const; + ConstExprPtr MakeZeroExpr(TypeTag t) const; + + // Returns the expression's operands, or nil if it doesn't + // have the given operand. + virtual ExprPtr GetOp1() const; + virtual ExprPtr GetOp2() const; + virtual ExprPtr GetOp3() const; + + // Sets the operands to new values. + virtual void SetOp1(ExprPtr new_op); + virtual void SetOp2(ExprPtr new_op); + virtual void SetOp3(ExprPtr new_op); + + // Helper function to reduce boring code runs. + StmtPtr MergeStmts(StmtPtr s1, StmtPtr s2, StmtPtr s3 = nullptr) const; + // Access to the original expression from which this one is derived, // or this one if we don't have an original. Returns a bare pointer // rather than an ExprPtr to emphasize that the access is read-only. @@ -272,6 +405,7 @@ public: explicit NameExpr(IDPtr id, bool const_init = false); ID* Id() const { return id.get(); } + IDPtr IdPtr(); ValPtr Eval(Frame* f) const override; void Assign(Frame* f, ValPtr v) override; @@ -282,10 +416,20 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasNoSideEffects() const override { return true; } + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override { return IsReduced(c); } + bool WillTransform(Reducer* c) const override { return ! IsReduced(c); } + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ValPtr FoldVal() const override; protected: void ExprDescribe(ODesc* d) const override; + // Returns true if our identifier is a global with a constant value + // that can be propagated; used for optimization. + bool FoldableGlobal() const; + IDPtr id; bool in_const_init; }; @@ -303,6 +447,7 @@ public: // Optimization-related: ExprPtr Duplicate() override; + ValPtr FoldVal() const override { return val; } protected: void ExprDescribe(ODesc* d) const override; @@ -325,6 +470,14 @@ public: // Optimization-related: ExprPtr Inline(Inliner* inl) override; + bool HasNoSideEffects() const override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + ExprPtr GetOp1() const override final { return op; } + void SetOp1(ExprPtr _op) override final { op = std::move(_op); } + protected: UnaryExpr(BroExprTag arg_tag, ExprPtr arg_op); @@ -353,6 +506,17 @@ public: // Optimization-related: ExprPtr Inline(Inliner* inl) override; + bool HasNoSideEffects() const override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + ExprPtr GetOp1() const override final { return op1; } + ExprPtr GetOp2() const override final { return op2; } + + void SetOp1(ExprPtr _op) override final { std::move(op1 = _op); } + void SetOp2(ExprPtr _op) override final { std::move(op2 = _op); } + protected: BinaryExpr(BroExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2) @@ -425,6 +589,12 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasNoSideEffects() const override; + bool WillTransform(Reducer* c) const override { return true; } + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override { return false; } + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ExprPtr ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) override; }; class ComplementExpr final : public UnaryExpr { @@ -433,6 +603,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr Fold(Val* v) const override; @@ -444,6 +616,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr Fold(Val* v) const override; @@ -455,6 +629,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr Fold(Val* v) const override; @@ -466,6 +642,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr Fold(Val* v) const override; @@ -490,6 +668,11 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + +protected: + ExprPtr BuildSub(const ExprPtr& op1, const ExprPtr& op2); }; class AddToExpr final : public BinaryExpr { @@ -499,6 +682,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override { return true; } + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class RemoveFromExpr final : public BinaryExpr { @@ -508,6 +693,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override { return true; } + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class SubExpr final : public BinaryExpr { @@ -516,6 +703,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class TimesExpr final : public BinaryExpr { @@ -525,6 +714,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class DivideExpr final : public BinaryExpr { @@ -533,6 +724,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr AddrFold(Val* v1, Val* v2) const override; @@ -555,6 +748,13 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override { return true; } + bool WillTransformInConditional(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + +protected: + bool IsTrue(const ExprPtr& e) const; + bool IsFalse(const ExprPtr& e) const; }; class BitExpr final : public BinaryExpr { @@ -563,6 +763,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class EqExpr final : public BinaryExpr { @@ -572,6 +774,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; protected: ValPtr Fold(Val* v1, Val* v2) const override; @@ -584,6 +788,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; }; class CondExpr final : public Expr { @@ -603,6 +809,20 @@ public: ExprPtr Duplicate() override; ExprPtr Inline(Inliner* inl) override; + bool WillTransform(Reducer* c) const override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + + ExprPtr GetOp1() const override final { return op1; } + ExprPtr GetOp2() const override final { return op2; } + ExprPtr GetOp3() const override final { return op3; } + + void SetOp1(ExprPtr _op) override final { op1 = std::move(_op); } + void SetOp2(ExprPtr _op) override final { op2 = std::move(_op); } + void SetOp3(ExprPtr _op) override final { op3 = std::move(_op); } + protected: void ExprDescribe(ODesc* d) const override; @@ -620,6 +840,14 @@ public: // Optimization-related: ExprPtr Duplicate() override; + + bool WillTransform(Reducer* c) const override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + // Reduce to simplifed LHS form, i.e., a reference to only a name. + StmtPtr ReduceToLHS(Reducer* c); }; class AssignExpr : public BinaryExpr { @@ -628,7 +856,8 @@ public: // yet still perform the assignment. Used for triggers. AssignExpr(ExprPtr op1, ExprPtr op2, bool is_init, ValPtr val = nullptr, - const AttributesPtr& attrs = nullptr); + const AttributesPtr& attrs = nullptr, + bool type_check = true); ValPtr Eval(Frame* f) const override; void EvalIntoAggregate(const zeek::Type* t, Val* aggr, Frame* f) const override; @@ -637,20 +866,29 @@ public: ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; bool IsPure() const override; - void SetOp2(ExprPtr e) - { - op2 = std::move(e); - } - // Optimization-related: ExprPtr Duplicate() override; + bool HasNoSideEffects() const override; + bool WillTransform(Reducer* c) const override { return true; } + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ExprPtr ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) override; + + // Whether this is an assignment to a temporary. + bool IsTemp() const { return is_temp; } + void SetIsTemp() { is_temp = true; } + protected: bool TypeCheck(const AttributesPtr& attrs = nullptr); bool TypeCheckArithmetics(TypeTag bt1, TypeTag bt2); bool is_init; ValPtr val; // optional + + // Optimization-related: + bool is_temp = false; }; class IndexSliceAssignExpr final : public AssignExpr { @@ -688,6 +926,9 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: ValPtr Fold(Val* v1, Val* v2) const override; @@ -797,6 +1038,10 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; @@ -823,6 +1068,10 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; @@ -849,6 +1098,10 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; @@ -867,6 +1120,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + protected: ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; @@ -885,6 +1140,9 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override { return true; } + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + protected: void ExprDescribe(ODesc* d) const override; @@ -898,6 +1156,9 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool WillTransform(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + protected: ValPtr FoldSingleVal(Val* v, InternalTypeTag t) const; ValPtr Fold(Val* v) const override; @@ -974,6 +1235,16 @@ public: ExprPtr Duplicate() override; ExprPtr Inline(Inliner* inl) override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + ExprPtr GetOp1() const override final; + ExprPtr GetOp2() const override final; + + void SetOp1(ExprPtr _op) override final; + void SetOp2(ExprPtr _op) override final; + protected: void ExprDescribe(ODesc* d) const override; @@ -988,6 +1259,8 @@ public: // Optimization-related: ExprPtr Duplicate() override; + bool HasReducedOps(Reducer* c) const override; + protected: ValPtr Fold(Val* v1, Val* v2) const override; @@ -1011,6 +1284,11 @@ public: ExprPtr Duplicate() override; ExprPtr Inline(Inliner* inl) override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: void ExprDescribe(ODesc* d) const override; @@ -1038,6 +1316,8 @@ public: ExprPtr Duplicate() override; ExprPtr Inline(Inliner* inl) override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + protected: void ExprDescribe(ODesc* d) const override; @@ -1048,30 +1328,8 @@ private: std::string my_name; }; -class EventExpr final : public Expr { -public: - EventExpr(const char* name, ListExprPtr args); - - const char* Name() const { return name.c_str(); } - ListExpr* Args() const { return args.get(); } - EventHandlerPtr Handler() const { return handler; } - - ValPtr Eval(Frame* f) const override; - - TraversalCode Traverse(TraversalCallback* cb) const override; - - // Optimization-related: - ExprPtr Duplicate() override; - ExprPtr Inline(Inliner* inl) override; - -protected: - void ExprDescribe(ODesc* d) const override; - - std::string name; - EventHandlerPtr handler; - ListExprPtr args; -}; - +// This comes before EventExpr so that EventExpr::GetOp1 can return its +// arguments as convertible to ExprPtr. class ListExpr : public Expr { public: ListExpr(); @@ -1099,6 +1357,11 @@ public: ExprPtr Duplicate() override; ExprPtr Inline(Inliner* inl) override; + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + protected: ValPtr AddSetInit(const zeek::Type* t, ValPtr aggr) const; @@ -1107,6 +1370,38 @@ protected: ExprPList exprs; }; +class EventExpr final : public Expr { +public: + EventExpr(const char* name, ListExprPtr args); + + const char* Name() const { return name.c_str(); } + ListExpr* Args() const { return args.get(); } + EventHandlerPtr Handler() const { return handler; } + + ValPtr Eval(Frame* f) const override; + + TraversalCode Traverse(TraversalCallback* cb) const override; + + // Optimization-related: + ExprPtr Duplicate() override; + ExprPtr Inline(Inliner* inl) override; + + bool IsReduced(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + StmtPtr ReduceToSingletons(Reducer* c) override; + + ExprPtr GetOp1() const override final { return args; } + void SetOp1(ExprPtr _op) override final + { args = {NewRef{}, _op->AsListExpr()}; } + +protected: + void ExprDescribe(ODesc* d) const override; + + std::string name; + EventHandlerPtr handler; + ListExprPtr args; +}; + class RecordAssignExpr final : public ListExpr { public: @@ -1155,6 +1450,9 @@ public: ExprPtr Duplicate() override; + bool IsReduced(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + TraversalCode Traverse(TraversalCallback* cb) const override; protected: @@ -1166,6 +1464,134 @@ protected: StmtPtr body; }; +// A companion to AddToExpr that's for vector-append, instantiated during +// the reduction process. +class AppendToExpr : public BinaryExpr { +public: + AppendToExpr(ExprPtr op1, ExprPtr op2); + ValPtr Eval(Frame* f) const override; + + bool IsReduced(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + ExprPtr Duplicate() override; +}; + +// An internal class for reduced form. +class IndexAssignExpr : public BinaryExpr { +public: + // "op1[op2] = op3", all reduced. + IndexAssignExpr(ExprPtr op1, ExprPtr op2, ExprPtr op3); + + ValPtr Eval(Frame* f) const override; + + ExprPtr Duplicate() override; + + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ExprPtr ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) override; + + ExprPtr GetOp3() const override final { return op3; } + void SetOp3(ExprPtr _op) override final { op3 = _op; } + + TraversalCode Traverse(TraversalCallback* cb) const override; + +protected: + void ExprDescribe(ODesc* d) const override; + + ExprPtr op3; // assignment RHS +}; + +// An internal class for reduced form. +class FieldLHSAssignExpr : public BinaryExpr { +public: + // "op1$field = RHS", where RHS is reduced with respect to + // ReduceToFieldAssignment(). + FieldLHSAssignExpr(ExprPtr op1, ExprPtr op2, const char* field_name, + int field); + + const char* FieldName() const { return field_name; } + int Field() const { return field; } + + ValPtr Eval(Frame* f) const override; + + ExprPtr Duplicate() override; + + bool IsReduced(Reducer* c) const override; + bool HasReducedOps(Reducer* c) const override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ExprPtr ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) override; + +protected: + void ExprDescribe(ODesc* d) const override; + + const char* field_name; + int field; +}; + +// Expression to explicitly capture conversion to an "any" type, rather +// than it occurring implicitly during script interpretation. +class CoerceToAnyExpr : public UnaryExpr { +public: + CoerceToAnyExpr(ExprPtr op); + +protected: + ValPtr Fold(Val* v) const override; + + ExprPtr Duplicate() override; +}; + +// Same, but for conversion from an "any" type. +class CoerceFromAnyExpr : public UnaryExpr { +public: + CoerceFromAnyExpr(ExprPtr op, TypePtr to_type); + +protected: + ValPtr Fold(Val* v) const override; + + ExprPtr Duplicate() override; +}; + +// Expression used to explicitly capture [a, b, c, ...] = x assignments. +class AnyIndexExpr : public UnaryExpr { +public: + AnyIndexExpr(ExprPtr op, int index); + + int Index() const { return index; } + +protected: + ValPtr Fold(Val* v) const override; + + void ExprDescribe(ODesc* d) const override; + + ExprPtr Duplicate() override; + ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + + int index; +}; + +// Used internally for optimization, when a placeholder is needed. +class NopExpr : public Expr { +public: + explicit NopExpr() : Expr(EXPR_NOP) { } + + ValPtr Eval(Frame* f) const override; + + ExprPtr Duplicate() override; + + TraversalCode Traverse(TraversalCallback* cb) const override; + +protected: + void ExprDescribe(ODesc* d) const override; +}; + + +// Assigns v1[v2] = v3. Returns an error message, or nullptr on success. +// Factored out so that compiled code can call it as well as the interpreter. +extern const char* assign_to_index(ValPtr v1, ValPtr v2, ValPtr v3, + bool& iterators_invalidated); + inline Val* Expr::ExprVal() const { diff --git a/src/Stmt.h b/src/Stmt.h index c72dcad196..931e97834f 100644 --- a/src/Stmt.h +++ b/src/Stmt.h @@ -11,9 +11,12 @@ #include "zeek/ID.h" ZEEK_FORWARD_DECLARE_NAMESPACED(CompositeHash, zeek::detail); +ZEEK_FORWARD_DECLARE_NAMESPACED(NameExpr, zeek::detail); namespace zeek::detail { +using NameExprPtr = IntrusivePtr; + class ExprListStmt : public Stmt { public: const ListExpr* ExprList() const { return l.get(); } @@ -366,6 +369,11 @@ public: StmtPtr Duplicate() override; void Inline(Inliner* inl) override; + // Idioms commonly used in reduction. + StmtList(StmtPtr s1, Stmt* s2); + StmtList(StmtPtr s1, StmtPtr s2); + StmtList(StmtPtr s1, StmtPtr s2, StmtPtr s3); + protected: bool IsPure() const override; @@ -439,6 +447,74 @@ protected: bool is_return; }; + +// Internal statement used for inlining. Executes a block and stops +// the propagation of any "return" inside the block. Generated in +// an already-reduced state. +class CatchReturnStmt : public Stmt { +public: + explicit CatchReturnStmt(StmtPtr block, NameExprPtr ret_var); + + StmtPtr Block() const { return block; } + + // This returns a bare pointer rather than a NameExprPtr only + // because we don't want to have to include Expr.h in this header. + const NameExpr* RetVar() const { return ret_var.get(); } + + // The assignment statement this statement transformed into, + // or nil if it hasn't (the common case). + StmtPtr AssignStmt() const { return assign_stmt; } + + ValPtr Exec(Frame* f, StmtFlowType& flow) const override; + + bool IsPure() const override; + + // Even though these objects are generated in reduced form, we still + // have a reduction method to support the subsequent optimizer pass. + StmtPtr DoReduce(Reducer* c) override; + + // Note, no need for a NoFlowAfter() method because anything that + // has "NoFlowAfter" inside the body still gets caught and we + // continue afterwards. + + StmtPtr Duplicate() override; + + void StmtDescribe(ODesc* d) const override; + + TraversalCode Traverse(TraversalCallback* cb) const override; + +protected: + // The inlined function body. + StmtPtr block; + + // Expression that holds the return value. Only used for compiling. + NameExprPtr ret_var; + + // If this statement transformed into an assignment, that + // corresponding statement. + StmtPtr assign_stmt; +}; + +// Statement that makes sure at run-time that an "any" type has the +// correct number of (list) entries to enable sub-assigning to it via +// statements like "[a, b, c] = x;". Generated in an already-reduced state. +class CheckAnyLenStmt : public ExprStmt { +public: + explicit CheckAnyLenStmt(ExprPtr e, int expected_len); + + ValPtr Exec(Frame* f, StmtFlowType& flow) const override; + + StmtPtr Duplicate() override; + + bool IsReduced(Reducer* c) const override; + StmtPtr DoReduce(Reducer* c) override; + + void StmtDescribe(ODesc* d) const override; + +protected: + int expected_len; +}; + } // namespace zeek::detail using ExprListStmt [[deprecated("Remove in v4.1. Use zeek::detail::ExprListStmt instead.")]] = zeek::detail::ExprListStmt; diff --git a/src/StmtBase.h b/src/StmtBase.h index 1edd1a4bcf..a5c727607a 100644 --- a/src/StmtBase.h +++ b/src/StmtBase.h @@ -24,11 +24,13 @@ using ValPtr = IntrusivePtr; namespace zeek::detail { -class StmtList; +class ExprStmt; class ForStmt; class InitStmt; -class WhenStmt; +class ReturnStmt; +class StmtList; class SwitchStmt; +class WhenStmt; class EventExpr; class ListExpr; @@ -37,6 +39,7 @@ using EventExprPtr = IntrusivePtr; using ListExprPtr = IntrusivePtr; class Inliner; +class Reducer; class Stmt; using StmtPtr = IntrusivePtr; @@ -50,6 +53,7 @@ public: virtual ValPtr Exec(Frame* f, StmtFlowType& flow) const = 0; Stmt* Ref() { zeek::Ref(this); return this; } + StmtPtr ThisPtr() { return {NewRef{}, this}; } bool SetLocationInfo(const Location* loc) override { return Stmt::SetLocationInfo(loc, loc); } @@ -64,7 +68,9 @@ public: ForStmt* AsForStmt(); const ForStmt* AsForStmt() const; + const ExprStmt* AsExprStmt() const; const InitStmt* AsInitStmt() const; + const ReturnStmt* AsReturnStmt() const; const WhenStmt* AsWhenStmt() const; const SwitchStmt* AsSwitchStmt() const; @@ -81,12 +87,37 @@ public: virtual TraversalCode Traverse(TraversalCallback* cb) const = 0; - // Returns a duplicate of the statement. + // Returns a duplicate of the statement so that modifications + // can be made to statements from inlining function bodies - or + // to the originals - without affecting other instances. + // + // It's tempting to think that there are some statements that + // are safe to share across multiple functions and could just + // return references to themselves - but since we associate + // information for script optimization with individual statements + // nodes, even these need to be duplicated. virtual StmtPtr Duplicate() = 0; // Recursively traverses the AST to inline eligible function calls. virtual void Inline(Inliner* inl) { } + // True if the statement is in reduced form. + virtual bool IsReduced(Reducer* c) const; + + // Returns a reduced version of the statement, as managed by + // the given Reducer. + StmtPtr Reduce(Reducer* c); + virtual StmtPtr DoReduce(Reducer* c) { return ThisPtr(); } + + // True if there's definitely no control flow past the statement. + // The argument governs whether to ignore "break" statements, given + // they mean two different things depending on whether they're in + // a loop or a switch. Also, if we want to know whether flow reaches + // the *end* of a loop, then we also want to ignore break's, as + // in that case, they do lead to flow reaching the end. + virtual bool NoFlowAfter(bool ignore_break) const + { return false; } + // Access to the original statement from which this one is derived, // or this one if we don't have an original. Returns a bare pointer // rather than a StmtPtr to emphasize that the access is read-only. @@ -124,6 +155,11 @@ public: protected: explicit Stmt(StmtTag arg_tag); + // Helper function called after reductions to perform canonical + // actions. Takes a bare pointer for new_me because usually + // it's been newly new'd, so this keeps down code clutter. + StmtPtr TransformMe(Stmt* new_me, Reducer* c); + void AddTag(ODesc* d) const; virtual void StmtDescribe(ODesc* d) const; void DescribeDone(ODesc* d) const; diff --git a/src/script_opt/Expr.cc b/src/script_opt/Expr.cc index 9106da8358..e2a6812dc4 100644 --- a/src/script_opt/Expr.cc +++ b/src/script_opt/Expr.cc @@ -11,29 +11,436 @@ #include "zeek/Traverse.h" #include "zeek/Reporter.h" #include "zeek/script_opt/Inline.h" +#include "zeek/script_opt/Reduce.h" namespace zeek::detail { +static bool same_singletons(ExprPtr e1, ExprPtr e2); + + +ConstExpr* Expr::AsConstExpr() + { + CHECK_TAG(tag, EXPR_CONST, "ExprVal::AsConstExpr", expr_name) + return (ConstExpr*) this; + } + +FieldExpr* Expr::AsFieldExpr() + { + CHECK_TAG(tag, EXPR_FIELD, "ExprVal::AsFieldExpr", expr_name) + return (FieldExpr*) this; + } + +IntrusivePtr Expr::AsFieldAssignExprPtr() + { + CHECK_TAG(tag, EXPR_FIELD_ASSIGN, "ExprVal::AsFieldAssignExpr", expr_name) + return {NewRef{}, (FieldAssignExpr*) this}; + } + +ExprPtr Expr::GetOp1() const { return nullptr; } +ExprPtr Expr::GetOp2() const { return nullptr; } +ExprPtr Expr::GetOp3() const { return nullptr; } + +void Expr::SetOp1(ExprPtr) { } +void Expr::SetOp2(ExprPtr) { } +void Expr::SetOp3(ExprPtr) { } + +bool Expr::IsReduced(Reducer* c) const + { + return true; + } + +bool Expr::HasReducedOps(Reducer* c) const + { + return true; + } + +bool Expr::IsReducedConditional(Reducer* c) const + { + switch ( tag ) { + case EXPR_CONST: + return true; + + case EXPR_NAME: + return IsReduced(c); + + case EXPR_IN: + { + auto op1 = GetOp1(); + auto op2 = GetOp2(); + + if ( op1->Tag() != EXPR_NAME && op1->Tag() != EXPR_LIST ) + return NonReduced(this); + + if ( op2->GetType()->Tag() != TYPE_TABLE || ! op2->IsReduced(c) ) + return NonReduced(this); + + if ( op1->Tag() == EXPR_LIST ) + { + auto l1 = op1->AsListExpr(); + auto& l1_e = l1->Exprs(); + + if ( l1_e.length() < 1 || l1_e.length() > 2 ) + return NonReduced(this); + } + + return true; + } + + case EXPR_EQ: + case EXPR_NE: + case EXPR_LE: + case EXPR_GE: + case EXPR_LT: + case EXPR_GT: + case EXPR_HAS_FIELD: + return HasReducedOps(c); + + default: + return false; + } + } + +bool Expr::IsReducedFieldAssignment(Reducer* c) const + { + if ( ! IsFieldAssignable(this) ) + return false; + + if ( tag == EXPR_CONST ) + return true; + + if ( tag == EXPR_NAME ) + return IsReduced(c); + + return HasReducedOps(c); + } + +bool Expr::IsFieldAssignable(const Expr* e) const + { + switch ( e->Tag() ) { + case EXPR_NAME: + case EXPR_CONST: + case EXPR_NOT: + case EXPR_COMPLEMENT: + case EXPR_POSITIVE: + case EXPR_NEGATE: + case EXPR_ADD: + case EXPR_SUB: + case EXPR_TIMES: + case EXPR_DIVIDE: + case EXPR_MOD: + case EXPR_AND: + case EXPR_OR: + case EXPR_XOR: + case EXPR_FIELD: + case EXPR_HAS_FIELD: + case EXPR_IN: + case EXPR_SIZE: + return true; + + // These would not be hard to add in principle, but at the expense + // of some added complexity in the templator. Seems unlikely the + // actual performance gain would make that worth it. + // case EXPR_LT: + // case EXPR_LE: + // case EXPR_EQ: + // case EXPR_NE: + // case EXPR_GE: + // case EXPR_GT: + + // These could be added if we subsetted them to versions for + // which we know it's safe to evaluate both operands. Again + // likely not worth it. + // case EXPR_AND_AND: + // case EXPR_OR_OR: + + default: + return false; + } + } + +ExprPtr Expr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + red_stmt = nullptr; + return ThisPtr(); + } + +StmtPtr Expr::ReduceToSingletons(Reducer* c) + { + auto op1 = GetOp1(); + auto op2 = GetOp2(); + auto op3 = GetOp3(); + + StmtPtr red1_stmt; + StmtPtr red2_stmt; + StmtPtr red3_stmt; + + if ( op1 && ! op1->IsSingleton(c) ) + SetOp1(op1->ReduceToSingleton(c, red1_stmt)); + if ( op2 && ! op2->IsSingleton(c) ) + SetOp2(op2->ReduceToSingleton(c, red2_stmt)); + if ( op3 && ! op3->IsSingleton(c) ) + SetOp3(op3->ReduceToSingleton(c, red3_stmt)); + + return MergeStmts(red1_stmt, red2_stmt, red3_stmt); + } + +ExprPtr Expr::ReduceToConditional(Reducer* c, StmtPtr& red_stmt) + { + switch ( tag ) { + case EXPR_CONST: + return ThisPtr(); + + case EXPR_NAME: + if ( c->Optimizing() ) + return ThisPtr(); + + return Reduce(c, red_stmt); + + case EXPR_IN: + { + // This is complicated because there are lots of forms + // of "in" expressions, and we're only interested in + // those with 1 or 2 indices, into a table. + auto op1 = GetOp1(); + auto op2 = GetOp2(); + + if ( c->Optimizing() ) + return Reduce(c, red_stmt); + + if ( op2->GetType()->Tag() != TYPE_TABLE ) + // Not a table de-reference. + return Reduce(c, red_stmt); + + if ( op1->Tag() == EXPR_LIST ) + { + auto l1 = op1->AsListExpr(); + auto& l1_e = l1->Exprs(); + + if ( l1_e.length() < 1 || l1_e.length() > 2 ) + // Wrong number of indices. + return Reduce(c, red_stmt); + } + + if ( ! op1->IsReduced(c) || ! op2->IsReduced(c) ) + { + auto red2_stmt = ReduceToSingletons(c); + auto res = ReduceToConditional(c, red_stmt); + red_stmt = MergeStmts(red2_stmt, red_stmt); + return res; + } + + return ThisPtr(); + } + + case EXPR_EQ: + case EXPR_NE: + case EXPR_LE: + case EXPR_GE: + case EXPR_LT: + case EXPR_GT: + red_stmt = ReduceToSingletons(c); + + if ( GetOp1()->IsConst() && GetOp2()->IsConst() ) + // Fold! + { + StmtPtr fold_stmts; + auto new_me = Reduce(c, fold_stmts); + red_stmt = MergeStmts(red_stmt, fold_stmts); + + return new_me; + } + + return ThisPtr(); + + case EXPR_HAS_FIELD: + red_stmt = ReduceToSingletons(c); + return ThisPtr(); + + default: + return Reduce(c, red_stmt); + } + } + +ExprPtr Expr::ReduceToFieldAssignment(Reducer* c, StmtPtr& red_stmt) + { + if ( ! IsFieldAssignable(this) || tag == EXPR_NAME ) + return ReduceToSingleton(c, red_stmt); + + red_stmt = ReduceToSingletons(c); + + return ThisPtr(); + } + +ExprPtr Expr::AssignToTemporary(ExprPtr e, Reducer* c, StmtPtr& red_stmt) + { + auto result_tmp = c->GenTemporaryExpr(GetType(), e); + + auto a_e = make_intrusive(result_tmp->MakeLvalue(), e, + false, nullptr, nullptr, false); + a_e->SetIsTemp(); + a_e->SetOriginal(ThisPtr()); + + auto a_e_s = make_intrusive(a_e); + red_stmt = MergeStmts(red_stmt, a_e_s); + + // Important: our result is not result_tmp, but a duplicate of it. + // This is important because subsequent passes that associate + // information with Expr's need to not mis-associate that + // information with both the assignment creating the temporary, + // and the subsequent use of the temporary. + return result_tmp->Duplicate(); + } + +ExprPtr Expr::TransformMe(Expr* new_me, Reducer* c, StmtPtr& red_stmt) + { + if ( new_me == this ) + return ThisPtr(); + + new_me->SetOriginal(ThisPtr()); + + // Unlike for Stmt's, we assume that new_me has already + // been reduced, so no need to do so further. + return new_me->ThisPtr(); + } + +StmtPtr Expr::MergeStmts(StmtPtr s1, StmtPtr s2, StmtPtr s3) const + { + int nums = (s1 != nullptr) + (s2 != nullptr) + (s3 != nullptr); + + if ( nums > 1 ) + return make_intrusive(s1, s2, s3); + else if ( s1 ) + return s1; + else if ( s2 ) + return s2; + else if ( s3 ) + return s3; + else + return nullptr; + } + +ValPtr Expr::MakeZero(TypeTag t) const + { + switch ( t ) { + case TYPE_BOOL: return val_mgr->False(); + case TYPE_INT: return val_mgr->Int(0); + case TYPE_COUNT: return val_mgr->Count(0); + + case TYPE_DOUBLE: return make_intrusive(0.0); + case TYPE_TIME: return make_intrusive(0.0); + case TYPE_INTERVAL: return make_intrusive(0.0, 1.0); + + default: + reporter->InternalError("bad call to MakeZero"); + } + } + +ConstExprPtr Expr::MakeZeroExpr(TypeTag t) const + { + return make_intrusive(MakeZero(t)); + } + ExprPtr NameExpr::Duplicate() { return SetSucc(new NameExpr(id, in_const_init)); } +bool NameExpr::IsReduced(Reducer* c) const + { + if ( FoldableGlobal() ) + return false; + + return c->NameIsReduced(this); + } + +ExprPtr NameExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + red_stmt = nullptr; + + if ( c->Optimizing() ) + return ThisPtr(); + + if ( FoldableGlobal() ) + { + ValPtr v = id->GetVal(); + ASSERT(v); + return TransformMe(new ConstExpr(v), c, red_stmt); + } + + return c->UpdateName(this)->ThisPtr(); + } + +ValPtr NameExpr::FoldVal() const + { + if ( ! id->IsConst() || id->GetAttr(ATTR_REDEF) || + id->GetType()->Tag() == TYPE_FUNC ) + return nullptr; + + return id->GetVal(); + } + +bool NameExpr::FoldableGlobal() const + { + return id->IsGlobal() && id->IsConst() && + is_atomic_type(id->GetType()) && + // Make sure constant can't be changed on the command line + // or such. + ! id->GetAttr(ATTR_REDEF); + } + ExprPtr ConstExpr::Duplicate() { return SetSucc(new ConstExpr(val)); } - + ExprPtr UnaryExpr::Inline(Inliner* inl) { op = op->Inline(inl); return ThisPtr(); } +bool UnaryExpr::HasNoSideEffects() const + { + return op->HasNoSideEffects(); + } + +bool UnaryExpr::IsReduced(Reducer* c) const + { + return NonReduced(this); + } + +bool UnaryExpr::HasReducedOps(Reducer* c) const + { + return op->IsSingleton(c); + } + +ExprPtr UnaryExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + op = c->UpdateExpr(op); + + red_stmt = nullptr; + + if ( ! op->IsSingleton(c) ) + op = op->ReduceToSingleton(c, red_stmt); + + auto op_val = op->FoldVal(); + if ( op_val ) + { + auto fold = Fold(op_val.get()); + return TransformMe(new ConstExpr(fold), c, red_stmt); + } + + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + ExprPtr BinaryExpr::Inline(Inliner* inl) { @@ -43,6 +450,54 @@ ExprPtr BinaryExpr::Inline(Inliner* inl) return ThisPtr(); } +bool BinaryExpr::HasNoSideEffects() const + { + return op1->HasNoSideEffects() && op2->HasNoSideEffects(); + } + +bool BinaryExpr::IsReduced(Reducer* c) const + { + return NonReduced(this); + } + +bool BinaryExpr::HasReducedOps(Reducer* c) const + { + return op1->IsSingleton(c) && op2->IsSingleton(c); + } + +ExprPtr BinaryExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + } + + red_stmt = nullptr; + + if ( ! op1->IsSingleton(c) ) + op1 = op1->ReduceToSingleton(c, red_stmt); + + StmtPtr red2_stmt; + if ( ! op2->IsSingleton(c) ) + op2 = op2->ReduceToSingleton(c, red2_stmt); + + red_stmt = MergeStmts(red_stmt, red2_stmt); + + auto op1_fold_val = op1->FoldVal(); + auto op2_fold_val = op2->FoldVal(); + if ( op1_fold_val && op2_fold_val ) + { + auto fold = Fold(op1_fold_val.get(), op2_fold_val.get()); + return TransformMe(new ConstExpr(fold), c, red_stmt); + } + + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + ExprPtr CloneExpr::Duplicate() { @@ -56,30 +511,207 @@ ExprPtr IncrExpr::Duplicate() return SetSucc(new IncrExpr(tag, op->Duplicate())); } +bool IncrExpr::HasNoSideEffects() const + { + return false; + } + +bool IncrExpr::IsReduced(Reducer* c) const + { + auto ref_op = op->AsRefExprPtr(); + auto target = ref_op->GetOp1(); + + if ( target->Tag() != EXPR_NAME || + ! IsIntegral(target->GetType()->Tag()) ) + return NonReduced(this); + + return ref_op->IsReduced(c); + } + +ExprPtr IncrExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->Tag() != EXPR_REF ) + Internal("confusion in IncrExpr::Reduce"); + + auto ref_op = op->AsRefExprPtr(); + auto target = ref_op->GetOp1(); + + if ( target->Tag() == EXPR_NAME && + IsIntegral(target->GetType()->Tag()) ) + { + if ( c->Optimizing() ) + op = c->UpdateExpr(op); + else + op = op->Reduce(c, red_stmt); + + return ThisPtr(); + } + + // First reduce the target's operands to singletons, so that when + // we re-use it in the assignment below, it has reduced operands. + auto init_red_stmt = target->ReduceToSingletons(c); + + // Now reduce it all the way to a single value, to use for the + // increment. + auto orig_target = target; + StmtPtr target_stmt; + target = target->ReduceToSingleton(c, target_stmt); + + auto incr_const = make_intrusive(val_mgr->Count(1)); + incr_const->SetOriginal(ThisPtr()); + + auto incr_expr = Tag() == EXPR_INCR ? + (Expr*) new AddExpr(target, incr_const) : + (Expr*) new SubExpr(target, incr_const); + incr_expr->SetOriginal(ThisPtr()); + StmtPtr incr_stmt; + auto incr_expr2 = incr_expr->Reduce(c, incr_stmt); + + StmtPtr assign_stmt; + auto rhs = incr_expr2->AssignToTemporary(c, assign_stmt); + + // Build a duplicate version of the original to use as the result. + if ( orig_target->Tag() == EXPR_NAME ) + orig_target = orig_target->Duplicate(); + + else if ( orig_target->Tag() == EXPR_INDEX ) + { + auto dup1 = orig_target->GetOp1()->Duplicate(); + auto dup2 = orig_target->GetOp2()->Duplicate(); + auto index = dup2->AsListExprPtr(); + orig_target = make_intrusive(dup1, index); + } + + else if ( orig_target->Tag() == EXPR_FIELD ) + { + auto dup1 = orig_target->GetOp1()->Duplicate(); + auto field_name = orig_target->AsFieldExpr()->FieldName(); + orig_target = make_intrusive(dup1, field_name); + } + + else + reporter->InternalError("confused in IncrExpr::Reduce"); + + auto assign = make_intrusive(orig_target, rhs, false, + nullptr, nullptr, false); + + orig_target->SetOriginal(ThisPtr()); + + // First reduce it regularly, so it can transform into $= or + // such as needed. Then reduce that to a singleton to provide + // the result for this expression. + StmtPtr assign_stmt2; + auto res = assign->Reduce(c, assign_stmt2); + res = res->ReduceToSingleton(c, red_stmt); + red_stmt = MergeStmts(MergeStmts(init_red_stmt, target_stmt), + MergeStmts(incr_stmt, assign_stmt, assign_stmt2), + red_stmt); + + return res; + } + +ExprPtr IncrExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) + { + auto ref_op = op->AsRefExprPtr(); + auto target = ref_op->GetOp1(); + + if ( target->Tag() == EXPR_NAME && + IsIntegral(target->GetType()->Tag()) ) + { + ExprPtr incr_expr = Duplicate(); + red_stmt = make_intrusive(incr_expr)->Reduce(c); + + StmtPtr targ_red_stmt; + auto targ_red = target->Reduce(c, targ_red_stmt); + + red_stmt = MergeStmts(red_stmt, targ_red_stmt); + + return targ_red; + } + + else + return UnaryExpr::ReduceToSingleton(c, red_stmt); + } + ExprPtr ComplementExpr::Duplicate() { return SetSucc(new ComplementExpr(op->Duplicate())); } +bool ComplementExpr::WillTransform(Reducer* c) const + { + return op->Tag() == EXPR_COMPLEMENT; + } + +ExprPtr ComplementExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->Tag() == EXPR_COMPLEMENT ) + return op->GetOp1()->ReduceToSingleton(c, red_stmt); + + return UnaryExpr::Reduce(c, red_stmt); + } + ExprPtr NotExpr::Duplicate() { return SetSucc(new NotExpr(op->Duplicate())); } +bool NotExpr::WillTransform(Reducer* c) const + { + return op->Tag() == EXPR_NOT && Op()->GetType()->Tag() == TYPE_BOOL; + } + +ExprPtr NotExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->Tag() == EXPR_NOT && Op()->GetType()->Tag() == TYPE_BOOL ) + return Op()->Reduce(c, red_stmt); + + return UnaryExpr::Reduce(c, red_stmt); + } + ExprPtr PosExpr::Duplicate() { return SetSucc(new PosExpr(op->Duplicate())); } +bool PosExpr::WillTransform(Reducer* c) const + { + return op->GetType()->Tag() != TYPE_COUNT; + } + +ExprPtr PosExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->GetType()->Tag() == TYPE_COUNT ) + // We need to keep the expression because it leads + // to a coercion from unsigned to signed. + return UnaryExpr::Reduce(c, red_stmt); + + else + return op->ReduceToSingleton(c, red_stmt); + } + ExprPtr NegExpr::Duplicate() { return SetSucc(new NegExpr(op->Duplicate())); } +bool NegExpr::WillTransform(Reducer* c) const + { + return op->Tag() == EXPR_NEGATE; + } + +ExprPtr NegExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->Tag() == EXPR_NEGATE ) + return op->GetOp1()->ReduceToSingleton(c, red_stmt); + + return UnaryExpr::Reduce(c, red_stmt); + } + ExprPtr SizeExpr::Duplicate() { @@ -94,6 +726,37 @@ ExprPtr AddExpr::Duplicate() return SetSucc(new AddExpr(op1_d, op2_d)); } +bool AddExpr::WillTransform(Reducer* c) const + { + return op1->IsZero() || op2->IsZero() || + op1->Tag() == EXPR_NEGATE || op2->Tag() == EXPR_NEGATE; + } + +ExprPtr AddExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op1->IsZero() ) + return op2->ReduceToSingleton(c, red_stmt); + + if ( op2->IsZero() ) + return op1->ReduceToSingleton(c, red_stmt); + + if ( op1->Tag() == EXPR_NEGATE ) + return BuildSub(op2, op1)->ReduceToSingleton(c, red_stmt); + + if ( op2->Tag() == EXPR_NEGATE ) + return BuildSub(op1, op2)->ReduceToSingleton(c, red_stmt); + + return BinaryExpr::Reduce(c, red_stmt); + } + +ExprPtr AddExpr::BuildSub(const ExprPtr& op1, const ExprPtr& op2) + { + auto rhs = op2->GetOp1(); + auto sub = make_intrusive(op1, rhs); + sub->SetOriginal(ThisPtr()); + return sub; + } + ExprPtr AddToExpr::Duplicate() { @@ -102,6 +765,44 @@ ExprPtr AddToExpr::Duplicate() return SetSucc(new AddToExpr(op1_d, op2_d)); } +ExprPtr AddToExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( IsVector(op1->GetType()->Tag()) ) + { + StmtPtr red_stmt1; + StmtPtr red_stmt2; + + if ( op1->Tag() == EXPR_FIELD ) + red_stmt1 = op1->ReduceToSingletons(c); + else + op1 = op1->Reduce(c, red_stmt1); + + op2 = op2->Reduce(c, red_stmt2); + + auto append = + make_intrusive(op1->Duplicate(), op2); + append->SetOriginal(ThisPtr()); + + auto append_stmt = make_intrusive(append); + + red_stmt = MergeStmts(red_stmt1, red_stmt2, append_stmt); + + return op1; + } + + else + { + // We could do an ASSERT that op1 is an EXPR_REF, but + // the following is basically equivalent. + auto rhs = op1->AsRefExprPtr()->GetOp1(); + auto do_incr = make_intrusive(rhs->Duplicate(), op2); + auto assign = new AssignExpr(op1, do_incr, false, nullptr, + nullptr, false); + + return assign->ReduceToSingleton(c, red_stmt); + } + } + ExprPtr SubExpr::Duplicate() { @@ -110,6 +811,48 @@ ExprPtr SubExpr::Duplicate() return SetSucc(new SubExpr(op1_d, op2_d)); } +bool SubExpr::WillTransform(Reducer* c) const + { + return op2->IsZero() || op2->Tag() == EXPR_NEGATE || + (type->Tag() != TYPE_VECTOR && type->Tag() != TYPE_TABLE && + op1->Tag() == EXPR_NAME && op2->Tag() == EXPR_NAME && + op1->AsNameExpr()->Id() == op2->AsNameExpr()->Id()); + } + +ExprPtr SubExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op2->IsZero() ) + return op1->ReduceToSingleton(c, red_stmt); + + if ( op2->Tag() == EXPR_NEGATE ) + { + auto rhs = op2->GetOp1(); + auto add = new AddExpr(op1, rhs); + add->SetOriginal(ThisPtr()); + return add->Reduce(c, red_stmt); + } + + if ( c->Optimizing() ) + { // Allow for alias expansion. + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + } + + if ( type->Tag() != TYPE_VECTOR && type->Tag() != TYPE_TABLE && + op1->Tag() == EXPR_NAME && op2->Tag() == EXPR_NAME ) + { + auto n1 = op1->AsNameExpr(); + auto n2 = op2->AsNameExpr(); + if ( n1->Id() == n2->Id() ) + { + auto zero = MakeZeroExpr(type->Tag()); + return TransformMe(zero, c, red_stmt); + } + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr RemoveFromExpr::Duplicate() { @@ -118,6 +861,16 @@ ExprPtr RemoveFromExpr::Duplicate() return SetSucc(new RemoveFromExpr(op1_d, op2_d)); } +ExprPtr RemoveFromExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + auto rhs = op1->AsRefExprPtr()->GetOp1(); + auto do_decr = make_intrusive(rhs->Duplicate(), op2); + auto assign = new AssignExpr(op1, do_decr, false, nullptr, nullptr, + false); + + return assign->Reduce(c, red_stmt); + } + ExprPtr TimesExpr::Duplicate() { @@ -126,6 +879,32 @@ ExprPtr TimesExpr::Duplicate() return SetSucc(new TimesExpr(op1_d, op2_d)); } +bool TimesExpr::WillTransform(Reducer* c) const + { + return op1->IsZero() || op2->IsZero() || op1->IsOne() || op2->IsOne(); + } + +ExprPtr TimesExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op1->IsOne() ) + return op2->ReduceToSingleton(c, red_stmt); + + if ( op2->IsOne() ) + return op1->ReduceToSingleton(c, red_stmt); + + // Optimize integral multiplication by zero ... but not + // double, due to cases like Inf*0 or NaN*0. + if ( (op1->IsZero() || op2->IsZero()) && + GetType()->Tag() != TYPE_DOUBLE ) + { + auto zero_val = op1->IsZero() ? + op1->Eval(nullptr) : op2->Eval(nullptr); + return make_intrusive(zero_val); + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr DivideExpr::Duplicate() { @@ -134,6 +913,22 @@ ExprPtr DivideExpr::Duplicate() return SetSucc(new DivideExpr(op1_d, op2_d)); } +bool DivideExpr::WillTransform(Reducer* c) const + { + return GetType()->Tag() != TYPE_SUBNET && op2->IsOne(); + } + +ExprPtr DivideExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( GetType()->Tag() != TYPE_SUBNET ) + { + if ( op2->IsOne() ) + return op1->ReduceToSingleton(c, red_stmt); + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr ModExpr::Duplicate() { @@ -143,6 +938,63 @@ ExprPtr ModExpr::Duplicate() } +// Helper functions used by BoolExpr. + +// Returns true if the given Expr is either of the form "/pat/ in var" or a +// (possibly extended) "||" disjunction of such nodes, for which "var" is +// always the same. If true, returns the IDPtr corresponding to "var", and +// collects the associated pattern constants in "patterns". +// +// Note that for an initial (non-recursive) call, "id" should be set to +// nullptr, and the caller should have ensured that the starting point is +// a disjunction (since a bare "/pat/ in var" by itself isn't a "cascade" +// and doesn't present a potential optimization opportunity. +static bool is_pattern_cascade(ExprPtr e, IDPtr& id, + std::vector& patterns) + { + auto lhs = e->GetOp1(); + auto rhs = e->GetOp2(); + + if ( e->Tag() == EXPR_IN ) + { + if ( lhs->Tag() != EXPR_CONST || + lhs->GetType()->Tag() != TYPE_PATTERN || + rhs->Tag() != EXPR_NAME ) + return false; + + auto rhs_id = rhs->AsNameExpr()->IdPtr(); + + if ( id && rhs_id != id ) + return false; + + id = rhs_id; + patterns.push_back(lhs->AsConstExprPtr()); + + return true; + } + + if ( e->Tag() != EXPR_OR_OR ) + return false; + + return is_pattern_cascade(lhs, id, patterns) && + is_pattern_cascade(rhs, id, patterns); + } + +// Given a set of pattern constants, returns a disjunction that +// includes all of them. +static ExprPtr build_disjunction(std::vector& patterns) + { + ASSERT(patterns.size() > 1); + + ExprPtr e = patterns[0]; + + for ( unsigned int i = 1; i < patterns.size(); ++i ) + e = make_intrusive(EXPR_OR, e, patterns[i]); + + return e; + } + + ExprPtr BoolExpr::Duplicate() { auto op1_d = op1->Duplicate(); @@ -150,6 +1002,103 @@ ExprPtr BoolExpr::Duplicate() return SetSucc(new BoolExpr(tag, op1_d, op2_d)); } +bool BoolExpr::WillTransformInConditional(Reducer* c) const + { + IDPtr common_id = nullptr; + std::vector patterns; + + ExprPtr e_ptr = {NewRef{}, (Expr*) this}; + + return tag == EXPR_OR_OR && + is_pattern_cascade(e_ptr, common_id, patterns); + } + +ExprPtr BoolExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + // First, look for a common idiom of "/foo/ in x || /bar/ in x" + // and translate it to "(/foo/ | /bar) in x", which is more + // efficient to match. + IDPtr common_id = nullptr; + std::vector patterns; + if ( tag == EXPR_OR_OR && + is_pattern_cascade(ThisPtr(), common_id, patterns) ) + { + auto new_pat = build_disjunction(patterns); + auto new_id = make_intrusive(common_id); + auto new_node = make_intrusive(new_pat, new_id); + return new_node->Reduce(c, red_stmt); + } + + // It's either an EXPR_AND_AND or an EXPR_OR_OR. + bool is_and = (tag == EXPR_AND_AND); + + if ( IsTrue(op1) ) + { + if ( is_and ) + return op2->ReduceToSingleton(c, red_stmt); + else + return op1->ReduceToSingleton(c, red_stmt); + } + + if ( IsFalse(op1) ) + { + if ( is_and ) + return op1->ReduceToSingleton(c, red_stmt); + else + return op2->ReduceToSingleton(c, red_stmt); + } + + if ( op1->HasNoSideEffects() ) + { + if ( IsTrue(op2) ) + { + if ( is_and ) + return op1->ReduceToSingleton(c, red_stmt); + else + return op2->ReduceToSingleton(c, red_stmt); + } + + if ( IsFalse(op2) ) + { + if ( is_and ) + return op2->ReduceToSingleton(c, red_stmt); + else + return op1->ReduceToSingleton(c, red_stmt); + } + } + + auto else_val = is_and ? val_mgr->False() : val_mgr->True(); + ExprPtr else_e = make_intrusive(else_val); + + Expr* cond; + if ( is_and ) + cond = new CondExpr(op1, op2, else_e); + else + cond = new CondExpr(op1, else_e, op2); + + auto cond_red = cond->ReduceToSingleton(c, red_stmt); + + return TransformMe(cond_red, c, red_stmt); + } + +bool BoolExpr::IsTrue(const ExprPtr& e) const + { + if ( ! e->IsConst() ) + return false; + + auto c_e = e->AsConstExpr(); + return c_e->Value()->IsOne(); + } + +bool BoolExpr::IsFalse(const ExprPtr& e) const + { + if ( ! e->IsConst() ) + return false; + + auto c_e = e->AsConstExpr(); + return c_e->Value()->IsZero(); + } + ExprPtr BitExpr::Duplicate() { @@ -158,6 +1107,55 @@ ExprPtr BitExpr::Duplicate() return SetSucc(new BitExpr(tag, op1_d, op2_d)); } +bool BitExpr::WillTransform(Reducer* c) const + { + return GetType()->Tag() == TYPE_COUNT && + (op1->IsZero() || op2->IsZero() || + (same_singletons(op1, op2) && op1->Tag() == EXPR_NAME)); + } + +ExprPtr BitExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( GetType()->Tag() != TYPE_COUNT ) + return BinaryExpr::Reduce(c, red_stmt); + + auto zero1 = op1->IsZero(); + auto zero2 = op2->IsZero(); + + if ( zero1 && zero2 ) + // No matter the operation, the answer is zero. + return op1->ReduceToSingleton(c, red_stmt); + + if ( zero1 || zero2 ) + { + ExprPtr& zero_op = zero1 ? op1 : op2; + ExprPtr& non_zero_op = zero1 ? op2 : op1; + + if ( Tag() == EXPR_AND ) + return zero_op->ReduceToSingleton(c, red_stmt); + else + // OR or XOR + return non_zero_op->ReduceToSingleton(c, red_stmt); + } + + if ( same_singletons(op1, op2) && op1->Tag() == EXPR_NAME ) + { + auto n = op1->AsNameExpr(); + + if ( Tag() == EXPR_XOR ) + { + auto zero = new ConstExpr(val_mgr->Count(0)); + zero->SetOriginal(ThisPtr()); + return zero->Reduce(c, red_stmt); + } + + else + return op1->ReduceToSingleton(c, red_stmt); + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr EqExpr::Duplicate() { @@ -166,6 +1164,24 @@ ExprPtr EqExpr::Duplicate() return SetSucc(new EqExpr(tag, op1_d, op2_d)); } +bool EqExpr::WillTransform(Reducer* c) const + { + return GetType()->Tag() == TYPE_BOOL && same_singletons(op1, op2); + } + +ExprPtr EqExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( GetType()->Tag() == TYPE_BOOL && same_singletons(op1, op2) ) + { + bool t = Tag() == EXPR_EQ; + auto res = make_intrusive(val_mgr->Bool(t)); + res->SetOriginal(ThisPtr()); + return res->Reduce(c, red_stmt); + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr RelExpr::Duplicate() { @@ -174,6 +1190,35 @@ ExprPtr RelExpr::Duplicate() return SetSucc(new RelExpr(tag, op1_d, op2_d)); } +bool RelExpr::WillTransform(Reducer* c) const + { + return GetType()->Tag() == TYPE_BOOL && same_singletons(op1, op2); + } + +ExprPtr RelExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( GetType()->Tag() == TYPE_BOOL ) + { + if ( same_singletons(op1, op2) ) + { + bool t = Tag() == EXPR_GE || Tag() == EXPR_LE; + auto res = new ConstExpr(val_mgr->Bool(t)); + res->SetOriginal(ThisPtr()); + return res->Reduce(c, red_stmt); + } + + if ( op1->IsZero() && op2->GetType()->Tag() == TYPE_COUNT && + (Tag() == EXPR_LE || Tag() == EXPR_GT) ) + Warn("degenerate comparison"); + + if ( op2->IsZero() && op1->GetType()->Tag() == TYPE_COUNT && + (Tag() == EXPR_LT || Tag() == EXPR_GE) ) + Warn("degenerate comparison"); + } + + return BinaryExpr::Reduce(c, red_stmt); + } + ExprPtr CondExpr::Duplicate() { @@ -192,12 +1237,187 @@ ExprPtr CondExpr::Inline(Inliner* inl) return ThisPtr(); } +bool CondExpr::IsReduced(Reducer* c) const + { + if ( ! IsVector(op1->GetType()->Tag()) || ! HasReducedOps(c) || + same_singletons(op2, op3) ) + return NonReduced(this); + + return true; + } + +bool CondExpr::HasReducedOps(Reducer* c) const + { + return op1->IsSingleton(c) && op2->IsSingleton(c) && + op3->IsSingleton(c) && ! op1->IsConst(); + } + +bool CondExpr::WillTransform(Reducer* c) const + { + return ! HasReducedOps(c); + } + +ExprPtr CondExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + op3 = c->UpdateExpr(op3); + } + + StmtPtr op1_red_stmt; + op1 = op1->ReduceToSingleton(c, op1_red_stmt); + + if ( op1->IsConst() ) + { + ExprPtr res; + if ( op1->AsConstExpr()->Value()->IsOne() ) + res = op2->ReduceToSingleton(c, red_stmt); + else + res = op3->ReduceToSingleton(c, red_stmt); + + red_stmt = MergeStmts(op1_red_stmt, red_stmt); + + return res; + } + + if ( same_singletons(op2, op3) ) + { + if ( op1->HasNoSideEffects() ) + { + if ( op1->Tag() != EXPR_CONST && + op1->Tag() != EXPR_NAME ) + op1 = op1->AssignToTemporary(c, red_stmt); + } + + red_stmt = MergeStmts(op1_red_stmt, red_stmt); + + return op2; + } + + if ( c->Optimizing() ) + return ThisPtr(); + + red_stmt = ReduceToSingletons(c); + + StmtPtr assign_stmt; + auto res = AssignToTemporary(c, assign_stmt); + + red_stmt = MergeStmts(op1_red_stmt, red_stmt, assign_stmt); + + return TransformMe(res, c, red_stmt); + } + +StmtPtr CondExpr::ReduceToSingletons(Reducer* c) + { + StmtPtr red1_stmt; + if ( ! op1->IsSingleton(c) ) + op1 = op1->ReduceToSingleton(c, red1_stmt); + + StmtPtr red2_stmt; + if ( ! op2->IsSingleton(c) ) + op2 = op2->ReduceToSingleton(c, red2_stmt); + + StmtPtr red3_stmt; + if ( ! op3->IsSingleton(c) ) + op3 = op3->ReduceToSingleton(c, red3_stmt); + + if ( IsVector(op1->GetType()->Tag()) ) + { + // In this particular case, it's okay to evaluate op2 and + // op3 fully ahead of time, because the selector has to be + // able to choose among them. + return MergeStmts(MergeStmts(red1_stmt, red2_stmt), red3_stmt); + } + + StmtPtr if_else; + + if ( red2_stmt || red3_stmt ) + { + if ( ! red2_stmt ) + red2_stmt = make_intrusive(); + if ( ! red3_stmt ) + red3_stmt = make_intrusive(); + + if_else = make_intrusive(op1->Duplicate(), + red2_stmt, red3_stmt); + } + + return MergeStmts(red1_stmt, if_else); + } + ExprPtr RefExpr::Duplicate() { return SetSucc(new RefExpr(op->Duplicate())); } +bool RefExpr::IsReduced(Reducer* c) const + { + if ( op->Tag() == EXPR_NAME ) + return op->IsReduced(c); + + return NonReduced(this); + } + +bool RefExpr::HasReducedOps(Reducer* c) const + { + switch ( op->Tag() ) { + case EXPR_NAME: + return op->IsReduced(c); + + case EXPR_FIELD: + return op->AsFieldExpr()->Op()->IsReduced(c); + + case EXPR_INDEX: + { + auto ind = op->AsIndexExpr(); + return ind->Op1()->IsReduced(c) && ind->Op2()->IsReduced(c); + } + + case EXPR_LIST: + return op->IsReduced(c); + + default: + Internal("bad operand in RefExpr::IsReduced"); + return true; + } + } + +bool RefExpr::WillTransform(Reducer* c) const + { + return op->Tag() != EXPR_NAME; + } + +ExprPtr RefExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( op->Tag() == EXPR_NAME ) + op = op->Reduce(c, red_stmt); + else + op = AssignToTemporary(c, red_stmt); + + return ThisPtr(); + } + +StmtPtr RefExpr::ReduceToLHS(Reducer* c) + { + if ( op->Tag() == EXPR_NAME ) + { + StmtPtr red_stmt; + op = op->Reduce(c, red_stmt); + return red_stmt; + } + + auto red_stmt1 = op->ReduceToSingletons(c); + auto op_ref = make_intrusive(op); + + StmtPtr red_stmt2; + op = AssignToTemporary(op_ref, c, red_stmt2); + + return MergeStmts(red_stmt1, red_stmt2); + } + ExprPtr AssignExpr::Duplicate() { @@ -206,6 +1426,209 @@ ExprPtr AssignExpr::Duplicate() return SetSucc(new AssignExpr(op1_d, op2_d, is_init, val)); } +bool AssignExpr::HasNoSideEffects() const + { + return false; + } + +bool AssignExpr::IsReduced(Reducer* c) const + { + if ( op2->Tag() == EXPR_ASSIGN ) + // Cascaded assignments are never reduced. + return false; + + auto lhs_is_any = op1->GetType()->Tag() == TYPE_ANY; + auto rhs_is_any = op2->GetType()->Tag() == TYPE_ANY; + + if ( lhs_is_any != rhs_is_any && op2->Tag() != EXPR_CONST ) + return NonReduced(this); + + auto t1 = op1->Tag(); + + if ( t1 == EXPR_REF && + op2->HasConstantOps() && op2->Tag() != EXPR_TO_ANY_COERCE ) + // We are not reduced because we should instead + // be folded. + return NonReduced(this); + + if ( IsTemp() ) + return true; + + if ( ! op2->HasReducedOps(c) ) + return NonReduced(this); + + if ( op1->IsSingleton(c) ) + return true; + + if ( op1->Tag() == EXPR_REF ) + return op1->AsRefExprPtr()->IsReduced(c); + + return NonReduced(this); + } + +bool AssignExpr::HasReducedOps(Reducer* c) const + { + return op1->IsReduced(c) && op2->IsSingleton(c); + } + +ExprPtr AssignExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + // Yields a fully reduced assignment expression. + + if ( c->Optimizing() ) + { + // Don't update the LHS, it's already in reduced form + // and it doesn't make sense to expand aliases or such. + op2 = c->UpdateExpr(op2); + return ThisPtr(); + } + + if ( IsTemp() ) + // These are generated for reduced expressions. + return ThisPtr(); + + auto lhs_is_any = op1->GetType()->Tag() == TYPE_ANY; + auto rhs_is_any = op2->GetType()->Tag() == TYPE_ANY; + + StmtPtr rhs_reduce; + + if ( lhs_is_any != rhs_is_any ) + { + auto op2_loc = op2->GetLocationInfo(); + + ExprPtr red_rhs = op2->ReduceToSingleton(c, rhs_reduce); + + if ( lhs_is_any ) + { + if ( red_rhs->Tag() == EXPR_CONST ) + op2 = red_rhs; + else + op2 = make_intrusive(red_rhs); + } + else + op2 = make_intrusive(red_rhs, + op1->GetType()); + + op2->SetLocationInfo(op2_loc); + } + + auto lhs_ref = op1->AsRefExprPtr(); + auto lhs_expr = lhs_ref->GetOp1(); + + if ( lhs_expr->Tag() == EXPR_INDEX ) + { + auto ind_e = lhs_expr->AsIndexExpr(); + + StmtPtr ind1_stmt; + StmtPtr ind2_stmt; + StmtPtr rhs_stmt; + + auto ind1_e = ind_e->Op1()->Reduce(c, ind1_stmt); + auto ind2_e = ind_e->Op2()->Reduce(c, ind2_stmt); + auto rhs_e = op2->Reduce(c, rhs_stmt); + + red_stmt = MergeStmts(MergeStmts(rhs_reduce, ind1_stmt), + ind2_stmt, rhs_stmt); + + auto index_assign = new IndexAssignExpr(ind1_e, ind2_e, rhs_e); + return TransformMe(index_assign, c, red_stmt); + } + + if ( lhs_expr->Tag() == EXPR_FIELD ) + { + auto field_e = lhs_expr->AsFieldExpr(); + + StmtPtr lhs_stmt; + StmtPtr rhs_stmt; + + auto lhs_e = field_e->Op()->Reduce(c, lhs_stmt); + auto rhs_e = op2->ReduceToFieldAssignment(c, rhs_stmt); + + red_stmt = MergeStmts(rhs_reduce, lhs_stmt, rhs_stmt); + + auto field_name = field_e->FieldName(); + auto field = field_e->Field(); + auto field_assign = + new FieldLHSAssignExpr(lhs_e, rhs_e, field_name, field); + + return TransformMe(field_assign, c, red_stmt); + } + + if ( lhs_expr->Tag() == EXPR_LIST ) + { + auto lhs_list = lhs_expr->AsListExpr()->Exprs(); + + StmtPtr rhs_stmt; + auto rhs_e = op2->Reduce(c, rhs_stmt); + + auto len = lhs_list.length(); + auto check_stmt = make_intrusive(rhs_e, len); + + red_stmt = MergeStmts(rhs_reduce, rhs_stmt, check_stmt); + + loop_over_list(lhs_list, i) + { + auto rhs_dup = rhs_e->Duplicate(); + auto rhs = make_intrusive(rhs_dup, i); + auto lhs = lhs_list[i]->ThisPtr(); + auto assign = make_intrusive(lhs, rhs, + false, nullptr, nullptr, false); + auto assign_stmt = make_intrusive(assign); + red_stmt = MergeStmts(red_stmt, assign_stmt); + } + + return TransformMe(new NopExpr(), c, red_stmt); + } + + if ( op2->WillTransform(c) ) + { + StmtPtr xform_stmt; + op2 = op2->ReduceToSingleton(c, xform_stmt); + red_stmt = MergeStmts(rhs_reduce, xform_stmt); + return ThisPtr(); + } + + red_stmt = op2->ReduceToSingletons(c); + + if ( op2->HasConstantOps() && op2->Tag() != EXPR_TO_ANY_COERCE ) + op2 = make_intrusive(op2->Eval(nullptr)); + + // Check once again for transformation, this time made possible + // because the operands have been reduced. We don't simply + // always first reduce the operands, because for expressions + // like && and ||, that's incorrect. + + if ( op2->WillTransform(c) ) + { + StmtPtr xform_stmt; + op2 = op2->ReduceToSingleton(c, xform_stmt); + red_stmt = MergeStmts(rhs_reduce, red_stmt, xform_stmt); + return ThisPtr(); + } + + StmtPtr lhs_stmt = lhs_ref->ReduceToLHS(c); + StmtPtr rhs_stmt = op2->ReduceToSingletons(c); + + red_stmt = MergeStmts(MergeStmts(rhs_reduce, red_stmt), + lhs_stmt, rhs_stmt); + + return ThisPtr(); + } + +ExprPtr AssignExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) + { + // Yields a statement performing the assignment and for the + // expression the LHS (but turned into an RHS). + if ( op1->Tag() != EXPR_REF ) + Internal("Confusion in AssignExpr::ReduceToSingleton"); + + ExprPtr assign_expr = Duplicate(); + auto ae_stmt = make_intrusive(assign_expr); + red_stmt = ae_stmt->Reduce(c); + + return op1->AsRefExprPtr()->GetOp1(); + } + ExprPtr IndexSliceAssignExpr::Duplicate() { @@ -222,6 +1645,33 @@ ExprPtr IndexExpr::Duplicate() return SetSucc(new IndexExpr(op1_d, op2_l, is_slice)); } +bool IndexExpr::HasReducedOps(Reducer* c) const + { + if ( ! op1->IsSingleton(c) ) + return NonReduced(this); + + if ( op2->Tag() == EXPR_LIST ) + return op2->HasReducedOps(c); + else + { + if ( op2->IsSingleton(c) ) + return true; + + return NonReduced(this); + } + } + +StmtPtr IndexExpr::ReduceToSingletons(Reducer* c) + { + StmtPtr red1_stmt; + if ( ! op1->IsSingleton(c) ) + SetOp1(op1->ReduceToSingleton(c, red1_stmt)); + + StmtPtr red2_stmt = op2->ReduceToSingletons(c); + + return MergeStmts(red1_stmt, red2_stmt); + } + ExprPtr IndexExprWhen::Duplicate() { @@ -249,6 +1699,62 @@ ExprPtr RecordConstructorExpr::Duplicate() return SetSucc(new RecordConstructorExpr(op_l)); } +bool RecordConstructorExpr::HasReducedOps(Reducer* c) const + { + auto& exprs = op->AsListExpr()->Exprs(); + + loop_over_list(exprs, i) + { + auto e_i = exprs[i]; + if ( ! e_i->AsFieldAssignExprPtr()->Op()->IsSingleton(c) ) + return false; + } + + return true; + } + +ExprPtr RecordConstructorExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + red_stmt = ReduceToSingletons(c); + + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + +StmtPtr RecordConstructorExpr::ReduceToSingletons(Reducer* c) + { + StmtPtr red_stmt; + auto& exprs = op->AsListExpr()->Exprs(); + + // Could consider merging this code with that for ListExpr::Reduce. + loop_over_list(exprs, i) + { + auto e_i = exprs[i]; + auto fa_i = e_i->AsFieldAssignExprPtr(); + auto fa_i_rhs = e_i->GetOp1(); + + if ( c->Optimizing() ) + { + fa_i->SetOp1(c->UpdateExpr(fa_i_rhs)); + continue; + } + + if ( fa_i_rhs->IsSingleton(c) ) + continue; + + StmtPtr e_stmt; + auto rhs_red = fa_i_rhs->ReduceToSingleton(c, e_stmt); + fa_i->SetOp1(rhs_red); + + if ( e_stmt ) + red_stmt = MergeStmts(red_stmt, e_stmt); + } + + return red_stmt; + } + ExprPtr TableConstructorExpr::Duplicate() { @@ -266,6 +1772,72 @@ ExprPtr TableConstructorExpr::Duplicate() return SetSucc(new TableConstructorExpr(op_l, nullptr, t, attrs)); } +bool TableConstructorExpr::HasReducedOps(Reducer* c) const + { + const auto& exprs = op->AsListExpr()->Exprs(); + + for ( const auto& expr : exprs ) + { + auto a = expr->AsAssignExpr(); + // LHS is a list, not a singleton. + if ( ! a->GetOp1()->HasReducedOps(c) || + ! a->GetOp2()->IsSingleton(c) ) + return NonReduced(this); + } + + return true; + } + +ExprPtr TableConstructorExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + red_stmt = ReduceToSingletons(c); + + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + +StmtPtr TableConstructorExpr::ReduceToSingletons(Reducer* c) + { + // Need to process the list of initializers directly, as + // they may be expressed as AssignExpr's, and those get + // treated quite differently during reduction. + const auto& exprs = op->AsListExpr()->Exprs(); + + StmtPtr red_stmt; + + for ( const auto& expr : exprs ) + { + if ( expr->Tag() == EXPR_ASSIGN ) + { + auto a = expr->AsAssignExpr(); + auto op1 = a->GetOp1(); + auto op2 = a->GetOp2(); + + if ( c->Optimizing() ) + { + a->SetOp1(c->UpdateExpr(op1)); + a->SetOp2(c->UpdateExpr(op2)); + continue; + } + + StmtPtr red1_stmt; + StmtPtr red2_stmt; + + a->SetOp1(op1->ReduceToSingleton(c, red1_stmt)); + a->SetOp2(op2->ReduceToSingleton(c, red2_stmt)); + + red_stmt = MergeStmts(red_stmt, red1_stmt, red2_stmt); + } + + else + reporter->InternalError("confused in TableConstructorExpr::Reduce"); + } + + return red_stmt; + } + ExprPtr SetConstructorExpr::Duplicate() { @@ -283,6 +1855,30 @@ ExprPtr SetConstructorExpr::Duplicate() return SetSucc(new SetConstructorExpr(op_l, nullptr, t, attrs)); } +bool SetConstructorExpr::HasReducedOps(Reducer* c) const + { + return op->IsReduced(c); + } + +ExprPtr SetConstructorExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + // We rely on the fact that ListExpr's don't change into + // temporaries. + red_stmt = nullptr; + + (void) op->Reduce(c, red_stmt); + + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + +StmtPtr SetConstructorExpr::ReduceToSingletons(Reducer* c) + { + return op->ReduceToSingletons(c); + } + ExprPtr VectorConstructorExpr::Duplicate() { @@ -294,6 +1890,11 @@ ExprPtr VectorConstructorExpr::Duplicate() return SetSucc(new VectorConstructorExpr(op_l, type)); } +bool VectorConstructorExpr::HasReducedOps(Reducer* c) const + { + return Op()->HasReducedOps(c); + } + ExprPtr FieldAssignExpr::Duplicate() { @@ -301,6 +1902,24 @@ ExprPtr FieldAssignExpr::Duplicate() return SetSucc(new FieldAssignExpr(field_name.c_str(), op_dup)); } +ExprPtr FieldAssignExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op = c->UpdateExpr(op); + return ThisPtr(); + } + + red_stmt = nullptr; + + if ( ! op->IsReduced(c) ) + op = op->ReduceToSingleton(c, red_stmt); + + // Doesn't seem worth checking for constant folding. + + return AssignToTemporary(c, red_stmt); + } + ExprPtr ArithCoerceExpr::Duplicate() { @@ -316,6 +1935,44 @@ ExprPtr ArithCoerceExpr::Duplicate() return SetSucc(new ArithCoerceExpr(op_dup, tag)); } +bool ArithCoerceExpr::WillTransform(Reducer* c) const + { + return op->Tag() == EXPR_CONST && + IsArithmetic(op->AsConstExpr()->Value()->GetType()->Tag()); + } + +ExprPtr ArithCoerceExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + op = c->UpdateExpr(op); + + red_stmt = nullptr; + + if ( ! op->IsReduced(c) ) + op = op->ReduceToSingleton(c, red_stmt); + + auto t = type->InternalType(); + + if ( op->Tag() == EXPR_CONST ) + { + auto cv = op->AsConstExpr()->Value(); + auto tag = cv->GetType()->Tag(); + + if ( IsArithmetic(tag) ) + return make_intrusive(FoldSingleVal(cv, t)); + } + + if ( c->Optimizing() ) + return ThisPtr(); + + auto bt = op->GetType()->InternalType(); + + if ( t == bt ) + return op; + + return AssignToTemporary(c, red_stmt); + } + ExprPtr RecordCoerceExpr::Duplicate() { @@ -353,6 +2010,65 @@ ExprPtr ScheduleExpr::Inline(Inliner* inl) return ThisPtr(); } +ExprPtr ScheduleExpr::GetOp1() const + { + return when; + } + +// We can't inline the following without moving the definition of +// EventExpr in Expr.h to come before that of ScheduleExpr. Just +// doing this out-of-line seems cleaner. +ExprPtr ScheduleExpr::GetOp2() const + { + return event; + } + +void ScheduleExpr::SetOp1(ExprPtr op) + { + when = op; + } + +void ScheduleExpr::SetOp2(ExprPtr op) + { + event = op->AsEventExprPtr(); + } + +bool ScheduleExpr::IsReduced(Reducer* c) const + { + return when->IsReduced(c) && event->IsReduced(c); + } + +bool ScheduleExpr::HasReducedOps(Reducer* c) const + { + if ( when->IsSingleton(c) && event->IsSingleton(c) ) + return true; + + return NonReduced(this); + } + +ExprPtr ScheduleExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + when = c->UpdateExpr(when); + auto e = c->UpdateExpr(event); + event = e->AsEventExprPtr(); + } + + red_stmt = nullptr; + + if ( ! when->IsReduced(c) ) + when = when->Reduce(c, red_stmt); + + StmtPtr red2_stmt; + // We assume that EventExpr won't transform itself fundamentally. + (void) event->Reduce(c, red2_stmt); + + red_stmt = MergeStmts(red_stmt, red2_stmt); + + return ThisPtr(); + } + ExprPtr InExpr::Duplicate() { @@ -361,6 +2077,11 @@ ExprPtr InExpr::Duplicate() return SetSucc(new InExpr(op1_d, op2_d)); } +bool InExpr::HasReducedOps(Reducer* c) const + { + return op1->HasReducedOps(c) && op2->IsSingleton(c); + } + ExprPtr CallExpr::Duplicate() { @@ -386,6 +2107,61 @@ ExprPtr CallExpr::Inline(Inliner* inl) return ThisPtr(); } +bool CallExpr::IsReduced(Reducer* c) const + { + return func->IsSingleton(c) && args->IsReduced(c); + } + +bool CallExpr::HasReducedOps(Reducer* c) const + { + if ( ! func->IsSingleton(c) ) + return NonReduced(this); + + return args->HasReducedOps(c); + } + +ExprPtr CallExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + func = c->UpdateExpr(func); + auto e = c->UpdateExpr(args); + args = e->AsListExprPtr(); + return ThisPtr(); + } + + red_stmt = nullptr; + + if ( ! func->IsSingleton(c) ) + func = func->ReduceToSingleton(c, red_stmt); + + StmtPtr red2_stmt; + // We assume that ListExpr won't transform itself fundamentally. + (void) args->Reduce(c, red2_stmt); + + // ### could check here for (1) pure function, and (2) all + // arguments constants, and call it to fold right now. + + red_stmt = MergeStmts(red_stmt, red2_stmt); + + if ( GetType()->Tag() == TYPE_VOID ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + +StmtPtr CallExpr::ReduceToSingletons(Reducer* c) + { + StmtPtr func_stmt; + + if ( ! func->IsSingleton(c) ) + func = func->Reduce(c, func_stmt); + + auto args_stmt = args->ReduceToSingletons(c); + + return MergeStmts(func_stmt, args_stmt); + } + ExprPtr LambdaExpr::Duplicate() { @@ -400,6 +2176,14 @@ ExprPtr LambdaExpr::Inline(Inliner* inl) return ThisPtr(); } +ExprPtr LambdaExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + return ThisPtr(); + else + return AssignToTemporary(c, red_stmt); + } + ExprPtr EventExpr::Duplicate() { @@ -413,6 +2197,34 @@ ExprPtr EventExpr::Inline(Inliner* inl) return ThisPtr(); } +bool EventExpr::IsReduced(Reducer* c) const + { + return Args()->IsReduced(c); + } + +ExprPtr EventExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + auto e = c->UpdateExpr(args); + auto args = e->AsListExprPtr(); + return ThisPtr(); + } + + red_stmt = nullptr; + + if ( ! Args()->IsReduced(c) ) + // We assume that ListExpr won't transform itself fundamentally. + (void) Args()->Reduce(c, red_stmt); + + return ThisPtr(); + } + +StmtPtr EventExpr::ReduceToSingletons(Reducer* c) + { + return args->ReduceToSingletons(c); + } + ExprPtr ListExpr::Duplicate() { @@ -432,6 +2244,81 @@ ExprPtr ListExpr::Inline(Inliner* inl) return ThisPtr(); } +bool ListExpr::IsReduced(Reducer* c) const + { + for ( const auto& expr : exprs ) + if ( ! expr->IsSingleton(c) ) + { + if ( expr->Tag() != EXPR_LIST || ! expr->IsReduced(c) ) + return NonReduced(expr); + } + + return true; + } + +bool ListExpr::HasReducedOps(Reducer* c) const + { + for ( const auto& expr : exprs ) + { + // Ugly hack for record constructors. + if ( expr->Tag() == EXPR_FIELD_ASSIGN ) + { + if ( ! expr->HasReducedOps(c) ) + return false; + } + else if ( ! expr->IsSingleton(c) ) + return false; + } + + return true; + } + +ExprPtr ListExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + red_stmt = nullptr; + + loop_over_list(exprs, i) + { + if ( c->Optimizing() ) + { + auto e_i = c->UpdateExpr(exprs[i]->ThisPtr()); + exprs.replace(i, e_i.release()); + continue; + } + + if ( exprs[i]->IsSingleton(c) ) + continue; + + StmtPtr e_stmt; + exprs.replace(i, + exprs[i]->ReduceToSingleton(c, e_stmt).release()); + + if ( e_stmt ) + red_stmt = MergeStmts(red_stmt, e_stmt); + } + + return ThisPtr(); + } + +StmtPtr ListExpr::ReduceToSingletons(Reducer* c) + { + StmtPtr red_stmt; + + loop_over_list(exprs, i) + { + if ( exprs[i]->IsSingleton(c) ) + continue; + + StmtPtr e_stmt; + exprs.replace(i, exprs[i]->Reduce(c, e_stmt).release()); + + if ( e_stmt ) + red_stmt = MergeStmts(red_stmt, e_stmt); + } + + return red_stmt; + } + ExprPtr CastExpr::Duplicate() { @@ -500,6 +2387,45 @@ ExprPtr InlineExpr::Duplicate() return SetSucc(new InlineExpr(args_d, params, body_d, frame_offset, type)); } +bool InlineExpr::IsReduced(Reducer* c) const + { + return NonReduced(this); + } + +ExprPtr InlineExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + // First, reduce each argument and assign it to a parameter. + // We do this one at a time because that will often allow the + // optimizer to collapse the final assignment. + + red_stmt = nullptr; + + auto args_list = args->Exprs(); + + loop_over_list(args_list, i) + { + StmtPtr arg_red_stmt; + auto red_i = args_list[i]->Reduce(c, arg_red_stmt); + + auto param_i = c->GenInlineBlockName(params[i]); + auto assign = make_intrusive(param_i, red_i, + false, nullptr, nullptr, false); + auto assign_stmt = make_intrusive(assign); + + red_stmt = MergeStmts(red_stmt, arg_red_stmt, assign_stmt); + } + + auto ret_val = c->PushInlineBlock(type); + body = body->Reduce(c); + c->PopInlineBlock(); + + auto catch_ret = make_intrusive(body, ret_val); + + red_stmt = MergeStmts(red_stmt, catch_ret); + + return ret_val ? ret_val->Duplicate() : nullptr; + } + TraversalCode InlineExpr::Traverse(TraversalCallback* cb) const { TraversalCode tc = cb->PreExpr(this); @@ -533,4 +2459,390 @@ void InlineExpr::ExprDescribe(ODesc* d) const } +AppendToExpr::AppendToExpr(ExprPtr arg_op1, ExprPtr arg_op2) + : BinaryExpr(EXPR_APPEND_TO, std::move(arg_op1), std::move(arg_op2)) + { + // This is an internal type, so we don't bother with type-checking + // or coercions, those have already been done before we're created. + SetType(op1->GetType()); + } + +ValPtr AppendToExpr::Eval(Frame* f) const + { + auto v1 = op1->Eval(f); + + if ( ! v1 ) + return nullptr; + + auto v2 = op2->Eval(f); + + if ( ! v2 ) + return nullptr; + + VectorVal* vv = v1->AsVectorVal(); + + if ( ! vv->Assign(vv->Size(), v2) ) + RuntimeError("type-checking failed in vector append"); + + return v1; + } + +ExprPtr AppendToExpr::Duplicate() + { + auto op1_d = op1->Duplicate(); + auto op2_d = op2->Duplicate(); + return SetSucc(new AppendToExpr(op1_d, op2_d)); + } + +bool AppendToExpr::IsReduced(Reducer* c) const + { + // These are created reduced. + return true; + } + +ExprPtr AppendToExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + } + + return ThisPtr(); + } + + +IndexAssignExpr::IndexAssignExpr(ExprPtr arg_op1, ExprPtr arg_op2, + ExprPtr arg_op3) +: BinaryExpr(EXPR_INDEX_ASSIGN, std::move(arg_op1), std::move(arg_op2)) + { + op3 = arg_op3; + SetType(op3->GetType()); + } + +ValPtr IndexAssignExpr::Eval(Frame* f) const + { + auto v1 = op1->Eval(f); + auto v2 = op2->Eval(f); + auto v3 = op3->Eval(f); + + AssignToIndex(v1, v2, v3); + + return nullptr; + } + +bool IndexAssignExpr::IsReduced(Reducer* c) const + { + // op2 is a ListExpr, not a singleton expression. + ASSERT(op1->IsSingleton(c) && op2->IsReduced(c) && op3->IsSingleton(c)); + return true; + } + +bool IndexAssignExpr::HasReducedOps(Reducer* c) const + { + return true; + } + +ExprPtr IndexAssignExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + op3 = c->UpdateExpr(op3); + } + + return ThisPtr(); + } + +ExprPtr IndexAssignExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) + { + // Yields a statement performing the assignment and for the + // expression the LHS (but turned into an RHS). + if ( op1->Tag() != EXPR_NAME ) + Internal("Confusion in IndexAssignExpr::ReduceToSingleton"); + + StmtPtr op1_red_stmt; + op1 = op1->Reduce(c, op1_red_stmt); + + auto assign_stmt = make_intrusive(Duplicate()); + + auto index = op2->AsListExprPtr(); + auto res = make_intrusive(GetOp1(), index, false); + auto final_res = res->ReduceToSingleton(c, red_stmt); + + red_stmt = MergeStmts(op1_red_stmt, assign_stmt, red_stmt); + + return final_res; + } + +ExprPtr IndexAssignExpr::Duplicate() + { + auto op1_d = op1->Duplicate(); + auto op2_d = op2->Duplicate(); + auto op3_d = op3->Duplicate(); + + return SetSucc(new IndexAssignExpr(op1_d, op2_d, op3_d)); + } + +TraversalCode IndexAssignExpr::Traverse(TraversalCallback* cb) const + { + TraversalCode tc = cb->PreExpr(this); + HANDLE_TC_EXPR_PRE(tc); + + tc = op1->Traverse(cb); + HANDLE_TC_EXPR_PRE(tc); + + tc = op2->Traverse(cb); + HANDLE_TC_EXPR_PRE(tc); + + tc = op3->Traverse(cb); + HANDLE_TC_EXPR_PRE(tc); + + tc = cb->PostExpr(this); + HANDLE_TC_EXPR_POST(tc); + } + +void IndexAssignExpr::ExprDescribe(ODesc* d) const + { + op1->Describe(d); + if ( d->IsReadable() ) + d->Add("["); + + op2->Describe(d); + if ( d->IsReadable() ) + { + d->Add("]"); + d->Add(" []= "); + } + + op3->Describe(d); + } + + +FieldLHSAssignExpr::FieldLHSAssignExpr(ExprPtr arg_op1, ExprPtr arg_op2, + const char* _field_name, int _field) +: BinaryExpr(EXPR_FIELD_LHS_ASSIGN, std::move(arg_op1), std::move(arg_op2)) + { + field_name = _field_name; + field = _field; + SetType(op2->GetType()); + } + +ValPtr FieldLHSAssignExpr::Eval(Frame* f) const + { + auto v1 = op1->Eval(f); + auto v2 = op2->Eval(f); + + if ( v1 && v2 ) + { + RecordVal* r = v1->AsRecordVal(); + r->Assign(field, std::move(v2)); + } + + return nullptr; + } + +ExprPtr FieldLHSAssignExpr::Duplicate() + { + auto op1_d = op1->Duplicate(); + auto op2_d = op2->Duplicate(); + + return SetSucc(new FieldLHSAssignExpr(op1_d, op2_d, field_name, field)); + } + +bool FieldLHSAssignExpr::IsReduced(Reducer* c) const + { + ASSERT(op1->IsSingleton(c) && op2->IsReducedFieldAssignment(c)); + return true; + } + +bool FieldLHSAssignExpr::HasReducedOps(Reducer* c) const + { + return true; + } + +ExprPtr FieldLHSAssignExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + if ( c->Optimizing() ) + { + op1 = c->UpdateExpr(op1); + op2 = c->UpdateExpr(op2); + } + + return ThisPtr(); + } + +ExprPtr FieldLHSAssignExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) + { + // Yields a statement performing the assignment and for the + // expression the LHS (but turned into an RHS). + if ( op1->Tag() != EXPR_NAME ) + Internal("Confusion in FieldLHSAssignExpr::ReduceToSingleton"); + + StmtPtr op1_red_stmt; + op1 = op1->Reduce(c, op1_red_stmt); + + auto assign_stmt = make_intrusive(Duplicate()); + + auto field_res = make_intrusive(op1, field_name); + StmtPtr field_res_stmt; + auto res = field_res->ReduceToSingleton(c, field_res_stmt); + + red_stmt = MergeStmts(MergeStmts(op1_red_stmt, assign_stmt), + red_stmt, field_res_stmt); + + return res; + } + +void FieldLHSAssignExpr::ExprDescribe(ODesc* d) const + { + op1->Describe(d); + if ( d->IsReadable() ) + d->Add("$"); + + d->Add(field_name); + + if ( d->IsReadable() ) + d->Add(" $= "); + + op2->Describe(d); + } + + +CoerceToAnyExpr::CoerceToAnyExpr(ExprPtr arg_op) + : UnaryExpr(EXPR_TO_ANY_COERCE, std::move(arg_op)) + { + type = base_type(TYPE_ANY); + } + +ValPtr CoerceToAnyExpr::Fold(Val* v) const + { + return {NewRef{}, v}; + } + +ExprPtr CoerceToAnyExpr::Duplicate() + { + return SetSucc(new CoerceToAnyExpr(op->Duplicate())); + } + + +CoerceFromAnyExpr::CoerceFromAnyExpr(ExprPtr arg_op, TypePtr to_type) + : UnaryExpr(EXPR_FROM_ANY_COERCE, std::move(arg_op)) + { + type = to_type; + } + +ValPtr CoerceFromAnyExpr::Fold(Val* v) const + { + auto t = GetType()->Tag(); + auto vt = v->GetType()->Tag(); + + if ( vt != t && vt != TYPE_ERROR ) + RuntimeError("incompatible \"any\" type"); + + return {NewRef{}, v}; + } + +ExprPtr CoerceFromAnyExpr::Duplicate() + { + return SetSucc(new CoerceFromAnyExpr(op->Duplicate(), type)); + } + + +AnyIndexExpr::AnyIndexExpr(ExprPtr arg_op, int _index) + : UnaryExpr(EXPR_ANY_INDEX, std::move(arg_op)) + { + index = _index; + type = op->GetType(); + } + +ValPtr AnyIndexExpr::Fold(Val* v) const + { + return v->AsListVal()->Idx(index); + } + +ExprPtr AnyIndexExpr::Duplicate() + { + return SetSucc(new AnyIndexExpr(op->Duplicate(), index)); + } + +ExprPtr AnyIndexExpr::Reduce(Reducer* c, StmtPtr& red_stmt) + { + return ThisPtr(); + } + +void AnyIndexExpr::ExprDescribe(ODesc* d) const + { + if ( d->IsReadable() ) + d->Add("("); + + op->Describe(d); + + if ( d->IsReadable() ) + d->Add(")any ["); + + d->Add(index); + + if ( d->IsReadable() ) + d->Add("]"); + } + + +void NopExpr::ExprDescribe(ODesc* d) const + { + if ( d->IsReadable() ) + d->Add("NOP"); + } + +ValPtr NopExpr::Eval(Frame* /* f */) const + { + return nullptr; + } + +ExprPtr NopExpr::Duplicate() + { + return SetSucc(new NopExpr()); + } + +TraversalCode NopExpr::Traverse(TraversalCallback* cb) const + { + TraversalCode tc = cb->PreExpr(this); + HANDLE_TC_EXPR_PRE(tc); + + tc = cb->PostExpr(this); + HANDLE_TC_EXPR_POST(tc); + } + + +static bool same_singletons(ExprPtr e1, ExprPtr e2) + { + auto e1t = e1->Tag(); + auto e2t = e2->Tag(); + + if ( (e1t != EXPR_NAME && e1t != EXPR_CONST) || + (e2t != EXPR_NAME && e2t != EXPR_CONST) ) + return false; + + if ( e1t != e2t ) + return false; + + if ( e1t == EXPR_CONST ) + { + auto c1 = e1->AsConstExpr()->Value(); + auto c2 = e2->AsConstExpr()->Value(); + + if ( ! is_atomic_val(c1) || ! is_atomic_val(c2) ) + return false; + + return same_atomic_val(c1, c2); + } + + auto i1 = e1->AsNameExpr()->Id(); + auto i2 = e2->AsNameExpr()->Id(); + + return i1 == i2; + } + + } // namespace zeek::detail diff --git a/src/script_opt/Reduce.h b/src/script_opt/Reduce.h new file mode 100644 index 0000000000..172295cf37 --- /dev/null +++ b/src/script_opt/Reduce.h @@ -0,0 +1,186 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +#pragma once + +#include "zeek/IntrusivePtr.h" + +namespace zeek::detail { + +class Expr; +class TempVar; +class ProfileFunc; + +class Reducer { +public: + Reducer(Scope* s); + ~Reducer(); + + StmtPtr Reduce(StmtPtr s) + { + return s->Reduce(this); + } + + ExprPtr GenTemporaryExpr(const TypePtr& t, ExprPtr rhs); + + NameExpr* UpdateName(NameExpr* n); + bool NameIsReduced(const NameExpr* n) const; + + void UpdateIDs(IDPList* ids); + bool IDsAreReduced(const IDPList* ids) const; + + void UpdateIDs(std::vector& ids); + bool IDsAreReduced(const std::vector& ids) const; + + IDPtr UpdateID(IDPtr id); + bool ID_IsReduced(const IDPtr& id) const + { return ID_IsReduced(id.get()); } + bool ID_IsReduced(const ID* id) const; + + // This is called *prior* to pushing a new inline block, in + // order to generate the equivalent of function parameters. + NameExprPtr GenInlineBlockName(IDPtr id); + + int NumNewLocals() const { return new_locals.size(); } + + // Returns the name of a temporary for holding the return + // value of the block, or nil if the type indicates there's + // o return value. + NameExprPtr PushInlineBlock(TypePtr type); + void PopInlineBlock(); + + // Whether it's okay to split a statement into two copies for if-else + // expansion. We only allow this to a particular depth because + // beyond that a function body can get too large to analyze. + bool BifurcationOkay() const { return bifurcation_level <= 12; } + int BifurcationLevel() const { return bifurcation_level; } + + void PushBifurcation() { ++bifurcation_level; } + void PopBifurcation() { --bifurcation_level; } + + int NumTemps() const { return temps.length(); } + + // True if this name already reflects the replacement. + bool IsNewLocal(const NameExpr* n) const + { return IsNewLocal(n->Id()); } + bool IsNewLocal(const ID* id) const; + + bool IsTemporary(const ID* id) const + { return FindTemporary(id) != nullptr; } + + // This is a stub for now, since it's not relevant for AST + // reduction by itself. However, many of the Reduce methods + // ultimately will call this predicate to control how they + // function during the second traversal used to optimize + // the reduced form, so we provide the hook now. + bool Optimizing() const { return false; } + + // A stub for now, but ultimately a predicate that indicates whether + // a given reduction pass is being made to prune unused statements. + bool IsPruning() const { return false; } + + // A stub for now, ultimately a predicate that returns true if + // the given statement should be removed due to AST optimization. + bool ShouldOmitStmt(const StmtPtr& s) const { return false; } + + // A stub for now, ultimately provides a replacement for the + // given statement due to AST optimization, or nil if there's + // no replacement. + StmtPtr ReplacementStmt(const StmtPtr& s) const { return nullptr; } + + // 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. + // + // Assumes reduction (including alias propagation) has + // already been applied. + bool IsCSE(const AssignExpr* a, const NameExpr* lhs, const Expr* rhs) + { return false; } + + // 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. + Stmt* 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; } + + const Scope* FuncScope() const { return scope; } + +protected: + bool SameVal(const Val* v1, const Val* v2) const; + + IDPtr GenTemporary(const TypePtr& t, ExprPtr rhs); + TempVar* FindTemporary(const ID* id) const; + + // Retrieve the identifier corresponding to the new local for + // the given expression. Creates the local if necessary. + IDPtr FindNewLocal(ID* id); + IDPtr FindNewLocal(const NameExpr* n) + { return FindNewLocal(n->Id()); } + + // Generate a new local to use in lieu of the original (seen + // in an inlined block). The difference is that the new + // version has a distinct name and has a correct frame offset + // for the current function. + IDPtr GenLocal(ID* orig); + + // 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 temps; + + // Temps for which we've processed their associated expression + // (and they didn't wind up being aliases). + PList expr_temps; + + // Let's us go from an identifier to an associated temporary + // variable, if it corresponds to one. + std::unordered_map ids_to_temps; + + std::unordered_set new_locals; + std::unordered_map orig_to_new_locals; + + // Tracks whether we're inside an inline block, and if so then + // how deeply. + int inline_block_level = 0; + + // Tracks how deeply we are in "bifurcation", i.e., duplicating + // code for if-else cascades. We need to cap this at a certain + // depth or else we can get functions whose size blows up + // exponentially. + int bifurcation_level = 0; + + // For a new expression we've created, map it to the expression + // it's replacing. This allows us to locate the RDs associated + // with the usage. + std::unordered_map new_expr_to_orig; +}; + +// Used for debugging, to communicate which expression wasn't +// reduced when we expected them all to be. +extern const Expr* non_reduced_perp; +extern bool checking_reduction; + +// Used to report a non-reduced expression. +extern bool NonReduced(const Expr* perp); + +} // zeek::detail diff --git a/src/script_opt/TempVar.cc b/src/script_opt/TempVar.cc index 4ce879e37e..e6b818c2e4 100644 --- a/src/script_opt/TempVar.cc +++ b/src/script_opt/TempVar.cc @@ -1,7 +1,7 @@ // See the file "COPYING" in the main distribution directory for copyright. -#include "TempVar.h" -#include "Reporter.h" +#include "zeek/script_opt/TempVar.h" +#include "zeek/Reporter.h" namespace zeek::detail { diff --git a/src/script_opt/TempVar.h b/src/script_opt/TempVar.h index 6360521d11..d79ade7902 100644 --- a/src/script_opt/TempVar.h +++ b/src/script_opt/TempVar.h @@ -5,8 +5,8 @@ // Class for managing temporary variables created during statement reduction // for compilation. -#include "ID.h" -#include "Expr.h" +#include "zeek/ID.h" +#include "zeek/Expr.h" namespace zeek::detail {