[torque] add internal classes that map to FixedArray instances

Bug: v8:7793
Change-Id: Ifc2bf26e9d3bc13d4f2455d6d04ce5e2682626db
Reviewed-on: https://chromium-review.googlesource.com/c/1454600
Reviewed-by: Daniel Clifford <danno@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59404}
This commit is contained in:
Tobias Tebbi 2019-02-05 13:39:44 +01:00 committed by Commit Bot
parent d09bea1b6f
commit 6c3c952d8d
15 changed files with 210 additions and 98 deletions

View File

@ -21,9 +21,7 @@ type Smi extends Tagged generates 'TNode<Smi>' constexpr 'Smi';
// A Smi that is greater than or equal to 0. See TaggedIsPositiveSmi.
type PositiveSmi extends Smi generates 'TNode<Smi>';
class HeapObject extends Tagged {
map: Map;
}
extern class HeapObject extends Tagged { map: Map; }
type Object = Smi | HeapObject;
type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
@ -69,25 +67,26 @@ type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type Map extends HeapObject generates 'TNode<Map>';
// These intrinsics should never be called from Torque code. They're used
// internally by the 'new' operator and only declared here because it's simpler
// than building the definition from C++.
intrinsic %GetAllocationBaseSize<Class: type>(map: Map): intptr;
intrinsic %Allocate<Class: type>(size: intptr): Class;
type FixedArrayBase extends HeapObject generates 'TNode<FixedArrayBase>';
type FixedArray extends FixedArrayBase generates 'TNode<FixedArray>';
type FixedDoubleArray extends FixedArrayBase
generates 'TNode<FixedDoubleArray>';
class JSReceiver extends HeapObject {
// These intrinsics should never be called from Torque code. They're used
// internally by the 'new' operator and only declared here because it's simpler
// than building the definition from C++.
intrinsic %GetAllocationBaseSize<Class: type>(map: Map): intptr;
intrinsic %Allocate<Class: type>(size: intptr): Class;
intrinsic %AllocateInternalClass<Class: type>(slotCount: constexpr intptr): Class;
extern class JSReceiver extends HeapObject {
properties_or_hash: FixedArrayBase | Smi;
}
type Constructor extends JSReceiver generates 'TNode<JSReceiver>';
type JSProxy extends JSReceiver generates 'TNode<JSProxy>';
class JSObject extends JSReceiver {
extern class JSObject extends JSReceiver {
constructor(
map: Map, properties: FixedArrayBase | Smi, elements: FixedArrayBase) {
super(map, properties);
@ -103,11 +102,9 @@ class JSObject extends JSReceiver {
elements: FixedArrayBase;
}
class JSArgumentsObjectWithLength extends JSObject {
length: Object;
}
extern class JSArgumentsObjectWithLength extends JSObject { length: Object; }
class JSArray extends JSObject {
extern class JSArray extends JSObject {
constructor(implicit context: Context)() {
super(
GetFastPackedSmiElementsJSArrayMap(), kEmptyFixedArray,
@ -143,7 +140,7 @@ type DebugInfo extends HeapObject;
type ScopeInfo extends Object generates 'TNode<ScopeInfo>';
class SharedFunctionInfo extends HeapObject {
extern class SharedFunctionInfo extends HeapObject {
weak function_data: Object;
name_or_scope_info: String | NoSharedNameSentinel | ScopeInfo;
outer_scope_info_or_feedback_metadata: HeapObject;
@ -156,11 +153,11 @@ class SharedFunctionInfo extends HeapObject {
flags: int32;
}
class SharedFunctionInfoWithID extends SharedFunctionInfo {
extern class SharedFunctionInfoWithID extends SharedFunctionInfo {
unique_id: int32;
}
class JSFunction extends JSObject {
extern class JSFunction extends JSObject {
shared_function_info: SharedFunctionInfo;
context: Context;
feedback_cell: Smi;
@ -168,7 +165,7 @@ class JSFunction extends JSObject {
weak prototype_or_initial_map: JSReceiver | Map;
}
class JSBoundFunction extends JSObject {
extern class JSBoundFunction extends JSObject {
bound_target_function: JSReceiver;
bound_this: Object;
bound_arguments: FixedArray;
@ -213,18 +210,18 @@ extern operator '[]=' macro StoreContextElement(
extern operator '[]' macro LoadContextElement(Context, intptr): Object;
extern operator '[]' macro LoadContextElement(Context, Smi): Object;
class JSArrayBuffer extends JSObject {
extern class JSArrayBuffer extends JSObject {
byte_length: uintptr;
backing_store: RawPtr;
}
class JSArrayBufferView extends JSObject {
extern class JSArrayBufferView extends JSObject {
buffer: JSArrayBuffer;
byte_offset: uintptr;
byte_length: uintptr;
}
class JSTypedArray extends JSArrayBufferView {}
extern class JSTypedArray extends JSArrayBufferView {}
type JSDataView extends JSArrayBufferView generates 'TNode<JSDataView>';

View File

@ -1217,6 +1217,12 @@ TNode<HeapObject> CodeStubAssembler::Allocate(TNode<IntPtrT> size_in_bytes,
AllocationFlags flags) {
Comment("Allocate");
bool const new_space = !(flags & kPretenured);
if (!(flags & kAllowLargeObjectAllocation)) {
intptr_t size_constant;
if (ToIntPtrConstant(size_in_bytes, size_constant)) {
CHECK_LE(size_constant, kMaxRegularHeapObjectSize);
}
}
if (!(flags & kDoubleAlignment) && !(flags & kAllowLargeObjectAllocation)) {
return OptimizedAllocate(size_in_bytes, new_space
? PretenureFlag::NOT_TENURED

View File

@ -1535,6 +1535,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
fixed_array_map);
}
TNode<FixedArray> AllocateUninitializedFixedArray(intptr_t capacity) {
return UncheckedCast<FixedArray>(AllocateFixedArray(
PACKED_ELEMENTS, IntPtrConstant(capacity), AllocationFlag::kNone));
}
TNode<FixedArray> AllocateZeroedFixedArray(TNode<IntPtrT> capacity) {
TNode<FixedArray> result = UncheckedCast<FixedArray>(
AllocateFixedArray(PACKED_ELEMENTS, capacity,

View File

@ -899,20 +899,23 @@ struct StructDeclaration : Declaration {
struct ClassDeclaration : Declaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ClassDeclaration)
ClassDeclaration(SourcePosition pos, std::string name, bool transient,
std::string super, base::Optional<std::string> generates,
ClassDeclaration(SourcePosition pos, std::string name, bool is_extern,
bool transient, base::Optional<std::string> super,
base::Optional<std::string> generates,
std::vector<Declaration*> methods,
std::vector<ClassFieldExpression> fields)
: Declaration(kKind, pos),
name(std::move(name)),
is_extern(is_extern),
transient(transient),
super(std::move(super)),
generates(std::move(generates)),
methods(std::move(methods)),
fields(std::move(fields)) {}
std::string name;
bool is_extern;
bool transient;
std::string super;
base::Optional<std::string> super;
base::Optional<std::string> generates;
std::vector<Declaration*> methods;
std::vector<ClassFieldExpression> fields;

View File

@ -244,6 +244,9 @@ void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
} else if (instruction.intrinsic->ExternalName() == "%Allocate") {
out_ << "ca_.UncheckedCast<" << return_type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).Allocate";
} else if (instruction.intrinsic->ExternalName() ==
"%AllocateInternalClass") {
out_ << "CodeStubAssembler(state_).AllocateUninitializedFixedArray";
} else {
ReportError("no built in intrinsic with name " +
instruction.intrinsic->ExternalName());
@ -690,14 +693,21 @@ void CSAGenerator::EmitInstruction(
std::tie(field_size, size_string, machine_type) =
field.GetFieldSizeInformation();
out_ << field.name_and_type.type->GetGeneratedTypeName() << " " << result_name
<< " = "
<< "ca_.UncheckedCast<"
<< field.name_and_type.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).LoadObjectField("
<< stack->Top() + ", " + field.aggregate->GetGeneratedTNodeTypeName() +
"::k" + CamelifyString(field.name_and_type.name) + "Offset, "
<< machine_type + "));\n";
if (instruction.class_type->IsExtern()) {
out_ << field.name_and_type.type->GetGeneratedTypeName() << " "
<< result_name << " = ca_.UncheckedCast<"
<< field.name_and_type.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).LoadObjectField(" << stack->Top()
<< ", " << field.aggregate->GetGeneratedTNodeTypeName() << "::k"
<< CamelifyString(field.name_and_type.name) << "Offset, "
<< machine_type + "));\n";
} else {
out_ << field.name_and_type.type->GetGeneratedTypeName() << " "
<< result_name << " = ca_.UncheckedCast<"
<< field.name_and_type.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).LoadFixedArrayElement(" << stack->Top()
<< ", " << (field.offset / kTaggedSize) << "));\n";
}
stack->Poke(stack->AboveTop() - 1, result_name);
}
@ -708,26 +718,31 @@ void CSAGenerator::EmitInstruction(
stack->Push(value);
const Field& field =
instruction.class_type->LookupField(instruction.field_name);
if (field.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
if (field.offset == 0) {
out_ << " CodeStubAssembler(state_).StoreMap(" + object + ", " +
value + ");\n";
if (instruction.class_type->IsExtern()) {
if (field.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
if (field.offset == 0) {
out_ << " CodeStubAssembler(state_).StoreMap(" << object << ", "
<< value << ");\n";
} else {
out_ << " CodeStubAssembler(state_).StoreObjectField(" << object
<< ", " << field.offset << ", " << value << ");\n";
}
} else {
out_ << " CodeStubAssembler(state_).StoreObjectField(" + object +
", " + std::to_string(field.offset) + ", " + value + ");\n";
size_t field_size;
std::string size_string;
std::string machine_type;
std::tie(field_size, size_string, machine_type) =
field.GetFieldSizeInformation();
if (field.offset == 0) {
ReportError("the first field in a class object must be a map");
}
out_ << " CodeStubAssembler(state_).StoreObjectFieldNoWriteBarrier("
<< object << ", " << field.offset << ", " << value << ", "
<< machine_type << ".representation());\n";
}
} else {
size_t field_size;
std::string size_string;
std::string machine_type;
std::tie(field_size, size_string, machine_type) =
field.GetFieldSizeInformation();
if (field.offset == 0) {
ReportError("the first field in a class object must be a map");
}
out_ << " CodeStubAssembler(state_).StoreObjectFieldNoWriteBarrier("
<< object << ", " << std::to_string(field.offset) + ", " << value
<< ", " << machine_type << ".representation());\n";
out_ << " CodeStubAssembler(state_).StoreFixedArrayElement(" << object
<< ", " << (field.offset / kTaggedSize) << ", " << value << ");\n";
}
}

View File

@ -348,30 +348,47 @@ void DeclarationVisitor::Visit(StructDeclaration* decl) {
}
void DeclarationVisitor::Visit(ClassDeclaration* decl) {
// Compute the offset of the class' first member. If the class extends
// another class, it's the size of the extended class, otherwise zero.
const Type* super_type = Declarations::LookupType(decl->super);
if (super_type != TypeOracle::GetTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError("class \"", decl->name,
"\" must extend either Tagged or an already declared class");
ClassType* new_class;
if (decl->is_extern) {
if (!decl->super) {
ReportError("Extern class must extend another type.");
}
}
// The generates clause must create a TNode<>
std::string generates = decl->name;
if (decl->generates) {
if (generates.length() < 7 || generates.substr(0, 6) != "TNode<" ||
generates.substr(generates.length() - 1, 1) != ">") {
ReportError("generated type \"", generates,
"\" should be of the form \"TNode<...>\"");
// Compute the offset of the class' first member. If the class extends
// another class, it's the size of the extended class, otherwise zero.
const Type* super_type = Declarations::LookupType(*decl->super);
if (super_type != TypeOracle::GetTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError(
"class \"", decl->name,
"\" must extend either Tagged or an already declared class");
}
}
generates = generates.substr(6, generates.length() - 7);
}
auto new_class = Declarations::DeclareClass(super_type, decl->name,
decl->transient, generates);
// The generates clause must create a TNode<>
std::string generates = decl->name;
if (decl->generates) {
if (generates.length() < 7 || generates.substr(0, 6) != "TNode<" ||
generates.substr(generates.length() - 1, 1) != ">") {
ReportError("generated type \"", generates,
"\" should be of the form \"TNode<...>\"");
}
generates = generates.substr(6, generates.length() - 7);
}
new_class = Declarations::DeclareClass(
super_type, decl->name, decl->is_extern, decl->transient, generates);
} else {
if (decl->super) {
ReportError("Only extern classes can inherit.");
}
if (decl->generates) {
ReportError("Only extern classes can specify a generated type.");
}
new_class = Declarations::DeclareClass(TypeOracle::GetTaggedType(),
decl->name, decl->is_extern,
decl->transient, "FixedArray");
}
GlobalContext::RegisterClass(decl->name, new_class);
class_declarations_.push_back(
std::make_tuple(CurrentScope::Get(), decl, new_class));
@ -534,6 +551,14 @@ void DeclarationVisitor::FinalizeClassFieldsAndMethods(
field_expression.name_and_type.type->pos);
const Type* field_type =
Declarations::GetType(field_expression.name_and_type.type);
if (!class_declaration->is_extern) {
if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
ReportError("Non-extern classes do not support untagged fields.");
}
if (field_expression.weak) {
ReportError("Non-extern classes do not support weak fields.");
}
}
const Field& field = class_type->RegisterField(
{field_expression.name_and_type.type->pos,
class_type,

View File

@ -174,10 +174,11 @@ StructType* Declarations::DeclareStruct(const std::string& name) {
}
ClassType* Declarations::DeclareClass(const Type* super_type,
const std::string& name, bool transient,
const std::string& name, bool is_extern,
bool transient,
const std::string& generates) {
ClassType* new_type =
TypeOracle::GetClassType(super_type, name, transient, generates);
ClassType* new_type = TypeOracle::GetClassType(super_type, name, is_extern,
transient, generates);
DeclareType(name, new_type, false);
return new_type;
}

View File

@ -84,7 +84,8 @@ class Declarations {
static StructType* DeclareStruct(const std::string& name);
static ClassType* DeclareClass(const Type* super, const std::string& name,
bool transient, const std::string& generates);
bool is_extern, bool transient,
const std::string& generates);
static Macro* CreateMacro(std::string external_name,
std::string readable_name,

View File

@ -150,7 +150,7 @@ void ImplementationVisitor::Visit(NamespaceConstant* decl) {
void ImplementationVisitor::Visit(TypeAlias* alias) {
if (alias->IsRedeclaration()) return;
const ClassType* class_type = ClassType::DynamicCast(alias->type());
if (class_type) {
if (class_type && class_type->IsExtern()) {
// Classes that are in the default namespace are defined in the C++
// world and all of their fields and methods are declared explicitly.
// Internal classes (e.g. ones used for testing that are not in the default
@ -170,7 +170,7 @@ void ImplementationVisitor::Visit(TypeAlias* alias) {
header_out() << " };\n";
} else if (!class_type->nspace()->IsDefaultNamespace()) {
ReportError(
"classes are currently only supported in the default and test "
"extern classes are currently only supported in the default and test "
"namespaces");
}
return;
@ -1281,16 +1281,26 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
// Output the code to generate an unitialized object of the class size in the
// GC heap.
VisitResult object_map = ProjectStructField(new_struct_result, "map");
Arguments size_arguments;
size_arguments.parameters.push_back(object_map);
VisitResult object_size = GenerateCall("%GetAllocationBaseSize",
size_arguments, {class_type}, false);
Arguments allocate_arguments;
allocate_arguments.parameters.push_back(object_size);
VisitResult allocate_result =
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
DCHECK(allocate_result.IsOnStack());
VisitResult allocate_result;
if (class_type->IsExtern()) {
VisitResult object_map = ProjectStructField(new_struct_result, "map");
Arguments size_arguments;
size_arguments.parameters.push_back(object_map);
VisitResult object_size = GenerateCall("%GetAllocationBaseSize",
size_arguments, {class_type}, false);
Arguments allocate_arguments;
allocate_arguments.parameters.push_back(object_size);
allocate_result =
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
DCHECK(allocate_result.IsOnStack());
} else {
Arguments allocate_arguments;
allocate_arguments.parameters.push_back(
VisitResult(TypeOracle::GetConstexprIntPtrType(),
std::to_string(class_type->size() / kTaggedSize)));
allocate_result = GenerateCall("%AllocateInternalClass", allocate_arguments,
{class_type}, false);
}
// Fill in the fields of the newly allocated class by copying the values
// from the struct that was built by the constructor. So that the generaeted

View File

@ -591,18 +591,19 @@ base::Optional<ParseResult> MakeMethodDeclaration(
base::Optional<ParseResult> MakeClassDeclaration(
ParseResultIterator* child_results) {
auto is_extern = child_results->NextAs<bool>();
auto transient = child_results->NextAs<bool>();
auto name = child_results->NextAs<std::string>();
if (!IsValidTypeName(name)) {
NamingConventionError("Type", name, "UpperCamelCase");
}
auto extends = child_results->NextAs<std::string>();
auto extends = child_results->NextAs<base::Optional<std::string>>();
auto generates = child_results->NextAs<base::Optional<std::string>>();
auto methods = child_results->NextAs<std::vector<Declaration*>>();
auto fields = child_results->NextAs<std::vector<ClassFieldExpression>>();
Declaration* result = MakeNode<ClassDeclaration>(
std::move(name), transient, std::move(extends), std::move(generates),
std::move(methods), fields);
std::move(name), is_extern, transient, std::move(extends),
std::move(generates), std::move(methods), fields);
return ParseResult{result};
}
@ -1619,8 +1620,9 @@ struct TorqueGrammar : Grammar {
Rule({Token("const"), &identifier, Token(":"), &type, Token("generates"),
&externalString, Token(";")},
MakeExternConstDeclaration),
Rule({CheckIf(Token("transient")), Token("class"), &identifier,
Sequence({Token("extends"), &identifier}),
Rule({CheckIf(Token("extern")), CheckIf(Token("transient")),
Token("class"), &identifier,
Optional<std::string>(Sequence({Token("extends"), &identifier})),
Optional<std::string>(
Sequence({Token("generates"), &externalString})),
Token("{"), List<Declaration*>(&method),

View File

@ -35,9 +35,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
}
static ClassType* GetClassType(const Type* parent, const std::string& name,
bool transient, const std::string& generates) {
ClassType* result =
new ClassType(parent, CurrentNamespace(), name, transient, generates);
bool is_extern, bool transient,
const std::string& generates) {
ClassType* result = new ClassType(parent, CurrentNamespace(), name,
is_extern, transient, generates);
Get().struct_types_.push_back(std::unique_ptr<ClassType>(result));
return result;
}
@ -95,6 +96,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(CONSTEXPR_BOOL_TYPE_STRING);
}
static const Type* GetConstexprIntPtrType() {
return Get().GetBuiltinType(CONSTEXPR_INTPTR_TYPE_STRING);
}
static const Type* GetVoidType() {
return Get().GetBuiltinType(VOID_TYPE_STRING);
}

View File

@ -262,6 +262,7 @@ std::string StructType::ToExplicitString() const {
}
std::string ClassType::GetGeneratedTNodeTypeName() const {
if (!IsExtern()) return generates_;
std::string prefix = nspace()->IsDefaultNamespace()
? std::string{}
: (nspace()->ExternalName() + "::");
@ -282,7 +283,7 @@ std::string ClassType::ToExplicitString() const {
}
bool ClassType::AllowInstantiation() const {
return nspace()->IsDefaultNamespace();
return !IsExtern() || nspace()->IsDefaultNamespace();
}
void PrintSignature(std::ostream& os, const Signature& sig, bool with_names) {

View File

@ -22,6 +22,7 @@ namespace torque {
static const char* const CONSTEXPR_TYPE_PREFIX = "constexpr ";
static const char* const NEVER_TYPE_STRING = "never";
static const char* const CONSTEXPR_BOOL_TYPE_STRING = "constexpr bool";
static const char* const CONSTEXPR_INTPTR_TYPE_STRING = "constexpr intptr";
static const char* const BOOL_TYPE_STRING = "bool";
static const char* const VOID_TYPE_STRING = "void";
static const char* const ARGUMENTS_TYPE_STRING = "constexpr Arguments";
@ -471,6 +472,7 @@ class ClassType final : public AggregateType {
std::string ToExplicitString() const override;
std::string GetGeneratedTypeName() const override;
std::string GetGeneratedTNodeTypeName() const override;
bool IsExtern() const { return is_extern_; }
bool IsTransient() const override { return transient_; }
size_t size() const { return size_; }
StructType* struct_type() const { return this_struct_; }
@ -485,9 +487,10 @@ class ClassType final : public AggregateType {
private:
friend class TypeOracle;
ClassType(const Type* parent, Namespace* nspace, const std::string& name,
bool transient, const std::string& generates)
bool is_extern, bool transient, const std::string& generates)
: AggregateType(Kind::kClassType, parent, nspace, name),
this_struct_(nullptr),
is_extern_(is_extern),
transient_(transient),
size_(0),
generates_(generates) {
@ -495,6 +498,7 @@ class ClassType final : public AggregateType {
}
StructType* this_struct_;
bool is_extern_;
bool transient_;
size_t size_;
const std::string generates_;

View File

@ -423,6 +423,22 @@ TEST(TestStructConstructor) {
ft.Call();
}
TEST(TestInternalClass) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
Handle<Context> context =
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestInternalClass(m.UncheckedCast<Context>(m.HeapConstant(context)));
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -774,7 +774,7 @@ namespace test {
assert(w.d == 2);
}
class TestClassWithAllTypes extends JSObject {
extern class TestClassWithAllTypes extends JSObject {
a: int8;
b: uint8;
b2: uint8;
@ -810,4 +810,25 @@ namespace test {
t.h = t.h;
t.i = t.i;
}
class InternalClass {
constructor(x: Smi) {
this.a = x;
this.b = x + 1;
}
Flip() labels NotASmi {
const tmp = Cast<Smi>(this.b) otherwise NotASmi;
this.b = this.a;
this.a = tmp;
}
a: Smi;
b: Number;
}
macro TestInternalClass(implicit context: Context)() {
const o = new InternalClass{5};
o.Flip() otherwise unreachable;
check(o.a == 6);
check(o.b == 5);
}
}