diff --git a/src/script_opt/ZAM/Gen-ZAM.cc b/src/script_opt/ZAM/Gen-ZAM.cc index 8ea060c82a..851d39c811 100644 --- a/src/script_opt/ZAM/Gen-ZAM.cc +++ b/src/script_opt/ZAM/Gen-ZAM.cc @@ -1468,16 +1468,12 @@ void ZAM_ExprOpTemplate::InstantiateEval(const vector& ot_orig, { string dispatch_params = "frame[z.v1].AsVectorRef(), frame[z.v2].AsVector()"; - string type_arg; if ( Arity() == 2 ) - { dispatch_params += ", frame[z.v3].AsVector()"; - type_arg = ", z.t"; - } auto op_code = g->GenOpCode(this, "_" + full_suffix); - auto dispatch = "vec_exec(" + op_code + type_arg + - ", " + dispatch_params + ", z);"; + auto dispatch = "vec_exec(" + op_code + ", z.t, " + + dispatch_params + ", z);"; ZAM_OpTemplate::InstantiateEval(Eval, full_suffix, dispatch, zc); @@ -1560,6 +1556,14 @@ void ZAM_UnaryExprOpTemplate::BuildInstruction(const vector& ot // These can't take the type from the LHS variable, since // that's the enclosing record and not the field within it. Emit("z.t = t;"); + + else if ( zc == ZIC_VEC ) + { + if ( constant_op ) + Emit("z.t = n->GetType();"); + else + Emit("z.t = n1->GetType();"); + } } diff --git a/src/script_opt/ZAM/Ops.in b/src/script_opt/ZAM/Ops.in index c84ee49dfc..a94519c0fb 100644 --- a/src/script_opt/ZAM/Ops.in +++ b/src/script_opt/ZAM/Ops.in @@ -1142,7 +1142,7 @@ eval frame[z.v1].double_val = double(frame[z.v2].uint_val); macro EvalCoerceVec(coercer) auto old_v1 = frame[z.v1].vector_val; - frame[z.v1].vector_val = coercer(frame[z.v2].vector_val); + frame[z.v1].vector_val = coercer(frame[z.v2].vector_val, z); Unref(old_v1); // delayed to allow for same value on both sides internal-op Coerce-UI-Vec diff --git a/src/script_opt/ZAM/ZBody.cc b/src/script_opt/ZAM/ZBody.cc index 62968fec27..ab22b02e8d 100644 --- a/src/script_opt/ZAM/ZBody.cc +++ b/src/script_opt/ZAM/ZBody.cc @@ -6,6 +6,7 @@ #include "zeek/EventHandler.h" #include "zeek/Trigger.h" #include "zeek/Traverse.h" +#include "zeek/Overflow.h" #include "zeek/Reporter.h" #include "zeek/script_opt/ScriptOpt.h" #include "zeek/script_opt/ZAM/Compile.h" @@ -77,21 +78,18 @@ static bool copy_vec_elem(VectorVal* vv, int ind, ZVal zv, const TypePtr& t) return true; } -// Unary vector operations never work on managed types, so no need -// to pass in the type ... However, the RHS, which normally would -// be const, needs to be non-const so we can use its Type() method -// to get at a shareable VectorType. -static void vec_exec(ZOp op, VectorVal*& v1, VectorVal* v2, const ZInst& z); +// Unary and binary element-by-element vector operations, yielding a new +// VectorVal with a yield type of 't'. 'z' is passed in only for localizing +// errors. +static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, const VectorVal* v2, + const ZInst& z); -// Binary operations *can* have managed types (strings). -static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, VectorVal* v2, +static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, const VectorVal* v2, const VectorVal* v3, const ZInst& z); // Vector coercion. -// -// ### Should check for underflow/overflow. -#define VEC_COERCE(tag, lhs_type, cast, rhs_accessor) \ - static VectorVal* vec_coerce_##tag(VectorVal* vec) \ +#define VEC_COERCE(tag, lhs_type, cast, rhs_accessor, ov_check, ov_err) \ + static VectorVal* vec_coerce_##tag(VectorVal* vec, const ZInst& z) \ { \ auto& v = *vec->RawVec(); \ auto yt = make_intrusive(base_type(lhs_type)); \ @@ -101,18 +99,32 @@ static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, VectorVal* v2, auto& res = *res_zv->RawVec(); \ for ( auto i = 0U; i < n; ++i ) \ if ( v[i] ) \ - res[i] = ZVal(cast((*v[i]).rhs_accessor)); \ + { \ + auto vi = (*v[i]).rhs_accessor; \ + if ( ov_check(vi) ) \ + { \ + std::string err = "overflow promoting from "; \ + err += ov_err; \ + err += " arithmetic value"; \ + ZAM_run_time_error(z.loc, err.c_str()); \ + res[i] = std::nullopt; \ + } \ + else \ + res[i] = ZVal(cast(vi)); \ + } \ else \ res[i] = std::nullopt; \ return res_zv; \ } -VEC_COERCE(IU, TYPE_INT, bro_int_t, AsCount()) -VEC_COERCE(ID, TYPE_INT, bro_int_t, AsDouble()) -VEC_COERCE(UI, TYPE_COUNT, bro_int_t, AsInt()) -VEC_COERCE(UD, TYPE_COUNT, bro_uint_t, AsDouble()) -VEC_COERCE(DI, TYPE_DOUBLE, double, AsInt()) -VEC_COERCE(DU, TYPE_DOUBLE, double, AsCount()) +#define false_func(x) false + +VEC_COERCE(DI, TYPE_DOUBLE, double, AsInt(), false_func, "") +VEC_COERCE(DU, TYPE_DOUBLE, double, AsCount(), false_func, "") +VEC_COERCE(ID, TYPE_INT, bro_int_t, AsDouble(), double_to_int_would_overflow, "double to signed") +VEC_COERCE(IU, TYPE_INT, bro_int_t, AsCount(), count_to_int_would_overflow, "unsigned to signed") +VEC_COERCE(UD, TYPE_COUNT, bro_uint_t, AsDouble(), double_to_count_would_overflow, "double to unsigned") +VEC_COERCE(UI, TYPE_COUNT, bro_int_t, AsInt(), int_to_count_would_overflow, "signed to unsigned") double curr_CPU_time() { @@ -483,7 +495,8 @@ TraversalCode ZAMResumption::Traverse(TraversalCallback* cb) const // Unary vector operation of v1 v2. -static void vec_exec(ZOp op, VectorVal*& v1, VectorVal* v2, const ZInst& z) +static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, const VectorVal* v2, + const ZInst& z) { // We could speed this up further still by gen'ing up an instance // of the loop inside each switch case (in which case we might as @@ -496,15 +509,20 @@ static void vec_exec(ZOp op, VectorVal*& v1, VectorVal* v2, const ZInst& z) auto& vec1 = *vec1_ptr; for ( auto i = 0U; i < n; ++i ) - switch ( op ) { + { + if ( vec2[i] ) + switch ( op ) { #include "ZAM-Vec1EvalDefs.h" - default: - reporter->InternalError("bad invocation of VecExec"); + default: + reporter->InternalError("bad invocation of VecExec"); + } + else + vec1[i] = std::nullopt; } - auto vt = cast_intrusive(v2->GetType()); + auto vt = cast_intrusive(std::move(t)); auto old_v1 = v1; v1 = new VectorVal(std::move(vt), vec1_ptr); Unref(old_v1); @@ -512,7 +530,7 @@ static void vec_exec(ZOp op, VectorVal*& v1, VectorVal* v2, const ZInst& z) // Binary vector operation of v1 = v2 v3. static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, - VectorVal* v2, const VectorVal* v3, const ZInst& z) + const VectorVal* v2, const VectorVal* v3, const ZInst& z) { // See comment above re further speed-up. @@ -523,12 +541,17 @@ static void vec_exec(ZOp op, TypePtr t, VectorVal*& v1, auto& vec1 = *vec1_ptr; for ( auto i = 0U; i < vec2.size(); ++i ) - switch ( op ) { + { + if ( vec2[i] && vec3[i] ) + switch ( op ) { #include "ZAM-Vec2EvalDefs.h" - default: - reporter->InternalError("bad invocation of VecExec"); + default: + reporter->InternalError("bad invocation of VecExec"); + } + else + vec1[i] = std::nullopt; } auto vt = cast_intrusive(std::move(t));