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
* 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_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->Tag() == detail::EXPR_CONST ) {
auto zv = ZVal(def_expr->Eval(nullptr), type);
if ( ZVal::IsManagedType(type) )
init = std::make_unique<detail::DirectManagedFieldInit>(zv);
init = std::make_shared<detail::DirectManagedFieldInit>(zv);
else
init = std::make_unique<detail::DirectFieldInit>(zv);
init = std::make_shared<detail::DirectFieldInit>(zv);
}
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));
}
}
@ -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<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));
}
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 )
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));

View file

@ -30,10 +30,12 @@ using TableValPtr = IntrusivePtr<TableVal>;
namespace detail {
class Attributes;
class CompositeHash;
class Expr;
class ListExpr;
class Attributes;
class ZAMCompiler;
using ListExprPtr = IntrusivePtr<ListExpr>;
// 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<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,
// rather than deferred. These are expressions whose value might
@ -742,10 +744,11 @@ private:
//
// Such initializations are uncommon, so we represent them using
// <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;
friend zeek::RecordVal;
friend zeek::detail::ZAMCompiler;
const auto& DeferredInits() const { return deferred_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::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<std::optional<ZVal>> 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<RecordVal>(rt, false);
rv->origin = nullptr;
state->NewClone(this, rv);
int n = NumFields();

View file

@ -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<std::optional<ZVal>> 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<RecordType*, std::vector<RecordValPtr>>;
static RecordTypeValMap parse_time_records;

View file

@ -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;
}
}

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) {
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<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 {
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<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);
}
@ -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;

View file

@ -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<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);
}
macro ConstructRecordPost()
auto& r = frame[z.v1].record_val;
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
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
@ -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<std::vector<ZVal>>();
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<byte_vec>(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

View file

@ -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<ListVal>(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<ListVal>(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<int> 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<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.