Merge remote-tracking branch 'origin/topic/vern/zam-rec-constr-opt2'

* origin/topic/vern/zam-rec-constr-opt2:
  ZAM optimizations for record creation
This commit is contained in:
Arne Welzel 2024-01-25 20:54:56 +01:00
commit b586b59b69
10 changed files with 350 additions and 208 deletions

View file

@ -1,3 +1,11 @@
6.2.0-dev.473 | 2024-01-25 20:54:56 +0100
* ZAM optimizations for record creation (Vern Paxson, Corelight)
includes reworking of managing "auxiliary" information for ZAM instructions
* testing/external: Revert commit hash for zeek-testing (Arne Welzel, Corelight)
6.2.0-dev.470 | 2024-01-25 12:25:57 +0100 6.2.0-dev.470 | 2024-01-25 12:25:57 +0100
* GH-3256: Intel: Introduce Intel::seen_policy() hook (Arne Welzel, Corelight) * GH-3256: Intel: Introduce Intel::seen_policy() hook (Arne Welzel, Corelight)

View file

@ -1 +1 @@
6.2.0-dev.470 6.2.0-dev.473

View file

@ -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_attr = a ? a->Find(detail::ATTR_DEFAULT) : nullptr;
auto def_expr = def_attr ? def_attr->GetExpr() : nullptr; auto def_expr = def_attr ? def_attr->GetExpr() : nullptr;
std::unique_ptr<detail::FieldInit> init; std::shared_ptr<detail::FieldInit> init;
if ( def_expr && ! IsErrorType(type->Tag()) ) { if ( def_expr && ! IsErrorType(type->Tag()) ) {
if ( def_expr->Tag() == detail::EXPR_CONST ) { if ( def_expr->Tag() == detail::EXPR_CONST ) {
auto zv = ZVal(def_expr->Eval(nullptr), type); auto zv = ZVal(def_expr->Eval(nullptr), type);
if ( ZVal::IsManagedType(type) ) if ( ZVal::IsManagedType(type) )
init = std::make_unique<detail::DirectManagedFieldInit>(zv); init = std::make_shared<detail::DirectManagedFieldInit>(zv);
else else
init = std::make_unique<detail::DirectFieldInit>(zv); init = std::make_shared<detail::DirectFieldInit>(zv);
} }
else { else {
auto efi = std::make_unique<detail::ExprFieldInit>(def_expr, type); auto efi = std::make_shared<detail::ExprFieldInit>(def_expr, type);
creation_inits.emplace_back(field, std::move(efi)); creation_inits.emplace_back(field, std::move(efi));
} }
} }
@ -1086,15 +1086,15 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td) {
// and RecordType::CreationInitisOptimizer. // and RecordType::CreationInitisOptimizer.
// //
// init (nil) is appended to deferred_inits as placeholder. // init (nil) is appended to deferred_inits as placeholder.
auto rfi = std::make_unique<detail::RecordFieldInit>(cast_intrusive<RecordType>(type)); auto rfi = std::make_shared<detail::RecordFieldInit>(cast_intrusive<RecordType>(type));
creation_inits.emplace_back(field, std::move(rfi)); creation_inits.emplace_back(field, std::move(rfi));
} }
else if ( tag == TYPE_TABLE ) else if ( tag == TYPE_TABLE )
init = std::make_unique<detail::TableFieldInit>(cast_intrusive<TableType>(type), a); init = std::make_shared<detail::TableFieldInit>(cast_intrusive<TableType>(type), a);
else if ( tag == TYPE_VECTOR ) else if ( tag == TYPE_VECTOR )
init = std::make_unique<detail::VectorFieldInit>(cast_intrusive<VectorType>(type)); init = std::make_shared<detail::VectorFieldInit>(cast_intrusive<VectorType>(type));
} }
deferred_inits.push_back(std::move(init)); deferred_inits.push_back(std::move(init));

View file

@ -30,10 +30,12 @@ using TableValPtr = IntrusivePtr<TableVal>;
namespace detail { namespace detail {
class Attributes;
class CompositeHash; class CompositeHash;
class Expr; class Expr;
class ListExpr; class ListExpr;
class Attributes; class ZAMCompiler;
using ListExprPtr = IntrusivePtr<ListExpr>; using ListExprPtr = IntrusivePtr<ListExpr>;
// The following tracks how to initialize a given record field. // The following tracks how to initialize a given record field.
@ -734,7 +736,7 @@ private:
// Field initializations that can be deferred to first access, // Field initializations that can be deferred to first access,
// beneficial for fields that are separately initialized prior // beneficial for fields that are separately initialized prior
// to first access. Nil pointers mean "skip initializing the field". // to first access. Nil pointers mean "skip initializing the field".
std::vector<std::unique_ptr<detail::FieldInit>> deferred_inits; std::vector<std::shared_ptr<detail::FieldInit>> deferred_inits;
// Field initializations that need to be done upon record creation, // Field initializations that need to be done upon record creation,
// rather than deferred. These are expressions whose value might // rather than deferred. These are expressions whose value might
@ -742,10 +744,11 @@ private:
// //
// Such initializations are uncommon, so we represent them using // Such initializations are uncommon, so we represent them using
// <fieldoffset, init> pairs. // <fieldoffset, init> pairs.
std::vector<std::pair<int, std::unique_ptr<detail::FieldInit>>> creation_inits; std::vector<std::pair<int, std::shared_ptr<detail::FieldInit>>> creation_inits;
class CreationInitsOptimizer; class CreationInitsOptimizer;
friend zeek::RecordVal; friend zeek::RecordVal;
friend zeek::detail::ZAMCompiler;
const auto& DeferredInits() const { return deferred_inits; } const auto& DeferredInits() const { return deferred_inits; }
const auto& CreationInits() const { return creation_inits; } const auto& CreationInits() const { return creation_inits; }

View file

@ -2784,7 +2784,6 @@ TableVal::TableRecordDependencies TableVal::parse_time_table_record_dependencies
RecordVal::RecordTypeValMap RecordVal::parse_time_records; RecordVal::RecordTypeValMap RecordVal::parse_time_records;
RecordVal::RecordVal(RecordTypePtr t, bool init_fields) : Val(t), is_managed(t->ManagedFields()) { RecordVal::RecordVal(RecordTypePtr t, bool init_fields) : Val(t), is_managed(t->ManagedFields()) {
origin = nullptr;
rt = std::move(t); rt = std::move(t);
int n = rt->NumFields(); int n = rt->NumFields();
@ -2810,6 +2809,12 @@ RecordVal::RecordVal(RecordTypePtr t, bool init_fields) : Val(t), is_managed(t->
record_val.reserve(n); record_val.reserve(n);
} }
RecordVal::RecordVal(RecordTypePtr t, std::vector<std::optional<ZVal>> init_vals)
: Val(t), is_managed(t->ManagedFields()) {
rt = std::move(t);
record_val = std::move(init_vals);
}
RecordVal::~RecordVal() { RecordVal::~RecordVal() {
auto n = record_val.size(); auto n = record_val.size();
@ -3016,11 +3021,10 @@ void RecordVal::DescribeReST(ODesc* d) const {
ValPtr RecordVal::DoClone(CloneState* state) { ValPtr RecordVal::DoClone(CloneState* state) {
// We set origin to 0 here. Origin only seems to be used for exactly one // 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 // 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) // appropriate time (as it seems to be guaranteed for the original record)
// we don't touch it. // we don't touch it.
auto rv = make_intrusive<RecordVal>(rt, false); auto rv = make_intrusive<RecordVal>(rt, false);
rv->origin = nullptr;
state->NewClone(this, rv); state->NewClone(this, rv);
int n = NumFields(); int n = NumFields();

View file

@ -1392,6 +1392,10 @@ protected:
friend class zeek::detail::CPPRuntime; friend class zeek::detail::CPPRuntime;
friend class zeek::detail::CompositeHash; friend class zeek::detail::CompositeHash;
// Constructor for use by script optimization, directly initializing
// record_vals from the second argument.
RecordVal(RecordTypePtr t, std::vector<std::optional<ZVal>> init_vals);
RecordValPtr DoCoerceTo(RecordTypePtr other, bool allow_orphaning) const; RecordValPtr DoCoerceTo(RecordTypePtr other, bool allow_orphaning) const;
/** /**
@ -1436,7 +1440,7 @@ protected:
void AddedField(int field) { Modified(); } void AddedField(int field) { Modified(); }
Obj* origin; Obj* origin = nullptr;
using RecordTypeValMap = std::unordered_map<RecordType*, std::vector<RecordValPtr>>; using RecordTypeValMap = std::unordered_map<RecordType*, std::vector<RecordValPtr>>;
static RecordTypeValMap parse_time_records; static RecordTypeValMap parse_time_records;

View file

@ -432,26 +432,27 @@ void ZAMCompiler::ComputeFrameLifetimes() {
case OP_LAMBDA_VV: { case OP_LAMBDA_VV: {
auto aux = inst->aux; auto aux = inst->aux;
int n = aux->n; int n = aux->n;
auto& slots = aux->slots; for ( int i = 0; i < n; ++i ) {
for ( int i = 0; i < n; ++i ) auto slot_i = aux->elems[i].Slot();
if ( slots[i] >= 0 ) if ( slot_i >= 0 )
ExtendLifetime(slots[i], EndOfLoop(inst, 1)); ExtendLifetime(slot_i, EndOfLoop(inst, 1));
}
break; break;
} }
default: default:
// Look for slots in auxiliary information. // Look for slots in auxiliary information.
auto aux = inst->aux; auto aux = inst->aux;
if ( ! aux || ! aux->slots ) if ( ! aux || ! aux->elems_has_slots )
break; break;
int n = aux->n; int n = aux->n;
auto& slots = aux->slots;
for ( auto j = 0; j < n; ++j ) { for ( auto j = 0; j < n; ++j ) {
if ( slots[j] < 0 ) auto slot_j = aux->elems[j].Slot();
if ( slot_j < 0 )
continue; continue;
ExtendLifetime(slots[j], EndOfLoop(inst, 1)); ExtendLifetime(slot_j, EndOfLoop(inst, 1));
} }
break; break;
} }
@ -562,11 +563,11 @@ void ZAMCompiler::ReMapFrame() {
default: default:
// Update slots in auxiliary information. // Update slots in auxiliary information.
auto aux = inst->aux; auto aux = inst->aux;
if ( ! aux || ! aux->slots ) if ( ! aux || ! aux->elems_has_slots )
break; break;
for ( auto j = 0; j < aux->n; ++j ) { for ( auto j = 0; j < aux->n; ++j ) {
auto& slot = aux->slots[j]; auto slot = aux->elems[j].Slot();
if ( slot < 0 ) if ( slot < 0 )
// This is instead a constant. // This is instead a constant.
@ -581,7 +582,7 @@ void ZAMCompiler::ReMapFrame() {
frame_denizens[slot]->Name()); frame_denizens[slot]->Name());
} }
slot = new_slot; aux->elems[j].SetSlot(new_slot);
} }
break; break;
} }
@ -858,9 +859,9 @@ bool ZAMCompiler::VarIsUsed(int slot) const {
return true; return true;
auto aux = inst->aux; auto aux = inst->aux;
if ( aux && aux->slots ) { if ( aux && aux->elems_has_slots ) {
for ( int j = 0; j < aux->n; ++j ) for ( int j = 0; j < aux->n; ++j )
if ( aux->slots[j] == slot ) if ( aux->elems[j].Slot() == slot )
return true; return true;
} }
} }

View file

@ -1100,23 +1100,84 @@ const ZAMStmt ZAMCompiler::ConstructSet(const NameExpr* n, const Expr* e) {
const ZAMStmt ZAMCompiler::ConstructRecord(const NameExpr* n, const Expr* e) { const ZAMStmt ZAMCompiler::ConstructRecord(const NameExpr* n, const Expr* e) {
auto rc = e->AsRecordConstructorExpr(); auto rc = e->AsRecordConstructorExpr();
auto rt = e->GetType()->AsRecordType();
ZInstI z; auto aux = InternalBuildVals(rc->Op().get());
if ( rc->Map() ) { // Note that we set the vector to the full size of the record being
z = GenInst(OP_CONSTRUCT_KNOWN_RECORD_V, n); // constructed, *not* the size of any map (which could be smaller).
z.aux = InternalBuildVals(rc->Op().get()); // This is because we want to provide the vector directly to the
z.aux->map = *rc->Map(); // 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<std::vector<std::pair<int, std::shared_ptr<detail::FieldInit>>>>();
// 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 { else {
z = GenInst(OP_CONSTRUCT_RECORD_V, n); op = OP_CONSTRUCT_KNOWN_RECORD_WITH_INITS_V;
z.aux = InternalBuildVals(rc->Op().get()); 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(); z.t = e->GetType();
if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, z.t) ) auto inst = AddInst(z);
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<int> 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<int>(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); return AddInst(z);
} }
@ -1212,7 +1273,7 @@ const ZAMStmt ZAMCompiler::RecordCoerce(const NameExpr* n, const Expr* e) {
z.aux->Add(i, map[i], nullptr); z.aux->Add(i, map[i], nullptr);
// Mark the integer entries in z.aux as not being frame slots as usual. // 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()) ) if ( pfs->HasSideEffects(SideEffectsOp::CONSTRUCTION, e->GetType()) )
z.aux->can_change_non_locals = true; z.aux->can_change_non_locals = true;

View file

@ -1198,35 +1198,48 @@ eval ConstructTableOrSetPre()
direct-unary-op Record-Constructor ConstructRecord direct-unary-op Record-Constructor ConstructRecord
internal-op Construct-Record macro ConstructRecordPost()
type V
eval EvalConstructRecord(, i)
macro EvalConstructRecord(map_init, map_accessor)
auto rt = cast_intrusive<RecordType>(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<VectorType>()->IsUnspecifiedVector() )
{
const auto& t_ind = rt->GetFieldType(ind);
v_i->AsVectorVal()->Concretize(t_ind->Yield());
}
new_r->Assign(ind, v_i);
}
auto& r = frame[z.v1].record_val; auto& r = frame[z.v1].record_val;
Unref(r); Unref(r);
r = new_r; r = new RecordVal(cast_intrusive<RecordType>(z.t), init_vals);
internal-op Construct-Known-Record op Construct-Direct-Record
type V 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<RecordType>(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<VectorType>()->IsUnspecifiedVector() )
{
const auto& t_i = rt->GetFieldType(i);
v_i->AsVectorVal()->Concretize(t_i->Yield());
}
}
direct-unary-op Vector-Constructor ConstructVector direct-unary-op Vector-Constructor ConstructVector
@ -1757,21 +1770,21 @@ internal-op Event3
type VVV type VVV
op1-read op1-read
eval ValVec args(3); eval ValVec args(3);
auto& aux = z.aux;
args[0] = frame[z.v1].ToVal(z.t); args[0] = frame[z.v1].ToVal(z.t);
args[1] = frame[z.v2].ToVal(z.t2); args[1] = frame[z.v2].ToVal(z.t2);
auto types = z.aux->types; args[2] = frame[z.v3].ToVal(aux->elems[2].GetType());
args[2] = frame[z.v3].ToVal(types[2]);
QueueEvent(z.event_handler, args); QueueEvent(z.event_handler, args);
internal-op Event4 internal-op Event4
type VVVV type VVVV
op1-read op1-read
eval ValVec args(4); eval ValVec args(4);
auto& aux = z.aux;
args[0] = frame[z.v1].ToVal(z.t); args[0] = frame[z.v1].ToVal(z.t);
args[1] = frame[z.v2].ToVal(z.t2); args[1] = frame[z.v2].ToVal(z.t2);
auto types = z.aux->types; args[2] = frame[z.v3].ToVal(aux->elems[2].GetType());
args[2] = frame[z.v3].ToVal(types[2]); args[3] = frame[z.v4].ToVal(aux->elems[3].GetType());
args[3] = frame[z.v4].ToVal(types[3]);
QueueEvent(z.event_handler, args); QueueEvent(z.event_handler, args);
@ -2256,11 +2269,11 @@ eval auto& aux = z.aux;
auto captures = std::make_unique<std::vector<ZVal>>(); auto captures = std::make_unique<std::vector<ZVal>>();
for ( auto i = 0; i < aux->n; ++i ) for ( auto i = 0; i < aux->n; ++i )
{ {
auto slot = aux->slots[i]; auto slot = aux->elems[i].Slot();
if ( slot >= 0 ) if ( slot >= 0 )
{ {
auto& cp = frame[aux->slots[i]]; auto& cp = frame[slot];
if ( aux->is_managed[i] ) if ( aux->elems[i].IsManaged() )
zeek::Ref(cp.ManagedVal()); zeek::Ref(cp.ManagedVal());
captures->push_back(cp); captures->push_back(cp);
} }
@ -2355,7 +2368,7 @@ eval LogWritePre(frame[z.v2].ToVal(log_ID_enum_type), v3)
internal-op Log-WriteC internal-op Log-WriteC
side-effects OP_LOG_WRITEC_V OP_V side-effects OP_LOG_WRITEC_V OP_V
type VV type VV
eval LogWritePre(z.aux->constants[0], v2) eval LogWritePre(z.aux->elems[0].Constant(), v2)
LogWriteResPost() LogWriteResPost()
# Versions that discard the return value. # Versions that discard the return value.
@ -2370,7 +2383,7 @@ internal-op Log-WriteC
side-effects side-effects
op1-read op1-read
type V type V
eval LogWritePre(z.aux->constants[0], v1) eval LogWritePre(z.aux->elems[0].Constant(), v1)
LogWriteNoResPost() LogWriteNoResPost()
internal-op Broker-Flush-Logs internal-op Broker-Flush-Logs
@ -2460,23 +2473,21 @@ eval Cat1FullVal(frame[z.v2])
internal-op CatN internal-op CatN
type V type V
eval auto aux = z.aux; eval auto aux = z.aux;
auto slots = z.aux->slots;
auto& ca = aux->cat_args; auto& ca = aux->cat_args;
int n = aux->n; int n = aux->n;
size_t max_size = 0; size_t max_size = 0;
for ( int i = 0; i < n; ++i ) 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 = new char[max_size + /* slop */ n + 1];
auto res_p = res; auto res_p = res;
for ( int i = 0; i < n; ++i ) 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'; *res_p = '\0';
auto s = new String(true, reinterpret_cast<byte_vec>(res), res_p - res); auto s = new String(true, reinterpret_cast<byte_vec>(res), res_p - res);
Cat1Op(ZVal(new StringVal(s))) Cat1Op(ZVal(new StringVal(s)))
macro CatNPre() macro CatNPre()
auto aux = z.aux; auto aux = z.aux;
auto slots = z.aux->slots;
auto& ca = aux->cat_args; auto& ca = aux->cat_args;
macro CatNMid() macro CatNMid()
@ -2491,113 +2502,113 @@ macro CatNPost()
internal-op Cat2 internal-op Cat2
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat3 internal-op Cat3
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat4 internal-op Cat4
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
max_size += ca[3]->MaxSize(frame, slots[3]); max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
ca[3]->RenderInto(frame, slots[3], res_p); ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat5 internal-op Cat5
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
max_size += ca[3]->MaxSize(frame, slots[3]); max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot());
max_size += ca[4]->MaxSize(frame, slots[4]); max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
ca[3]->RenderInto(frame, slots[3], res_p); ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p);
ca[4]->RenderInto(frame, slots[4], res_p); ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat6 internal-op Cat6
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
max_size += ca[3]->MaxSize(frame, slots[3]); max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot());
max_size += ca[4]->MaxSize(frame, slots[4]); max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot());
max_size += ca[5]->MaxSize(frame, slots[5]); max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
ca[3]->RenderInto(frame, slots[3], res_p); ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p);
ca[4]->RenderInto(frame, slots[4], res_p); ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p);
ca[5]->RenderInto(frame, slots[5], res_p); ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat7 internal-op Cat7
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
max_size += ca[3]->MaxSize(frame, slots[3]); max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot());
max_size += ca[4]->MaxSize(frame, slots[4]); max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot());
max_size += ca[5]->MaxSize(frame, slots[5]); max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot());
max_size += ca[6]->MaxSize(frame, slots[6]); max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
ca[3]->RenderInto(frame, slots[3], res_p); ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p);
ca[4]->RenderInto(frame, slots[4], res_p); ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p);
ca[5]->RenderInto(frame, slots[5], res_p); ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p);
ca[6]->RenderInto(frame, slots[6], res_p); ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p);
CatNPost() CatNPost()
internal-op Cat8 internal-op Cat8
type V type V
eval CatNPre() eval CatNPre()
size_t max_size = ca[0]->MaxSize(frame, slots[0]); size_t max_size = ca[0]->MaxSize(frame, aux->elems[0].Slot());
max_size += ca[1]->MaxSize(frame, slots[1]); max_size += ca[1]->MaxSize(frame, aux->elems[1].Slot());
max_size += ca[2]->MaxSize(frame, slots[2]); max_size += ca[2]->MaxSize(frame, aux->elems[2].Slot());
max_size += ca[3]->MaxSize(frame, slots[3]); max_size += ca[3]->MaxSize(frame, aux->elems[3].Slot());
max_size += ca[4]->MaxSize(frame, slots[4]); max_size += ca[4]->MaxSize(frame, aux->elems[4].Slot());
max_size += ca[5]->MaxSize(frame, slots[5]); max_size += ca[5]->MaxSize(frame, aux->elems[5].Slot());
max_size += ca[6]->MaxSize(frame, slots[6]); max_size += ca[6]->MaxSize(frame, aux->elems[6].Slot());
max_size += ca[7]->MaxSize(frame, slots[7]); max_size += ca[7]->MaxSize(frame, aux->elems[7].Slot());
CatNMid() CatNMid()
ca[0]->RenderInto(frame, slots[0], res_p); ca[0]->RenderInto(frame, aux->elems[0].Slot(), res_p);
ca[1]->RenderInto(frame, slots[1], res_p); ca[1]->RenderInto(frame, aux->elems[1].Slot(), res_p);
ca[2]->RenderInto(frame, slots[2], res_p); ca[2]->RenderInto(frame, aux->elems[2].Slot(), res_p);
ca[3]->RenderInto(frame, slots[3], res_p); ca[3]->RenderInto(frame, aux->elems[3].Slot(), res_p);
ca[4]->RenderInto(frame, slots[4], res_p); ca[4]->RenderInto(frame, aux->elems[4].Slot(), res_p);
ca[5]->RenderInto(frame, slots[5], res_p); ca[5]->RenderInto(frame, aux->elems[5].Slot(), res_p);
ca[6]->RenderInto(frame, slots[6], res_p); ca[6]->RenderInto(frame, aux->elems[6].Slot(), res_p);
ca[7]->RenderInto(frame, slots[7], res_p); ca[7]->RenderInto(frame, aux->elems[7].Slot(), res_p);
CatNPost() CatNPost()
internal-op Analyzer--Name internal-op Analyzer--Name

View file

@ -302,6 +302,65 @@ private:
void InitConst(const ConstExpr* ce); 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 // Auxiliary information, used when the fixed ZInst layout lacks
// sufficient expressiveness to represent all of the elements that // sufficient expressiveness to represent all of the elements that
// an instruction needs. // an instruction needs.
@ -311,53 +370,41 @@ public:
// tracking slots, constants, and types. // tracking slots, constants, and types.
ZInstAux(int _n) { ZInstAux(int _n) {
n = _n; n = _n;
if ( n > 0 ) { if ( n > 0 )
slots = ints = new int[n]; elems = new AuxElem[n];
constants = new ValPtr[n];
types = new TypePtr[n];
is_managed = new bool[n];
}
} }
~ZInstAux() { ~ZInstAux() {
delete[] ints; delete[] elems;
delete[] constants;
delete[] types;
delete[] is_managed;
delete[] cat_args; delete[] cat_args;
} }
// Returns the i'th element of the parallel arrays as a ValPtr. // Returns the i'th element of the elements as a ValPtr.
ValPtr ToVal(const ZVal* frame, int i) const { ValPtr ToVal(const ZVal* frame, int i) const { return elems[i].ToVal(frame); }
if ( constants[i] ) ZVal ToZVal(const ZVal* frame, int i) const { return elems[i].ToZVal(frame); }
return constants[i];
else
return frame[slots[i]].ToVal(types[i]);
}
// Returns the parallel arrays as a ListValPtr. // Returns the elements as a ListValPtr.
ListValPtr ToListVal(const ZVal* frame) const { ListValPtr ToListVal(const ZVal* frame) const {
auto lv = make_intrusive<ListVal>(TYPE_ANY); auto lv = make_intrusive<ListVal>(TYPE_ANY);
for ( auto i = 0; i < n; ++i ) for ( auto i = 0; i < n; ++i )
lv->Append(ToVal(frame, i)); lv->Append(elems[i].ToVal(frame));
return lv; return lv;
} }
// Converts the parallel arrays to a ListValPtr suitable for // Converts the elements to a ListValPtr suitable for use as indices
// use as indices for indexing a table or set. "offset" specifies // for indexing a table or set. "offset" specifies which index we're
// which index we're looking for (there can be a bunch for // looking for (there can be a bunch for constructors), and "width"
// constructors), and "width" the number of elements in a single // the number of elements in a single index.
// index.
ListValPtr ToIndices(const ZVal* frame, int offset, int width) const { ListValPtr ToIndices(const ZVal* frame, int offset, int width) const {
auto lv = make_intrusive<ListVal>(TYPE_ANY); auto lv = make_intrusive<ListVal>(TYPE_ANY);
for ( auto i = 0; i < 0 + width; ++i ) for ( auto i = 0; i < 0 + width; ++i )
lv->Append(ToVal(frame, offset + i)); lv->Append(elems[offset + i].ToVal(frame));
return lv; 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) { const ValVec& ToValVec(const ZVal* frame) {
vv.clear(); vv.clear();
FillValVec(vv, frame); FillValVec(vv, frame);
@ -365,49 +412,45 @@ public:
} }
// Populates the given vector of ValPtr's with the conversion // 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 { void FillValVec(ValVec& vec, const ZVal* frame) const {
for ( auto i = 0; i < n; ++i ) 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 // Returns the elements converted to a vector of ZVal's.
// arrays to a given frame slot and type. const auto& ToZValVec(const ZVal* frame) {
void Add(int i, int slot, TypePtr t) { for ( auto i = 0; i < n; ++i )
ints[i] = slot; zvec[i] = elems[i].ToZVal(frame);
constants[i] = nullptr; return zvec;
types[i] = t;
is_managed[i] = t ? ZVal::IsManagedType(t) : false;
} }
// 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. // Same but for constants.
void Add(int i, ValPtr c) { void Add(int i, ValPtr c) { elems[i].SetConstant(c); }
ints[i] = -1;
constants[i] = c;
types[i] = nullptr;
is_managed[i] = false;
}
// Member variables. We could add accessors for manipulating // Member variables. We could add accessors for manipulating
// these (and make the variables private), but for convenience we // these (and make the variables private), but for convenience we
// make them directly available. // make them directly available.
// These are parallel arrays, used to build up lists of values. int n; // size of elements
// Each element is either an integer or a constant. Usually the AuxElem* elems = nullptr;
// integer is a frame slot (in which case "slots" points to "ints"; bool elems_has_slots = true;
// 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;
// Ingredients associated with lambdas ... // Ingredients associated with lambdas ...
ScriptFuncPtr primary_func; ScriptFuncPtr primary_func;
@ -429,8 +472,8 @@ public:
// store here. // store here.
bool can_change_non_locals = false; bool can_change_non_locals = false;
// The following is only used for OP_CONSTRUCT_KNOWN_RECORD_V, // The following is used for constructing records, to map elements in
// to map elements in slots/constants/types to record field offsets. // slots/constants/types to record field offsets.
std::vector<int> map; std::vector<int> map;
///// The following four apply to looping over the elements of tables. ///// 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 // If we cared about memory penny-pinching, we could make this
// a pointer and only instantiate as needed. // a pointer and only instantiate as needed.
ValVec vv; ValVec vv;
// Similar, but for ZVal's (used when constructing RecordVal's).
std::vector<std::optional<ZVal>> 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<std::vector<std::pair<int, std::shared_ptr<detail::FieldInit>>>> field_inits;
}; };
// Returns a human-readable version of the given ZAM op-code. // Returns a human-readable version of the given ZAM op-code.