[torque] Generate shorter code for indexed field accesses
Currently, when accessing a field that doesn't have a constant offset, Torque emits code to compute each preceding indexed field's length and add them all together. This works, but such code can get super long if a class has many indexed fields, and especially if the length expressions of some indexed fields refer to other indexed fields. We'd like the output of the new C++ backend to be short enough to go in inline headers which will be included in many compilation units. This change attempts to reorganize the code so that the computation of each length expression can only be emitted exactly once. This only shortens the generated C++ code; the resulting TurboFan output should be identical. There are two main parts: 1. For each indexed field, we already generate a macro that can get a Slice referring to that field. Update these macros to not use the dot operator on that field. Using the dot operator on the predecessor field is allowed. 2. Update the dot operator for indexed fields to emit a call to the macro from step 1. This sort of reverses the dependency added by the previous change https://crrev.com/c/2429566 : rather than the slice macros depending on the dot operator, this change makes the dot operator depend on the slice macros. The overall torque_generated directory shrinks by under 1% with this change, but the runtime_macros.cc file (which should eventually become inline headers) shrinks by 24%. More to the point, this change keeps runtime_macros.cc from ballooning out of control when we add a work-in-progress Torque definition for ScopeInfo ( https://crrev.com/c/2357758 ). Bug: v8:7793 Change-Id: I989dda9c3666f1a49281fef03acb35baebb5b63a Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2432070 Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Cr-Commit-Position: refs/heads/master@{#70325}
This commit is contained in:
parent
76ad3ab597
commit
73a8eded22
@ -226,6 +226,11 @@ macro DownCastForTorqueClass<T : type extends HeapObject>(o: HeapObject):
|
||||
|
||||
extern macro StaticAssert(bool, constexpr string);
|
||||
|
||||
// This is for the implementation of the dot operator. In any context where the
|
||||
// dot operator is available, the correct way to get the length of an indexed
|
||||
// field x from object o is `(&o.x).length`.
|
||||
intrinsic %IndexedFieldLength<T: type>(o: T, f: constexpr string);
|
||||
|
||||
} // namespace torque_internal
|
||||
|
||||
// Indicates that an array-field should not be initialized.
|
||||
|
@ -1211,15 +1211,15 @@ void SmallOrderedHashSet::SmallOrderedHashSetVerify(Isolate* isolate) {
|
||||
intptr_t offset;
|
||||
intptr_t length;
|
||||
std::tie(std::ignore, offset, length) =
|
||||
TqRuntimeFieldRefSmallOrderedHashSetDataTable(isolate, *this);
|
||||
TqRuntimeFieldSliceSmallOrderedHashSetDataTable(isolate, *this);
|
||||
CHECK_EQ(offset, DataTableStartOffset());
|
||||
CHECK_EQ(length, Capacity());
|
||||
std::tie(std::ignore, offset, length) =
|
||||
TqRuntimeFieldRefSmallOrderedHashSetHashTable(isolate, *this);
|
||||
TqRuntimeFieldSliceSmallOrderedHashSetHashTable(isolate, *this);
|
||||
CHECK_EQ(offset, GetBucketsStartOffset());
|
||||
CHECK_EQ(length, NumberOfBuckets());
|
||||
std::tie(std::ignore, offset, length) =
|
||||
TqRuntimeFieldRefSmallOrderedHashSetChainTable(isolate, *this);
|
||||
TqRuntimeFieldSliceSmallOrderedHashSetChainTable(isolate, *this);
|
||||
CHECK_EQ(offset, GetChainTableOffset());
|
||||
CHECK_EQ(length, Capacity());
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ namespace torque {
|
||||
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
|
||||
V(BasicTypeExpression) \
|
||||
V(FunctionTypeExpression) \
|
||||
V(PrecomputedTypeExpression) \
|
||||
V(UnionTypeExpression)
|
||||
|
||||
#define AST_STATEMENT_NODE_KIND_LIST(V) \
|
||||
@ -651,6 +652,17 @@ struct FunctionTypeExpression : TypeExpression {
|
||||
TypeExpression* return_type;
|
||||
};
|
||||
|
||||
// A PrecomputedTypeExpression is never created directly by the parser. Later
|
||||
// stages can use this to insert AST snippets where the type has already been
|
||||
// resolved.
|
||||
class Type;
|
||||
struct PrecomputedTypeExpression : TypeExpression {
|
||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(PrecomputedTypeExpression)
|
||||
PrecomputedTypeExpression(SourcePosition pos, const Type* type)
|
||||
: TypeExpression(kKind, pos), type(type) {}
|
||||
const Type* type;
|
||||
};
|
||||
|
||||
struct UnionTypeExpression : TypeExpression {
|
||||
DEFINE_AST_NODE_LEAF_BOILERPLATE(UnionTypeExpression)
|
||||
UnionTypeExpression(SourcePosition pos, TypeExpression* a, TypeExpression* b)
|
||||
@ -1237,6 +1249,58 @@ T* MakeNode(Args... args) {
|
||||
std::make_unique<T>(CurrentSourcePosition::Get(), std::move(args)...));
|
||||
}
|
||||
|
||||
inline FieldAccessExpression* MakeFieldAccessExpression(Expression* object,
|
||||
std::string field) {
|
||||
return MakeNode<FieldAccessExpression>(
|
||||
object, MakeNode<Identifier>(std::move(field)));
|
||||
}
|
||||
|
||||
inline IdentifierExpression* MakeIdentifierExpression(
|
||||
std::vector<std::string> namespace_qualification, std::string name,
|
||||
std::vector<TypeExpression*> args = {}) {
|
||||
return MakeNode<IdentifierExpression>(std::move(namespace_qualification),
|
||||
MakeNode<Identifier>(std::move(name)),
|
||||
std::move(args));
|
||||
}
|
||||
|
||||
inline IdentifierExpression* MakeIdentifierExpression(std::string name) {
|
||||
return MakeIdentifierExpression({}, std::move(name));
|
||||
}
|
||||
|
||||
inline CallExpression* MakeCallExpression(
|
||||
IdentifierExpression* callee, std::vector<Expression*> arguments,
|
||||
std::vector<Identifier*> labels = {}) {
|
||||
return MakeNode<CallExpression>(callee, std::move(arguments),
|
||||
std::move(labels));
|
||||
}
|
||||
|
||||
inline CallExpression* MakeCallExpression(
|
||||
std::string callee, std::vector<Expression*> arguments,
|
||||
std::vector<Identifier*> labels = {}) {
|
||||
return MakeCallExpression(MakeIdentifierExpression(std::move(callee)),
|
||||
std::move(arguments), std::move(labels));
|
||||
}
|
||||
|
||||
inline VarDeclarationStatement* MakeConstDeclarationStatement(
|
||||
std::string name, Expression* initializer) {
|
||||
return MakeNode<VarDeclarationStatement>(
|
||||
/*const_qualified=*/true, MakeNode<Identifier>(std::move(name)),
|
||||
base::Optional<TypeExpression*>{}, initializer);
|
||||
}
|
||||
|
||||
inline BasicTypeExpression* MakeBasicTypeExpression(
|
||||
std::vector<std::string> namespace_qualification, std::string name,
|
||||
std::vector<TypeExpression*> generic_arguments = {}) {
|
||||
return MakeNode<BasicTypeExpression>(std::move(namespace_qualification),
|
||||
std::move(name),
|
||||
std::move(generic_arguments));
|
||||
}
|
||||
|
||||
inline StructExpression* MakeStructExpression(
|
||||
TypeExpression* type, std::vector<NameAndExpression> initializers) {
|
||||
return MakeNode<StructExpression>(type, std::move(initializers));
|
||||
}
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1313,53 +1313,20 @@ InitializerResults ImplementationVisitor::VisitInitializerResults(
|
||||
|
||||
LocationReference ImplementationVisitor::GenerateFieldReference(
|
||||
VisitResult object, const Field& field, const ClassType* class_type) {
|
||||
if (field.index.has_value()) {
|
||||
return LocationReference::HeapSlice(
|
||||
GenerateCall(class_type->GetSliceMacroName(field), {{object}, {}}));
|
||||
}
|
||||
DCHECK(field.offset.has_value());
|
||||
StackRange result_range = assembler().TopRange(0);
|
||||
result_range.Extend(GenerateCopy(object).stack_range());
|
||||
VisitResult offset;
|
||||
if (field.offset.has_value()) {
|
||||
offset =
|
||||
VisitResult(TypeOracle::GetConstInt31Type(), ToString(*field.offset));
|
||||
offset = GenerateImplicitConvert(TypeOracle::GetIntPtrType(), offset);
|
||||
} else {
|
||||
StackScope stack_scope(this);
|
||||
for (const Field& f : class_type->ComputeAllFields()) {
|
||||
if (f.offset) {
|
||||
offset =
|
||||
VisitResult(TypeOracle::GetConstInt31Type(), ToString(*f.offset));
|
||||
}
|
||||
if (f.name_and_type.name == field.name_and_type.name) break;
|
||||
if (f.index) {
|
||||
if (!offset.IsOnStack()) {
|
||||
offset = GenerateImplicitConvert(TypeOracle::GetIntPtrType(), offset);
|
||||
}
|
||||
VisitResult array_length = GenerateArrayLength(object, f);
|
||||
size_t element_size;
|
||||
std::string element_size_string;
|
||||
std::tie(element_size, element_size_string) =
|
||||
*SizeOf(f.name_and_type.type);
|
||||
VisitResult array_element_size =
|
||||
VisitResult(TypeOracle::GetConstInt31Type(), element_size_string);
|
||||
// In contrast to the code used for allocation, we don't need overflow
|
||||
// checks here because we already know all the offsets fit into memory.
|
||||
VisitResult array_size =
|
||||
GenerateCall("*", {{array_length, array_element_size}, {}});
|
||||
offset = GenerateCall("+", {{offset, array_size}, {}});
|
||||
}
|
||||
}
|
||||
DCHECK(offset.IsOnStack());
|
||||
offset = stack_scope.Yield(offset);
|
||||
}
|
||||
VisitResult offset =
|
||||
VisitResult(TypeOracle::GetConstInt31Type(), ToString(*field.offset));
|
||||
offset = GenerateImplicitConvert(TypeOracle::GetIntPtrType(), offset);
|
||||
result_range.Extend(offset.stack_range());
|
||||
if (field.index) {
|
||||
VisitResult length = GenerateArrayLength(object, field);
|
||||
result_range.Extend(length.stack_range());
|
||||
const Type* slice_type = TypeOracle::GetSliceType(field.name_and_type.type);
|
||||
return LocationReference::HeapSlice(VisitResult(slice_type, result_range));
|
||||
} else {
|
||||
const Type* type = TypeOracle::GetReferenceType(field.name_and_type.type,
|
||||
field.const_qualified);
|
||||
return LocationReference::HeapReference(VisitResult(type, result_range));
|
||||
}
|
||||
const Type* type = TypeOracle::GetReferenceType(field.name_and_type.type,
|
||||
field.const_qualified);
|
||||
return LocationReference::HeapReference(VisitResult(type, result_range));
|
||||
}
|
||||
|
||||
// This is used to generate field references during initialization, where we can
|
||||
@ -2858,6 +2825,15 @@ VisitResult ImplementationVisitor::GenerateCall(
|
||||
result << constexpr_arguments[0];
|
||||
result << ")";
|
||||
return VisitResult(return_type, result.str());
|
||||
} else if (intrinsic->ExternalName() == "%IndexedFieldLength") {
|
||||
const Type* type = specialization_types[0];
|
||||
const ClassType* class_type = ClassType::DynamicCast(type);
|
||||
if (!class_type) {
|
||||
ReportError("%IndexedFieldLength must take a class type parameter");
|
||||
}
|
||||
const Field& field =
|
||||
class_type->LookupField(StringLiteralUnquote(constexpr_arguments[0]));
|
||||
return GenerateArrayLength(VisitResult(type, argument_range), field);
|
||||
} else {
|
||||
assembler().Emit(CallIntrinsicInstruction{intrinsic, specialization_types,
|
||||
constexpr_arguments});
|
||||
|
@ -354,14 +354,17 @@ const Type* TypeVisitor::ComputeType(TypeExpression* type_expression) {
|
||||
UnionTypeExpression::DynamicCast(type_expression)) {
|
||||
return TypeOracle::GetUnionType(ComputeType(union_type->a),
|
||||
ComputeType(union_type->b));
|
||||
} else {
|
||||
auto* function_type_exp = FunctionTypeExpression::cast(type_expression);
|
||||
} else if (auto* function_type_exp =
|
||||
FunctionTypeExpression::DynamicCast(type_expression)) {
|
||||
TypeVector argument_types;
|
||||
for (TypeExpression* type_exp : function_type_exp->parameters) {
|
||||
argument_types.push_back(ComputeType(type_exp));
|
||||
}
|
||||
return TypeOracle::GetBuiltinPointerType(
|
||||
argument_types, ComputeType(function_type_exp->return_type));
|
||||
} else {
|
||||
auto* precomputed = PrecomputedTypeExpression::cast(type_expression);
|
||||
return precomputed->type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,6 +653,29 @@ bool ClassType::HasIndexedFieldsIncludingInParents() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Field* ClassType::GetFieldPreceding(size_t field_index) const {
|
||||
if (field_index > 0) {
|
||||
return &fields_[field_index - 1];
|
||||
}
|
||||
if (const ClassType* parent = GetSuperClass()) {
|
||||
return parent->GetFieldPreceding(parent->fields_.size());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ClassType* ClassType::GetClassDeclaringField(const Field& f) const {
|
||||
for (const Field& field : fields_) {
|
||||
if (f.name_and_type.name == field.name_and_type.name) return this;
|
||||
}
|
||||
return GetSuperClass()->GetClassDeclaringField(f);
|
||||
}
|
||||
|
||||
std::string ClassType::GetSliceMacroName(const Field& field) const {
|
||||
const ClassType* declarer = GetClassDeclaringField(field);
|
||||
return "FieldSlice" + declarer->name() +
|
||||
CamelifyString(field.name_and_type.name);
|
||||
}
|
||||
|
||||
void ClassType::GenerateAccessors() {
|
||||
bool at_or_after_indexed_field = false;
|
||||
if (const ClassType* parent = GetSuperClass()) {
|
||||
@ -660,7 +683,8 @@ void ClassType::GenerateAccessors() {
|
||||
}
|
||||
// For each field, construct AST snippets that implement a CSA accessor
|
||||
// function. The implementation iterator will turn the snippets into code.
|
||||
for (auto& field : fields_) {
|
||||
for (size_t field_index = 0; field_index < fields_.size(); ++field_index) {
|
||||
Field& field = fields_[field_index];
|
||||
if (field.name_and_type.type == TypeOracle::GetVoidType()) {
|
||||
continue;
|
||||
}
|
||||
@ -668,42 +692,21 @@ void ClassType::GenerateAccessors() {
|
||||
at_or_after_indexed_field || field.index.has_value();
|
||||
CurrentSourcePosition::Scope position_activator(field.pos);
|
||||
|
||||
IdentifierExpression* parameter =
|
||||
MakeNode<IdentifierExpression>(MakeNode<Identifier>(std::string{"o"}));
|
||||
IdentifierExpression* index =
|
||||
MakeNode<IdentifierExpression>(MakeNode<Identifier>(std::string{"i"}));
|
||||
IdentifierExpression* parameter = MakeIdentifierExpression("o");
|
||||
IdentifierExpression* index = MakeIdentifierExpression("i");
|
||||
|
||||
std::string camel_field_name = CamelifyString(field.name_and_type.name);
|
||||
|
||||
if (at_or_after_indexed_field) {
|
||||
// Generate a C++ function for getting a slice or reference to this field.
|
||||
// In Torque, this function would be written as
|
||||
// FieldRefClassNameFieldName(o: ClassName) {
|
||||
// return &o.field_name;
|
||||
// }
|
||||
std::string ref_macro_name = "FieldRef" + this->name() + camel_field_name;
|
||||
Signature ref_signature;
|
||||
ref_signature.parameter_names.push_back(MakeNode<Identifier>("o"));
|
||||
ref_signature.parameter_types.types.push_back(this);
|
||||
ref_signature.parameter_types.var_args = false;
|
||||
// It doesn't really matter whether we say this reference is mutable or
|
||||
// const, because that information is not exposed to the calling C++ code.
|
||||
ref_signature.return_type =
|
||||
field.index
|
||||
? TypeOracle::GetSliceType(field.name_and_type.type)
|
||||
: TypeOracle::GetConstReferenceType(field.name_and_type.type);
|
||||
Expression* ref_expression = MakeNode<FieldAccessExpression>(
|
||||
parameter, MakeNode<Identifier>(field.name_and_type.name));
|
||||
ref_expression = MakeNode<CallExpression>(
|
||||
MakeNode<IdentifierExpression>(
|
||||
std::vector<std::string>{},
|
||||
MakeNode<Identifier>(std::string{"&"})),
|
||||
std::vector<Expression*>{ref_expression}, std::vector<Identifier*>{});
|
||||
Statement* ref_body = MakeNode<ReturnStatement>(ref_expression);
|
||||
Macro* ref_macro =
|
||||
Declarations::DeclareMacro(ref_macro_name, true, base::nullopt,
|
||||
ref_signature, ref_body, base::nullopt);
|
||||
GlobalContext::EnsureInCCOutputList(TorqueMacro::cast(ref_macro));
|
||||
if (!field.index.has_value()) {
|
||||
// There's no fundamental reason we couldn't generate functions to get
|
||||
// references instead of slices, but it's not yet implemented.
|
||||
ReportError(
|
||||
"Torque doesn't yet support non-indexed fields after indexed "
|
||||
"fields");
|
||||
}
|
||||
|
||||
GenerateSliceAccessor(field_index);
|
||||
}
|
||||
|
||||
// For now, only generate indexed accessors for simple types
|
||||
@ -724,8 +727,8 @@ void ClassType::GenerateAccessors() {
|
||||
load_signature.parameter_types.var_args = false;
|
||||
load_signature.return_type = field.name_and_type.type;
|
||||
|
||||
Expression* load_expression = MakeNode<FieldAccessExpression>(
|
||||
parameter, MakeNode<Identifier>(field.name_and_type.name));
|
||||
Expression* load_expression =
|
||||
MakeFieldAccessExpression(parameter, field.name_and_type.name);
|
||||
if (field.index) {
|
||||
load_expression =
|
||||
MakeNode<ElementAccessExpression>(load_expression, index);
|
||||
@ -736,8 +739,7 @@ void ClassType::GenerateAccessors() {
|
||||
|
||||
// Store accessor
|
||||
if (!field.const_qualified) {
|
||||
IdentifierExpression* value = MakeNode<IdentifierExpression>(
|
||||
std::vector<std::string>{}, MakeNode<Identifier>(std::string{"v"}));
|
||||
IdentifierExpression* value = MakeIdentifierExpression("v");
|
||||
std::string store_macro_name = "Store" + this->name() + camel_field_name;
|
||||
Signature store_signature;
|
||||
store_signature.parameter_names.push_back(MakeNode<Identifier>("o"));
|
||||
@ -752,8 +754,8 @@ void ClassType::GenerateAccessors() {
|
||||
store_signature.parameter_types.var_args = false;
|
||||
// TODO(danno): Store macros probably should return their value argument
|
||||
store_signature.return_type = TypeOracle::GetVoidType();
|
||||
Expression* store_expression = MakeNode<FieldAccessExpression>(
|
||||
parameter, MakeNode<Identifier>(field.name_and_type.name));
|
||||
Expression* store_expression =
|
||||
MakeFieldAccessExpression(parameter, field.name_and_type.name);
|
||||
if (field.index) {
|
||||
store_expression =
|
||||
MakeNode<ElementAccessExpression>(store_expression, index);
|
||||
@ -767,6 +769,131 @@ void ClassType::GenerateAccessors() {
|
||||
}
|
||||
}
|
||||
|
||||
void ClassType::GenerateSliceAccessor(size_t field_index) {
|
||||
// Generate a Torque macro for getting a Slice to this field. This macro can
|
||||
// be called by the dot operator for this field. In Torque, this function for
|
||||
// class "ClassName" and field "field_name" and field type "FieldType" would
|
||||
// be written as one of the following:
|
||||
//
|
||||
// If the field has a known offset (in this example, 16):
|
||||
// FieldSliceClassNameFieldName(o: ClassName) {
|
||||
// return torque_internal::Slice<FieldType> {
|
||||
// object: o,
|
||||
// offset: 16,
|
||||
// length: torque_internal::%IndexedFieldLength<ClassName>(
|
||||
// o, "field_name")),
|
||||
// unsafeMarker: torque_internal::Unsafe {}
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// If the field has an unknown offset, and the previous field is named p, and
|
||||
// an item in the previous field has size 4:
|
||||
// FieldSliceClassNameFieldName(o: ClassName) {
|
||||
// const previous = &o.p;
|
||||
// return torque_internal::Slice<FieldType> {
|
||||
// object: o,
|
||||
// offset: previous.offset + 4 * previous.length,
|
||||
// length: torque_internal::%IndexedFieldLength<ClassName>(
|
||||
// o, "field_name")),
|
||||
// unsafeMarker: torque_internal::Unsafe {}
|
||||
// };
|
||||
// }
|
||||
const Field& field = fields_[field_index];
|
||||
std::string macro_name = GetSliceMacroName(field);
|
||||
Signature signature;
|
||||
Identifier* parameter_identifier = MakeNode<Identifier>("o");
|
||||
signature.parameter_names.push_back(parameter_identifier);
|
||||
signature.parameter_types.types.push_back(this);
|
||||
signature.parameter_types.var_args = false;
|
||||
signature.return_type = TypeOracle::GetSliceType(field.name_and_type.type);
|
||||
|
||||
std::vector<Statement*> statements;
|
||||
Expression* offset_expression = nullptr;
|
||||
IdentifierExpression* parameter =
|
||||
MakeNode<IdentifierExpression>(parameter_identifier);
|
||||
|
||||
if (field.offset.has_value()) {
|
||||
offset_expression =
|
||||
MakeNode<NumberLiteralExpression>(static_cast<double>(*field.offset));
|
||||
} else {
|
||||
const Field* previous = GetFieldPreceding(field_index);
|
||||
DCHECK_NOT_NULL(previous);
|
||||
|
||||
// o.p
|
||||
Expression* previous_expression =
|
||||
MakeFieldAccessExpression(parameter, previous->name_and_type.name);
|
||||
|
||||
// &o.p
|
||||
previous_expression = MakeCallExpression("&", {previous_expression});
|
||||
|
||||
// const previous = &o.p;
|
||||
Statement* define_previous =
|
||||
MakeConstDeclarationStatement("previous", previous_expression);
|
||||
statements.push_back(define_previous);
|
||||
|
||||
// 4
|
||||
size_t previous_element_size;
|
||||
std::tie(previous_element_size, std::ignore) =
|
||||
*SizeOf(previous->name_and_type.type);
|
||||
Expression* previous_element_size_expression =
|
||||
MakeNode<NumberLiteralExpression>(
|
||||
static_cast<double>(previous_element_size));
|
||||
|
||||
// previous.length
|
||||
Expression* previous_length_expression = MakeFieldAccessExpression(
|
||||
MakeIdentifierExpression("previous"), "length");
|
||||
|
||||
// previous.offset
|
||||
Expression* previous_offset_expression = MakeFieldAccessExpression(
|
||||
MakeIdentifierExpression("previous"), "offset");
|
||||
|
||||
// 4 * previous.length
|
||||
// In contrast to the code used for allocation, we don't need overflow
|
||||
// checks here because we already know all the offsets fit into memory.
|
||||
offset_expression = MakeCallExpression(
|
||||
"*", {previous_element_size_expression, previous_length_expression});
|
||||
|
||||
// previous.offset + 4 * previous.length
|
||||
offset_expression = MakeCallExpression(
|
||||
"+", {previous_offset_expression, offset_expression});
|
||||
}
|
||||
|
||||
// torque_internal::%IndexedFieldLength<ClassName>(o, "field_name")
|
||||
Expression* length_expression = MakeCallExpression(
|
||||
MakeIdentifierExpression({"torque_internal"}, "%IndexedFieldLength",
|
||||
{MakeNode<PrecomputedTypeExpression>(this)}),
|
||||
{parameter, MakeNode<StringLiteralExpression>(
|
||||
StringLiteralQuote(field.name_and_type.name))});
|
||||
|
||||
// torque_internal::Unsafe {}
|
||||
Expression* unsafe_expression = MakeStructExpression(
|
||||
MakeBasicTypeExpression({"torque_internal"}, "Unsafe"), {});
|
||||
|
||||
// torque_internal::Slice<FieldType> {
|
||||
// object: o,
|
||||
// offset: <<offset_expression>>,
|
||||
// length: torque_internal::%IndexedFieldLength<ClassName>(
|
||||
// o, "field_name")),
|
||||
// unsafeMarker: torque_internal::Unsafe {}
|
||||
// }
|
||||
Expression* slice_expression = MakeStructExpression(
|
||||
MakeBasicTypeExpression(
|
||||
{"torque_internal"}, "Slice",
|
||||
{MakeNode<PrecomputedTypeExpression>(field.name_and_type.type)}),
|
||||
{{MakeNode<Identifier>("object"), parameter},
|
||||
{MakeNode<Identifier>("offset"), offset_expression},
|
||||
{MakeNode<Identifier>("length"), length_expression},
|
||||
{MakeNode<Identifier>("unsafeMarker"), unsafe_expression}});
|
||||
|
||||
statements.push_back(MakeNode<ReturnStatement>(slice_expression));
|
||||
Statement* block =
|
||||
MakeNode<BlockStatement>(/*deferred=*/false, std::move(statements));
|
||||
|
||||
Macro* macro = Declarations::DeclareMacro(macro_name, true, base::nullopt,
|
||||
signature, block, base::nullopt);
|
||||
GlobalContext::EnsureInCCOutputList(TorqueMacro::cast(macro));
|
||||
}
|
||||
|
||||
bool ClassType::HasStaticSize() const {
|
||||
// Abstract classes don't have instances directly, so asking this question
|
||||
// doesn't make sense.
|
||||
|
@ -704,6 +704,13 @@ class ClassType final : public AggregateType {
|
||||
base::Optional<ObjectSlotKind> ComputeArraySlotKind() const;
|
||||
bool HasNoPointerSlots() const;
|
||||
bool HasIndexedFieldsIncludingInParents() const;
|
||||
const Field* GetFieldPreceding(size_t field_index) const;
|
||||
|
||||
// Given that the field exists in this class or a superclass, returns the
|
||||
// specific class that declared the field.
|
||||
const ClassType* GetClassDeclaringField(const Field& f) const;
|
||||
|
||||
std::string GetSliceMacroName(const Field& field) const;
|
||||
|
||||
const InstanceTypeConstraints& GetInstanceTypeConstraints() const {
|
||||
return decl_->instance_type_constraints;
|
||||
@ -734,6 +741,8 @@ class ClassType final : public AggregateType {
|
||||
ClassFlags flags, const std::string& generates,
|
||||
const ClassDeclaration* decl, const TypeAlias* alias);
|
||||
|
||||
void GenerateSliceAccessor(size_t field_index);
|
||||
|
||||
size_t header_size_;
|
||||
ResidueClass size_;
|
||||
mutable ClassFlags flags_;
|
||||
|
Loading…
Reference in New Issue
Block a user