[vm] Remove context allocations for tear-offs
Previously, when taking a tear-off, a separate Context object was allocated in order to capture receiver. Now, receiver is stored directly in the Closure object in the 'context' field. This saves 1 object allocation per tear-off and makes tear-offs cheaper compared to explicit closures which can share context with other closures. Benchmarks in AOT mode: x64: TearOff.NotInlined +40% TearOff.NotInlined.InTry +43% TearOff.Inlined.InTry +47% arm64: TearOff.NotInlined +27-43% TearOff.NotInlined.InTry +29-43% TearOff.Inlined.InTry +58-94% arm64c: TearOff.NotInlined +71% TearOff.NotInlined.InTry +72% TearOff.Inlined.InTry +96% TEST=ci Issue: https://github.com/dart-lang/sdk/issues/54808 Change-Id: I3ad95e8a8a4fc23f856bbc0fe238da58a9d25b8d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/350945 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
6d57fe5a1c
commit
a261196ea7
@@ -224,49 +224,14 @@ void StubCodeCompiler::GenerateBuildMethodExtractorStub(
|
||||
__ ldr(R0, Address(FP, kReceiverOffset * target::kWordSize), NE);
|
||||
__ ldr(R3, Address(R0, R4), NE);
|
||||
|
||||
// Push type arguments & extracted method.
|
||||
// Push type arguments.
|
||||
__ Push(R3);
|
||||
__ Push(R1);
|
||||
|
||||
// Allocate context.
|
||||
{
|
||||
Label done, slow_path;
|
||||
if (!FLAG_use_slow_path && FLAG_inline_alloc) {
|
||||
__ TryAllocateArray(kContextCid, target::Context::InstanceSize(1),
|
||||
&slow_path,
|
||||
R0, // instance
|
||||
R1, // end address
|
||||
R2, R3);
|
||||
__ ldr(R1, Address(THR, target::Thread::object_null_offset()));
|
||||
__ str(R1, FieldAddress(R0, target::Context::parent_offset()));
|
||||
__ LoadImmediate(R1, 1);
|
||||
__ str(R1, FieldAddress(R0, target::Context::num_variables_offset()));
|
||||
__ b(&done);
|
||||
}
|
||||
|
||||
__ Bind(&slow_path);
|
||||
|
||||
__ LoadImmediate(/*num_vars=*/R1, 1);
|
||||
__ LoadObject(CODE_REG, context_allocation_stub);
|
||||
__ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
|
||||
__ blx(R0);
|
||||
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
// Put context in right register for AllocateClosure call.
|
||||
__ MoveRegister(AllocateClosureABI::kContextReg, R0);
|
||||
|
||||
// Store receiver in context
|
||||
__ ldr(AllocateClosureABI::kScratchReg,
|
||||
// Put function and context (receiver) in right registers for
|
||||
// AllocateClosure stub.
|
||||
__ MoveRegister(AllocateClosureABI::kFunctionReg, R1);
|
||||
__ ldr(AllocateClosureABI::kContextReg,
|
||||
Address(FP, target::kWordSize * kReceiverOffset));
|
||||
__ StoreIntoObject(AllocateClosureABI::kContextReg,
|
||||
FieldAddress(AllocateClosureABI::kContextReg,
|
||||
target::Context::variable_offset(0)),
|
||||
AllocateClosureABI::kScratchReg);
|
||||
|
||||
// Pop function.
|
||||
__ Pop(AllocateClosureABI::kFunctionReg);
|
||||
|
||||
// Allocate closure. After this point, we only use the registers in
|
||||
// AllocateClosureABI.
|
||||
|
||||
Reference in New Issue
Block a user