Commit 32a92cf5 authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

[torque] Correctly support JSObject allocation with 'new'

This requires honoring the instance size of the object stored in the
map for JSObject. To do this, allocation is now split into two
instrinsics, one that calculates the base size of the allocated object
(%GetAllocationBaseSize) and one that actually allocates (%Allocate).

In the process, remove objects.tq, which only existed to contain a
macro to fetch the default JSObject map, which is functionality that
is now in the JSObject class constructor.

Bug: v8:7793
Change-Id: I426a7943aac67eacad46d4ff39f5c821489a04bc
Reviewed-on: https://chromium-review.googlesource.com/c/1426959
Commit-Queue: Daniel Clifford <danno@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59052}
parent f4978ed3
......@@ -873,7 +873,6 @@ torque_files = [
"src/builtins/collections.tq",
"src/builtins/data-view.tq",
"src/builtins/extras-utils.tq",
"src/builtins/object.tq",
"src/builtins/object-fromentries.tq",
"src/builtins/iterator.tq",
"src/builtins/typed-array.tq",
......
......@@ -52,6 +52,8 @@ type Number = Smi | HeapNumber;
type BigInt extends HeapObject generates 'TNode<BigInt>';
type Numeric = Number | BigInt;
type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type Map extends HeapObject generates 'TNode<Map>';
// The accessors for HeapObject's map cannot be declared before Map
// is declared because forward declarations are not (yet) supported.
......@@ -61,9 +63,10 @@ type Map extends HeapObject generates 'TNode<Map>';
extern operator '.map' macro LoadMap(HeapObject): Map;
extern transitioning operator '.map=' macro StoreMap(HeapObject, Map);
// This intrinsic should never be called from Torque code. It's used internally
// by the 'new' operator and only declared here because it's simpler than
// building the definition from C++.
// 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>';
......@@ -79,6 +82,17 @@ type Constructor extends JSReceiver generates 'TNode<JSReceiver>';
type JSProxy extends JSReceiver generates 'TNode<JSProxy>';
class JSObject extends JSReceiver {
constructor(map: Map, properties: FixedArrayBase, elements: FixedArrayBase) {
super(map, properties);
this.elements = elements;
}
constructor(implicit context: Context)() {
const objectFunction: JSFunction = GetObjectFunction();
const map: Map = Cast<Map>(objectFunction.prototype_or_initial_map)
otherwise unreachable;
super(map, kEmptyFixedArray);
this.elements = kEmptyFixedArray;
}
elements: FixedArrayBase;
}
......@@ -149,7 +163,7 @@ type NumberDictionary extends HeapObject
// RawObjectCasts should *never* be used anywhere in Torque code except for
// in Torque-based UnsafeCast operators preceeded by an appropriate
// type assert()
intrinsic %RawObjectCast<A: type>(o: Object): A;
intrinsic %RawObjectCast<A: type>(o: Tagged): A;
intrinsic %RawPointerCast<A: type>(p: RawPtr): A;
intrinsic %RawConstexprCast<To: type, From: type>(f: From): To;
......@@ -188,7 +202,6 @@ type ExtractFixedArrayFlags
constexpr 'CodeStubAssembler::ExtractFixedArrayFlags';
type ParameterMode
generates 'TNode<Int32T>' constexpr 'ParameterMode';
type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type WriteBarrierMode
generates 'TNode<Int32T>' constexpr 'WriteBarrierMode';
......
......@@ -11,7 +11,7 @@ namespace object {
const elements: FixedArray =
Cast<FixedArray>(array.elements) otherwise IfSlow;
const length: Smi = array.length;
const result: JSObject = AllocateEmptyJSObject();
const result: JSObject = new JSObject{};
for (let k: Smi = 0; k < length; ++k) {
const value: Object = array::LoadElementOrUndefined(elements, k);
......@@ -41,7 +41,7 @@ namespace object {
return ObjectFromEntriesFastCase(iterable) otherwise IfSlow;
}
label IfSlow {
const result: JSObject = AllocateEmptyJSObject();
const result: JSObject = new JSObject{};
const fastIteratorResultMap: Map =
Cast<Map>(LoadNativeContext(context)[ITERATOR_RESULT_MAP_INDEX])
otherwise unreachable;
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
namespace object {
macro AllocateEmptyJSObject(implicit context: Context)(): JSObject {
const objectFunction: JSFunction = GetObjectFunction();
const map: Map = Cast<Map>(objectFunction.prototype_or_initial_map)
otherwise unreachable;
return AllocateJSObjectFromMap(map);
}
}
......@@ -234,6 +234,24 @@ void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
s << "%FromConstexpr does not support return type " << *return_type;
ReportError(s.str());
}
} else if (instruction.intrinsic->ExternalName() ==
"%GetAllocationBaseSize") {
if (instruction.specialization_types.size() != 1) {
ReportError(
"incorrect number of specialization classes for "
"%GetAllocationBaseSize (should be one)");
}
const ClassType* class_type =
ClassType::cast(instruction.specialization_types[0]);
// Special case classes that may not always have a fixed size (e.g.
// JSObjects). Their size must be fetched from the map.
if (class_type != TypeOracle::GetJSObjectType()) {
out_ << "CodeStubAssembler(state_).IntPtrConstant((";
args[0] = std::to_string(class_type->size());
} else {
out_ << "CodeStubAssembler(state_).TimesTaggedSize(CodeStubAssembler("
"state_).LoadMapInstanceSizeInWords(";
}
} else if (instruction.intrinsic->ExternalName() == "%Allocate") {
out_ << "ca_.UncheckedCast<" << return_type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).Allocate";
......@@ -245,11 +263,21 @@ void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
out_ << "(";
PrintCommaSeparatedList(out_, args);
if (instruction.intrinsic->ExternalName() == "%Allocate") out_ << ")";
if (instruction.intrinsic->ExternalName() == "%GetAllocationBaseSize")
out_ << "))";
if (return_type->IsStructType()) {
out_ << ").Flatten();\n";
} else {
out_ << ");\n";
}
if (instruction.intrinsic->ExternalName() == "%Allocate") {
out_ << " CodeStubAssembler(state_).InitializeFieldsWithRoot("
<< results[0] << ", ";
out_ << "CodeStubAssembler(state_).IntPtrConstant("
<< std::to_string(ClassType::cast(return_type)->size()) << "), ";
PrintCommaSeparatedList(out_, args);
out_ << ", RootIndex::kUndefinedValue);\n";
}
}
void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
......
......@@ -274,7 +274,7 @@ void DeclarationVisitor::DeclareMethods(
}
}
if (container_type->Constructors().size() != 0) return;
if (constructor_this_type->Constructors().size() != 0) return;
// Generate default constructor.
Signature constructor_signature;
......
......@@ -1251,9 +1251,18 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
// Output the code to generate an unitialized object of the class size in the
// GC heap.
VisitResult raw_object_map =
ProjectStructField(new_struct_result, "map_untyped");
Arguments map_cast_arguments;
map_cast_arguments.parameters.push_back(raw_object_map);
VisitResult object_map = GenerateCall("%RawObjectCast", map_cast_arguments,
{TypeOracle::GetMapType()}, false);
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(VisitResult(
TypeOracle::GetConstInt31Type(), std::to_string(class_type->size())));
allocate_arguments.parameters.push_back(object_size);
VisitResult allocate_result =
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
DCHECK(allocate_result.IsOnStack());
......@@ -2083,8 +2092,8 @@ VisitResult ImplementationVisitor::GenerateCall(
result << ")";
return VisitResult(return_type, result.str());
} else {
assembler().Emit(
CallIntrinsicInstruction{intrinsic, constexpr_arguments});
assembler().Emit(CallIntrinsicInstruction{intrinsic, specialization_types,
constexpr_arguments});
size_t return_slot_count =
LoweredSlotCount(intrinsic->signature().return_type);
return VisitResult(return_type, assembler().TopRange(return_slot_count));
......
......@@ -230,10 +230,14 @@ struct StoreObjectFieldInstruction : InstructionBase {
struct CallIntrinsicInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
CallIntrinsicInstruction(Intrinsic* intrinsic,
TypeVector specialization_types,
std::vector<std::string> constexpr_arguments)
: intrinsic(intrinsic), constexpr_arguments(constexpr_arguments) {}
: intrinsic(intrinsic),
specialization_types(std::move(specialization_types)),
constexpr_arguments(constexpr_arguments) {}
Intrinsic* intrinsic;
TypeVector specialization_types;
std::vector<std::string> constexpr_arguments;
};
......
......@@ -107,10 +107,18 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(RAWPTR_TYPE_STRING);
}
static const Type* GetMapType() {
return Get().GetBuiltinType(MAP_TYPE_STRING);
}
static const Type* GetObjectType() {
return Get().GetBuiltinType(OBJECT_TYPE_STRING);
}
static const Type* GetJSObjectType() {
return Get().GetBuiltinType(JSOBJECT_TYPE_STRING);
}
static const Type* GetTaggedType() {
return Get().GetBuiltinType(TAGGED_TYPE_STRING);
}
......
......@@ -26,7 +26,9 @@ 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";
static const char* const CONTEXT_TYPE_STRING = "Context";
static const char* const MAP_TYPE_STRING = "Map";
static const char* const OBJECT_TYPE_STRING = "Object";
static const char* const JSOBJECT_TYPE_STRING = "JSObject";
static const char* const SMI_TYPE_STRING = "Smi";
static const char* const TAGGED_TYPE_STRING = "Tagged";
static const char* const RAWPTR_TYPE_STRING = "RawPtr";
......
......@@ -46,11 +46,12 @@ def preprocess(input):
return input
def postprocess(output):
output = re.sub(r'% RawObjectCast', r'%RawObjectCast', output)
output = re.sub(r'% RawPointerCast', r'%RawPointerCast', output)
output = re.sub(r'% RawConstexprCast', r'%RawConstexprCast', output)
output = re.sub(r'% FromConstexpr', r'%FromConstexpr', output)
output = re.sub(r'% Allocate', r'%Allocate', output)
output = re.sub(r'%\s*RawObjectCast', r'%RawObjectCast', output)
output = re.sub(r'%\s*RawPointerCast', r'%RawPointerCast', output)
output = re.sub(r'%\s*RawConstexprCast', r'%RawConstexprCast', output)
output = re.sub(r'%\s*FromConstexpr', r'%FromConstexpr', output)
output = re.sub(r'%\s*Allocate', r'%Allocate', output)
output = re.sub(r'%\s*GetAllocationBaseSize', r'%GetAllocationBaseSize', output)
output = re.sub(r'\/\*COxp\*\/', r'constexpr', output)
output = re.sub(r'(\S+)\s*: type([,>])', r'\1: type\2', output)
output = re.sub(r'(\n\s*)labels( [A-Z])', r'\1 labels\2', output)
......
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