mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
3106 lines
68 KiB
C++
3106 lines
68 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(red1_stmt, red2_stmt, red3_stmt);
|
|
}
|
|
|
|
ExprPtr Expr::ReduceToConditional(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
switch ( tag )
|
|
{
|
|
case EXPR_CONST:
|
|
return ThisPtr();
|
|
|
|
case EXPR_NAME:
|
|
if ( c->Optimizing() )
|
|
return ThisPtr();
|
|
|
|
return Reduce(c, red_stmt);
|
|
|
|
case EXPR_IN:
|
|
{
|
|
// This is complicated because there are lots of forms
|
|
// of "in" expressions, and we're only interested in
|
|
// those with 1 or 2 indices, into a table.
|
|
auto op1 = GetOp1();
|
|
auto op2 = GetOp2();
|
|
|
|
if ( c->Optimizing() )
|
|
return Reduce(c, red_stmt);
|
|
|
|
if ( op2->GetType()->Tag() != TYPE_TABLE )
|
|
// Not a table de-reference.
|
|
return Reduce(c, red_stmt);
|
|
|
|
if ( op1->Tag() == EXPR_LIST )
|
|
{
|
|
auto l1 = op1->AsListExpr();
|
|
auto& l1_e = l1->Exprs();
|
|
|
|
if ( l1_e.length() < 1 || l1_e.length() > 2 )
|
|
// Wrong number of indices.
|
|
return Reduce(c, red_stmt);
|
|
}
|
|
|
|
if ( ! op1->IsReduced(c) || ! op2->IsReduced(c) )
|
|
{
|
|
auto red2_stmt = ReduceToSingletons(c);
|
|
auto res = ReduceToConditional(c, red_stmt);
|
|
red_stmt = MergeStmts(red2_stmt, red_stmt);
|
|
return res;
|
|
}
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
case EXPR_EQ:
|
|
case EXPR_NE:
|
|
case EXPR_LE:
|
|
case EXPR_GE:
|
|
case EXPR_LT:
|
|
case EXPR_GT:
|
|
red_stmt = ReduceToSingletons(c);
|
|
|
|
if ( GetOp1()->IsConst() && GetOp2()->IsConst() )
|
|
// Fold!
|
|
{
|
|
StmtPtr fold_stmts;
|
|
auto new_me = Reduce(c, fold_stmts);
|
|
red_stmt = MergeStmts(red_stmt, fold_stmts);
|
|
|
|
return new_me;
|
|
}
|
|
|
|
return ThisPtr();
|
|
|
|
case EXPR_HAS_FIELD:
|
|
red_stmt = ReduceToSingletons(c);
|
|
return ThisPtr();
|
|
|
|
default:
|
|
return Reduce(c, red_stmt);
|
|
}
|
|
}
|
|
|
|
ExprPtr Expr::ReduceToFieldAssignment(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
if ( ! IsFieldAssignable(this) || tag == EXPR_NAME )
|
|
return ReduceToSingleton(c, red_stmt);
|
|
|
|
red_stmt = ReduceToSingletons(c);
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
ExprPtr Expr::AssignToTemporary(ExprPtr e, Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
auto result_tmp = c->GenTemporaryExpr(GetType(), e);
|
|
|
|
auto a_e = make_intrusive<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 mis-associate that
|
|
// information with both the assignment creating the temporary,
|
|
// and the subsequent use of the temporary.
|
|
return result_tmp->Duplicate();
|
|
}
|
|
|
|
ExprPtr Expr::TransformMe(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, 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 && 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 && ! 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(), red2_stmt, red3_stmt);
|
|
}
|
|
|
|
return MergeStmts(red1_stmt, if_else);
|
|
}
|
|
|
|
ExprPtr RefExpr::Duplicate()
|
|
{
|
|
return SetSucc(new RefExpr(op->Duplicate()));
|
|
}
|
|
|
|
bool RefExpr::IsReduced(Reducer* c) const
|
|
{
|
|
if ( op->Tag() == EXPR_NAME )
|
|
return op->IsReduced(c);
|
|
|
|
return NonReduced(this);
|
|
}
|
|
|
|
bool RefExpr::HasReducedOps(Reducer* c) const
|
|
{
|
|
switch ( op->Tag() )
|
|
{
|
|
case EXPR_NAME:
|
|
return op->IsReduced(c);
|
|
|
|
case EXPR_FIELD:
|
|
return op->AsFieldExpr()->Op()->IsReduced(c);
|
|
|
|
case EXPR_INDEX:
|
|
{
|
|
auto ind = op->AsIndexExpr();
|
|
return ind->Op1()->IsReduced(c) && ind->Op2()->IsReduced(c);
|
|
}
|
|
|
|
case EXPR_LIST:
|
|
return op->IsReduced(c);
|
|
|
|
default:
|
|
Internal("bad operand in RefExpr::IsReduced");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool RefExpr::WillTransform(Reducer* c) const
|
|
{
|
|
return op->Tag() != EXPR_NAME;
|
|
}
|
|
|
|
ExprPtr RefExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
if ( op->Tag() == EXPR_NAME )
|
|
op = op->Reduce(c, red_stmt);
|
|
else
|
|
op = AssignToTemporary(c, red_stmt);
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
StmtPtr RefExpr::ReduceToLHS(Reducer* c)
|
|
{
|
|
if ( op->Tag() == EXPR_NAME )
|
|
{
|
|
StmtPtr red_stmt;
|
|
op = op->Reduce(c, red_stmt);
|
|
return red_stmt;
|
|
}
|
|
|
|
auto red_stmt1 = op->ReduceToSingletons(c);
|
|
auto op_ref = make_intrusive<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 = 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);
|
|
|
|
return op1->AsRefExprPtr()->GetOp1();
|
|
}
|
|
|
|
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, 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)));
|
|
}
|
|
|
|
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();
|
|
// LHS is a list, not a singleton.
|
|
if ( ! a->GetOp1()->HasReducedOps(c) || ! a->GetOp2()->IsSingleton(c) )
|
|
return NonReduced(this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ExprPtr TableConstructorExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
red_stmt = ReduceToSingletons(c);
|
|
|
|
if ( c->Optimizing() )
|
|
return ThisPtr();
|
|
else
|
|
return AssignToTemporary(c, red_stmt);
|
|
}
|
|
|
|
StmtPtr TableConstructorExpr::ReduceToSingletons(Reducer* c)
|
|
{
|
|
// Need to process the list of initializers directly, as
|
|
// they may be expressed as AssignExpr's, and those get
|
|
// treated quite differently during reduction.
|
|
const auto& exprs = op->AsListExpr()->Exprs();
|
|
|
|
StmtPtr red_stmt;
|
|
|
|
for ( const auto& expr : exprs )
|
|
{
|
|
if ( expr->Tag() == EXPR_ASSIGN )
|
|
{
|
|
auto a = expr->AsAssignExpr();
|
|
auto op1 = a->GetOp1();
|
|
auto op2 = a->GetOp2();
|
|
|
|
if ( c->Optimizing() )
|
|
{
|
|
a->SetOp1(c->UpdateExpr(op1));
|
|
a->SetOp2(c->UpdateExpr(op2));
|
|
continue;
|
|
}
|
|
|
|
StmtPtr red1_stmt;
|
|
StmtPtr red2_stmt;
|
|
|
|
a->SetOp1(op1->ReduceToSingleton(c, red1_stmt));
|
|
a->SetOp2(op2->ReduceToSingleton(c, red2_stmt));
|
|
|
|
red_stmt = MergeStmts(red_stmt, red1_stmt, red2_stmt);
|
|
}
|
|
|
|
else
|
|
reporter->InternalError("confused in TableConstructorExpr::Reduce");
|
|
}
|
|
|
|
return red_stmt;
|
|
}
|
|
|
|
ExprPtr SetConstructorExpr::Duplicate()
|
|
{
|
|
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, 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));
|
|
}
|
|
|
|
ExprPtr CallExpr::Inline(Inliner* 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;
|
|
|
|
// We're not inlining, but perhaps our elements should be.
|
|
func = func->Inline(inl);
|
|
args = cast_intrusive<ListExpr>(args->Inline(inl));
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
bool CallExpr::IsReduced(Reducer* c) const
|
|
{
|
|
return func->IsSingleton(c) && args->IsReduced(c);
|
|
}
|
|
|
|
bool CallExpr::HasReducedOps(Reducer* c) const
|
|
{
|
|
if ( ! func->IsSingleton(c) )
|
|
return NonReduced(this);
|
|
|
|
return args->HasReducedOps(c);
|
|
}
|
|
|
|
ExprPtr CallExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
if ( c->Optimizing() )
|
|
{
|
|
func = c->UpdateExpr(func);
|
|
auto e = c->UpdateExpr(args);
|
|
args = e->AsListExprPtr();
|
|
return ThisPtr();
|
|
}
|
|
|
|
red_stmt = nullptr;
|
|
|
|
if ( ! func->IsSingleton(c) )
|
|
func = func->ReduceToSingleton(c, red_stmt);
|
|
|
|
StmtPtr red2_stmt;
|
|
// We assume that ListExpr won't transform itself fundamentally.
|
|
(void)args->Reduce(c, red2_stmt);
|
|
|
|
// ### could check here for (1) pure function, and (2) all
|
|
// arguments constants, and call it to fold right now.
|
|
|
|
red_stmt = MergeStmts(red_stmt, red2_stmt);
|
|
|
|
if ( GetType()->Tag() == TYPE_VOID )
|
|
return ThisPtr();
|
|
else
|
|
return AssignToTemporary(c, red_stmt);
|
|
}
|
|
|
|
StmtPtr CallExpr::ReduceToSingletons(Reducer* c)
|
|
{
|
|
StmtPtr func_stmt;
|
|
|
|
if ( ! func->IsSingleton(c) )
|
|
func = func->Reduce(c, func_stmt);
|
|
|
|
auto args_stmt = args->ReduceToSingletons(c);
|
|
|
|
return MergeStmts(func_stmt, args_stmt);
|
|
}
|
|
|
|
ExprPtr LambdaExpr::Duplicate()
|
|
{
|
|
auto ingr = std::make_unique<function_ingredients>(*ingredients);
|
|
ingr->body = ingr->body->Duplicate();
|
|
return SetSucc(new LambdaExpr(std::move(ingr), outer_ids));
|
|
}
|
|
|
|
ExprPtr LambdaExpr::Inline(Inliner* inl)
|
|
{
|
|
// Don't inline these, we currently don't get the closure right.
|
|
return ThisPtr();
|
|
}
|
|
|
|
ExprPtr LambdaExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
if ( c->Optimizing() )
|
|
return ThisPtr();
|
|
else
|
|
return AssignToTemporary(c, red_stmt);
|
|
}
|
|
|
|
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 constructors.
|
|
if ( expr->Tag() == EXPR_FIELD_ASSIGN )
|
|
{
|
|
if ( ! expr->HasReducedOps(c) )
|
|
return false;
|
|
}
|
|
else if ( ! expr->IsSingleton(c) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ExprPtr ListExpr::Reduce(Reducer* c, StmtPtr& red_stmt)
|
|
{
|
|
red_stmt = nullptr;
|
|
|
|
loop_over_list(exprs, i)
|
|
{
|
|
if ( c->Optimizing() )
|
|
{
|
|
auto e_i = c->UpdateExpr(exprs[i]->ThisPtr());
|
|
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)
|
|
{
|
|
if ( exprs[i]->IsSingleton(c) )
|
|
continue;
|
|
|
|
StmtPtr e_stmt;
|
|
auto old = exprs.replace(i, exprs[i]->Reduce(c, e_stmt).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();
|
|
|
|
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);
|
|
}
|
|
|
|
auto ret_val = c->PushInlineBlock(type);
|
|
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("){");
|
|
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
|