diff --git a/src/Expr.h b/src/Expr.h index 5e8abbab4a..d60891f130 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -329,6 +329,11 @@ public: // Reduces the expression to one that can appear as a conditional. ExprPtr ReduceToConditional(Reducer* c, StmtPtr& red_stmt); + // Transforms the expression into its form suitable for use in + // a conditional. Only meaningful for expressions that return true + // for WillTransformInConditional(). + virtual ExprPtr TransformToConditional(Reducer* c, StmtPtr& red_stmt); + // Reduces the expression to one that can appear as a field // assignment. ExprPtr ReduceToFieldAssignment(Reducer* c, StmtPtr& red_stmt); @@ -806,6 +811,7 @@ public: bool WillTransform(Reducer* c) const override; bool WillTransformInConditional(Reducer* c) const override; ExprPtr Reduce(Reducer* c, StmtPtr& red_stmt) override; + ExprPtr TransformToConditional(Reducer* c, StmtPtr& red_stmt) override; protected: bool IsTrue(const ExprPtr& e) const; diff --git a/src/script_opt/Expr.cc b/src/script_opt/Expr.cc index 0a917d5106..2b5e4910ee 100644 --- a/src/script_opt/Expr.cc +++ b/src/script_opt/Expr.cc @@ -274,6 +274,18 @@ StmtPtr Expr::ReduceToSingletons(Reducer* c) { } 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(red_stmt, red_stmt2); + + return new_me; + } + switch ( tag ) { case EXPR_CONST: return ThisPtr(); @@ -355,6 +367,13 @@ ExprPtr Expr::ReduceToConditional(Reducer* c, StmtPtr& 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); @@ -1291,7 +1310,7 @@ bool CondExpr::IsReduced(Reducer* c) const { } bool CondExpr::HasReducedOps(Reducer* c) const { - return op1->IsSingleton(c) && op2->IsSingleton(c) && op3->IsSingleton(c) && ! op1->IsConst(); + return ! IsMinOrMax(c) && op1->IsSingleton(c) && op2->IsSingleton(c) && op3->IsSingleton(c) && ! op1->IsConst(); } bool CondExpr::WillTransform(Reducer* c) const { return ! HasReducedOps(c); } @@ -1303,6 +1322,16 @@ ExprPtr CondExpr::Reduce(Reducer* c, StmtPtr& red_stmt) { 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); @@ -1391,6 +1420,36 @@ StmtPtr CondExpr::ReduceToSingletons(Reducer* c) { 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(built_in, relop1, relop2), this); +} + ExprPtr RefExpr::Duplicate() { return SetSucc(new RefExpr(op->Duplicate())); } bool RefExpr::IsReduced(Reducer* c) const {