mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
3096 lines
89 KiB
C++
3096 lines
89 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
// Optimization-related methods for Expr classes.
|
|
|
|
#include "zeek/script_opt/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/FuncInfo.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;
|
|
}
|
|
|
|
FieldAssignExpr* Expr::AsFieldAssignExpr() {
|
|
CHECK_TAG(tag, EXPR_FIELD_ASSIGN, "ExprVal::AsFieldAssignExpr", expr_name)
|
|
return (FieldAssignExpr*)this;
|
|
}
|
|
|
|
IntrusivePtr<FieldAssignExpr> Expr::AsFieldAssignExprPtr() {
|
|
CHECK_TAG(tag, EXPR_FIELD_ASSIGN, "ExprVal::AsFieldAssignExpr", expr_name)
|
|
return {NewRef{}, (FieldAssignExpr*)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 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;
|
|
}
|
|
|
|
RefExpr* Expr::AsRefExpr() {
|
|
CHECK_TAG(tag, EXPR_REF, "ExprVal::AsRefExpr", expr_name)
|
|
return (RefExpr*)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_CALL: {
|
|
if ( ! HasReducedOps(c) )
|
|
return false;
|
|
|
|
return IsZAM_BuiltInCond(static_cast<const CallExpr*>(this));
|
|
}
|
|
|
|
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->IsSingleton(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_SCRIPT_OPT_BUILTIN: return GetType()->Tag() == TYPE_BOOL;
|
|
|
|
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_MASK:
|
|
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) {
|
|
if ( WillTransformInConditional(c) ) {
|
|
auto new_me = TransformToConditional(c, red_stmt);
|
|
|
|
// Now that we've transformed, reduce the result for use in a
|
|
// conditional.
|
|
StmtPtr red_stmt2;
|
|
new_me = new_me->ReduceToConditional(c, red_stmt2);
|
|
red_stmt = MergeStmts(std::move(red_stmt), std::move(red_stmt2));
|
|
|
|
return new_me;
|
|
}
|
|
|
|
switch ( tag ) {
|
|
case EXPR_CONST: return ThisPtr();
|
|
|
|
case EXPR_NAME:
|
|
if ( c->Optimizing() )
|
|
return ThisPtr();
|
|
|
|
return Reduce(c, red_stmt);
|
|
|
|
case EXPR_CALL: {
|
|
auto ce = static_cast<CallExpr*>(this);
|
|
red_stmt = ce->ReduceToSingletons(c);
|
|
|
|
if ( IsZAM_BuiltInCond(ce) )
|
|
return ThisPtr();
|
|
|
|
StmtPtr red_stmt2;
|
|
auto res = Reduce(c, red_stmt2);
|
|
red_stmt = MergeStmts(std::move(red_stmt), std::move(red_stmt2));
|
|
return res;
|
|
}
|
|
|
|
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_NOT:
|
|
if ( GetOp1()->Tag() == EXPR_SCRIPT_OPT_BUILTIN ) {
|
|
red_stmt = GetOp1()->ReduceToSingletons(c);
|
|
return ThisPtr();
|
|
}
|
|
else
|
|
return Reduce(c, red_stmt);
|
|
|
|
case EXPR_SCRIPT_OPT_BUILTIN:
|
|
if ( GetType()->Tag() != TYPE_BOOL )
|
|
return Reduce(c, red_stmt);
|
|
|
|
// fall through
|
|
|
|
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::TransformToConditional(Reducer* c, StmtPtr& red_stmt) {
|
|
// This shouldn't happen since every expression that can return
|
|
// true for WillTransformInConditional() should implement this
|
|
// method.
|
|
reporter->InternalError("Expr::TransformToConditional called");
|
|
}
|
|
|
|
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->SetLocationInfo(GetLocationInfo());
|
|
a_e->SetIsTemp();
|
|
|
|
auto a_e_s = with_location_of(make_intrusive<ExprStmt>(a_e), this);
|
|
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->SetLocationInfo(GetLocationInfo());
|
|
|
|
// 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 with_location_of(make_intrusive<StmtList>(s1, s2, s3), this);
|
|
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 {
|
|
auto z = make_intrusive<ConstExpr>(MakeZero(t));
|
|
z->SetLocationInfo(GetLocationInfo());
|
|
return z;
|
|
}
|
|
|
|
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();
|
|
|
|
if ( ! op1_fold_val && op1->Tag() == EXPR_LIST && op1->AsListExpr()->HasConstantOps() )
|
|
// We can turn the list into a ListVal.
|
|
op1_fold_val = op1->Eval(nullptr);
|
|
|
|
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 = with_location_of(make_intrusive<ConstExpr>(val_mgr->Count(1)), this);
|
|
|
|
ExprPtr incr_expr;
|
|
|
|
if ( Tag() == EXPR_INCR )
|
|
incr_expr = with_location_of(make_intrusive<AddExpr>(target, incr_const), this);
|
|
else
|
|
incr_expr = with_location_of(make_intrusive<SubExpr>(target, incr_const), this);
|
|
|
|
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 = with_location_of(make_intrusive<IndexExpr>(dup1, index), this);
|
|
}
|
|
|
|
else if ( orig_target->Tag() == EXPR_FIELD ) {
|
|
auto dup1 = orig_target->GetOp1()->Duplicate();
|
|
auto field_name = orig_target->AsFieldExpr()->FieldName();
|
|
orig_target = with_location_of(make_intrusive<FieldExpr>(dup1, field_name), this);
|
|
}
|
|
|
|
else
|
|
reporter->InternalError("confused in IncrExpr::Reduce");
|
|
|
|
auto assign = with_location_of(make_intrusive<AssignExpr>(orig_target, rhs, false, nullptr, nullptr, false), this);
|
|
|
|
// 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 = with_location_of(make_intrusive<ExprStmt>(incr_expr), this)->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 )
|
|
return op->GetOp1()->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();
|
|
return with_location_of(make_intrusive<SubExpr>(op1, rhs), this);
|
|
}
|
|
|
|
ExprPtr AggrAddDelExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( type )
|
|
return UnaryExpr::Reduce(c, red_stmt);
|
|
|
|
if ( c->Optimizing() )
|
|
op = c->UpdateExpr(op);
|
|
|
|
red_stmt = op->ReduceToSingletons(c);
|
|
return ThisPtr();
|
|
}
|
|
|
|
ExprPtr AggrAddExpr::Duplicate() { return SetSucc(new AggrAddExpr(op->Duplicate())); }
|
|
|
|
ExprPtr AggrDelExpr::Duplicate() { return SetSucc(new AggrDelExpr(op->Duplicate())); }
|
|
|
|
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->IsSingleton(c);
|
|
|
|
if ( tag == TYPE_TABLE )
|
|
return op1->IsReduced(c) && op2->IsSingleton(c);
|
|
|
|
if ( tag == TYPE_VECTOR && IsVector(op2->GetType()->Tag()) && same_type(t, op2->GetType()) )
|
|
return op1->IsReduced(c) && op2->IsSingleton(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->ReduceToSingleton(c, red_stmt2);
|
|
|
|
red_stmt = MergeStmts(red_stmt1, red_stmt2);
|
|
|
|
if ( is_vector_elem_append ) {
|
|
auto append = with_location_of(make_intrusive<AppendToExpr>(op1->Duplicate(), op2), this);
|
|
auto append_stmt = with_location_of(make_intrusive<ExprStmt>(append), this);
|
|
|
|
red_stmt = MergeStmts(red_stmt, append_stmt);
|
|
|
|
return op1;
|
|
}
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
default: {
|
|
auto rhs = op1->AsRefExprPtr()->GetOp1();
|
|
auto do_incr = with_location_of(make_intrusive<AddExpr>(rhs->Duplicate(), op2), this);
|
|
auto assign =
|
|
with_location_of(make_intrusive<AssignExpr>(op1, do_incr, false, nullptr, nullptr, false), this);
|
|
|
|
return assign->ReduceToSingleton(c, red_stmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
ExprPtr AddToExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) {
|
|
auto at_stmt = with_location_of(make_intrusive<ExprStmt>(Duplicate()), this);
|
|
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 = with_location_of(make_intrusive<AddExpr>(op1, rhs), this);
|
|
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->IsSingleton(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->ReduceToSingleton(c, red_stmt2);
|
|
|
|
red_stmt = MergeStmts(red_stmt1, red_stmt2);
|
|
|
|
return ThisPtr();
|
|
}
|
|
|
|
auto lhs = op1->AsRefExprPtr()->GetOp1();
|
|
auto do_decr = with_location_of(make_intrusive<SubExpr>(lhs->Duplicate(), op2), this);
|
|
auto assign = with_location_of(make_intrusive<AssignExpr>(op1, do_decr, false, nullptr, nullptr, false), this);
|
|
|
|
return assign->Reduce(c, red_stmt);
|
|
}
|
|
|
|
ExprPtr RemoveFromExpr::ReduceToSingleton(Reducer* c, StmtPtr& red_stmt) {
|
|
auto rf_stmt = with_location_of(make_intrusive<ExprStmt>(Duplicate()), this);
|
|
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 op2->IsOne(); }
|
|
|
|
ExprPtr DivideExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( op2->IsOne() )
|
|
return op1->ReduceToSingleton(c, red_stmt);
|
|
|
|
return BinaryExpr::Reduce(c, red_stmt);
|
|
}
|
|
|
|
ExprPtr MaskExpr::Duplicate() {
|
|
auto op1_d = op1->Duplicate();
|
|
auto op2_d = op2->Duplicate();
|
|
return SetSucc(new MaskExpr(op1_d, op2_d));
|
|
}
|
|
|
|
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 Expr* 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.get(), id, patterns) && is_pattern_cascade(rhs.get(), id, patterns);
|
|
}
|
|
|
|
// Given a set of pattern constants, returns a disjunction that
|
|
// includes all of them.
|
|
static ExprPtr build_disjunction(std::vector<ConstExprPtr>& patterns, const Obj* obj) {
|
|
ASSERT(patterns.size() > 1);
|
|
|
|
ExprPtr e = patterns[0];
|
|
|
|
for ( auto& p : patterns )
|
|
e = with_location_of(make_intrusive<BitExpr>(EXPR_OR, e, p), obj);
|
|
|
|
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;
|
|
return tag == EXPR_OR_OR && is_pattern_cascade(this, 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(this, common_id, patterns) )
|
|
return TransformToConditional(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 = with_location_of(make_intrusive<ConstExpr>(else_val), this);
|
|
|
|
ExprPtr cond;
|
|
if ( is_and )
|
|
cond = with_location_of(make_intrusive<CondExpr>(op1, op2, else_e), this);
|
|
else
|
|
cond = with_location_of(make_intrusive<CondExpr>(op1, else_e, op2), this);
|
|
|
|
auto cond_red = cond->ReduceToSingleton(c, red_stmt);
|
|
return TransformMe(cond_red, c, red_stmt);
|
|
}
|
|
|
|
ExprPtr BoolExpr::TransformToConditional(Reducer* c, StmtPtr& red_stmt) {
|
|
// This only happens for pattern cascades.
|
|
|
|
// Here in some contexts we're re-doing work that our caller did, but
|
|
// these cascades are quite rare, and re-doing the work keeps the
|
|
// coupling simpler.
|
|
IDPtr common_id = nullptr;
|
|
std::vector<ConstExprPtr> patterns;
|
|
auto is_cascade = is_pattern_cascade(this, common_id, patterns);
|
|
ASSERT(is_cascade);
|
|
|
|
auto new_pat = build_disjunction(patterns, this);
|
|
auto new_id = with_location_of(make_intrusive<NameExpr>(common_id), this);
|
|
auto new_node = with_location_of(make_intrusive<InExpr>(new_pat, new_id), this);
|
|
return new_node->Reduce(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 = with_location_of(make_intrusive<ConstExpr>(val_mgr->Count(0)), this);
|
|
return zero->Reduce(c, red_stmt);
|
|
}
|
|
|
|
else
|
|
return op1->ReduceToSingleton(c, red_stmt);
|
|
}
|
|
|
|
return BinaryExpr::Reduce(c, red_stmt);
|
|
}
|
|
|
|
bool CmpExpr::WillTransform(Reducer* c) const {
|
|
if ( IsHasElementsTest() )
|
|
return true;
|
|
return GetType()->Tag() == TYPE_BOOL && same_singletons(op1, op2);
|
|
}
|
|
|
|
bool CmpExpr::WillTransformInConditional(Reducer* c) const { return WillTransform(c); }
|
|
|
|
bool CmpExpr::IsReduced(Reducer* c) const {
|
|
if ( IsHasElementsTest() )
|
|
return NonReduced(this);
|
|
return true;
|
|
}
|
|
|
|
static std::map<ExprTag, ExprTag> has_elements_swap_tag = {
|
|
{EXPR_EQ, EXPR_EQ}, {EXPR_NE, EXPR_NE}, {EXPR_LT, EXPR_GT},
|
|
{EXPR_LE, EXPR_GE}, {EXPR_GE, EXPR_LE}, {EXPR_GT, EXPR_LT},
|
|
};
|
|
|
|
bool CmpExpr::IsHasElementsTest() const {
|
|
static std::set<ExprTag> rel_tags = {EXPR_EQ, EXPR_NE, EXPR_LT, EXPR_LE, EXPR_GE, EXPR_GT};
|
|
|
|
auto t = Tag(); // note, we may invert t below
|
|
if ( rel_tags.count(t) == 0 )
|
|
return false;
|
|
|
|
auto op1 = GetOp1();
|
|
auto op2 = GetOp2();
|
|
|
|
ASSERT(op1 && op2);
|
|
|
|
if ( op1->Tag() != EXPR_SIZE && op2->Tag() != EXPR_SIZE )
|
|
return false;
|
|
|
|
if ( ! op1->IsZero() && ! op1->IsOne() && ! op2->IsZero() && ! op2->IsOne() )
|
|
return false;
|
|
|
|
if ( op1->Tag() == EXPR_CONST ) {
|
|
t = has_elements_swap_tag[t];
|
|
std::swap(op1, op2);
|
|
}
|
|
|
|
auto op1_t = op1->GetOp1()->GetType()->Tag();
|
|
if ( op1_t != TYPE_TABLE && op1_t != TYPE_VECTOR )
|
|
return false;
|
|
|
|
static std::map<ExprTag, bool> zero_req = {
|
|
{EXPR_EQ, true}, {EXPR_NE, true}, {EXPR_LT, false}, {EXPR_LE, true}, {EXPR_GE, false}, {EXPR_GT, true},
|
|
};
|
|
|
|
return zero_req[t] ? op2->IsZero() : op2->IsOne();
|
|
}
|
|
|
|
ExprPtr CmpExpr::TransformToConditional(Reducer* c, StmtPtr& red_stmt) { return BuildHasElementsTest(); }
|
|
|
|
ExprPtr CmpExpr::BuildHasElementsTest() const {
|
|
auto t = Tag();
|
|
auto op1 = GetOp1();
|
|
auto op2 = GetOp2();
|
|
|
|
if ( op1->Tag() == EXPR_CONST ) {
|
|
t = has_elements_swap_tag[t];
|
|
std::swap(op1, op2);
|
|
}
|
|
|
|
ExprPtr he =
|
|
with_location_of(make_intrusive<ScriptOptBuiltinExpr>(ScriptOptBuiltinExpr::HAS_ELEMENTS, op1->GetOp1()), this);
|
|
|
|
static std::map<ExprTag, bool> has_elements = {
|
|
{EXPR_EQ, false}, {EXPR_NE, true}, {EXPR_LT, false}, {EXPR_LE, false}, {EXPR_GE, true}, {EXPR_GT, true},
|
|
};
|
|
|
|
if ( ! has_elements[t] )
|
|
he = with_location_of(make_intrusive<NotExpr>(he), this);
|
|
|
|
return he;
|
|
}
|
|
|
|
ExprPtr EqExpr::Duplicate() {
|
|
auto op1_d = op1->Duplicate();
|
|
auto op2_d = op2->Duplicate();
|
|
return SetSucc(new EqExpr(tag, op1_d, op2_d));
|
|
}
|
|
|
|
ExprPtr EqExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( IsHasElementsTest() )
|
|
return BuildHasElementsTest()->Reduce(c, red_stmt);
|
|
|
|
if ( GetType()->Tag() == TYPE_BOOL && same_singletons(op1, op2) ) {
|
|
bool t = Tag() == EXPR_EQ;
|
|
auto res = with_location_of(make_intrusive<ConstExpr>(val_mgr->Bool(t)), this);
|
|
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));
|
|
}
|
|
|
|
ExprPtr RelExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( IsHasElementsTest() )
|
|
return BuildHasElementsTest()->Reduce(c, red_stmt);
|
|
|
|
if ( GetType()->Tag() == TYPE_BOOL ) {
|
|
if ( same_singletons(op1, op2) ) {
|
|
bool t = Tag() == EXPR_GE || Tag() == EXPR_LE;
|
|
auto res = with_location_of(make_intrusive<ConstExpr>(val_mgr->Bool(t)), this);
|
|
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 ! IsMinOrMax(c) && 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);
|
|
}
|
|
|
|
while ( op1->Tag() == EXPR_NOT ) {
|
|
op1 = op1->GetOp1();
|
|
std::swap(op2, op3);
|
|
}
|
|
|
|
if ( IsMinOrMax(c) ) {
|
|
auto res = TransformToMinOrMax();
|
|
return res->Reduce(c, red_stmt);
|
|
}
|
|
|
|
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 TransformMe(make_intrusive<NotExpr>(op1), c, red_stmt);
|
|
}
|
|
|
|
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 = with_location_of(make_intrusive<NullStmt>(), this);
|
|
if ( ! red3_stmt )
|
|
red3_stmt = with_location_of(make_intrusive<NullStmt>(), this);
|
|
|
|
if_else = with_location_of(make_intrusive<IfStmt>(op1->Duplicate(), std::move(red2_stmt), std::move(red3_stmt)),
|
|
this);
|
|
}
|
|
|
|
return MergeStmts(red1_stmt, if_else);
|
|
}
|
|
|
|
bool CondExpr::IsMinOrMax(Reducer* c) const {
|
|
switch ( op1->Tag() ) {
|
|
case EXPR_LT:
|
|
case EXPR_LE:
|
|
case EXPR_GE:
|
|
case EXPR_GT: break;
|
|
|
|
default: return false;
|
|
}
|
|
|
|
auto relop1 = op1->GetOp1();
|
|
auto relop2 = op1->GetOp2();
|
|
|
|
return (same_expr(relop1, op2) && same_expr(relop2, op3)) || (same_expr(relop1, op3) && same_expr(relop2, op2));
|
|
}
|
|
|
|
ExprPtr CondExpr::TransformToMinOrMax() const {
|
|
auto relop1 = op1->GetOp1();
|
|
auto relop2 = op1->GetOp2();
|
|
|
|
auto is_min = (op1->Tag() == EXPR_LT || op1->Tag() == EXPR_LE);
|
|
|
|
if ( same_expr(relop1, op3) )
|
|
is_min = ! is_min;
|
|
|
|
auto built_in = is_min ? ScriptOptBuiltinExpr::MINIMUM : ScriptOptBuiltinExpr::MAXIMUM;
|
|
|
|
return with_location_of(make_intrusive<ScriptOptBuiltinExpr>(built_in, relop1, relop2), this);
|
|
}
|
|
|
|
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 = with_location_of(make_intrusive<RefExpr>(op), this);
|
|
|
|
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 = with_location_of(make_intrusive<ConstExpr>(val), this);
|
|
val = nullptr;
|
|
red_stmt = with_location_of(make_intrusive<ExprStmt>(ThisPtr()), this);
|
|
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_orig = op2;
|
|
|
|
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_orig->GetLocationInfo());
|
|
}
|
|
|
|
if ( t1->Tag() == TYPE_VECTOR && t1->Yield()->Tag() != TYPE_ANY && t2->Yield() && t2->Yield()->Tag() == TYPE_ANY ) {
|
|
ExprPtr red_rhs = op2->ReduceToSingleton(c, rhs_reduce);
|
|
op2 = with_location_of(make_intrusive<CoerceFromAnyVecExpr>(red_rhs, t1), op2);
|
|
}
|
|
|
|
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->ReduceToSingleton(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 = with_location_of(make_intrusive<AnyIndexExpr>(rhs_dup, i), this);
|
|
auto lhs = lhs_list[i]->ThisPtr();
|
|
lhs->SetLocationInfo(GetLocationInfo());
|
|
auto assign = make_intrusive<AssignExpr>(lhs, rhs, false, nullptr, nullptr, false);
|
|
|
|
auto assign_stmt = with_location_of(make_intrusive<ExprStmt>(assign), this);
|
|
red_stmt = MergeStmts(red_stmt, assign_stmt);
|
|
}
|
|
|
|
return TransformMe(make_intrusive<NopExpr>(), c, red_stmt);
|
|
}
|
|
|
|
if ( op2->WillTransform(c) ) {
|
|
StmtPtr xform_stmt;
|
|
StmtPtr lhs_stmt = lhs_ref->ReduceToLHS(c);
|
|
op2 = op2->ReduceToSingleton(c, xform_stmt);
|
|
red_stmt = MergeStmts(lhs_stmt, 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 = with_location_of(make_intrusive<ExprStmt>(assign_expr), this);
|
|
red_stmt = ae_stmt->Reduce(c);
|
|
|
|
if ( val )
|
|
return TransformMe(make_intrusive<ConstExpr>(val), c, red_stmt);
|
|
|
|
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 TransformMe(make_intrusive<ConstExpr>(v), c, red_stmt);
|
|
// 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>()));
|
|
}
|
|
|
|
bool RecordCoerceExpr::IsReduced(Reducer* c) const {
|
|
if ( WillTransform(c) )
|
|
return NonReduced(this);
|
|
|
|
return UnaryExpr::IsReduced(c);
|
|
}
|
|
|
|
bool RecordCoerceExpr::WillTransform(Reducer* c) const { return op->Tag() == EXPR_RECORD_CONSTRUCTOR; }
|
|
|
|
ExprPtr RecordCoerceExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( WillTransform(c) ) {
|
|
auto rt = cast_intrusive<RecordType>(type);
|
|
ASSERT(op->Tag() == EXPR_RECORD_CONSTRUCTOR);
|
|
auto rc_op = static_cast<const RecordConstructorExpr*>(op.get());
|
|
auto known_constr = with_location_of(make_intrusive<RecordConstructorExpr>(rt, rc_op->Op()), this);
|
|
auto red_e = known_constr->Reduce(c, red_stmt);
|
|
return TransformMe(std::move(red_e), c, red_stmt);
|
|
}
|
|
|
|
return UnaryExpr::Reduce(c, red_stmt);
|
|
}
|
|
|
|
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>()));
|
|
}
|
|
|
|
bool VectorCoerceExpr::IsReduced(Reducer* c) const {
|
|
if ( WillTransform(c) )
|
|
return NonReduced(this);
|
|
|
|
return UnaryExpr::IsReduced(c);
|
|
}
|
|
|
|
bool VectorCoerceExpr::WillTransform(Reducer* c) const {
|
|
return op->Tag() == EXPR_VECTOR_CONSTRUCTOR && op->GetType<VectorType>()->IsUnspecifiedVector();
|
|
}
|
|
|
|
ExprPtr VectorCoerceExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( WillTransform(c) ) {
|
|
auto op1_list = op->GetOp1();
|
|
ASSERT(op1_list->Tag() == EXPR_LIST);
|
|
auto empty_list = cast_intrusive<ListExpr>(op1_list);
|
|
auto new_me = with_location_of(make_intrusive<VectorConstructorExpr>(empty_list, type), this);
|
|
auto red_e = new_me->Reduce(c, red_stmt);
|
|
return TransformMe(std::move(red_e), c, red_stmt);
|
|
}
|
|
|
|
return UnaryExpr::Reduce(c, red_stmt);
|
|
}
|
|
|
|
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::IsReduced(Reducer* c) const {
|
|
if ( op2->Tag() == EXPR_SET_CONSTRUCTOR && op2->GetOp1()->AsListExpr()->HasConstantOps() )
|
|
return NonReduced(this);
|
|
|
|
return BinaryExpr::IsReduced(c);
|
|
}
|
|
|
|
bool InExpr::HasReducedOps(Reducer* c) const { return op1->HasReducedOps(c) && op2->IsSingleton(c); }
|
|
|
|
ExprPtr InExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
if ( op2->Tag() == EXPR_SET_CONSTRUCTOR && op2->GetOp1()->AsListExpr()->HasConstantOps() )
|
|
op2 = with_location_of(make_intrusive<ConstExpr>(op2->Eval(nullptr)), this);
|
|
|
|
return BinaryExpr::Reduce(c, red_stmt);
|
|
}
|
|
|
|
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) && ! WillTransform(c); }
|
|
|
|
bool CallExpr::WillTransform(Reducer* c) const { return CheckForBuiltin() || IsFoldableBiF(); }
|
|
|
|
bool CallExpr::HasReducedOps(Reducer* c) const {
|
|
if ( WillTransform(c) )
|
|
return false;
|
|
|
|
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();
|
|
}
|
|
|
|
red_stmt = nullptr;
|
|
|
|
if ( ! func->IsSingleton(c) )
|
|
func = func->ReduceToSingleton(c, red_stmt);
|
|
|
|
StmtPtr red2_stmt = args->ReduceToSingletons(c);
|
|
|
|
red_stmt = MergeStmts(red_stmt, std::move(red2_stmt));
|
|
|
|
if ( CheckForBuiltin() ) {
|
|
StmtPtr red3_stmt;
|
|
auto res = TransformToBuiltin()->Reduce(c, red3_stmt);
|
|
red_stmt = MergeStmts(red_stmt, std::move(red3_stmt));
|
|
return res;
|
|
}
|
|
|
|
if ( IsFoldableBiF() ) {
|
|
auto res = Eval(nullptr);
|
|
ASSERT(res);
|
|
return with_location_of(make_intrusive<ConstExpr>(res), this);
|
|
}
|
|
|
|
if ( c->Optimizing() || 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);
|
|
}
|
|
|
|
bool CallExpr::IsFoldableBiF() const {
|
|
if ( IsAggr(type) )
|
|
return false;
|
|
|
|
if ( ! AllConstArgs() )
|
|
return false;
|
|
|
|
if ( func->Tag() != EXPR_NAME )
|
|
return false;
|
|
|
|
return is_foldable(func->AsNameExpr()->Id()->Name());
|
|
}
|
|
|
|
bool CallExpr::AllConstArgs() const {
|
|
for ( auto e : Args()->Exprs() )
|
|
if ( e->Tag() != EXPR_CONST )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::map<std::string, ScriptOptBuiltinExpr::SOBuiltInTag> known_funcs = {
|
|
{"id_string", ScriptOptBuiltinExpr::FUNC_ID_STRING}};
|
|
|
|
bool CallExpr::CheckForBuiltin() const {
|
|
if ( func->Tag() != EXPR_NAME )
|
|
return false;
|
|
|
|
auto f_id = func->AsNameExpr()->Id();
|
|
|
|
auto kf = known_funcs.find(f_id->Name());
|
|
if ( kf == known_funcs.end() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ExprPtr CallExpr::TransformToBuiltin() {
|
|
auto kf = known_funcs[func->AsNameExpr()->Id()->Name()];
|
|
CallExprPtr this_ptr = {NewRef{}, this};
|
|
return with_location_of(make_intrusive<ScriptOptBuiltinExpr>(kf, this_ptr), this);
|
|
}
|
|
|
|
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(ScriptFuncPtr arg_sf, ListExprPtr arg_args, std::vector<IDPtr> arg_params,
|
|
std ::vector<bool> arg_param_is_modified, StmtPtr arg_body, int _frame_offset, TypePtr ret_type)
|
|
: Expr(EXPR_INLINE), sf(std::move(arg_sf)), args(std::move(arg_args)), body(std::move(arg_body)) {
|
|
params = std::move(arg_params);
|
|
param_is_modified = std::move(arg_param_is_modified);
|
|
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(sf, args_d, params, param_is_modified, 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 assign_stmt = with_location_of(c->GenParam(params[i], red_i, param_is_modified[i]), this);
|
|
red_stmt = MergeStmts(red_stmt, arg_red_stmt, assign_stmt);
|
|
}
|
|
|
|
auto ret_val = c->PushInlineBlock(type);
|
|
if ( ret_val )
|
|
ret_val->SetLocationInfo(GetLocationInfo());
|
|
|
|
body = body->Reduce(c);
|
|
c->PopInlineBlock();
|
|
|
|
auto catch_ret = with_location_of(make_intrusive<CatchReturnStmt>(sf, body, ret_val), this);
|
|
|
|
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 != ¶ms[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 = with_location_of(make_intrusive<ExprStmt>(Duplicate()), this);
|
|
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 = with_location_of(make_intrusive<ExprStmt>(Duplicate()), this);
|
|
|
|
auto index = op2->AsListExprPtr();
|
|
auto res = with_location_of(make_intrusive<IndexExpr>(GetOp1(), index, false), this);
|
|
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 = with_location_of(make_intrusive<ExprStmt>(Duplicate()), this);
|
|
|
|
auto field_res = with_location_of(make_intrusive<FieldExpr>(op1, field_name), this);
|
|
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("]");
|
|
}
|
|
|
|
ScriptOptBuiltinExpr::ScriptOptBuiltinExpr(SOBuiltInTag _tag, ExprPtr _arg1, ExprPtr _arg2)
|
|
: Expr(EXPR_SCRIPT_OPT_BUILTIN), tag(_tag), arg1(std::move(_arg1)), arg2(std::move(_arg2)) {
|
|
BuildEvalExpr();
|
|
SetType(eval_expr->GetType());
|
|
}
|
|
|
|
ScriptOptBuiltinExpr::ScriptOptBuiltinExpr(SOBuiltInTag _tag, CallExprPtr _call)
|
|
: Expr(EXPR_SCRIPT_OPT_BUILTIN), tag(_tag), call(std::move(_call)) {
|
|
const auto& args = call->Args()->Exprs();
|
|
ASSERT(args.size() <= 2);
|
|
|
|
if ( args.size() > 0 ) {
|
|
arg1 = args[0]->Duplicate();
|
|
if ( args.size() > 1 ) {
|
|
arg2 = args[1]->Duplicate();
|
|
}
|
|
}
|
|
|
|
BuildEvalExpr();
|
|
|
|
SetType(eval_expr->GetType());
|
|
}
|
|
|
|
ValPtr ScriptOptBuiltinExpr::Eval(Frame* f) const { return eval_expr->Eval(f); }
|
|
|
|
void ScriptOptBuiltinExpr::ExprDescribe(ODesc* d) const {
|
|
switch ( tag ) {
|
|
case MINIMUM: d->Add("ZAM_minimum"); break;
|
|
case MAXIMUM: d->Add("ZAM_maximum"); break;
|
|
case HAS_ELEMENTS: d->Add("ZAM_has_elements"); break;
|
|
case FUNC_ID_STRING: d->Add("ZAM_id_string"); break;
|
|
}
|
|
|
|
d->Add("(");
|
|
arg1->Describe(d);
|
|
|
|
if ( arg2 ) {
|
|
d->AddSP(",");
|
|
arg2->Describe(d);
|
|
}
|
|
|
|
d->Add(")");
|
|
}
|
|
|
|
TraversalCode ScriptOptBuiltinExpr::Traverse(TraversalCallback* cb) const {
|
|
TraversalCode tc = cb->PreExpr(this);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
tc = arg1->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
|
|
if ( arg2 ) {
|
|
tc = arg2->Traverse(cb);
|
|
HANDLE_TC_EXPR_PRE(tc);
|
|
}
|
|
|
|
tc = cb->PostExpr(this);
|
|
HANDLE_TC_EXPR_POST(tc);
|
|
}
|
|
|
|
bool ScriptOptBuiltinExpr::IsPure() const { return arg1->IsPure() && (! arg2 || arg2->IsPure()); }
|
|
|
|
ExprPtr ScriptOptBuiltinExpr::Duplicate() {
|
|
auto new_me = make_intrusive<ScriptOptBuiltinExpr>(tag, arg1, arg2);
|
|
return with_location_of(std::move(new_me), this);
|
|
}
|
|
|
|
bool ScriptOptBuiltinExpr::IsReduced(Reducer* c) const {
|
|
if ( ! arg1->IsReduced(c) )
|
|
return NonReduced(arg1.get());
|
|
|
|
if ( arg2 && ! arg2->IsReduced(c) )
|
|
return NonReduced(arg2.get());
|
|
|
|
if ( arg1->IsConst() && (! arg2 || arg2->IsConst()) )
|
|
return NonReduced(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
ExprPtr ScriptOptBuiltinExpr::Reduce(Reducer* c, StmtPtr& red_stmt) {
|
|
auto orig_arg1 = arg1;
|
|
auto orig_arg2 = arg2;
|
|
|
|
if ( c->Optimizing() ) {
|
|
arg1 = c->UpdateExpr(arg1);
|
|
if ( arg2 )
|
|
arg2 = c->UpdateExpr(arg2);
|
|
}
|
|
else {
|
|
arg1 = arg1->Reduce(c, red_stmt);
|
|
if ( arg2 ) {
|
|
StmtPtr red_stmt2;
|
|
arg2 = arg2->Reduce(c, red_stmt2);
|
|
red_stmt = MergeStmts(std::move(red_stmt), std::move(red_stmt2));
|
|
}
|
|
}
|
|
|
|
if ( arg1 != orig_arg1 || arg2 != orig_arg2 )
|
|
BuildEvalExpr();
|
|
|
|
if ( arg1->IsConst() && (! arg2 || arg2->IsConst()) ) {
|
|
auto res = eval_expr->Eval(nullptr);
|
|
ASSERT(res);
|
|
return with_location_of(make_intrusive<ConstExpr>(res), this);
|
|
}
|
|
|
|
if ( c->Optimizing() )
|
|
return ThisPtr();
|
|
else
|
|
return AssignToTemporary(c, red_stmt);
|
|
}
|
|
|
|
void ScriptOptBuiltinExpr::BuildEvalExpr() {
|
|
switch ( tag ) {
|
|
case MINIMUM: {
|
|
auto cmp = make_intrusive<RelExpr>(EXPR_LT, arg1, arg2);
|
|
eval_expr = make_intrusive<CondExpr>(cmp, arg1, arg2);
|
|
break;
|
|
}
|
|
|
|
case MAXIMUM: {
|
|
auto cmp = make_intrusive<RelExpr>(EXPR_GT, arg1, arg2);
|
|
eval_expr = make_intrusive<CondExpr>(cmp, arg1, arg2);
|
|
break;
|
|
}
|
|
|
|
case HAS_ELEMENTS: {
|
|
auto size = make_intrusive<SizeExpr>(arg1);
|
|
auto zero = make_intrusive<ConstExpr>(val_mgr->Count(0));
|
|
eval_expr = make_intrusive<EqExpr>(EXPR_NE, size, zero);
|
|
break;
|
|
}
|
|
|
|
case FUNC_ID_STRING: {
|
|
auto args = make_intrusive<ListExpr>();
|
|
if ( arg1 ) {
|
|
args->Append(arg1);
|
|
if ( arg2 )
|
|
args->Append(arg2);
|
|
}
|
|
eval_expr = make_intrusive<CallExpr>(call->FuncPtr(), args);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SetType(eval_expr->GetType());
|
|
}
|
|
|
|
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
|