diff --git a/src/Expr.h b/src/Expr.h index 6e0f2b70d2..2f9ee0d722 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -883,6 +883,9 @@ public: protected: void ExprDescribe(ODesc* d) const override; + bool IsMinOrMax(Reducer* c) const; + ExprPtr TransformToMinOrMax() const; + ExprPtr op1; ExprPtr op2; ExprPtr op3; diff --git a/src/Stmt.h b/src/Stmt.h index 03653b6fbc..5e575f0555 100644 --- a/src/Stmt.h +++ b/src/Stmt.h @@ -128,6 +128,9 @@ protected: ValPtr DoExec(Frame* f, Val* v, StmtFlowType& flow) override; bool IsPure() const override; + bool IsMinMaxConstruct() const; + StmtPtr ConvertToMinMaxConstruct(); + StmtPtr s1; StmtPtr s2; }; diff --git a/src/script_opt/Stmt.cc b/src/script_opt/Stmt.cc index 666c012c23..4593a8133c 100644 --- a/src/script_opt/Stmt.cc +++ b/src/script_opt/Stmt.cc @@ -166,7 +166,7 @@ void IfStmt::Inline(Inliner* inl) { } bool IfStmt::IsReduced(Reducer* c) const { - if ( e->IsConst() || ! e->IsReducedConditional(c) ) + if ( e->IsConst() || ! e->IsReducedConditional(c) || IsMinMaxConstruct() ) return NonReduced(e.get()); return s1->IsReduced(c) && s2->IsReduced(c); @@ -179,11 +179,11 @@ StmtPtr IfStmt::DoReduce(Reducer* c) { e = e->ReduceToConditional(c, red_e_stmt); // First, assess some fundamental transformations. - if ( e->Tag() == EXPR_NOT ) { // Change "if ( ! x ) s1 else s2" to "if ( x ) s2 else s1". - auto s1_orig = s1; - s1 = s2; - s2 = s1_orig; + if ( IsMinMaxConstruct() ) + return ConvertToMinMaxConstruct()->Reduce(c); + if ( e->Tag() == EXPR_NOT ) { // Change "if ( ! x ) s1 else s2" to "if ( x ) s2 else s1". + std::swap(s1, s2); e = e->GetOp1(); } @@ -274,6 +274,81 @@ bool IfStmt::CouldReturn(bool ignore_break) const { return (s1 && s1->CouldReturn(ignore_break)) || (s2 && s2->CouldReturn(ignore_break)); } +bool IfStmt::IsMinMaxConstruct() const { + if ( ! s1 || ! s2 ) + // not an if-else construct + return false; + + if ( s1->Tag() != STMT_EXPR || s2->Tag() != STMT_EXPR ) + // definitely not if-else assignments + return false; + + auto es1 = s1->AsExprStmt()->StmtExpr(); + auto es2 = s2->AsExprStmt()->StmtExpr(); + + if ( es1->Tag() != EXPR_ASSIGN || es2->Tag() != EXPR_ASSIGN ) + return false; + + switch ( e->Tag() ) { + case EXPR_LT: + case EXPR_LE: + case EXPR_GE: + case EXPR_GT: break; + + default: + // Not an apt conditional. + return false; + } + + auto a1 = es1->AsAssignExpr(); + auto a2 = es2->AsAssignExpr(); + auto a1_lhs = a1->GetOp1(); + auto a2_lhs = a2->GetOp1(); + + if ( ! same_expr(a1_lhs, a2_lhs) ) + // if-else assignments are not to the same variable + return false; + + auto a1_rhs = a1->GetOp2(); + auto a2_rhs = a2->GetOp2(); + auto op1 = e->GetOp1(); + auto op2 = e->GetOp2(); + + if ( ! same_expr(op1, a1_rhs) && ! same_expr(op1, a2_rhs) ) + // Operand does not appear in the assignment RHS. + return false; + + if ( ! same_expr(op2, a1_rhs) && ! same_expr(op2, a2_rhs) ) + // Operand does not appear in the assignment RHS. + return false; + + if ( same_expr(op1, op2) ) + // This is degenerate and should be found by other reductions. + return false; + + return true; +} + +StmtPtr IfStmt::ConvertToMinMaxConstruct() { + auto relop1 = e->GetOp1(); + auto relop2 = e->GetOp2(); + + auto is_min = (e->Tag() == EXPR_LT || e->Tag() == EXPR_LE); + auto assign2 = s2->AsExprStmt()->StmtExpr(); + auto lhs2 = assign2->GetOp1(); + auto rhs2 = assign2->GetOp2(); + + if ( same_expr(relop1, rhs2) ) + is_min = ! is_min; + + auto built_in = is_min ? ScriptOptBuiltinExpr::MINIMUM : ScriptOptBuiltinExpr::MAXIMUM; + + auto bi = with_location_of(make_intrusive(built_in, relop1, relop2), this); + auto new_assign = with_location_of(make_intrusive(lhs2, bi, false), this); + + return with_location_of(make_intrusive(new_assign), this); +} + IntrusivePtr Case::Duplicate() { if ( expr_cases ) { auto new_exprs = expr_cases->Duplicate()->AsListExprPtr(); diff --git a/src/script_opt/ZAM/Expr.cc b/src/script_opt/ZAM/Expr.cc index 9f3953cb1f..65530ca56b 100644 --- a/src/script_opt/ZAM/Expr.cc +++ b/src/script_opt/ZAM/Expr.cc @@ -4,6 +4,7 @@ #include "zeek/Desc.h" #include "zeek/Reporter.h" +#include "zeek/script_opt/ZAM/BuiltIn.h" #include "zeek/script_opt/ZAM/Compile.h" namespace zeek::detail { @@ -134,8 +135,12 @@ const ZAMStmt ZAMCompiler::CompileAssignExpr(const AssignExpr* e) { auto op2 = e->GetOp2(); auto lhs = op1->AsRefExpr()->GetOp1()->AsNameExpr(); - auto lt = lhs->GetType().get(); auto rhs = op2.get(); + + if ( rhs->Tag() == EXPR_SCRIPT_OPT_BUILTIN ) + return CompileZAMBuiltin(lhs, static_cast(rhs)); + + auto lt = lhs->GetType().get(); auto r1 = rhs->GetOp1(); if ( rhs->Tag() == EXPR_INDEX && (r1->Tag() == EXPR_NAME || r1->Tag() == EXPR_CONST) ) @@ -178,8 +183,10 @@ const ZAMStmt ZAMCompiler::CompileAssignExpr(const AssignExpr* e) { return L_In_VecVLC(lhs, r1->AsListExpr(), r2c); } - if ( rhs->Tag() == EXPR_ANY_INDEX ) - return AnyIndexVVi(lhs, r1->AsNameExpr(), rhs->AsAnyIndexExpr()->Index()); + if ( rhs->Tag() == EXPR_ANY_INDEX ) { + auto rhs_as_any = static_cast(rhs); + return AnyIndexVVi(lhs, r1->AsNameExpr(), rhs_as_any->Index()); + } if ( rhs->Tag() == EXPR_LAMBDA ) return BuildLambda(lhs, rhs->AsLambdaExpr()); @@ -220,6 +227,72 @@ const ZAMStmt ZAMCompiler::CompileAssignExpr(const AssignExpr* e) { #include "ZAM-GenExprsDefsV.h" } +const ZAMStmt ZAMCompiler::CompileZAMBuiltin(const NameExpr* lhs, const ScriptOptBuiltinExpr* zbi) { + auto op1 = zbi->GetOp1(); + auto op2 = zbi->GetOp2(); + + switch ( zbi->Tag() ) { + case ScriptOptBuiltinExpr::MINIMUM: + case ScriptOptBuiltinExpr::MAXIMUM: { + auto t1 = op1->GetType()->InternalType(); + ASSERT(t1 == op2->GetType()->InternalType()); + + bool is_min = zbi->Tag() == ScriptOptBuiltinExpr::MINIMUM; + // Canonicalize to have constant as second op. + if ( op1->Tag() == EXPR_CONST ) { + ASSERT(op2->Tag() != EXPR_CONST); + std::swap(op1, op2); + is_min = ! is_min; + } + + ZOp op; + + auto n1 = op1->AsNameExpr(); + auto n2 = op2->Tag() == EXPR_NAME ? op2->AsNameExpr() : nullptr; + auto c = op2->Tag() == EXPR_CONST ? op2->AsConstExpr() : nullptr; + + if ( c ) { + if ( t1 == TYPE_INTERNAL_UNSIGNED ) + op = is_min ? OP_MINU_VVC : OP_MAXU_VVC; + else if ( t1 == TYPE_INTERNAL_INT ) + op = is_min ? OP_MINI_VVC : OP_MAXI_VVC; + else { + ASSERT(t1 == TYPE_INTERNAL_DOUBLE); + op = is_min ? OP_MIND_VVC : OP_MAXD_VVC; + } + } + else { + if ( t1 == TYPE_INTERNAL_UNSIGNED ) + op = is_min ? OP_MINU_VVV : OP_MAXU_VVV; + else if ( t1 == TYPE_INTERNAL_INT ) + op = is_min ? OP_MINI_VVV : OP_MAXI_VVV; + else { + ASSERT(t1 == TYPE_INTERNAL_DOUBLE); + op = is_min ? OP_MIND_VVV : OP_MAXD_VVV; + } + } + + if ( c ) + return AddInst(GenInst(op, lhs, n1, c)); + else + return AddInst(GenInst(op, lhs, n1, n2)); + } + + case ScriptOptBuiltinExpr::HAS_ELEMENTS: { + auto n = op1->AsNameExpr(); + auto op = op1->GetType()->Tag() == TYPE_TABLE ? OP_TABLE_HAS_ELEMENTS_VV : OP_VECTOR_HAS_ELEMENTS_VV; + return AddInst(GenInst(op, lhs, n)); + } + + case ScriptOptBuiltinExpr::FUNC_ID_STRING: { + auto n = op1->AsNameExpr(); + return AddInst(GenInst(OP_FUNC_ID_STRING_VV, lhs, n)); + } + + default: reporter->InternalError("bad built-in tag in ZAMCompiler::CompileZAMBuiltin"); + } +} + const ZAMStmt ZAMCompiler::CompileAssignToIndex(const NameExpr* lhs, const IndexExpr* rhs) { auto aggr = rhs->GetOp1(); auto const_aggr = aggr->Tag() == EXPR_CONST; diff --git a/src/script_opt/ZAM/Ops.in b/src/script_opt/ZAM/Ops.in index 2af74fcf66..3f5ebd8da0 100644 --- a/src/script_opt/ZAM/Ops.in +++ b/src/script_opt/ZAM/Ops.in @@ -666,6 +666,38 @@ type VVV eval if ( frame[z.v1].record_val->HasField(z.v2) ) BRANCH(v3) +internal-op Table-Has-Elements +type VV +eval frame[z.v1].int_val = frame[z.v2].table_val->Size() > 0; + +internal-op Table-Has-Elements-Cond +op1-read +type VV +eval if ( frame[z.v1].table_val->Size() == 0 ) + BRANCH(v2) + +internal-op Not-Table-Has-Elements-Cond +op1-read +type VV +eval if ( frame[z.v1].table_val->Size() > 0 ) + BRANCH(v2) + +internal-op Vector-Has-Elements +type VV +eval frame[z.v1].int_val = frame[z.v2].vector_val->Size() > 0; + +internal-op Vector-Has-Elements-Cond +op1-read +type VV +eval if ( frame[z.v1].vector_val->Size() == 0 ) + BRANCH(v2) + +internal-op Not-Vector-Has-Elements-Cond +op1-read +type VV +eval if ( frame[z.v1].vector_val->Size() > 0 ) + BRANCH(v2) + expr-op In type VVV custom-method return CompileInExpr(n1, n2, n3);