[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:
Alexander Markov
2024-02-13 15:33:38 +00:00
committed by Commit Queue
parent 6d57fe5a1c
commit a261196ea7
28 changed files with 168 additions and 333 deletions
+5 -40
View File
@@ -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.