Commit 6b663123 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] allow expressions for array lengths

This allows arbitrary expressions to specify the length of an array.
These expressions get access to globally declared things and the
preceding fields of the current object.
Unfortunately, this breaks generated C++ runtime code, so as a
workaround, I special-case expressions that are just an identifier
and handle them as before. We might want to support more cases there
in the future, probably also with special-casing since having a full
C++ back-end for Torque is infeasible.

Bug: v8:10004 v8:7793

Change-Id: I0d5d1200c0e727766beed7bfb2d43a8abb9cacf0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1942610
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65427}
parent a1a87800
......@@ -868,7 +868,7 @@ struct Annotation {
struct ClassFieldExpression {
NameAndTypeExpression name_and_type;
base::Optional<std::string> index;
base::Optional<Expression*> index;
std::vector<ConditionalAnnotation> conditions;
bool weak;
bool const_qualified;
......
......@@ -242,6 +242,7 @@ void GenerateFieldValueAccessor(const Field& field,
// std::move(descriptors_struct_field_list), // Struct fields
// GetArrayKind(indexed_field_count.validity))); // Field kind
void GenerateGetPropsChunkForField(const Field& field,
base::Optional<NameAndType> array_length,
std::ostream& get_props_impl) {
DebugFieldType debug_field_type(field);
......@@ -272,8 +273,8 @@ void GenerateGetPropsChunkForField(const Field& field,
// If the field is indexed, emit a fetch of the array length, and change
// count_value and property_kind to be the correct values for an array.
if (field.index) {
const Type* index_type = field.index->type;
if (array_length) {
const Type* index_type = array_length->type;
std::string index_type_name;
if (index_type == TypeOracle::GetSmiType()) {
index_type_name = "uintptr_t";
......@@ -288,7 +289,8 @@ void GenerateGetPropsChunkForField(const Field& field,
}
get_props_impl << " Value<" << index_type_name
<< "> indexed_field_count = Get"
<< CamelifyString(field.index->name) << "Value(accessor);\n";
<< CamelifyString(array_length->name)
<< "Value(accessor);\n";
property_kind = "GetArrayKind(indexed_field_count.validity)";
}
......@@ -396,7 +398,15 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
if (field.name_and_type.type == TypeOracle::GetVoidType()) continue;
GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
GenerateGetPropsChunkForField(field, get_props_impl);
base::Optional<NameAndType> array_length;
if (field.index) {
array_length = ExtractSimpleFieldArraySize(type, *field.index);
if (!array_length) {
// Unsupported complex array length, skipping this field.
continue;
}
}
GenerateGetPropsChunkForField(field, array_length, get_props_impl);
}
h_contents << "};\n";
......
......@@ -1266,16 +1266,40 @@ void ImplementationVisitor::InitializeClass(
}
}
VisitResult ImplementationVisitor::GenerateArrayLength(
Expression* array_length, Namespace* nspace,
const std::map<std::string, LocationReference>& bindings) {
StackScope stack_scope(this);
// Switch to the namespace where the class was declared.
CurrentScope::Scope current_scope_scope(nspace);
// Reset local bindings and install local binding for the preceding fields.
BindingsManagersScope bindings_managers_scope;
BlockBindings<LocalValue> field_bindings(&ValueBindingsManager::Get());
for (auto& p : bindings) {
field_bindings.Add(p.first, LocalValue{p.second}, true);
}
VisitResult length = Visit(array_length);
VisitResult converted_length =
GenerateCall("Convert", Arguments{{length}, {}},
{TypeOracle::GetIntPtrType(), length.type()}, false);
return stack_scope.Yield(converted_length);
}
VisitResult ImplementationVisitor::GenerateArrayLength(VisitResult object,
const Field& field) {
DCHECK(field.index);
StackScope stack_scope(this);
VisitResult length = GenerateFetchFromLocation(GenerateFieldReference(
object, *field.index, *object.type()->ClassSupertype()));
length = GenerateCall("Convert", Arguments{{length}, {}},
{TypeOracle::GetIntPtrType(), length.type()}, false);
return stack_scope.Yield(length);
const ClassType* class_type = *object.type()->ClassSupertype();
std::map<std::string, LocationReference> bindings;
for (Field f : class_type->ComputeAllFields()) {
if (f.index) break;
bindings.insert({f.name_and_type.name,
GenerateFieldReference(object, f.name_and_type,
*object.type()->ClassSupertype())});
}
return stack_scope.Yield(
GenerateArrayLength(*field.index, class_type->nspace(), bindings));
}
VisitResult ImplementationVisitor::GenerateArrayLength(
......@@ -1284,11 +1308,16 @@ VisitResult ImplementationVisitor::GenerateArrayLength(
DCHECK(field.index);
StackScope stack_scope(this);
VisitResult length =
initializer_results.field_value_map.at(field.index->name);
length = GenerateCall("Convert", Arguments{{length}, {}},
{TypeOracle::GetIntPtrType(), length.type()}, false);
return stack_scope.Yield(length);
std::map<std::string, LocationReference> bindings;
for (Field f : class_type->ComputeAllFields()) {
if (f.index) break;
const std::string& fieldname = f.name_and_type.name;
VisitResult value = initializer_results.field_value_map.at(fieldname);
bindings.insert({fieldname, LocationReference::Temporary(
value, "initial field " + fieldname)});
}
return stack_scope.Yield(
GenerateArrayLength(*field.index, class_type->nspace(), bindings));
}
VisitResult ImplementationVisitor::GenerateObjectSize(
......@@ -3653,24 +3682,32 @@ void GenerateClassFieldVerifier(const std::string& class_name,
if (TypeOracle::GetUninitializedType()->IsSubtypeOf(field_type)) return;
if (f.index) {
const Type* index_type = f.index->type;
std::string index_offset =
class_name + "::k" + CamelifyString(f.index->name) + "Offset";
base::Optional<NameAndType> array_length =
ExtractSimpleFieldArraySize(class_type, *f.index);
if (!array_length) {
Error("Cannot generate verifier for array field with complex length.")
.Position((*f.index)->pos)
.Throw();
}
std::string length_field_offset =
class_name + "::k" + CamelifyString(array_length->name) + "Offset";
cc_contents << " for (int i = 0; i < ";
if (index_type == TypeOracle::GetSmiType()) {
if (array_length->type == TypeOracle::GetSmiType()) {
// We already verified the index field because it was listed earlier, so
// we can assume it's safe to read here.
cc_contents << "TaggedField<Smi, " << index_offset
cc_contents << "TaggedField<Smi, " << length_field_offset
<< ">::load(o).value()";
} else {
const Type* constexpr_version = index_type->ConstexprVersion();
const Type* constexpr_version = array_length->type->ConstexprVersion();
if (constexpr_version == nullptr) {
Error("constexpr representation for type ", index_type->ToString(),
Error("constexpr representation for type ",
array_length->type->ToString(),
" is required due to usage as index")
.Position(f.pos);
}
cc_contents << "o.ReadField<" << constexpr_version->GetGeneratedTypeName()
<< ">(" << index_offset << ")";
<< ">(" << length_field_offset << ")";
}
cc_contents << "; ++i) {\n";
} else {
......
......@@ -373,6 +373,9 @@ class ImplementationVisitor {
LocationReference GenerateFieldReference(VisitResult object,
const NameAndType& field,
const ClassType* class_type);
VisitResult GenerateArrayLength(
Expression* array_length, Namespace* nspace,
const std::map<std::string, LocationReference>& bindings);
VisitResult GenerateArrayLength(VisitResult object, const Field& field);
VisitResult GenerateArrayLength(const ClassType* class_type,
const InitializerResults& initializer_results,
......
......@@ -1559,7 +1559,7 @@ base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
auto weak = child_results->NextAs<bool>();
auto const_qualified = child_results->NextAs<bool>();
auto name = child_results->NextAs<Identifier*>();
auto index = child_results->NextAs<base::Optional<std::string>>();
auto index = child_results->NextAs<base::Optional<Expression*>>();
auto type = child_results->NextAs<TypeExpression*>();
return ParseResult{ClassFieldExpression{{name, type},
index,
......@@ -1692,6 +1692,9 @@ struct TorqueGrammar : Grammar {
TorqueGrammar() : Grammar(&file) { SetWhitespace(MatchWhitespace); }
// Result: Expression*
Symbol* expression = &assignmentExpression;
// Result: std::string
Symbol identifier = {Rule({Pattern(MatchIdentifier)}, YieldMatchedInput),
Rule({Token("runtime")}, YieldMatchedInput)};
......@@ -1818,14 +1821,17 @@ struct TorqueGrammar : Grammar {
// Result: NameAndTypeExpression
Symbol nameAndType = {Rule({&name, Token(":"), &type}, MakeNameAndType)};
// Result: base::Optional<Expression*>
Symbol* optionalArraySpecifier =
Optional<std::string>(Sequence({Token("["), &identifier, Token("]")}));
Optional<Expression*>(Sequence({Token("["), expression, Token("]")}));
// Result: ClassFieldExpression
Symbol classField = {
Rule({annotations, CheckIf(Token("weak")), CheckIf(Token("const")), &name,
optionalArraySpecifier, Token(":"), &type, Token(";")},
MakeClassField)};
// Result: StructFieldExpression
Symbol structField = {
Rule({CheckIf(Token("const")), &name, Token(":"), &type, Token(";")},
MakeStructField)};
......@@ -1866,9 +1872,6 @@ struct TorqueGrammar : Grammar {
return result;
}
// Result: Expression*
Symbol* expression = &assignmentExpression;
// Result: IncrementDecrementOperator
Symbol incrementDecrementOperator = {
Rule({Token("++")},
......
......@@ -433,7 +433,7 @@ void TypeVisitor::VisitClassFieldsAndMethods(
}
}
}
base::Optional<NameAndType> index_field;
base::Optional<Expression*> array_length;
if (field_expression.index) {
if (seen_indexed_field ||
(super_class && super_class->HasIndexedField())) {
......@@ -441,8 +441,7 @@ void TypeVisitor::VisitClassFieldsAndMethods(
"only one indexable field is currently supported per class");
}
seen_indexed_field = true;
index_field = class_type->LookupFieldInternal(*field_expression.index)
.name_and_type;
array_length = *field_expression.index;
} else {
if (seen_indexed_field) {
ReportError("cannot declare non-indexable field \"",
......@@ -454,7 +453,7 @@ void TypeVisitor::VisitClassFieldsAndMethods(
const Field& field = class_type->RegisterField(
{field_expression.name_and_type.name->pos,
class_type,
index_field,
array_length,
{field_expression.name_and_type.name->value, field_type},
class_offset,
field_expression.weak,
......
......@@ -800,6 +800,17 @@ bool IsAllowedAsBitField(const Type* type) {
type->IsSubtypeOf(TypeOracle::GetBoolType());
}
base::Optional<NameAndType> ExtractSimpleFieldArraySize(
const ClassType& class_type, Expression* array_size) {
IdentifierExpression* identifier =
IdentifierExpression::DynamicCast(array_size);
if (!identifier || !identifier->generic_arguments.empty() ||
!identifier->namespace_qualification.empty())
return {};
if (!class_type.HasField(identifier->name->value)) return {};
return class_type.LookupField(identifier->name->value).name_and_type;
}
} // namespace torque
} // namespace internal
} // namespace v8
......@@ -190,7 +190,7 @@ struct Field {
SourcePosition pos;
const AggregateType* aggregate;
base::Optional<NameAndType> index;
base::Optional<Expression*> index;
NameAndType name_and_type;
// The byte offset of this field from the beginning of the containing class or
......@@ -789,6 +789,9 @@ base::Optional<std::tuple<size_t, std::string>> SizeOf(const Type* type);
bool IsAnyUnsignedInteger(const Type* type);
bool IsAllowedAsBitField(const Type* type);
base::Optional<NameAndType> ExtractSimpleFieldArraySize(
const ClassType& class_type, Expression* array_size);
} // namespace torque
} // namespace internal
} // namespace v8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment