zeek/src/script_opt/Expr.cc
2023-09-01 12:20:35 -07:00

3194 lines
70 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
// Optimization-related methods for Expr classes.
#include "zeek/Expr.h"
#include "zeek/Desc.h"
#include "zeek/Frame.h"
#include "zeek/Func.h"
#include "zeek/Reporter.h"
#include "zeek/Scope.h"
#include "zeek/Stmt.h"
#include "zeek/Traverse.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;
}
const FieldExpr* Expr::AsFieldExpr() const
{
CHECK_TAG(tag, EXPR_FIELD, "ExprVal::AsFieldExpr", expr_name)
return (const FieldExpr*)this;
}
FieldExpr* Expr::AsFieldExpr()
{
CHECK_TAG(tag, EXPR_FIELD, "ExprVal::AsFieldExpr", expr_name)
return (FieldExpr*)this;
}
IntrusivePtr<FieldAssignExpr> Expr::AsFieldAssignExprPtr()
{
CHECK_TAG(tag, EXPR_FIELD_ASSIGN, "ExprVal::AsFieldAssignExpr", expr_name)
return {NewRef{}, (FieldAssignExpr*)this};
}
const IndexAssignExpr* Expr::AsIndexAssignExpr() const
{
CHECK_TAG(tag, EXPR_INDEX_ASSIGN, "ExprVal::AsIndexAssignExpr", expr_name)
return (const IndexAssignExpr*)this;
}
const FieldLHSAssignExpr* Expr::AsFieldLHSAssignExpr() const
{
CHECK_TAG(tag, EXPR_FIELD_LHS_ASSIGN, "ExprVal::AsFieldLHSAssignExpr", expr_name)
return (const FieldLHSAssignExpr*)this;
}
HasFieldExpr* Expr::AsHasFieldExpr()
{
CHECK_TAG(tag, EXPR_HAS_FIELD, "ExprVal::AsHasFieldExpr", expr_name)
return (HasFieldExpr*)this;
}
const HasFieldExpr* Expr::AsHasFieldExpr() const
{
CHECK_TAG(tag, EXPR_HAS_FIELD, "ExprVal::AsHasFieldExpr", expr_name)
return (const HasFieldExpr*)this;
}
const AddToExpr* Expr::AsAddToExpr() const
{
CHECK_TAG(tag, EXPR_ADD_TO, "ExprVal::AsAddToExpr", expr_name)
return (const AddToExpr*)this;
}
const IsExpr* Expr::AsIsExpr() const
{
CHECK_TAG(tag, EXPR_IS, "ExprVal::AsIsExpr", expr_name)
return (const IsExpr*)this;
}
CallExpr* Expr::AsCallExpr()
{
CHECK_TAG(tag, EXPR_CALL, "ExprVal::AsCallExpr", expr_name)
return (CallExpr*)this;
}
FieldAssignExpr* Expr::AsFieldAssignExpr()
{
CHECK_TAG(tag, EXPR_FIELD_ASSIGN, "ExprVal::AsFieldAssignExpr", expr_name)
return (FieldAssignExpr*)this;
}
const RecordCoerceExpr* Expr::AsRecordCoerceExpr() const
{
CHECK_TAG(tag, EXPR_RECORD_COERCE, "ExprVal::AsRecordCoerceExpr", expr_name)
return (const RecordCoerceExpr*)this;
}
const RecordConstructorExpr* Expr::AsRecordConstructorExpr() const
{
CHECK_TAG(tag, EXPR_RECORD_CONSTRUCTOR, "ExprVal::AsRecordConstructorExpr", expr_name)
return (const RecordConstructorExpr*)this;
}
const TableConstructorExpr* Expr::AsTableConstructorExpr() const
{
CHECK_TAG(tag, EXPR_TABLE_CONSTRUCTOR, "ExprVal::AsTableConstructorExpr", expr_name)
return (const TableConstructorExpr*)this;
}
const SetConstructorExpr* Expr::AsSetConstructorExpr() const
{
CHECK_TAG(tag, EXPR_SET_CONSTRUCTOR, "ExprVal::AsSetConstructorExpr", expr_name)
return (const SetConstructorExpr*)this;
}
RefExpr* Expr::AsRefExpr()
{
CHECK_TAG(tag, EXPR_REF, "ExprVal::AsRefExpr", expr_name)
return (RefExpr*)this;
}
const InlineExpr* Expr::AsInlineExpr() const
{
CHECK_TAG(tag, EXPR_INLINE, "ExprVal::AsInlineExpr", expr_name)
return (const InlineExpr*)this;
}
AnyIndexExpr* Expr::AsAnyIndexExpr()
{
CHECK_TAG(tag, EXPR_ANY_INDEX, "ExprVal::AsAnyIndexExpr", expr_name)
return (AnyIndexExpr*)this;
}
const AnyIndexExpr* Expr::AsAnyIndexExpr() const
{
CHECK_TAG(tag, EXPR_ANY_INDEX, "ExprVal::AsAnyIndexExpr", expr_name)
return (const AnyIndexExpr*)this;
}
LambdaExpr* Expr::AsLambdaExpr()
{
CHECK_TAG(tag, EXPR_LAMBDA, "ExprVal::AsLambdaExpr", expr_name)
return (LambdaExpr*)this;
}
const LambdaExpr* Expr::AsLambdaExpr() const
{
CHECK_TAG(tag, EXPR_LAMBDA, "ExprVal::AsLambdaExpr", expr_name)
return (const LambdaExpr*)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_LSHIFT:
case EXPR_RSHIFT:
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(std::move(red1_stmt), std::move(red2_stmt), std::move(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->IsSingleton(c) )
{
auto red2_stmt = ReduceToSingletons(c);
auto res = ReduceToConditional(c, red_stmt);
red_stmt = MergeStmts(std::move(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<AssignExpr>(result_tmp->MakeLvalue(), e, false, nullptr, nullptr,
false);
a_e->SetIsTemp();
a_e->SetOriginal(ThisPtr());
auto a_e_s = make_intrusive<ExprStmt>(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 misassociate that
// information with both the assignment creating the temporary,
// and the subsequent use of the temporary.
return result_tmp->Duplicate();
}
ExprPtr Expr::TransformMe(ExprPtr new_me, Reducer* c, StmtPtr& red_stmt)
{
if ( new_me == this )
return new_me;
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;
}
StmtPtr Expr::MergeStmts(StmtPtr s1, StmtPtr s2, StmtPtr s3) const
{
int nums = (s1 != nullptr) + (s2 != nullptr) + (s3 != nullptr);
if ( nums > 1 )
return make_intrusive<StmtList>(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<DoubleVal>(0.0);
case TYPE_TIME:
return make_intrusive<TimeVal>(0.0);
case TYPE_INTERVAL:
return make_intrusive<IntervalVal>(0.0, 1.0);
default:
reporter->InternalError("bad call to MakeZero");
}
}
ConstExprPtr Expr::MakeZeroExpr(TypeTag t) const
{
return make_intrusive<ConstExpr>(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(make_intrusive<ConstExpr>(v), c, red_stmt);
}
return c->UpdateName({NewRef{}, this});
}
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(make_intrusive<ConstExpr>(fold), c, red_stmt);
}
if ( c->Optimizing() )
return ThisPtr();
else
return AssignToTemporary(c, red_stmt);
}
ExprPtr BinaryExpr::Inline(Inliner* inl)
{
op1 = op1->Inline(inl);
op2 = op2->Inline(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, std::move(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(make_intrusive<ConstExpr>(fold), c, red_stmt);
}
if ( c->Optimizing() )
return ThisPtr();
else
return AssignToTemporary(c, red_stmt);
}
ExprPtr CloneExpr::Duplicate()
{
// oh the irony
return SetSucc(new CloneExpr(op->Duplicate()));
}
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<ConstExpr>(val_mgr->Count(1));
incr_const->SetOriginal(ThisPtr());
ExprPtr incr_expr;
if ( Tag() == EXPR_INCR )
incr_expr = make_intrusive<AddExpr>(target, incr_const);
else
incr_expr = make_intrusive<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<IndexExpr>(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<FieldExpr>(dup1, field_name);
}
else
reporter->InternalError("confused in IncrExpr::Reduce");
auto assign = make_intrusive<AssignExpr>(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<ExprStmt>(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()
{
return SetSucc(new SizeExpr(op->Duplicate()));
}
ExprPtr AddExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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<SubExpr>(op1, rhs);
sub->SetOriginal(ThisPtr());
return sub;
}
ExprPtr AddToExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
return SetSucc(new AddToExpr(op1_d, op2_d));
}
bool AddToExpr::IsReduced(Reducer* c) const
{
auto t = op1->GetType();
auto tag = t->Tag();
if ( tag == TYPE_PATTERN )
return op1->HasReducedOps(c) && op2->IsReduced(c);
if ( tag == TYPE_TABLE )
return op1->IsReduced(c) && op2->IsReduced(c);
if ( tag == TYPE_VECTOR && IsVector(op2->GetType()->Tag()) && same_type(t, op2->GetType()) )
return op1->IsReduced(c) && op2->IsReduced(c);
return NonReduced(this);
}
ExprPtr AddToExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
{
auto tag = op1->GetType()->Tag();
switch ( tag )
{
case TYPE_PATTERN:
case TYPE_TABLE:
case TYPE_VECTOR:
{
StmtPtr red_stmt1;
StmtPtr red_stmt2;
if ( tag == TYPE_PATTERN && op1->Tag() == EXPR_FIELD )
red_stmt1 = op1->ReduceToSingletons(c);
else
op1 = op1->Reduce(c, red_stmt1);
auto& t = op1->GetType();
op2 = op2->Reduce(c, red_stmt2);
red_stmt = MergeStmts(red_stmt1, red_stmt2);
if ( tag == TYPE_VECTOR &&
(! IsVector(op2->GetType()->Tag()) || ! same_type(t, op2->GetType())) )
{
auto append = make_intrusive<AppendToExpr>(op1->Duplicate(), op2);
append->SetOriginal(ThisPtr());
auto append_stmt = make_intrusive<ExprStmt>(append);
red_stmt = MergeStmts(red_stmt, append_stmt);
return op1;
}
return ThisPtr();
}
default:
{
auto rhs = op1->AsRefExprPtr()->GetOp1();
auto do_incr = make_intrusive<AddExpr>(rhs->Duplicate(), op2);
auto assign = make_intrusive<AssignExpr>(op1, do_incr, false, nullptr, nullptr, false);
return assign->ReduceToSingleton(c, red_stmt);
}
}
}
ExprPtr AddToExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt)
{
auto at_stmt = make_intrusive<ExprStmt>(Duplicate());
red_stmt = at_stmt->Reduce(c);
return op1;
}
ExprPtr SubExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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 = make_intrusive<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()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
return SetSucc(new RemoveFromExpr(op1_d, op2_d));
}
bool RemoveFromExpr::IsReduced(Reducer* c) const
{
if ( op1->GetType()->Tag() == TYPE_TABLE )
return op1->IsReduced(c) && op2->IsReduced(c);
return NonReduced(this);
}
ExprPtr RemoveFromExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
{
if ( op1->GetType()->Tag() == TYPE_TABLE )
{
StmtPtr red_stmt1;
StmtPtr red_stmt2;
op1 = op1->Reduce(c, red_stmt1);
op2 = op2->Reduce(c, red_stmt2);
red_stmt = MergeStmts(red_stmt1, red_stmt2);
return ThisPtr();
}
auto lhs = op1->AsRefExprPtr()->GetOp1();
auto do_decr = make_intrusive<SubExpr>(lhs->Duplicate(), op2);
auto assign = make_intrusive<AssignExpr>(op1, do_decr, false, nullptr, nullptr, false);
return assign->Reduce(c, red_stmt);
}
ExprPtr RemoveFromExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt)
{
auto rf_stmt = make_intrusive<ExprStmt>(Duplicate());
red_stmt = rf_stmt->Reduce(c);
return op1;
}
ExprPtr TimesExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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 )
{
if ( op1->IsZero() )
return c->Fold(op1);
else
return c->Fold(op2);
}
return BinaryExpr::Reduce(c, red_stmt);
}
ExprPtr DivideExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
return SetSucc(new ModExpr(op1_d, op2_d));
}
// 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(const ExprPtr& e, IDPtr& id, std::vector<ConstExprPtr>& 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;
const 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<ConstExprPtr>& patterns)
{
ASSERT(patterns.size() > 1);
ExprPtr e = patterns[0];
for ( auto& p : patterns )
e = make_intrusive<BitExpr>(EXPR_OR, e, p);
return e;
}
ExprPtr BoolExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
return SetSucc(new BoolExpr(tag, op1_d, op2_d));
}
bool BoolExpr::WillTransform(Reducer* c) const
{
return ! IsVector(op1->GetType()->Tag());
}
bool BoolExpr::WillTransformInConditional(Reducer* c) const
{
IDPtr common_id;
std::vector<ConstExprPtr> 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<ConstExprPtr> patterns;
if ( tag == EXPR_OR_OR && is_pattern_cascade(ThisPtr(), common_id, patterns) )
{
auto new_pat = build_disjunction(patterns);
auto new_id = make_intrusive<NameExpr>(common_id);
auto new_node = make_intrusive<InExpr>(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);
}
}
if ( IsVector(op1->GetType()->Tag()) )
{
if ( c->Optimizing() )
return ThisPtr();
else
return AssignToTemporary(c, red_stmt);
}
auto else_val = is_and ? val_mgr->False() : val_mgr->True();
ExprPtr else_e = make_intrusive<ConstExpr>(else_val);
ExprPtr cond;
if ( is_and )
cond = make_intrusive<CondExpr>(op1, op2, else_e);
else
cond = make_intrusive<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()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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 ( ! IsIntegral(GetType()->Tag()) )
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 or LSHIFT or RSHIFT
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 = make_intrusive<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()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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<ConstExpr>(val_mgr->Bool(t));
res->SetOriginal(ThisPtr());
return res->Reduce(c, red_stmt);
}
return BinaryExpr::Reduce(c, red_stmt);
}
ExprPtr RelExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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 = make_intrusive<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()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
auto op3_d = op3->Duplicate();
return SetSucc(new CondExpr(op1_d, op2_d, op3_d));
}
ExprPtr CondExpr::Inline(Inliner* inl)
{
op1 = op1->Inline(inl);
op2 = op2->Inline(inl);
op3 = op3->Inline(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 ( op2->IsConst() && op3->IsConst() && GetType()->Tag() == TYPE_BOOL )
{
auto op2_t = op2->IsOne();
ASSERT(op2_t != op3->IsOne());
red_stmt = MergeStmts(op1_red_stmt, red_stmt);
if ( op2_t )
// This is "var ? T : F", which can be replaced by var.
return op1;
// Instead we have "var ? F : T".
return make_intrusive<NotExpr>(op1);
}
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<NullStmt>();
if ( ! red3_stmt )
red3_stmt = make_intrusive<NullStmt>();
if_else = make_intrusive<IfStmt>(op1->Duplicate(), std::move(red2_stmt),
std::move(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<RefExpr>(op);
StmtPtr red_stmt2;
op = AssignToTemporary(op_ref, c, red_stmt2);
return MergeStmts(red_stmt1, red_stmt2);
}
ExprPtr AssignExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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;
if ( val )
// Initializations of "local" variables in "when" statements
// are never reduced.
return false;
const auto& t1 = op1->GetType();
const auto& t2 = op2->GetType();
auto lhs_is_any = t1->Tag() == TYPE_ANY;
auto rhs_is_any = t2->Tag() == TYPE_ANY;
if ( lhs_is_any != rhs_is_any && op2->Tag() != EXPR_CONST )
return NonReduced(this);
if ( t1->Tag() == TYPE_VECTOR && t1->Yield()->Tag() != TYPE_ANY && t2->Yield() &&
t2->Yield()->Tag() == TYPE_ANY )
return NonReduced(this);
if ( op1->Tag() == 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.
auto orig_op2 = op2;
op2 = c->UpdateExpr(op2);
if ( op2 != orig_op2 && op2->Tag() == EXPR_CONST && op1->Tag() == EXPR_REF )
{
auto lhs = op1->GetOp1();
auto op2_c = cast_intrusive<ConstExpr>(op2);
if ( lhs->Tag() == EXPR_NAME )
c->FoldedTo(orig_op2, op2_c);
}
return ThisPtr();
}
if ( IsTemp() )
// These are generated for reduced expressions.
return ThisPtr();
if ( val )
{
// These are reduced to the assignment followed by
// the assignment value.
auto assign_val = make_intrusive<ConstExpr>(val);
val = nullptr;
red_stmt = make_intrusive<ExprStmt>(ThisPtr());
return assign_val;
}
auto& t1 = op1->GetType();
auto& t2 = op2->GetType();
auto lhs_is_any = t1->Tag() == TYPE_ANY;
auto rhs_is_any = t2->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<CoerceToAnyExpr>(red_rhs);
}
else
op2 = make_intrusive<CoerceFromAnyExpr>(red_rhs, t1);
op2->SetLocationInfo(op2_loc);
}
if ( t1->Tag() == TYPE_VECTOR && t1->Yield()->Tag() != TYPE_ANY && t2->Yield() &&
t2->Yield()->Tag() == TYPE_ANY )
{
auto op2_loc = op2->GetLocationInfo();
ExprPtr red_rhs = op2->ReduceToSingleton(c, rhs_reduce);
op2 = make_intrusive<CoerceFromAnyVecExpr>(red_rhs, t1);
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 = make_intrusive<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 = util::copy_string(field_e->FieldName());
auto field = field_e->Field();
auto field_assign = make_intrusive<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<CheckAnyLenStmt>(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<AnyIndexExpr>(rhs_dup, i);
auto lhs = lhs_list[i]->ThisPtr();
auto assign = make_intrusive<AssignExpr>(lhs, rhs, false, nullptr, nullptr, false);
auto assign_stmt = make_intrusive<ExprStmt>(assign);
red_stmt = MergeStmts(red_stmt, assign_stmt);
}
return TransformMe(make_intrusive<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 = c->Fold(op2);
// 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), or the assignment
// value if present.
if ( op1->Tag() != EXPR_REF )
Internal("Confusion in AssignExpr::ReduceToSingleton");
ExprPtr assign_expr = Duplicate();
auto ae_stmt = make_intrusive<ExprStmt>(assign_expr);
red_stmt = ae_stmt->Reduce(c);
if ( val )
return make_intrusive<ConstExpr>(val);
auto lhs = op1->AsRefExprPtr()->GetOp1();
StmtPtr lhs_stmt;
auto new_op1 = lhs->ReduceToSingleton(c, lhs_stmt);
red_stmt = MergeStmts(red_stmt, lhs_stmt);
return new_op1;
}
ExprPtr IndexSliceAssignExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->Duplicate();
return SetSucc(new IndexSliceAssignExpr(op1_d, op2_d, is_init));
}
ExprPtr IndexExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_l = op2->Duplicate()->AsListExprPtr();
return SetSucc(new IndexExpr(op1_d, op2_l, is_slice, is_inside_when));
}
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, std::move(red2_stmt));
}
ExprPtr IndexExprWhen::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_l = op2->Duplicate()->AsListExprPtr();
return SetSucc(new IndexExprWhen(op1_d, op2_l, is_slice));
}
ExprPtr FieldExpr::Duplicate()
{
return SetSucc(new FieldExpr(op->Duplicate(), field_name));
}
ExprPtr HasFieldExpr::Duplicate()
{
return SetSucc(new HasFieldExpr(op->Duplicate(), util::copy_string(field_name)));
}
bool HasFieldExpr::IsReduced(Reducer* c) const
{
return op->GetType<RecordType>()->FieldHasAttr(field, ATTR_OPTIONAL);
}
ExprPtr HasFieldExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
{
if ( ! op->GetType<RecordType>()->FieldHasAttr(field, ATTR_OPTIONAL) )
{
auto true_constant = make_intrusive<ConstExpr>(val_mgr->True());
return TransformMe(std::move(true_constant), c, red_stmt);
}
return UnaryExpr::Reduce(c, red_stmt);
}
ExprPtr RecordConstructorExpr::Duplicate()
{
auto op_l = op->Duplicate()->AsListExprPtr();
if ( map )
{
auto rt = cast_intrusive<RecordType>(type);
return SetSucc(new RecordConstructorExpr(rt, op_l));
}
else
return SetSucc(new RecordConstructorExpr(op_l));
}
ExprPtr RecordConstructorExpr::Inline(Inliner* inl)
{
op = op->Inline(inl)->AsListExprPtr();
return ThisPtr();
}
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()
{
auto op_l = op->Duplicate()->AsListExprPtr();
TypePtr t;
if ( (type && type->GetName().size() > 0) || ! op->AsListExpr()->Exprs().empty() )
t = type;
else
// Use a null type rather than the one inferred, to instruct
// the constructor to again infer the type.
t = nullptr;
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();
auto lhs = a->GetOp1();
auto rhs = a->GetOp2();
// LHS is a list, not a singleton.
if ( ! lhs->HasReducedOps(c) )
return NonReduced(this);
// RHS might also be a list, if it's a table-of-sets or such.
if ( rhs->Tag() == EXPR_LIST )
{
if ( ! rhs->HasReducedOps(c) )
return NonReduced(this);
}
else if ( ! rhs->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()
{
auto op_l = op->Duplicate()->AsListExprPtr();
TypePtr t;
if ( (type && type->GetName().size() > 0) || ! op->AsListExpr()->Exprs().empty() )
t = type;
else
// Use a null type rather than the one inferred, to instruct
// the constructor to again infer the type.
t = nullptr;
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()
{
auto op_l = op->Duplicate()->AsListExprPtr();
if ( op->AsListExpr()->Exprs().empty() )
return SetSucc(new VectorConstructorExpr(op_l, nullptr));
else
return SetSucc(new VectorConstructorExpr(op_l, type));
}
bool VectorConstructorExpr::HasReducedOps(Reducer* c) const
{
return Op()->HasReducedOps(c);
}
ExprPtr FieldAssignExpr::Duplicate()
{
auto op_dup = op->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()
{
auto op_dup = op->Duplicate();
TypeTag tag;
if ( type->Tag() == TYPE_VECTOR )
tag = type->AsVectorType()->Yield()->Tag();
else
tag = type->Tag();
return SetSucc(new ArithCoerceExpr(op_dup, tag));
}
bool ArithCoerceExpr::WillTransform(Reducer* c) const
{
if ( op->Tag() != EXPR_CONST )
return false;
if ( IsArithmetic(GetType()->Tag()) )
return true;
return IsArithmetic(op->AsConstExpr()->Value()->GetType()->Tag());
}
ExprPtr ArithCoerceExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
{
if ( c->Optimizing() )
op = c->UpdateExpr(op);
red_stmt = nullptr;
op = op->ReduceToSingleton(c, red_stmt);
if ( op->Tag() == EXPR_CONST )
{
const auto& t = GetType();
auto cv = op->AsConstExpr()->ValuePtr();
const auto& ct = cv->GetType();
if ( IsArithmetic(t->Tag()) || IsArithmetic(ct->Tag()) )
{
if ( auto v = FoldSingleVal(cv, t) )
return make_intrusive<ConstExpr>(v);
// else there was a coercion error, fall through
}
}
if ( c->Optimizing() )
return ThisPtr();
const auto& ot = op->GetType();
auto bt = ot->InternalType();
auto tt = type->InternalType();
if ( ot->Tag() == TYPE_VECTOR )
{
bt = ot->Yield()->InternalType();
tt = type->Yield()->InternalType();
}
if ( bt == tt )
// Can drop the conversion.
return op;
return AssignToTemporary(c, red_stmt);
}
ExprPtr RecordCoerceExpr::Duplicate()
{
auto op_dup = op->Duplicate();
return SetSucc(new RecordCoerceExpr(op_dup, GetType<RecordType>()));
}
ExprPtr TableCoerceExpr::Duplicate()
{
auto op_dup = op->Duplicate();
return SetSucc(new TableCoerceExpr(op_dup, GetType<TableType>()));
}
ExprPtr VectorCoerceExpr::Duplicate()
{
auto op_dup = op->Duplicate();
return SetSucc(new VectorCoerceExpr(op_dup, GetType<VectorType>()));
}
ExprPtr ScheduleExpr::Duplicate()
{
auto when_d = when->Duplicate();
auto event_d = event->Duplicate()->AsEventExprPtr();
return SetSucc(new ScheduleExpr(when_d, event_d));
}
ExprPtr ScheduleExpr::Inline(Inliner* inl)
{
when = when->Inline(inl);
event = event->Inline(inl)->AsEventExprPtr();
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, std::move(red2_stmt));
return ThisPtr();
}
ExprPtr InExpr::Duplicate()
{
auto op1_d = op1->Duplicate();
auto op2_d = op2->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()
{
auto func_d = func->Duplicate();
auto args_d = args->Duplicate()->AsListExprPtr();
auto func_type = func->GetType();
auto in_hook = func_type->AsFuncType()->Flavor() == FUNC_FLAVOR_HOOK;
return SetSucc(new CallExpr(func_d, args_d, in_hook, in_when));
}
ExprPtr CallExpr::Inline(Inliner* inl)
{
// First check our elements.
func = func->Inline(inl);
args = cast_intrusive<ListExpr>(args->Inline(inl));
auto new_me = inl->CheckForInlining({NewRef{}, this});
if ( ! new_me )
// All done with inlining.
return ThisPtr();
if ( new_me.get() != this )
return new_me;
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);
// We don't use args->HasReducedOps() here because for ListExpr's
// the method has some special-casing that isn't germane for calls.
for ( const auto& expr : args->Exprs() )
if ( ! expr->IsSingleton(c) )
return false;
return true;
}
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 = args->ReduceToSingletons(c);
// ### could check here for (1) pure function, and (2) all
// arguments constants, and call it to fold right now.
red_stmt = MergeStmts(red_stmt, std::move(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()
{
return SetSucc(new LambdaExpr(this));
}
bool LambdaExpr::IsReduced(Reducer* c) const
{
if ( ! captures )
return true;
for ( auto& cp : *captures )
{
auto& cid = cp.Id();
if ( private_captures.count(cid.get()) == 0 && ! c->ID_IsReduced(cid) )
return NonReduced(this);
}
return true;
}
bool LambdaExpr::HasReducedOps(Reducer* c) const
{
return IsReduced(c);
}
ExprPtr LambdaExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
{
if ( c->Optimizing() )
return ThisPtr();
UpdateCaptures(c);
return AssignToTemporary(c, red_stmt);
}
StmtPtr LambdaExpr::ReduceToSingletons(Reducer* c)
{
UpdateCaptures(c);
return nullptr;
}
void LambdaExpr::UpdateCaptures(Reducer* c)
{
if ( captures )
{
for ( auto& cp : *captures )
{
auto& cid = cp.Id();
if ( private_captures.count(cid.get()) == 0 )
cp.SetID(c->UpdateID(cid));
}
c->UpdateIDs(&outer_ids);
}
}
ExprPtr EventExpr::Duplicate()
{
auto args_d = args->Duplicate()->AsListExprPtr();
return SetSucc(new EventExpr(name.c_str(), args_d));
}
ExprPtr EventExpr::Inline(Inliner* inl)
{
args = cast_intrusive<ListExpr>(args->Inline(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()
{
auto new_l = new ListExpr();
loop_over_list(exprs, i) new_l->Append(exprs[i]->Duplicate());
return SetSucc(new_l);
}
ExprPtr ListExpr::Inline(Inliner* inl)
{
loop_over_list(exprs, i)
{
auto in_expr = exprs[i]->Inline(inl);
Unref(exprs[i]);
exprs[i] = in_expr.release();
}
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 and complex table constructors.
if ( expr->Tag() == EXPR_FIELD_ASSIGN || expr->Tag() == EXPR_LIST )
{
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());
auto old = exprs.replace(i, e_i.release());
Unref(old);
continue;
}
if ( exprs[i]->IsSingleton(c) )
continue;
StmtPtr e_stmt;
auto old = exprs.replace(i, exprs[i]->ReduceToSingleton(c, e_stmt).release());
Unref(old);
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)
{
auto& e_i = exprs[i];
if ( e_i->IsSingleton(c) )
continue;
StmtPtr e_stmt;
auto new_e_i = e_i->ReduceToSingleton(c, e_stmt);
auto old = exprs.replace(i, new_e_i.release());
Unref(old);
if ( e_stmt )
red_stmt = MergeStmts(red_stmt, e_stmt);
}
return red_stmt;
}
ExprPtr CastExpr::Duplicate()
{
return SetSucc(new CastExpr(op->Duplicate(), type));
}
ExprPtr IsExpr::Duplicate()
{
return SetSucc(new IsExpr(op->Duplicate(), t));
}
InlineExpr::InlineExpr(ListExprPtr arg_args, std::vector<IDPtr> arg_params, StmtPtr arg_body,
int _frame_offset, TypePtr ret_type)
: Expr(EXPR_INLINE), args(std::move(arg_args)), body(std::move(arg_body))
{
params = std::move(arg_params);
frame_offset = _frame_offset;
type = std::move(ret_type);
}
bool InlineExpr::IsPure() const
{
return args->IsPure() && body->IsPure();
}
ValPtr InlineExpr::Eval(Frame* f) const
{
auto v = eval_list(f, args.get());
if ( ! v )
return nullptr;
int nargs = args->Exprs().length();
f->Reset(frame_offset + nargs);
f->AdjustOffset(frame_offset);
// Assign the arguments.
for ( auto i = 0; i < nargs; ++i )
f->SetElement(i, (*v)[i]);
auto flow = FLOW_NEXT;
ValPtr result;
try
{
result = body->Exec(f, flow);
}
catch ( InterpreterException& e )
{
f->AdjustOffset(-frame_offset);
throw;
}
f->AdjustOffset(-frame_offset);
return result;
}
ExprPtr InlineExpr::Duplicate()
{
auto args_d = args->Duplicate()->AsListExprPtr();
auto body_d = body->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();
auto ret_val = c->PushInlineBlock(type);
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<AssignExpr>(param_i, red_i, false, nullptr, nullptr, false);
auto assign_stmt = make_intrusive<ExprStmt>(assign);
red_stmt = MergeStmts(red_stmt, arg_red_stmt, assign_stmt);
}
body = body->Reduce(c);
c->PopInlineBlock();
auto catch_ret = make_intrusive<CatchReturnStmt>(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);
HANDLE_TC_EXPR_PRE(tc);
tc = args->Traverse(cb);
HANDLE_TC_EXPR_PRE(tc);
tc = body->Traverse(cb);
HANDLE_TC_EXPR_PRE(tc);
tc = cb->PostExpr(this);
HANDLE_TC_EXPR_POST(tc);
}
void InlineExpr::ExprDescribe(ODesc* d) const
{
if ( d->IsReadable() )
{
d->Add("inline(");
args->Describe(d);
d->Add(")(");
for ( auto& p : params )
{
if ( &p != &params[0] )
d->AddSP(",");
d->Add(p->Name());
}
d->Add("){");
body->Describe(d);
d->Add("}");
}
else
{
args->Describe(d);
body->Describe(d);
}
}
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();
}
ExprPtr AppendToExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt)
{
auto at_stmt = make_intrusive<ExprStmt>(Duplicate());
red_stmt = at_stmt->Reduce(c);
return op1->AsRefExprPtr()->GetOp1();
}
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<ExprStmt>(Duplicate());
auto index = op2->AsListExprPtr();
auto res = make_intrusive<IndexExpr>(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<ExprStmt>(Duplicate());
auto field_res = make_intrusive<FieldExpr>(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 = std::move(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));
}
CoerceFromAnyVecExpr::CoerceFromAnyVecExpr(ExprPtr arg_op, TypePtr to_type)
: UnaryExpr(EXPR_FROM_ANY_VEC_COERCE, std::move(arg_op))
{
type = std::move(to_type);
}
ValPtr CoerceFromAnyVecExpr::Eval(Frame* f) const
{
if ( IsError() )
return nullptr;
auto v = op->Eval(f);
if ( ! v )
return nullptr;
auto vv = v->AsVectorVal();
if ( ! vv->Concretize(type->Yield()) )
RuntimeError("incompatible \"vector of any\" type");
return v;
}
ExprPtr CoerceFromAnyVecExpr::Duplicate()
{
return SetSucc(new CoerceFromAnyVecExpr(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