Merge remote-tracking branch 'origin/topic/vern/record-optimizations.Apr23B'

* origin/topic/vern/record-optimizations.Apr23B:
  different fix for MSVC compiler issues
  more general approach for addressing MSVC compiler issues with IntrusivePtr
  restored RecordType::Create, now marked as deprecated tidying of namespaces and private class members simplification of flagging record field initializations that should be skipped address peculiar MSVC compilation complaint for IntrusivePtr's
  clarifications and tidying for record field initializations
  optimize record construction by deferring initializations of aggregates
  compile-scripts-to-C++ speedups by switching to raw record access
  logging speedup by switching to raw record access
  remove redundant record coercions

Removed the `#if 0` hunk during merging: Probably could have gone with a
doctest instead.
This commit is contained in:
Arne Welzel 2023-04-19 11:58:09 +02:00
commit 89c828ac14
14 changed files with 399 additions and 205 deletions

View file

@ -992,41 +992,118 @@ void TypeDecl::DescribeReST(ODesc* d, bool roles_only) const
}
}
// The following tracks how to initialize a given field, for fast execution
// of Create().
namespace detail
{
class FieldInit
// A record field initialization that directly assigns a fixed value ...
class DirectFieldInit final : public FieldInit
{
public:
// The type of initialization for the field.
enum
{
R_INIT_NONE, // skip this entry
DirectFieldInit(ZVal _init_val) : init_val(_init_val) { }
R_INIT_DIRECT, // look in direct_init for raw value
R_INIT_DIRECT_MANAGED, // same, but managed type
ZVal Generate() const override { return init_val; }
R_INIT_DEF, // look in def_expr for expression
R_INIT_RECORD, // field requires a new record
R_INIT_TABLE, // field requires a new table/set
R_INIT_VECTOR, // field requires a new vector
} init_type = R_INIT_NONE;
bool def_coerce = false; // whether coercion's required
// For R_INIT_DIRECT/R_INIT_DIRECT_MANAGED:
ZVal direct_init;
detail::ExprPtr def_expr;
TypePtr def_type;
RecordTypePtr r_type; // for R_INIT_RECORD
TableTypePtr t_type; // for R_INIT_TABLE
detail::AttributesPtr attrs; // attributes for R_INIT_TABLE
VectorTypePtr v_type; // for R_INIT_VECTOR
private:
ZVal init_val;
};
// ... the same, but for a value that needs memory management.
class DirectManagedFieldInit final : public FieldInit
{
public:
DirectManagedFieldInit(ZVal _init_val) : init_val(_init_val) { }
~DirectManagedFieldInit() { ZVal::DeleteManagedType(init_val); }
ZVal Generate() const override
{
zeek::Ref(init_val.ManagedVal());
return init_val;
}
private:
ZVal init_val;
};
// A record field initialization that's done by evaluating an expression.
class ExprFieldInit final : public FieldInit
{
public:
// Initialization requires evaluating the given expression,
// yielding the a value of the given type (which might require
// coercion for some records).
ExprFieldInit(detail::ExprPtr _init_expr, TypePtr _init_type)
: init_expr(std::move(_init_expr)), init_type(std::move(_init_type))
{
if ( init_type->Tag() == TYPE_RECORD && ! same_type(init_expr->GetType(), init_type) )
coerce_type = cast_intrusive<RecordType>(init_type);
}
ZVal Generate() const override
{
auto v = init_expr->Eval(nullptr);
if ( ! v )
{
reporter->Error("failed &default in record creation");
return ZVal();
}
if ( coerce_type )
v = v->AsRecordVal()->CoerceTo(coerce_type);
return ZVal(v, init_type);
}
private:
detail::ExprPtr init_expr;
TypePtr init_type;
RecordTypePtr coerce_type; // non-nil iff coercion is required
};
// A record field initialization where the field is initialized to an
// empty/default record of the given type.
class RecordFieldInit final : public FieldInit
{
public:
RecordFieldInit(RecordTypePtr _init_type) : init_type(std::move(_init_type)) { }
ZVal Generate() const override { return ZVal(new RecordVal(init_type)); }
private:
RecordTypePtr init_type;
};
// A record field initialization where the field is initialized to an
// empty table of the given type.
class TableFieldInit final : public FieldInit
{
public:
TableFieldInit(TableTypePtr _init_type, detail::AttributesPtr _attrs)
: init_type(std::move(_init_type)), attrs(std::move(_attrs))
{
}
ZVal Generate() const override { return ZVal(new TableVal(init_type, attrs)); }
private:
TableTypePtr init_type;
detail::AttributesPtr attrs;
};
// A record field initialization where the field is initialized to an
// empty vector of the given type.
class VectorFieldInit final : public FieldInit
{
public:
VectorFieldInit(VectorTypePtr _init_type) : init_type(std::move(_init_type)) { }
ZVal Generate() const override { return ZVal(new VectorVal(init_type)); }
private:
VectorTypePtr init_type;
};
} // namespace detail
RecordType::RecordType(type_decl_list* arg_types) : Type(TYPE_RECORD)
{
types = arg_types;
@ -1062,65 +1139,52 @@ RecordType::~RecordType()
delete types;
}
for ( auto fi : field_inits )
delete fi;
}
void RecordType::AddField(unsigned int field, const TypeDecl* td)
{
ASSERT(field == field_inits.size());
ASSERT(field == deferred_inits.size());
ASSERT(field == managed_fields.size());
managed_fields.push_back(ZVal::IsManagedType(td->type));
auto init = new FieldInit();
init->init_type = FieldInit::R_INIT_NONE;
init->attrs = td->attrs;
// We defer error-checking until here so that we can keep field_inits
// We defer error-checking until here so that we can keep deferred_inits
// and managed_fields correctly tracking the associated fields.
if ( field_ids.count(td->id) != 0 )
{
reporter->Error("duplicate field '%s' found in record definition", td->id);
field_inits.push_back(init);
deferred_inits.push_back(nullptr);
return;
}
field_ids.insert(std::string(td->id));
auto a = init->attrs;
auto a = td->attrs;
auto type = td->type;
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;
if ( def_expr && ! IsErrorType(type->Tag()) )
{
if ( type->Tag() == TYPE_RECORD && def_expr->GetType()->Tag() == TYPE_RECORD &&
! same_type(def_expr->GetType(), type) )
init->def_coerce = true;
if ( def_expr->Tag() == detail::EXPR_CONST )
{
auto v = def_expr->Eval(nullptr);
auto zv = ZVal(v, type);
if ( ZVal::IsManagedType(type) )
init->init_type = FieldInit::R_INIT_DIRECT_MANAGED;
init = std::make_unique<detail::DirectManagedFieldInit>(zv);
else
init->init_type = FieldInit::R_INIT_DIRECT;
init->direct_init = ZVal(v, type);
init = std::make_unique<detail::DirectFieldInit>(zv);
}
else
{
init->init_type = FieldInit::R_INIT_DEF;
init->def_expr = def_expr;
init->def_type = def_expr->GetType();
auto efi = std::make_unique<detail::ExprFieldInit>(def_expr, type);
creation_inits.emplace_back(std::make_pair(field, std::move(efi)));
}
}
@ -1129,25 +1193,16 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td)
TypeTag tag = type->Tag();
if ( tag == TYPE_RECORD )
{
init->init_type = FieldInit::R_INIT_RECORD;
init->r_type = cast_intrusive<RecordType>(type);
}
init = std::make_unique<detail::RecordFieldInit>(cast_intrusive<RecordType>(type));
else if ( tag == TYPE_TABLE )
{
init->init_type = FieldInit::R_INIT_TABLE;
init->t_type = cast_intrusive<TableType>(type);
}
init = std::make_unique<detail::TableFieldInit>(cast_intrusive<TableType>(type), a);
else if ( tag == TYPE_VECTOR )
{
init->init_type = FieldInit::R_INIT_VECTOR;
init->v_type = cast_intrusive<VectorType>(type);
}
init = std::make_unique<detail::VectorFieldInit>(cast_intrusive<VectorType>(type));
}
field_inits.push_back(init);
deferred_inits.push_back(std::move(init));
}
bool RecordType::HasField(const char* field) const
@ -1344,64 +1399,14 @@ void RecordType::AddFieldsDirectly(const type_decl_list& others, bool add_log_at
void RecordType::Create(std::vector<std::optional<ZVal>>& r) const
{
int n = NumFields();
for ( auto& di : deferred_inits )
if ( di )
r.push_back(di->Generate());
else
r.push_back(std::nullopt);
for ( int i = 0; i < n; ++i )
{
auto* init = field_inits[i];
ZVal r_i;
switch ( init->init_type )
{
case FieldInit::R_INIT_NONE:
r.push_back(std::nullopt);
continue;
case FieldInit::R_INIT_DIRECT:
r_i = init->direct_init;
break;
case FieldInit::R_INIT_DIRECT_MANAGED:
r_i = init->direct_init;
zeek::Ref(r_i.ManagedVal());
break;
case FieldInit::R_INIT_DEF:
{
auto v = init->def_expr->Eval(nullptr);
if ( v )
{
const auto& t = init->def_type;
if ( init->def_coerce )
{
auto rt = cast_intrusive<RecordType>(t);
v = v->AsRecordVal()->CoerceTo(rt);
}
r_i = ZVal(v, t);
}
else
reporter->Error("failed &default in record creation");
}
break;
case FieldInit::R_INIT_RECORD:
r_i = ZVal(new RecordVal(init->r_type));
break;
case FieldInit::R_INIT_TABLE:
r_i = ZVal(new TableVal(init->t_type, init->attrs));
break;
case FieldInit::R_INIT_VECTOR:
r_i = ZVal(new VectorVal(init->v_type));
break;
}
r.push_back(r_i);
}
for ( auto& ci : creation_inits )
r[ci.first] = ci.second->Generate();
}
void RecordType::DescribeFields(ODesc* d) const