diff --git a/src/script_opt/ZAM/Expr.cc b/src/script_opt/ZAM/Expr.cc index 881fd35441..27fdae8ff3 100644 --- a/src/script_opt/ZAM/Expr.cc +++ b/src/script_opt/ZAM/Expr.cc @@ -1100,16 +1100,34 @@ const ZAMStmt ZAMCompiler::ConstructSet(const NameExpr* n, const Expr* e) { const ZAMStmt ZAMCompiler::ConstructRecord(const NameExpr* n, const Expr* e) { auto rc = e->AsRecordConstructorExpr(); + auto rt = e->GetType()->AsRecordType(); + auto map = rc->Map(); ZInstI z; - if ( rc->Map() ) { - z = GenInst(OP_CONSTRUCT_KNOWN_RECORD_V, n); + if ( map ) { + // Compute whether we need at least one of the default field + // initializations. We don't need so if every one of the fields + // with such an initializer is already covered in the map (meaning + // it has an explicit initialization value). + auto& ci = rt->CreationInits(); + zeek_uint_t common = 0; + for ( auto& c : ci ) + for ( auto r : *map ) + if ( c.first == r ) + ++common; + + auto need_init = common != ci.size(); + + z = GenInst(OP_CONSTRUCT_KNOWN_RECORD_Vi, n, need_init); z.aux = InternalBuildVals(rc->Op().get()); - z.aux->map = *rc->Map(); + z.aux->map = *map; } else { - z = GenInst(OP_CONSTRUCT_RECORD_V, n); + // Constructors that don't need maps are explicitly initializing + // every field, so they don't need default initializations, hence + // "false" in the following. + z = GenInst(OP_CONSTRUCT_DIRECT_RECORD_Vi, n, false); z.aux = InternalBuildVals(rc->Op().get()); } @@ -1118,6 +1136,33 @@ const ZAMStmt ZAMCompiler::ConstructRecord(const NameExpr* n, const Expr* e) { if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, z.t) ) z.aux->can_change_non_locals = true; + // If one of the initialization values is an unspecified vector (which + // in general we can't know until run-time) then we'll need to + // "concretize" it. We first see whether this is a possibility, since + // it usually isn't, by counting up how many of the record fields are + // vectors. + std::vector vector_fields; // holds indices of the vector fields + for ( int i = 0; i < z.aux->n; ++i ) { + auto field_ind = map ? (*map)[i] : i; + if ( rt->GetFieldType(field_ind)->Tag() == TYPE_VECTOR ) + vector_fields.push_back(field_ind); + } + + auto inst = AddInst(z); + + if ( vector_fields.empty() ) + // Common case of no vector fields, we're done. + return inst; + + // Need to add a separate instruction for concretizing the fields. + z = GenInst(OP_CONCRETIZE_VECTOR_FIELDS_V, n); + z.t = e->GetType(); + int nf = static_cast(vector_fields.size()); + z.aux = new ZInstAux(nf); + z.aux->elems_has_slots = false; // we're storing field offsets, not slots + for ( int i = 0; i < nf; ++i ) + z.aux->Add(i, vector_fields[i]); + return AddInst(z); } diff --git a/src/script_opt/ZAM/Ops.in b/src/script_opt/ZAM/Ops.in index 00dbbaebdb..699007f771 100644 --- a/src/script_opt/ZAM/Ops.in +++ b/src/script_opt/ZAM/Ops.in @@ -1199,35 +1199,49 @@ eval ConstructTableOrSetPre() direct-unary-op Record-Constructor ConstructRecord -internal-op Construct-Record -type V +# v2 is boolean of whether to initialize fields. For this instruction, +# it's always false. +op Construct-Direct-Record +type Vi eval EvalConstructRecord(, i) +# v2 is boolean of whether to initialize fields. +op Construct-Known-Record +type Vi +eval EvalConstructRecord(auto& map = aux->map;, map[i]) + macro EvalConstructRecord(map_init, map_accessor) auto rt = cast_intrusive(z.t); - auto new_r = new RecordVal(rt); + auto new_r = new RecordVal(rt, z.v2 ? RecordVal::RV_FULL_INIT : RecordVal::RV_SLOTS_INIT); auto aux = z.aux; auto n = aux->n; map_init for ( auto i = 0; i < n; ++i ) - { - auto v_i = aux->ToVal(frame, i); - auto ind = map_accessor; - if ( v_i && v_i->GetType()->Tag() == TYPE_VECTOR && - v_i->GetType()->IsUnspecifiedVector() ) - { - const auto& t_ind = rt->GetFieldType(ind); - v_i->AsVectorVal()->Concretize(t_ind->Yield()); - } - new_r->Assign(ind, v_i); - } + new_r->InitField(map_accessor, aux->ToZVal(frame, i)); auto& r = frame[z.v1].record_val; Unref(r); r = new_r; -internal-op Construct-Known-Record +# Special instruction for concretizing vectors that are fields in a +# newly-constructed record. "aux" holds which fields in the record to +# inspect. +op Concretize-Vector-Fields +op1-read type V -eval EvalConstructRecord(auto& map = aux->map;, map[i]) +eval auto rt = cast_intrusive(z.t); + auto r = frame[z.v1].record_val; + auto aux = z.aux; + auto n = aux->n; + for ( auto i = 0; i < n; ++i ) + { + auto v_i = r->GetField(aux->elems[i].IntVal()); + ASSERT(v_i); + if ( v_i->GetType()->IsUnspecifiedVector() ) + { + const auto& t_i = rt->GetFieldType(i); + v_i->AsVectorVal()->Concretize(t_i->Yield()); + } + } direct-unary-op Vector-Constructor ConstructVector diff --git a/src/script_opt/ZAM/ZInst.h b/src/script_opt/ZAM/ZInst.h index 9e1c840d7c..41854fe2c6 100644 --- a/src/script_opt/ZAM/ZInst.h +++ b/src/script_opt/ZAM/ZInst.h @@ -418,6 +418,9 @@ public: // and type. void Add(int i, int slot, TypePtr t) { elems[i].SetInt(slot, t); } + // Same, but for non-slot integers. + void Add(int i, int v_i) { elems[i].SetInt(v_i); } + // Same but for constants. void Add(int i, ValPtr c) { elems[i].SetConstant(c); }