mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
[vm] Specialize allocation for small records
This change introduces specialized stubs and IL instruction for allocating records with 2 or 3 fields. This makes allocation of small records slightly faster compared to a construction of similar class instances and makes code size of record allocation smaller. Benchmark: MultipleReturns.NotInlined.Record(RunTime) 77150 -> 66222 MultipleReturns.NotInlined.RecordNamed(RunTime) 78073 -> 67044 MultipleReturns.Forwarded.Record(RunTime) 97130 -> 77635 MultipleReturns.Forwarded.RecordNamed(RunTime) 96495 -> 77904 TEST=ci Issue: https://github.com/dart-lang/sdk/issues/49719 Change-Id: I8ed7add06b39ba79dfd78bbe2afaefe606cc505b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/266420 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
parent
e417e3c3f7
commit
b9dfd1a651
21 changed files with 3528 additions and 3144 deletions
|
@ -942,6 +942,11 @@ void ConstantPropagator::VisitAllocateRecord(AllocateRecordInstr* instr) {
|
|||
SetValue(instr, non_constant_);
|
||||
}
|
||||
|
||||
void ConstantPropagator::VisitAllocateSmallRecord(
|
||||
AllocateSmallRecordInstr* instr) {
|
||||
SetValue(instr, non_constant_);
|
||||
}
|
||||
|
||||
void ConstantPropagator::VisitLoadUntagged(LoadUntaggedInstr* instr) {
|
||||
SetValue(instr, non_constant_);
|
||||
}
|
||||
|
|
|
@ -7679,6 +7679,68 @@ void AllocateRecordInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
locs(), deopt_id(), env());
|
||||
}
|
||||
|
||||
LocationSummary* AllocateSmallRecordInstr::MakeLocationSummary(Zone* zone,
|
||||
bool opt) const {
|
||||
ASSERT(num_fields() == 2 || num_fields() == 3);
|
||||
const intptr_t kNumInputs = InputCount();
|
||||
const intptr_t kNumTemps = 0;
|
||||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
|
||||
if (has_named_fields()) {
|
||||
locs->set_in(
|
||||
0, Location::RegisterLocation(AllocateSmallRecordABI::kFieldNamesReg));
|
||||
locs->set_in(
|
||||
1, Location::RegisterLocation(AllocateSmallRecordABI::kValue0Reg));
|
||||
locs->set_in(
|
||||
2, Location::RegisterLocation(AllocateSmallRecordABI::kValue1Reg));
|
||||
if (num_fields() > 2) {
|
||||
locs->set_in(
|
||||
3, Location::RegisterLocation(AllocateSmallRecordABI::kValue2Reg));
|
||||
}
|
||||
} else {
|
||||
locs->set_in(
|
||||
0, Location::RegisterLocation(AllocateSmallRecordABI::kValue0Reg));
|
||||
locs->set_in(
|
||||
1, Location::RegisterLocation(AllocateSmallRecordABI::kValue1Reg));
|
||||
if (num_fields() > 2) {
|
||||
locs->set_in(
|
||||
2, Location::RegisterLocation(AllocateSmallRecordABI::kValue2Reg));
|
||||
}
|
||||
}
|
||||
locs->set_out(0, Location::RegisterLocation(AllocateRecordABI::kResultReg));
|
||||
return locs;
|
||||
}
|
||||
|
||||
void AllocateSmallRecordInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
auto object_store = compiler->isolate_group()->object_store();
|
||||
Code& stub = Code::ZoneHandle(compiler->zone());
|
||||
if (has_named_fields()) {
|
||||
switch (num_fields()) {
|
||||
case 2:
|
||||
stub = object_store->allocate_record2_named_stub();
|
||||
break;
|
||||
case 3:
|
||||
stub = object_store->allocate_record3_named_stub();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
switch (num_fields()) {
|
||||
case 2:
|
||||
stub = object_store->allocate_record2_stub();
|
||||
break;
|
||||
case 3:
|
||||
stub = object_store->allocate_record3_stub();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
|
||||
locs(), deopt_id(), env());
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -458,6 +458,7 @@ struct InstrAttrs {
|
|||
M(AllocateObject, _) \
|
||||
M(AllocateClosure, _) \
|
||||
M(AllocateRecord, _) \
|
||||
M(AllocateSmallRecord, _) \
|
||||
M(AllocateTypedData, _) \
|
||||
M(LoadField, _) \
|
||||
M(LoadUntagged, kNoGC) \
|
||||
|
@ -7026,6 +7027,82 @@ class AllocateRecordInstr : public TemplateAllocation<1> {
|
|||
DISALLOW_COPY_AND_ASSIGN(AllocateRecordInstr);
|
||||
};
|
||||
|
||||
// Allocates and initializes fields of a small record object
|
||||
// (with 2 or 3 fields).
|
||||
class AllocateSmallRecordInstr : public TemplateAllocation<4> {
|
||||
public:
|
||||
AllocateSmallRecordInstr(const InstructionSource& source,
|
||||
intptr_t num_fields, // 2 or 3.
|
||||
Value* field_names, // Optional.
|
||||
Value* value0,
|
||||
Value* value1,
|
||||
Value* value2, // Optional.
|
||||
intptr_t deopt_id)
|
||||
: TemplateAllocation(source, deopt_id),
|
||||
num_fields_(num_fields),
|
||||
has_named_fields_(field_names != nullptr) {
|
||||
ASSERT(num_fields == 2 || num_fields == 3);
|
||||
ASSERT((num_fields > 2) == (value2 != nullptr));
|
||||
if (has_named_fields_) {
|
||||
SetInputAt(0, field_names);
|
||||
SetInputAt(1, value0);
|
||||
SetInputAt(2, value1);
|
||||
if (num_fields > 2) {
|
||||
SetInputAt(3, value2);
|
||||
}
|
||||
} else {
|
||||
SetInputAt(0, value0);
|
||||
SetInputAt(1, value1);
|
||||
if (num_fields > 2) {
|
||||
SetInputAt(2, value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION(AllocateSmallRecord)
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
intptr_t num_fields() const { return num_fields_; }
|
||||
bool has_named_fields() const { return has_named_fields_; }
|
||||
|
||||
virtual intptr_t InputCount() const {
|
||||
return (has_named_fields_ ? 1 : 0) + num_fields_;
|
||||
}
|
||||
|
||||
virtual const Slot* SlotForInput(intptr_t pos) {
|
||||
if (has_named_fields_) {
|
||||
if (pos == 0) {
|
||||
return &Slot::Record_field_names();
|
||||
} else {
|
||||
return &Slot::GetRecordFieldSlot(
|
||||
Thread::Current(), compiler::target::Record::field_offset(pos - 1));
|
||||
}
|
||||
} else {
|
||||
return &Slot::GetRecordFieldSlot(
|
||||
Thread::Current(), compiler::target::Record::field_offset(pos));
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HasUnknownSideEffects() const { return false; }
|
||||
|
||||
virtual bool WillAllocateNewOrRemembered() const {
|
||||
return Heap::IsAllocatableInNewSpace(
|
||||
compiler::target::Record::InstanceSize(num_fields_));
|
||||
}
|
||||
|
||||
#define FIELD_LIST(F) \
|
||||
F(const intptr_t, num_fields_) \
|
||||
F(const bool, has_named_fields_)
|
||||
|
||||
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(AllocateSmallRecordInstr,
|
||||
TemplateAllocation,
|
||||
FIELD_LIST)
|
||||
#undef FIELD_LIST
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AllocateSmallRecordInstr);
|
||||
};
|
||||
|
||||
// This instruction captures the state of the object which had its allocation
|
||||
// removed during the AllocationSinking pass.
|
||||
// It does not produce any real code only deoptimization information.
|
||||
|
|
|
@ -3859,6 +3859,10 @@ void AllocationSinking::CreateMaterializationAt(
|
|||
cls = &Class::ZoneHandle(
|
||||
flow_graph_->isolate_group()->class_table()->At(kRecordCid));
|
||||
num_elements = instr->num_fields();
|
||||
} else if (auto instr = alloc->AsAllocateSmallRecord()) {
|
||||
cls = &Class::ZoneHandle(
|
||||
flow_graph_->isolate_group()->class_table()->At(kRecordCid));
|
||||
num_elements = instr->num_fields();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -1653,6 +1653,10 @@ CompileType AllocateRecordInstr::ComputeType() const {
|
|||
return CompileType::FromCid(kRecordCid);
|
||||
}
|
||||
|
||||
CompileType AllocateSmallRecordInstr::ComputeType() const {
|
||||
return CompileType::FromCid(kRecordCid);
|
||||
}
|
||||
|
||||
CompileType LoadUntaggedInstr::ComputeType() const {
|
||||
return CompileType::Dynamic();
|
||||
}
|
||||
|
|
|
@ -948,6 +948,21 @@ Fragment BaseFlowGraphBuilder::AllocateRecord(TokenPosition position,
|
|||
return Fragment(allocate);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::AllocateSmallRecord(TokenPosition position,
|
||||
intptr_t num_fields,
|
||||
bool has_named_fields) {
|
||||
ASSERT(num_fields == 2 || num_fields == 3);
|
||||
Value* value2 = (num_fields > 2) ? Pop() : nullptr;
|
||||
Value* value1 = Pop();
|
||||
Value* value0 = Pop();
|
||||
Value* field_names = has_named_fields ? Pop() : nullptr;
|
||||
AllocateSmallRecordInstr* allocate = new (Z) AllocateSmallRecordInstr(
|
||||
InstructionSource(position), num_fields, field_names, value0, value1,
|
||||
value2, GetNextDeoptId());
|
||||
Push(allocate);
|
||||
return Fragment(allocate);
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::AllocateTypedData(TokenPosition position,
|
||||
classid_t class_id) {
|
||||
Value* num_elements = Pop();
|
||||
|
|
|
@ -354,6 +354,9 @@ class BaseFlowGraphBuilder {
|
|||
Fragment AllocateClosure(TokenPosition position = TokenPosition::kNoSource);
|
||||
Fragment CreateArray();
|
||||
Fragment AllocateRecord(TokenPosition position, intptr_t num_fields);
|
||||
Fragment AllocateSmallRecord(TokenPosition position,
|
||||
intptr_t num_fields,
|
||||
bool has_named_fields);
|
||||
Fragment AllocateTypedData(TokenPosition position, classid_t class_id);
|
||||
Fragment InstantiateType(const AbstractType& type);
|
||||
Fragment InstantiateTypeArguments(const TypeArguments& type_arguments);
|
||||
|
|
|
@ -4061,11 +4061,31 @@ Fragment StreamingFlowGraphBuilder::BuildRecordLiteral(TokenPosition* p) {
|
|||
}
|
||||
}
|
||||
const intptr_t num_fields = positional_count + named_count;
|
||||
|
||||
// TODO(dartbug.com/49719): provide specialized allocation stubs for small
|
||||
// records.
|
||||
|
||||
Fragment instructions;
|
||||
|
||||
if (num_fields == 2 ||
|
||||
(num_fields == 3 && AllocateSmallRecordABI::kValue2Reg != kNoRegister)) {
|
||||
// Generate specialized allocation for a small number of fields.
|
||||
const bool has_named_fields = named_count > 0;
|
||||
if (has_named_fields) {
|
||||
instructions += Constant(*field_names);
|
||||
}
|
||||
for (intptr_t i = 0; i < positional_count; ++i) {
|
||||
instructions += BuildExpression(); // read ith expression.
|
||||
}
|
||||
ReadListLength(); // read list length.
|
||||
for (intptr_t i = 0; i < named_count; ++i) {
|
||||
SkipStringReference(); // read ith name.
|
||||
instructions += BuildExpression(); // read ith expression.
|
||||
}
|
||||
SkipDartType(); // read recordType.
|
||||
|
||||
instructions +=
|
||||
B->AllocateSmallRecord(position, num_fields, has_named_fields);
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
instructions += Constant(*field_names);
|
||||
instructions += B->AllocateRecord(position, num_fields);
|
||||
LocalVariable* record = MakeTemporary();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1156,6 +1156,104 @@ void StubCodeCompiler::GenerateAllocateRecordStub(Assembler* assembler) {
|
|||
__ Ret();
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateSmallRecordStub(Assembler* assembler,
|
||||
intptr_t num_fields,
|
||||
bool has_named_fields) {
|
||||
ASSERT(num_fields == 2 || num_fields == 3);
|
||||
const Register result_reg = AllocateSmallRecordABI::kResultReg;
|
||||
const Register field_names_reg = AllocateSmallRecordABI::kFieldNamesReg;
|
||||
const Register value0_reg = AllocateSmallRecordABI::kValue0Reg;
|
||||
const Register value1_reg = AllocateSmallRecordABI::kValue1Reg;
|
||||
const Register value2_reg = AllocateSmallRecordABI::kValue2Reg;
|
||||
const Register temp_reg = AllocateSmallRecordABI::kTempReg;
|
||||
Label slow_case;
|
||||
|
||||
if ((num_fields > 2) && (value2_reg == kNoRegister)) {
|
||||
// Not implemented.
|
||||
__ Breakpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
// Need to account for the debug checks added by
|
||||
// StoreCompressedIntoObjectNoBarrier.
|
||||
const auto distance = Assembler::kFarJump;
|
||||
#else
|
||||
const auto distance = Assembler::kNearJump;
|
||||
#endif
|
||||
__ TryAllocateObject(kRecordCid, target::Record::InstanceSize(num_fields),
|
||||
&slow_case, distance, result_reg, temp_reg);
|
||||
|
||||
__ LoadImmediate(temp_reg, num_fields);
|
||||
__ StoreToOffset(
|
||||
temp_reg, FieldAddress(result_reg, target::Record::num_fields_offset()),
|
||||
kFourBytes);
|
||||
|
||||
if (!has_named_fields) {
|
||||
__ LoadObject(field_names_reg, Object::empty_array());
|
||||
}
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg,
|
||||
FieldAddress(result_reg, target::Record::field_names_offset()),
|
||||
field_names_reg);
|
||||
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg, FieldAddress(result_reg, target::Record::field_offset(0)),
|
||||
value0_reg);
|
||||
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg, FieldAddress(result_reg, target::Record::field_offset(1)),
|
||||
value1_reg);
|
||||
|
||||
if (num_fields > 2) {
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg, FieldAddress(result_reg, target::Record::field_offset(2)),
|
||||
value2_reg);
|
||||
}
|
||||
|
||||
__ Ret();
|
||||
|
||||
__ Bind(&slow_case);
|
||||
|
||||
__ EnterStubFrame();
|
||||
__ PushObject(NullObject()); // Space on the stack for the return value.
|
||||
__ PushObject(Smi::ZoneHandle(Smi::New(num_fields)));
|
||||
if (has_named_fields) {
|
||||
__ PushRegister(field_names_reg);
|
||||
} else {
|
||||
__ PushObject(Object::empty_array());
|
||||
}
|
||||
__ PushRegistersInOrder({value0_reg, value1_reg});
|
||||
if (num_fields > 2) {
|
||||
__ PushRegister(value2_reg);
|
||||
} else {
|
||||
__ PushObject(NullObject());
|
||||
}
|
||||
__ CallRuntime(kAllocateSmallRecordRuntimeEntry, 5);
|
||||
__ Drop(5);
|
||||
__ PopRegister(result_reg);
|
||||
|
||||
EnsureIsNewOrRemembered(assembler, /*preserve_registers=*/false);
|
||||
__ LeaveStubFrame();
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateRecord2Stub(Assembler* assembler) {
|
||||
GenerateAllocateSmallRecordStub(assembler, 2, /*has_named_fields=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateRecord2NamedStub(Assembler* assembler) {
|
||||
GenerateAllocateSmallRecordStub(assembler, 2, /*has_named_fields=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateRecord3Stub(Assembler* assembler) {
|
||||
GenerateAllocateSmallRecordStub(assembler, 3, /*has_named_fields=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateRecord3NamedStub(Assembler* assembler) {
|
||||
GenerateAllocateSmallRecordStub(assembler, 3, /*has_named_fields=*/true);
|
||||
}
|
||||
|
||||
// The UnhandledException class lives in the VM isolate, so it cannot cache
|
||||
// an allocation stub for itself. Instead, we cache it in the stub code list.
|
||||
void StubCodeCompiler::GenerateAllocateUnhandledExceptionStub(
|
||||
|
|
|
@ -175,6 +175,10 @@ class StubCodeCompiler : public AllStatic {
|
|||
static void GenerateAllocateTypedDataArrayStub(Assembler* assembler,
|
||||
intptr_t cid);
|
||||
|
||||
static void GenerateAllocateSmallRecordStub(Assembler* assembler,
|
||||
intptr_t num_fields,
|
||||
bool has_named_fields);
|
||||
|
||||
static void GenerateSharedStubGeneric(
|
||||
Assembler* assembler,
|
||||
bool save_fpu_registers,
|
||||
|
|
|
@ -523,6 +523,17 @@ struct AllocateRecordABI {
|
|||
static const Register kTemp2Reg = R4;
|
||||
};
|
||||
|
||||
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
|
||||
// AllocateRecord3, AllocateRecord3Named).
|
||||
struct AllocateSmallRecordABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static const Register kFieldNamesReg = R1;
|
||||
static const Register kValue0Reg = R2;
|
||||
static const Register kValue1Reg = R3;
|
||||
static const Register kValue2Reg = R4;
|
||||
static const Register kTempReg = R9;
|
||||
};
|
||||
|
||||
// ABI for AllocateTypedDataArrayStub.
|
||||
struct AllocateTypedDataArrayABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
|
|
|
@ -357,6 +357,17 @@ struct AllocateRecordABI {
|
|||
static const Register kTemp2Reg = R4;
|
||||
};
|
||||
|
||||
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
|
||||
// AllocateRecord3, AllocateRecord3Named).
|
||||
struct AllocateSmallRecordABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static const Register kFieldNamesReg = R1;
|
||||
static const Register kValue0Reg = R2;
|
||||
static const Register kValue1Reg = R3;
|
||||
static const Register kValue2Reg = R4;
|
||||
static const Register kTempReg = R5;
|
||||
};
|
||||
|
||||
// ABI for AllocateTypedDataArrayStub.
|
||||
struct AllocateTypedDataArrayABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
|
|
|
@ -253,6 +253,17 @@ struct AllocateRecordABI {
|
|||
static const Register kTemp2Reg = EDI;
|
||||
};
|
||||
|
||||
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
|
||||
// AllocateRecord3, AllocateRecord3Named).
|
||||
struct AllocateSmallRecordABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static const Register kFieldNamesReg = EBX;
|
||||
static const Register kValue0Reg = ECX;
|
||||
static const Register kValue1Reg = EDX;
|
||||
static const Register kValue2Reg = kNoRegister;
|
||||
static const Register kTempReg = EDI;
|
||||
};
|
||||
|
||||
// ABI for AllocateTypedDataArrayStub.
|
||||
struct AllocateTypedDataArrayABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
|
|
|
@ -366,6 +366,17 @@ struct AllocateRecordABI {
|
|||
static const Register kTemp2Reg = T4;
|
||||
};
|
||||
|
||||
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
|
||||
// AllocateRecord3, AllocateRecord3Named).
|
||||
struct AllocateSmallRecordABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static const Register kFieldNamesReg = T2;
|
||||
static const Register kValue0Reg = T3;
|
||||
static const Register kValue1Reg = T4;
|
||||
static const Register kValue2Reg = A1;
|
||||
static const Register kTempReg = T1;
|
||||
};
|
||||
|
||||
// ABI for AllocateTypedDataArrayStub.
|
||||
struct AllocateTypedDataArrayABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
|
|
|
@ -328,6 +328,17 @@ struct AllocateRecordABI {
|
|||
static const Register kTemp2Reg = RCX;
|
||||
};
|
||||
|
||||
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
|
||||
// AllocateRecord3, AllocateRecord3Named).
|
||||
struct AllocateSmallRecordABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static const Register kFieldNamesReg = R10;
|
||||
static const Register kValue0Reg = RBX;
|
||||
static const Register kValue1Reg = RDX;
|
||||
static const Register kValue2Reg = RCX;
|
||||
static const Register kTempReg = RDI;
|
||||
};
|
||||
|
||||
// ABI for AllocateTypedDataArrayStub.
|
||||
struct AllocateTypedDataArrayABI {
|
||||
static const Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
|
|
|
@ -242,6 +242,10 @@ class ObjectPointerVisitor;
|
|||
RW(Code, allocate_object_stub) \
|
||||
RW(Code, allocate_object_parametrized_stub) \
|
||||
RW(Code, allocate_record_stub) \
|
||||
RW(Code, allocate_record2_stub) \
|
||||
RW(Code, allocate_record2_named_stub) \
|
||||
RW(Code, allocate_record3_stub) \
|
||||
RW(Code, allocate_record3_named_stub) \
|
||||
RW(Code, allocate_unhandled_exception_stub) \
|
||||
RW(Code, clone_context_stub) \
|
||||
RW(Code, write_barrier_wrappers_stub) \
|
||||
|
@ -334,6 +338,10 @@ class ObjectPointerVisitor;
|
|||
DO(allocate_object_stub, AllocateObject) \
|
||||
DO(allocate_object_parametrized_stub, AllocateObjectParameterized) \
|
||||
DO(allocate_record_stub, AllocateRecord) \
|
||||
DO(allocate_record2_stub, AllocateRecord2) \
|
||||
DO(allocate_record2_named_stub, AllocateRecord2Named) \
|
||||
DO(allocate_record3_stub, AllocateRecord3) \
|
||||
DO(allocate_record3_named_stub, AllocateRecord3Named) \
|
||||
DO(allocate_unhandled_exception_stub, AllocateUnhandledException) \
|
||||
DO(clone_context_stub, CloneContext) \
|
||||
DO(call_closure_no_such_method_stub, CallClosureNoSuchMethod) \
|
||||
|
|
|
@ -733,6 +733,29 @@ DEFINE_RUNTIME_ENTRY(AllocateRecord, 2) {
|
|||
arguments.SetReturn(record);
|
||||
}
|
||||
|
||||
// Allocate a new small record instance and initialize its fields.
|
||||
// Arg0: number of fields.
|
||||
// Arg1: field names.
|
||||
// Arg2-Arg4: field values.
|
||||
// Return value: newly allocated record.
|
||||
DEFINE_RUNTIME_ENTRY(AllocateSmallRecord, 5) {
|
||||
const Smi& num_fields = Smi::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const auto& field_names = Array::CheckedHandle(zone, arguments.ArgAt(1));
|
||||
const auto& value0 = Instance::CheckedHandle(zone, arguments.ArgAt(2));
|
||||
const auto& value1 = Instance::CheckedHandle(zone, arguments.ArgAt(3));
|
||||
const auto& value2 = Instance::CheckedHandle(zone, arguments.ArgAt(4));
|
||||
const Record& record =
|
||||
Record::Handle(zone, Record::New(num_fields.Value(), field_names,
|
||||
SpaceForRuntimeAllocation()));
|
||||
ASSERT(num_fields.Value() == 2 || num_fields.Value() == 3);
|
||||
record.SetFieldAt(0, value0);
|
||||
record.SetFieldAt(1, value1);
|
||||
if (num_fields.Value() > 2) {
|
||||
record.SetFieldAt(2, value2);
|
||||
}
|
||||
arguments.SetReturn(record);
|
||||
}
|
||||
|
||||
// Allocate a SuspendState object.
|
||||
// Arg0: frame size.
|
||||
// Arg1: existing SuspendState object or function data.
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace dart {
|
|||
V(AllocateContext) \
|
||||
V(AllocateObject) \
|
||||
V(AllocateRecord) \
|
||||
V(AllocateSmallRecord) \
|
||||
V(AllocateSuspendState) \
|
||||
V(BoxDouble) \
|
||||
V(BoxFloat32x4) \
|
||||
|
|
|
@ -57,6 +57,10 @@ namespace dart {
|
|||
V(AllocateObjectParameterized) \
|
||||
V(AllocateObjectSlow) \
|
||||
V(AllocateRecord) \
|
||||
V(AllocateRecord2) \
|
||||
V(AllocateRecord2Named) \
|
||||
V(AllocateRecord3) \
|
||||
V(AllocateRecord3Named) \
|
||||
V(AllocateUnhandledException) \
|
||||
V(BoxDouble) \
|
||||
V(BoxFloat32x4) \
|
||||
|
|
|
@ -171,6 +171,7 @@ class Thread;
|
|||
V(ObjectPtr, object_null_, Object::null(), nullptr) \
|
||||
V(BoolPtr, bool_true_, Object::bool_true().ptr(), nullptr) \
|
||||
V(BoolPtr, bool_false_, Object::bool_false().ptr(), nullptr) \
|
||||
V(ArrayPtr, empty_array_, Object::empty_array().ptr(), nullptr) \
|
||||
V(TypePtr, dynamic_type_, Type::dynamic_type().ptr(), nullptr)
|
||||
|
||||
// List of VM-global objects/addresses cached in each Thread object.
|
||||
|
|
Loading…
Reference in a new issue