reductions of expressions in ASTs - code compiles but doesn't yet link

This commit is contained in:
Vern Paxson 2021-01-10 14:04:01 -08:00
parent 6aa84087b0
commit 10e80dfcd3
8 changed files with 3264 additions and 164 deletions

View file

@ -41,6 +41,10 @@ const char* expr_name(BroExprTag t)
"coerce", "record_coerce", "table_coerce", "vector_coerce", "coerce", "record_coerce", "table_coerce", "vector_coerce",
"sizeof", "cast", "is", "[:]=", "sizeof", "cast", "is", "[:]=",
"inline()", "inline()",
"[]=", "$=",
"vec+=",
"to_any_coerce", "from_any_coerce",
"any[]",
"nop", "nop",
}; };
@ -95,12 +99,24 @@ NameExpr* Expr::AsNameExpr()
return (NameExpr*) this; return (NameExpr*) this;
} }
NameExprPtr Expr::AsNameExprPtr()
{
CHECK_TAG(tag, EXPR_NAME, "ExprVal::AsNameExpr", expr_name)
return {NewRef{}, (NameExpr*) this};
}
const ConstExpr* Expr::AsConstExpr() const const ConstExpr* Expr::AsConstExpr() const
{ {
CHECK_TAG(tag, EXPR_CONST, "ExprVal::AsConstExpr", expr_name) CHECK_TAG(tag, EXPR_CONST, "ExprVal::AsConstExpr", expr_name)
return (const ConstExpr*) this; 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 const CallExpr* Expr::AsCallExpr() const
{ {
CHECK_TAG(tag, EXPR_CALL, "ExprVal::AsCallExpr", expr_name) CHECK_TAG(tag, EXPR_CALL, "ExprVal::AsCallExpr", expr_name)
@ -143,6 +159,12 @@ EventExprPtr Expr::AsEventExprPtr()
return {NewRef{}, (EventExpr*) this}; return {NewRef{}, (EventExpr*) this};
} }
RefExprPtr Expr::AsRefExprPtr()
{
CHECK_TAG(tag, EXPR_REF, "ExprVal::AsRefExpr", expr_name)
return {NewRef{}, (RefExpr*) this};
}
bool Expr::CanAdd() const bool Expr::CanAdd() const
{ {
return false; return false;
@ -182,6 +204,132 @@ void Expr::Assign(Frame* /* f */, ValPtr /* v */)
Internal("Expr::Assign called"); 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 TypePtr Expr::InitType() const
{ {
return type; return type;
@ -312,6 +460,12 @@ NameExpr::NameExpr(IDPtr arg_id, bool const_init)
h->SetUsed(); 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 NameExpr::Eval(Frame* f) const
{ {
ValPtr v; ValPtr v;
@ -436,7 +590,14 @@ ValPtr UnaryExpr::Eval(Frame* f) const
if ( ! v ) if ( ! v )
return nullptr; 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(); VectorVal* v_op = v->AsVectorVal();
VectorTypePtr out_t; VectorTypePtr out_t;
@ -457,10 +618,8 @@ ValPtr UnaryExpr::Eval(Frame* f) const
return result; return result;
} }
else else
{
return Fold(v.get()); return Fold(v.get());
} }
}
bool UnaryExpr::IsPure() const bool UnaryExpr::IsPure() const
{ {
@ -2129,7 +2288,8 @@ void RefExpr::Assign(Frame* f, ValPtr v)
AssignExpr::AssignExpr(ExprPtr arg_op1, AssignExpr::AssignExpr(ExprPtr arg_op1,
ExprPtr arg_op2, ExprPtr arg_op2,
bool arg_is_init, ValPtr arg_val, bool arg_is_init, ValPtr arg_val,
const AttributesPtr& attrs) const AttributesPtr& attrs,
bool typecheck)
: BinaryExpr(EXPR_ASSIGN, arg_is_init ? : BinaryExpr(EXPR_ASSIGN, arg_is_init ?
std::move(arg_op1) : arg_op1->MakeLvalue(), std::move(arg_op1) : arg_op1->MakeLvalue(),
std::move(arg_op2)) std::move(arg_op2))
@ -2152,6 +2312,7 @@ AssignExpr::AssignExpr(ExprPtr arg_op1,
return; return;
} }
if ( typecheck )
// We discard the status from TypeCheck since it has already // We discard the status from TypeCheck since it has already
// generated error messages. // generated error messages.
(void) TypeCheck(attrs); (void) TypeCheck(attrs);
@ -2754,16 +2915,6 @@ ValPtr IndexExpr::Eval(Frame* f) const
return Fold(v1.get(), v2.get()); 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 ValPtr IndexExpr::Fold(Val* v1, Val* v2) const
{ {
if ( IsError() ) if ( IsError() )
@ -2855,105 +3006,9 @@ void IndexExpr::Assign(Frame* f, ValPtr v)
return; return;
auto v1 = op1->Eval(f); auto v1 = op1->Eval(f);
if ( ! v1 )
return;
auto v2 = op2->Eval(f); auto v2 = op2->Eval(f);
if ( ! v1 || ! v2 ) AssignToIndex(v1, v2, v);
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;
}
} }
void IndexExpr::ExprDescribe(ODesc* d) const void IndexExpr::ExprDescribe(ODesc* d) const
@ -5168,7 +5223,16 @@ bool check_and_promote_args(ListExpr* const args, RecordType* types)
return false; 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 ) for ( const auto& elem : def_elements )

View file

@ -68,6 +68,15 @@ enum BroExprTag : int {
EXPR_IS, EXPR_IS,
EXPR_INDEX_SLICE_ASSIGN, EXPR_INDEX_SLICE_ASSIGN,
EXPR_INLINE, 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, EXPR_NOP,
#define NUM_EXPRS (int(EXPR_NOP) + 1) #define NUM_EXPRS (int(EXPR_NOP) + 1)
@ -75,18 +84,27 @@ enum BroExprTag : int {
extern const char* expr_name(BroExprTag t); extern const char* expr_name(BroExprTag t);
class ListExpr;
class NameExpr;
class ConstExpr;
class IndexExpr;
class AssignExpr; class AssignExpr;
class CallExpr; class CallExpr;
class ConstExpr;
class EventExpr; class EventExpr;
class Stmt; class FieldAssignExpr;
class FieldExpr;
class ForExpr;
class IndexExpr;
class ListExpr;
class NameExpr;
class RefExpr;
class Expr; class Expr;
using ExprPtr = IntrusivePtr<Expr>; using CallExprPtr = IntrusivePtr<CallExpr>;
using ConstExprPtr = IntrusivePtr<ConstExpr>;
using EventExprPtr = IntrusivePtr<EventExpr>; using EventExprPtr = IntrusivePtr<EventExpr>;
using ExprPtr = IntrusivePtr<Expr>;
using NameExprPtr = IntrusivePtr<NameExpr>;
using RefExprPtr = IntrusivePtr<RefExpr>;
class Stmt;
using StmtPtr = IntrusivePtr<Stmt>; using StmtPtr = IntrusivePtr<Stmt>;
class Expr : public Obj { class Expr : public Obj {
@ -182,13 +200,17 @@ public:
ctype* As ## ctype (); \ ctype* As ## ctype (); \
IntrusivePtr<ctype> As ## ctype ## Ptr (); IntrusivePtr<ctype> 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(ListExpr)
ZEEK_EXPR_ACCESSOR_DECLS(NameExpr) ZEEK_EXPR_ACCESSOR_DECLS(NameExpr)
ZEEK_EXPR_ACCESSOR_DECLS(ConstExpr) ZEEK_EXPR_ACCESSOR_DECLS(RefExpr)
ZEEK_EXPR_ACCESSOR_DECLS(CallExpr)
ZEEK_EXPR_ACCESSOR_DECLS(AssignExpr)
ZEEK_EXPR_ACCESSOR_DECLS(IndexExpr)
ZEEK_EXPR_ACCESSOR_DECLS(EventExpr)
void Describe(ODesc* d) const override final; void Describe(ODesc* d) const override final;
@ -200,6 +222,117 @@ public:
// Recursively traverses the AST to inline eligible function calls. // Recursively traverses the AST to inline eligible function calls.
virtual ExprPtr Inline(Inliner* inl) { return ThisPtr(); } 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, // 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 // 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. // 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); explicit NameExpr(IDPtr id, bool const_init = false);
ID* Id() const { return id.get(); } ID* Id() const { return id.get(); }
IDPtr IdPtr();
ValPtr Eval(Frame* f) const override; ValPtr Eval(Frame* f) const override;
void Assign(Frame* f, ValPtr v) override; void Assign(Frame* f, ValPtr v) override;
@ -282,10 +416,20 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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: protected:
void ExprDescribe(ODesc* d) const override; 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; IDPtr id;
bool in_const_init; bool in_const_init;
}; };
@ -303,6 +447,7 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ValPtr FoldVal() const override { return val; }
protected: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -325,6 +470,14 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Inline(Inliner* inl) override; 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: protected:
UnaryExpr(BroExprTag arg_tag, ExprPtr arg_op); UnaryExpr(BroExprTag arg_tag, ExprPtr arg_op);
@ -353,6 +506,17 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Inline(Inliner* inl) override; 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: protected:
BinaryExpr(BroExprTag arg_tag, BinaryExpr(BroExprTag arg_tag,
ExprPtr arg_op1, ExprPtr arg_op2) ExprPtr arg_op1, ExprPtr arg_op2)
@ -425,6 +589,12 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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 { class ComplementExpr final : public UnaryExpr {
@ -433,6 +603,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr Fold(Val* v) const override; ValPtr Fold(Val* v) const override;
@ -444,6 +616,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr Fold(Val* v) const override; ValPtr Fold(Val* v) const override;
@ -455,6 +629,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr Fold(Val* v) const override; ValPtr Fold(Val* v) const override;
@ -466,6 +642,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr Fold(Val* v) const override; ValPtr Fold(Val* v) const override;
@ -490,6 +668,11 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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 { class AddToExpr final : public BinaryExpr {
@ -499,6 +682,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override { return true; }
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class RemoveFromExpr final : public BinaryExpr { class RemoveFromExpr final : public BinaryExpr {
@ -508,6 +693,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override { return true; }
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class SubExpr final : public BinaryExpr { class SubExpr final : public BinaryExpr {
@ -516,6 +703,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class TimesExpr final : public BinaryExpr { class TimesExpr final : public BinaryExpr {
@ -525,6 +714,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class DivideExpr final : public BinaryExpr { class DivideExpr final : public BinaryExpr {
@ -533,6 +724,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr AddrFold(Val* v1, Val* v2) const override; ValPtr AddrFold(Val* v1, Val* v2) const override;
@ -555,6 +748,13 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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 { class BitExpr final : public BinaryExpr {
@ -563,6 +763,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class EqExpr final : public BinaryExpr { class EqExpr final : public BinaryExpr {
@ -572,6 +774,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr Fold(Val* v1, Val* v2) const override; ValPtr Fold(Val* v1, Val* v2) const override;
@ -584,6 +788,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
}; };
class CondExpr final : public Expr { class CondExpr final : public Expr {
@ -603,6 +809,20 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ExprPtr Inline(Inliner* inl) 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: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -620,6 +840,14 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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 { class AssignExpr : public BinaryExpr {
@ -628,7 +856,8 @@ public:
// yet still perform the assignment. Used for triggers. // yet still perform the assignment. Used for triggers.
AssignExpr(ExprPtr op1, ExprPtr op2, bool is_init, AssignExpr(ExprPtr op1, ExprPtr op2, bool is_init,
ValPtr val = nullptr, ValPtr val = nullptr,
const AttributesPtr& attrs = nullptr); const AttributesPtr& attrs = nullptr,
bool type_check = true);
ValPtr Eval(Frame* f) const override; ValPtr Eval(Frame* f) const override;
void EvalIntoAggregate(const zeek::Type* t, Val* aggr, 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; ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override;
bool IsPure() const override; bool IsPure() const override;
void SetOp2(ExprPtr e)
{
op2 = std::move(e);
}
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; 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: protected:
bool TypeCheck(const AttributesPtr& attrs = nullptr); bool TypeCheck(const AttributesPtr& attrs = nullptr);
bool TypeCheckArithmetics(TypeTag bt1, TypeTag bt2); bool TypeCheckArithmetics(TypeTag bt1, TypeTag bt2);
bool is_init; bool is_init;
ValPtr val; // optional ValPtr val; // optional
// Optimization-related:
bool is_temp = false;
}; };
class IndexSliceAssignExpr final : public AssignExpr { class IndexSliceAssignExpr final : public AssignExpr {
@ -688,6 +926,9 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
StmtPtr ReduceToSingletons(Reducer* c) override;
protected: protected:
ValPtr Fold(Val* v1, Val* v2) const override; ValPtr Fold(Val* v1, Val* v2) const override;
@ -797,6 +1038,10 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
StmtPtr ReduceToSingletons(Reducer* c) override;
protected: protected:
ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override;
@ -823,6 +1068,10 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
StmtPtr ReduceToSingletons(Reducer* c) override;
protected: protected:
ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override;
@ -849,6 +1098,10 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
StmtPtr ReduceToSingletons(Reducer* c) override;
protected: protected:
ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override;
@ -867,6 +1120,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
protected: protected:
ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override; ValPtr InitVal(const zeek::Type* t, ValPtr aggr) const override;
@ -885,6 +1140,9 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override { return true; }
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -898,6 +1156,9 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool WillTransform(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
ValPtr FoldSingleVal(Val* v, InternalTypeTag t) const; ValPtr FoldSingleVal(Val* v, InternalTypeTag t) const;
ValPtr Fold(Val* v) const override; ValPtr Fold(Val* v) const override;
@ -974,6 +1235,16 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ExprPtr Inline(Inliner* inl) 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: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -988,6 +1259,8 @@ public:
// Optimization-related: // Optimization-related:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool HasReducedOps(Reducer* c) const override;
protected: protected:
ValPtr Fold(Val* v1, Val* v2) const override; ValPtr Fold(Val* v1, Val* v2) const override;
@ -1011,6 +1284,11 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ExprPtr Inline(Inliner* inl) 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: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -1038,6 +1316,8 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ExprPtr Inline(Inliner* inl) override; ExprPtr Inline(Inliner* inl) override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
protected: protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
@ -1048,30 +1328,8 @@ private:
std::string my_name; std::string my_name;
}; };
class EventExpr final : public Expr { // This comes before EventExpr so that EventExpr::GetOp1 can return its
public: // arguments as convertible to ExprPtr.
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;
};
class ListExpr : public Expr { class ListExpr : public Expr {
public: public:
ListExpr(); ListExpr();
@ -1099,6 +1357,11 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
ExprPtr Inline(Inliner* inl) 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: protected:
ValPtr AddSetInit(const zeek::Type* t, ValPtr aggr) const; ValPtr AddSetInit(const zeek::Type* t, ValPtr aggr) const;
@ -1107,6 +1370,38 @@ protected:
ExprPList exprs; 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 { class RecordAssignExpr final : public ListExpr {
public: public:
@ -1155,6 +1450,9 @@ public:
ExprPtr Duplicate() override; ExprPtr Duplicate() override;
bool IsReduced(Reducer* c) const override;
ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override;
TraversalCode Traverse(TraversalCallback* cb) const override; TraversalCode Traverse(TraversalCallback* cb) const override;
protected: protected:
@ -1166,6 +1464,134 @@ protected:
StmtPtr body; 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 inline Val* Expr::ExprVal() const
{ {

View file

@ -11,9 +11,12 @@
#include "zeek/ID.h" #include "zeek/ID.h"
ZEEK_FORWARD_DECLARE_NAMESPACED(CompositeHash, zeek::detail); ZEEK_FORWARD_DECLARE_NAMESPACED(CompositeHash, zeek::detail);
ZEEK_FORWARD_DECLARE_NAMESPACED(NameExpr, zeek::detail);
namespace zeek::detail { namespace zeek::detail {
using NameExprPtr = IntrusivePtr<zeek::detail::NameExpr>;
class ExprListStmt : public Stmt { class ExprListStmt : public Stmt {
public: public:
const ListExpr* ExprList() const { return l.get(); } const ListExpr* ExprList() const { return l.get(); }
@ -366,6 +369,11 @@ public:
StmtPtr Duplicate() override; StmtPtr Duplicate() override;
void Inline(Inliner* inl) 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: protected:
bool IsPure() const override; bool IsPure() const override;
@ -439,6 +447,74 @@ protected:
bool is_return; 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 } // namespace zeek::detail
using ExprListStmt [[deprecated("Remove in v4.1. Use zeek::detail::ExprListStmt instead.")]] = zeek::detail::ExprListStmt; using ExprListStmt [[deprecated("Remove in v4.1. Use zeek::detail::ExprListStmt instead.")]] = zeek::detail::ExprListStmt;

View file

@ -24,11 +24,13 @@ using ValPtr = IntrusivePtr<Val>;
namespace zeek::detail { namespace zeek::detail {
class StmtList; class ExprStmt;
class ForStmt; class ForStmt;
class InitStmt; class InitStmt;
class WhenStmt; class ReturnStmt;
class StmtList;
class SwitchStmt; class SwitchStmt;
class WhenStmt;
class EventExpr; class EventExpr;
class ListExpr; class ListExpr;
@ -37,6 +39,7 @@ using EventExprPtr = IntrusivePtr<EventExpr>;
using ListExprPtr = IntrusivePtr<ListExpr>; using ListExprPtr = IntrusivePtr<ListExpr>;
class Inliner; class Inliner;
class Reducer;
class Stmt; class Stmt;
using StmtPtr = IntrusivePtr<Stmt>; using StmtPtr = IntrusivePtr<Stmt>;
@ -50,6 +53,7 @@ public:
virtual ValPtr Exec(Frame* f, StmtFlowType& flow) const = 0; virtual ValPtr Exec(Frame* f, StmtFlowType& flow) const = 0;
Stmt* Ref() { zeek::Ref(this); return this; } Stmt* Ref() { zeek::Ref(this); return this; }
StmtPtr ThisPtr() { return {NewRef{}, this}; }
bool SetLocationInfo(const Location* loc) override bool SetLocationInfo(const Location* loc) override
{ return Stmt::SetLocationInfo(loc, loc); } { return Stmt::SetLocationInfo(loc, loc); }
@ -64,7 +68,9 @@ public:
ForStmt* AsForStmt(); ForStmt* AsForStmt();
const ForStmt* AsForStmt() const; const ForStmt* AsForStmt() const;
const ExprStmt* AsExprStmt() const;
const InitStmt* AsInitStmt() const; const InitStmt* AsInitStmt() const;
const ReturnStmt* AsReturnStmt() const;
const WhenStmt* AsWhenStmt() const; const WhenStmt* AsWhenStmt() const;
const SwitchStmt* AsSwitchStmt() const; const SwitchStmt* AsSwitchStmt() const;
@ -81,12 +87,37 @@ public:
virtual TraversalCode Traverse(TraversalCallback* cb) const = 0; 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; virtual StmtPtr Duplicate() = 0;
// Recursively traverses the AST to inline eligible function calls. // Recursively traverses the AST to inline eligible function calls.
virtual void Inline(Inliner* inl) { } 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, // 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 // 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. // rather than a StmtPtr to emphasize that the access is read-only.
@ -124,6 +155,11 @@ public:
protected: protected:
explicit Stmt(StmtTag arg_tag); 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; void AddTag(ODesc* d) const;
virtual void StmtDescribe(ODesc* d) const; virtual void StmtDescribe(ODesc* d) const;
void DescribeDone(ODesc* d) const; void DescribeDone(ODesc* d) const;

File diff suppressed because it is too large Load diff

186
src/script_opt/Reduce.h Normal file
View file

@ -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<IDPtr>& ids);
bool IDsAreReduced(const std::vector<IDPtr>& 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<TempVar> temps;
// Temps for which we've processed their associated expression
// (and they didn't wind up being aliases).
PList<TempVar> expr_temps;
// Let's us go from an identifier to an associated temporary
// variable, if it corresponds to one.
std::unordered_map<const ID*, TempVar*> ids_to_temps;
std::unordered_set<ID*> new_locals;
std::unordered_map<const ID*, IDPtr> 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<const Expr*, const Expr*> 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

View file

@ -1,7 +1,7 @@
// See the file "COPYING" in the main distribution directory for copyright. // See the file "COPYING" in the main distribution directory for copyright.
#include "TempVar.h" #include "zeek/script_opt/TempVar.h"
#include "Reporter.h" #include "zeek/Reporter.h"
namespace zeek::detail { namespace zeek::detail {

View file

@ -5,8 +5,8 @@
// Class for managing temporary variables created during statement reduction // Class for managing temporary variables created during statement reduction
// for compilation. // for compilation.
#include "ID.h" #include "zeek/ID.h"
#include "Expr.h" #include "zeek/Expr.h"
namespace zeek::detail { namespace zeek::detail {