From 91cab9931dd484bfd36d86d0e55e4529d253030f Mon Sep 17 00:00:00 2001 From: Vern Paxson Date: Thu, 18 Jan 2024 10:15:29 -0800 Subject: [PATCH] ZAM optimizations for record creation includes reworking of managing "auxiliary" information for ZAM instructions --- src/Type.cc | 14 +-- src/Type.h | 9 +- src/Val.cc | 10 +- src/Val.h | 6 +- src/script_opt/ZAM/AM-Opt.cc | 27 +++-- src/script_opt/ZAM/Expr.cc | 85 +++++++++++-- src/script_opt/ZAM/Ops.in | 229 ++++++++++++++++++----------------- src/script_opt/ZAM/ZInst.h | 168 ++++++++++++++++--------- 8 files changed, 341 insertions(+), 207 deletions(-) diff --git a/src/Type.cc b/src/Type.cc index 925f46191e..c2966cbef6 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -1059,20 +1059,20 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td) { auto def_attr = a ? a->Find(detail::ATTR_DEFAULT) : nullptr; auto def_expr = def_attr ? def_attr->GetExpr() : nullptr; - std::unique_ptr init; + std::shared_ptr init; if ( def_expr && ! IsErrorType(type->Tag()) ) { if ( def_expr->Tag() == detail::EXPR_CONST ) { auto zv = ZVal(def_expr->Eval(nullptr), type); if ( ZVal::IsManagedType(type) ) - init = std::make_unique(zv); + init = std::make_shared(zv); else - init = std::make_unique(zv); + init = std::make_shared(zv); } else { - auto efi = std::make_unique(def_expr, type); + auto efi = std::make_shared(def_expr, type); creation_inits.emplace_back(field, std::move(efi)); } } @@ -1086,15 +1086,15 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td) { // and RecordType::CreationInitisOptimizer. // // init (nil) is appended to deferred_inits as placeholder. - auto rfi = std::make_unique(cast_intrusive(type)); + auto rfi = std::make_shared(cast_intrusive(type)); creation_inits.emplace_back(field, std::move(rfi)); } else if ( tag == TYPE_TABLE ) - init = std::make_unique(cast_intrusive(type), a); + init = std::make_shared(cast_intrusive(type), a); else if ( tag == TYPE_VECTOR ) - init = std::make_unique(cast_intrusive(type)); + init = std::make_shared(cast_intrusive(type)); } deferred_inits.push_back(std::move(init)); diff --git a/src/Type.h b/src/Type.h index fafa79dee1..440094705a 100644 --- a/src/Type.h +++ b/src/Type.h @@ -30,10 +30,12 @@ using TableValPtr = IntrusivePtr; namespace detail { +class Attributes; class CompositeHash; class Expr; class ListExpr; -class Attributes; +class ZAMCompiler; + using ListExprPtr = IntrusivePtr; // The following tracks how to initialize a given record field. @@ -734,7 +736,7 @@ private: // Field initializations that can be deferred to first access, // beneficial for fields that are separately initialized prior // to first access. Nil pointers mean "skip initializing the field". - std::vector> deferred_inits; + std::vector> deferred_inits; // Field initializations that need to be done upon record creation, // rather than deferred. These are expressions whose value might @@ -742,10 +744,11 @@ private: // // Such initializations are uncommon, so we represent them using // pairs. - std::vector>> creation_inits; + std::vector>> creation_inits; class CreationInitsOptimizer; friend zeek::RecordVal; + friend zeek::detail::ZAMCompiler; const auto& DeferredInits() const { return deferred_inits; } const auto& CreationInits() const { return creation_inits; } diff --git a/src/Val.cc b/src/Val.cc index bd0068daeb..ac3fd5fd91 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -2784,7 +2784,6 @@ TableVal::TableRecordDependencies TableVal::parse_time_table_record_dependencies RecordVal::RecordTypeValMap RecordVal::parse_time_records; RecordVal::RecordVal(RecordTypePtr t, bool init_fields) : Val(t), is_managed(t->ManagedFields()) { - origin = nullptr; rt = std::move(t); int n = rt->NumFields(); @@ -2810,6 +2809,12 @@ RecordVal::RecordVal(RecordTypePtr t, bool init_fields) : Val(t), is_managed(t-> record_val.reserve(n); } +RecordVal::RecordVal(RecordTypePtr t, std::vector> init_vals) + : Val(t), is_managed(t->ManagedFields()) { + rt = std::move(t); + record_val = std::move(init_vals); +} + RecordVal::~RecordVal() { auto n = record_val.size(); @@ -3016,11 +3021,10 @@ void RecordVal::DescribeReST(ODesc* d) const { ValPtr RecordVal::DoClone(CloneState* state) { // We set origin to 0 here. Origin only seems to be used for exactly one // purpose - to find the connection record that is associated with a - // record. As we cannot guarantee that it will ber zeroed out at the + // record. As we cannot guarantee that it will be zeroed out at the // appropriate time (as it seems to be guaranteed for the original record) // we don't touch it. auto rv = make_intrusive(rt, false); - rv->origin = nullptr; state->NewClone(this, rv); int n = NumFields(); diff --git a/src/Val.h b/src/Val.h index 7a6bdf0379..a87ea91a37 100644 --- a/src/Val.h +++ b/src/Val.h @@ -1392,6 +1392,10 @@ protected: friend class zeek::detail::CPPRuntime; friend class zeek::detail::CompositeHash; + // Constructor for use by script optimization, directly initializing + // record_vals from the second argument. + RecordVal(RecordTypePtr t, std::vector> init_vals); + RecordValPtr DoCoerceTo(RecordTypePtr other, bool allow_orphaning) const; /** @@ -1436,7 +1440,7 @@ protected: void AddedField(int field) { Modified(); } - Obj* origin; + Obj* origin = nullptr; using RecordTypeValMap = std::unordered_map>; static RecordTypeValMap parse_time_records; diff --git a/src/script_opt/ZAM/AM-Opt.cc b/src/script_opt/ZAM/AM-Opt.cc index cd4a3e6b6f..68fd594370 100644 --- a/src/script_opt/ZAM/AM-Opt.cc +++ b/src/script_opt/ZAM/AM-Opt.cc @@ -432,26 +432,27 @@ void ZAMCompiler::ComputeFrameLifetimes() { case OP_LAMBDA_VV: { auto aux = inst->aux; int n = aux->n; - auto& slots = aux->slots; - for ( int i = 0; i < n; ++i ) - if ( slots[i] >= 0 ) - ExtendLifetime(slots[i], EndOfLoop(inst, 1)); + for ( int i = 0; i < n; ++i ) { + auto slot_i = aux->elems[i].Slot(); + if ( slot_i >= 0 ) + ExtendLifetime(slot_i, EndOfLoop(inst, 1)); + } break; } default: // Look for slots in auxiliary information. auto aux = inst->aux; - if ( ! aux || ! aux->slots ) + if ( ! aux || ! aux->elems_has_slots ) break; int n = aux->n; - auto& slots = aux->slots; for ( auto j = 0; j < n; ++j ) { - if ( slots[j] < 0 ) + auto slot_j = aux->elems[j].Slot(); + if ( slot_j < 0 ) continue; - ExtendLifetime(slots[j], EndOfLoop(inst, 1)); + ExtendLifetime(slot_j, EndOfLoop(inst, 1)); } break; } @@ -562,11 +563,11 @@ void ZAMCompiler::ReMapFrame() { default: // Update slots in auxiliary information. auto aux = inst->aux; - if ( ! aux || ! aux->slots ) + if ( ! aux || ! aux->elems_has_slots ) break; for ( auto j = 0; j < aux->n; ++j ) { - auto& slot = aux->slots[j]; + auto slot = aux->elems[j].Slot(); if ( slot < 0 ) // This is instead a constant. @@ -581,7 +582,7 @@ void ZAMCompiler::ReMapFrame() { frame_denizens[slot]->Name()); } - slot = new_slot; + aux->elems[j].SetSlot(new_slot); } break; } @@ -858,9 +859,9 @@ bool ZAMCompiler::VarIsUsed(int slot) const { return true; auto aux = inst->aux; - if ( aux && aux->slots ) { + if ( aux && aux->elems_has_slots ) { for ( int j = 0; j < aux->n; ++j ) - if ( aux->slots[j] == slot ) + if ( aux->elems[j].Slot() == slot ) return true; } } diff --git a/src/script_opt/ZAM/Expr.cc b/src/script_opt/ZAM/Expr.cc index 9f874edd0d..5afd2f82cf 100644 --- a/src/script_opt/ZAM/Expr.cc +++ b/src/script_opt/ZAM/Expr.cc @@ -1100,23 +1100,84 @@ 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(); - ZInstI z; + auto aux = InternalBuildVals(rc->Op().get()); - if ( rc->Map() ) { - z = GenInst(OP_CONSTRUCT_KNOWN_RECORD_V, n); - z.aux = InternalBuildVals(rc->Op().get()); - z.aux->map = *rc->Map(); - } - else { - z = GenInst(OP_CONSTRUCT_RECORD_V, n); - z.aux = InternalBuildVals(rc->Op().get()); + // Note that we set the vector to the full size of the record being + // constructed, *not* the size of any map (which could be smaller). + // This is because we want to provide the vector directly to the + // constructor. + aux->zvec.resize(rt->NumFields()); + + if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, e->GetType()) ) + aux->can_change_non_locals = true; + + ZOp op; + + const auto& map = rc->Map(); + if ( map ) { + aux->map = *map; + + auto fi = std::make_unique>>>(); + + // Populate the field inits as needed. + for ( auto& c : rt->CreationInits() ) { + bool seen = false; + for ( auto r : *map ) + if ( c.first == r ) { + // Superseded by a constructor element; + seen = true; + break; + } + + if ( ! seen ) + // Need to generate field dynamically. + fi->push_back(c); + } + + if ( fi->empty() ) + op = OP_CONSTRUCT_KNOWN_RECORD_V; + else { + op = OP_CONSTRUCT_KNOWN_RECORD_WITH_INITS_V; + aux->field_inits = std::move(fi); + } } + else + op = OP_CONSTRUCT_DIRECT_RECORD_V; + ZInstI z = GenInst(op, n); + + z.aux = aux; z.t = e->GetType(); - if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, z.t) ) - z.aux->can_change_non_locals = true; + auto inst = AddInst(z); + + // 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; + auto& field_t = rt->GetFieldType(field_ind); + if ( field_t->Tag() == TYPE_VECTOR && field_t->Yield()->Tag() != TYPE_ANY ) + vector_fields.push_back(field_ind); + } + + 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); } @@ -1212,7 +1273,7 @@ const ZAMStmt ZAMCompiler::RecordCoerce(const NameExpr* n, const Expr* e) { z.aux->Add(i, map[i], nullptr); // Mark the integer entries in z.aux as not being frame slots as usual. - z.aux->slots = nullptr; + z.aux->elems_has_slots = false; if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, e->GetType()) ) z.aux->can_change_non_locals = true; diff --git a/src/script_opt/ZAM/Ops.in b/src/script_opt/ZAM/Ops.in index 38413bb092..a4b2130d58 100644 --- a/src/script_opt/ZAM/Ops.in +++ b/src/script_opt/ZAM/Ops.in @@ -1198,35 +1198,48 @@ eval ConstructTableOrSetPre() direct-unary-op Record-Constructor ConstructRecord -internal-op Construct-Record -type V -eval EvalConstructRecord(, i) - -macro EvalConstructRecord(map_init, map_accessor) - auto rt = cast_intrusive(z.t); - auto new_r = new RecordVal(rt); - 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); - } +macro ConstructRecordPost() auto& r = frame[z.v1].record_val; Unref(r); - r = new_r; + r = new RecordVal(cast_intrusive(z.t), init_vals); -internal-op Construct-Known-Record +op Construct-Direct-Record type V -eval EvalConstructRecord(auto& map = aux->map;, map[i]) +eval auto& init_vals = z.aux->ToZValVec(frame); + ConstructRecordPost() + +op Construct-Known-Record +type V +eval auto& init_vals = z.aux->ToZValVecWithMap(frame); + ConstructRecordPost() + +op Construct-Known-Record-With-Inits +type V +eval auto& init_vals = z.aux->ToZValVecWithMap(frame); + for ( auto& fi : *z.aux->field_inits ) + init_vals[fi.first] = fi.second->Generate(); + ConstructRecordPost() + +# 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 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 @@ -1757,21 +1770,21 @@ internal-op Event3 type VVV op1-read eval ValVec args(3); + auto& aux = z.aux; args[0] = frame[z.v1].ToVal(z.t); args[1] = frame[z.v2].ToVal(z.t2); - auto types = z.aux->types; - args[2] = frame[z.v3].ToVal(types[2]); + args[2] = frame[z.v3].ToVal(aux->elems[2].GetType()); QueueEvent(z.event_handler, args); internal-op Event4 type VVVV op1-read eval ValVec args(4); + auto& aux = z.aux; args[0] = frame[z.v1].ToVal(z.t); args[1] = frame[z.v2].ToVal(z.t2); - auto types = z.aux->types; - args[2] = frame[z.v3].ToVal(types[2]); - args[3] = frame[z.v4].ToVal(types[3]); + args[2] = frame[z.v3].ToVal(aux->elems[2].GetType()); + args[3] = frame[z.v4].ToVal(aux->elems[3].GetType()); QueueEvent(z.event_handler, args); @@ -2256,11 +2269,11 @@ eval auto& aux = z.aux; auto captures = std::make_unique>(); for ( auto i = 0; i < aux->n; ++i ) { - auto slot = aux->slots[i]; + auto slot = aux->elems[i].Slot(); if ( slot >= 0 ) { - auto& cp = frame[aux->slots[i]]; - if ( aux->is_managed[i] ) + auto& cp = frame[slot]; + if ( aux->elems[i].IsManaged() ) zeek::Ref(cp.ManagedVal()); captures->push_back(cp); } @@ -2355,7 +2368,7 @@ eval LogWritePre(frame[z.v2].ToVal(log_ID_enum_type), v3) internal-op Log-WriteC side-effects OP_LOG_WRITEC_V OP_V type VV -eval LogWritePre(z.aux->constants[0], v2) +eval LogWritePre(z.aux->elems[0].Constant(), v2) LogWriteResPost() # Versions that discard the return value. @@ -2370,7 +2383,7 @@ internal-op Log-WriteC side-effects op1-read type V -eval LogWritePre(z.aux->constants[0], v1) +eval LogWritePre(z.aux->elems[0].Constant(), v1) LogWriteNoResPost() internal-op Broker-Flush-Logs @@ -2460,23 +2473,21 @@ eval Cat1FullVal(frame[z.v2]) internal-op CatN type V eval auto aux = z.aux; - auto slots = z.aux->slots; auto& ca = aux->cat_args; int n = aux->n; size_t max_size = 0; for ( int i = 0; i < n; ++i ) - max_size += ca[i]->MaxSize(frame, slots[i]); + max_size += ca[i]->MaxSize(frame, aux->elems[i].Slot()); auto res = new char[max_size + /* slop */ n + 1]; auto res_p = res; for ( int i = 0; i < n; ++i ) - ca[i]->RenderInto(frame, slots[i], res_p); + ca[i]->RenderInto(frame, aux->elems[i].Slot(), res_p); *res_p = '\0'; auto s = new String(true, reinterpret_cast(res), res_p - res); Cat1Op(ZVal(new StringVal(s))) macro CatNPre() auto aux = z.aux; - auto slots = z.aux->slots; auto& ca = aux->cat_args; macro CatNMid() @@ -2491,113 +2502,113 @@ macro CatNPost() internal-op Cat2 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); CatNPost() internal-op Cat3 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); CatNPost() internal-op Cat4 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); - max_size += ca[3]->MaxSize(frame, slots[3]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); + max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); - ca[3]->RenderInto(frame, slots[3], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); + ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); CatNPost() internal-op Cat5 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); - max_size += ca[3]->MaxSize(frame, slots[3]); - max_size += ca[4]->MaxSize(frame, slots[4]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); + max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); + max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); - ca[3]->RenderInto(frame, slots[3], res_p); - ca[4]->RenderInto(frame, slots[4], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); + ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); + ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); CatNPost() internal-op Cat6 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); - max_size += ca[3]->MaxSize(frame, slots[3]); - max_size += ca[4]->MaxSize(frame, slots[4]); - max_size += ca[5]->MaxSize(frame, slots[5]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); + max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); + max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); + max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); - ca[3]->RenderInto(frame, slots[3], res_p); - ca[4]->RenderInto(frame, slots[4], res_p); - ca[5]->RenderInto(frame, slots[5], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); + ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); + ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); + ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); CatNPost() internal-op Cat7 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); - max_size += ca[3]->MaxSize(frame, slots[3]); - max_size += ca[4]->MaxSize(frame, slots[4]); - max_size += ca[5]->MaxSize(frame, slots[5]); - max_size += ca[6]->MaxSize(frame, slots[6]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); + max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); + max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); + max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); + max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); - ca[3]->RenderInto(frame, slots[3], res_p); - ca[4]->RenderInto(frame, slots[4], res_p); - ca[5]->RenderInto(frame, slots[5], res_p); - ca[6]->RenderInto(frame, slots[6], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); + ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); + ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); + ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); + ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p); CatNPost() internal-op Cat8 type V eval CatNPre() - size_t max_size = ca[0]->MaxSize(frame, slots[0]); - max_size += ca[1]->MaxSize(frame, slots[1]); - max_size += ca[2]->MaxSize(frame, slots[2]); - max_size += ca[3]->MaxSize(frame, slots[3]); - max_size += ca[4]->MaxSize(frame, slots[4]); - max_size += ca[5]->MaxSize(frame, slots[5]); - max_size += ca[6]->MaxSize(frame, slots[6]); - max_size += ca[7]->MaxSize(frame, slots[7]); + size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot()); + max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot()); + max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot()); + max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot()); + max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot()); + max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot()); + max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot()); + max_size += ca[7]->MaxSize(frame, aux->elems[7].Slot()); CatNMid() - ca[0]->RenderInto(frame, slots[0], res_p); - ca[1]->RenderInto(frame, slots[1], res_p); - ca[2]->RenderInto(frame, slots[2], res_p); - ca[3]->RenderInto(frame, slots[3], res_p); - ca[4]->RenderInto(frame, slots[4], res_p); - ca[5]->RenderInto(frame, slots[5], res_p); - ca[6]->RenderInto(frame, slots[6], res_p); - ca[7]->RenderInto(frame, slots[7], res_p); + ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p); + ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p); + ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p); + ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p); + ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p); + ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p); + ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p); + ca[7]->RenderInto(frame, aux->elems[7].Slot(), res_p); CatNPost() internal-op Analyzer--Name diff --git a/src/script_opt/ZAM/ZInst.h b/src/script_opt/ZAM/ZInst.h index 8e53d39b69..11dccade13 100644 --- a/src/script_opt/ZAM/ZInst.h +++ b/src/script_opt/ZAM/ZInst.h @@ -302,6 +302,65 @@ private: void InitConst(const ConstExpr* ce); }; +// Class for tracking one element of auxiliary information. This can be an +// integer, often specifying a frame slot, or a Val representing a constant. +// The class also tracks any associated type and caches whether it's "managed". +class AuxElem { +public: + AuxElem() {} + + // Different ways of setting the specifics of the element. + void SetInt(int _i) { i = _i; } + void SetInt(int _i, TypePtr _t) { + i = _i; + SetType(_t); + } + void SetSlot(int slot) { i = slot; } + void SetConstant(ValPtr _c) { + c = std::move(_c); + // c might be null in some contexts. + if ( c ) { + SetType(c->GetType()); + zc = ZVal(c, t); + } + } + + // Returns the element as a Val object. + ValPtr ToVal(const ZVal* frame) const { + if ( c ) + return c; + else + return frame[i].ToVal(t); + } + + // Returns the element as a ZVal object. + ZVal ToZVal(const ZVal* frame) const { + ZVal zv = c ? zc : frame[i]; + if ( is_managed ) + Ref(zv.ManagedVal()); + return zv; + } + + int Slot() const { return i; } + int IntVal() const { return i; } + const ValPtr& Constant() const { return c; } + ZVal ZConstant() const { return zc; } + const TypePtr& GetType() const { return t; } + bool IsManaged() const { return is_managed; } + +private: + void SetType(TypePtr _t) { + t = std::move(_t); + is_managed = t ? ZVal::IsManagedType(t) : false; + } + + int i = -1; // -1 = "not a slot" + ValPtr c; + ZVal zc; + TypePtr t; + bool is_managed = false; +}; + // Auxiliary information, used when the fixed ZInst layout lacks // sufficient expressiveness to represent all of the elements that // an instruction needs. @@ -311,53 +370,41 @@ public: // tracking slots, constants, and types. ZInstAux(int _n) { n = _n; - if ( n > 0 ) { - slots = ints = new int[n]; - constants = new ValPtr[n]; - types = new TypePtr[n]; - is_managed = new bool[n]; - } + if ( n > 0 ) + elems = new AuxElem[n]; } ~ZInstAux() { - delete[] ints; - delete[] constants; - delete[] types; - delete[] is_managed; + delete[] elems; delete[] cat_args; } - // Returns the i'th element of the parallel arrays as a ValPtr. - ValPtr ToVal(const ZVal* frame, int i) const { - if ( constants[i] ) - return constants[i]; - else - return frame[slots[i]].ToVal(types[i]); - } + // Returns the i'th element of the elements as a ValPtr. + ValPtr ToVal(const ZVal* frame, int i) const { return elems[i].ToVal(frame); } + ZVal ToZVal(const ZVal* frame, int i) const { return elems[i].ToZVal(frame); } - // Returns the parallel arrays as a ListValPtr. + // Returns the elements as a ListValPtr. ListValPtr ToListVal(const ZVal* frame) const { auto lv = make_intrusive(TYPE_ANY); for ( auto i = 0; i < n; ++i ) - lv->Append(ToVal(frame, i)); + lv->Append(elems[i].ToVal(frame)); return lv; } - // Converts the parallel arrays to a ListValPtr suitable for - // use as indices for indexing a table or set. "offset" specifies - // which index we're looking for (there can be a bunch for - // constructors), and "width" the number of elements in a single - // index. + // Converts the elements to a ListValPtr suitable for use as indices + // for indexing a table or set. "offset" specifies which index we're + // looking for (there can be a bunch for constructors), and "width" + // the number of elements in a single index. ListValPtr ToIndices(const ZVal* frame, int offset, int width) const { auto lv = make_intrusive(TYPE_ANY); for ( auto i = 0; i < 0 + width; ++i ) - lv->Append(ToVal(frame, offset + i)); + lv->Append(elems[offset + i].ToVal(frame)); return lv; } - // Returns the parallel arrays converted to a vector of ValPtr's. + // Returns the elements converted to a vector of ValPtr's. const ValVec& ToValVec(const ZVal* frame) { vv.clear(); FillValVec(vv, frame); @@ -365,49 +412,45 @@ public: } // Populates the given vector of ValPtr's with the conversion - // of the parallel arrays. + // of the elements. void FillValVec(ValVec& vec, const ZVal* frame) const { for ( auto i = 0; i < n; ++i ) - vec.push_back(ToVal(frame, i)); + vec.push_back(elems[i].ToVal(frame)); } - // When building up a ZInstAux, sets one element of the parallel - // arrays to a given frame slot and type. - void Add(int i, int slot, TypePtr t) { - ints[i] = slot; - constants[i] = nullptr; - types[i] = t; - is_managed[i] = t ? ZVal::IsManagedType(t) : false; + // Returns the elements converted to a vector of ZVal's. + const auto& ToZValVec(const ZVal* frame) { + for ( auto i = 0; i < n; ++i ) + zvec[i] = elems[i].ToZVal(frame); + return zvec; } + // Same, but using the "map" to determine where to place the values. + // Returns a non-const value because in this situation other updates + // may be coming to the vector, too. + auto& ToZValVecWithMap(const ZVal* frame) { + for ( auto i = 0; i < n; ++i ) + zvec[map[i]] = elems[i].ToZVal(frame); + return zvec; + } + + // When building up a ZInstAux, sets one element to a given frame slot + // 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) { - ints[i] = -1; - constants[i] = c; - types[i] = nullptr; - is_managed[i] = false; - } + void Add(int i, ValPtr c) { elems[i].SetConstant(c); } // Member variables. We could add accessors for manipulating // these (and make the variables private), but for convenience we // make them directly available. - // These are parallel arrays, used to build up lists of values. - // Each element is either an integer or a constant. Usually the - // integer is a frame slot (in which case "slots" points to "ints"; - // if not, it's nil). - // - // We track associated types, too, enabling us to use - // ZVal::ToVal to convert frame slots or constants to ValPtr's; - // and, as a performance optimization, whether those types - // indicate the slot needs to be managed. - - int n; // size of arrays - int* slots = nullptr; // either nil or points to ints - int* ints = nullptr; - ValPtr* constants = nullptr; - TypePtr* types = nullptr; - bool* is_managed = nullptr; + int n; // size of elements + AuxElem* elems = nullptr; + bool elems_has_slots = true; // Ingredients associated with lambdas ... ScriptFuncPtr primary_func; @@ -429,8 +472,8 @@ public: // store here. bool can_change_non_locals = false; - // The following is only used for OP_CONSTRUCT_KNOWN_RECORD_V, - // to map elements in slots/constants/types to record field offsets. + // The following is used for constructing records, to map elements in + // slots/constants/types to record field offsets. std::vector map; ///// The following four apply to looping over the elements of tables. @@ -453,6 +496,13 @@ public: // If we cared about memory penny-pinching, we could make this // a pointer and only instantiate as needed. ValVec vv; + + // Similar, but for ZVal's (used when constructing RecordVal's). + std::vector> zvec; + + // If non-nil, used for constructing records. Each pair gives the index + // into the final record and the associated field initializer. + std::unique_ptr>>> field_inits; }; // Returns a human-readable version of the given ZAM op-code.