Skip to content

Commit

Permalink
[vm/ffi] Outlining state transitions in AOT
Browse files Browse the repository at this point in the history
For single argument FFI calls:
- reduces trampoline size significantly (up to 50%, 150-170 bytes),
- reduces the compressed size of GPay by 2.5kb on arm64,
- regresses performance on arm64 (up to 2.5%).
For more arguments, percentage-wise size gains and speed regressions
are smaller.

Only applied on arm and arm64, we care about code size for these.

Note: On Raspberry Pie (arm), the performance regression on single-
argument calls regresses up to 30%.

TEST=tests/ffi/*

Design doc: https://go/dart-ffi-outline-state-transitions
Closes: #50094
Change-Id: I8b8d7da45f69be6ac1432b11b695de71e56acfd1
Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-android-debug-arm-try,vm-kernel-nnbd-mac-debug-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262343
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
  • Loading branch information
dcharkes authored and Commit Queue committed Oct 25, 2022
1 parent c13676f commit 762c507
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 58 deletions.
38 changes: 12 additions & 26 deletions runtime/vm/compiler/backend/il_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1501,32 +1501,18 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
env());

// Update information in the thread object and enter a safepoint.
if (CanExecuteGeneratedCodeInSafepoint()) {
__ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi());
__ TransitionGeneratedToNative(branch, FPREG, temp1, saved_fp_or_sp,
/*enter_safepoint=*/true);

__ blx(branch);

// Update information in the thread object and leave the safepoint.
__ TransitionNativeToGenerated(saved_fp_or_sp, temp1,
/*leave_safepoint=*/true);
} else {
// We cannot trust that this code will be executable within a safepoint.
// Therefore we delegate the responsibility of entering/exiting the
// safepoint to a stub which in the VM isolate's heap, which will never
// lose execute permission.
__ ldr(temp1,
compiler::Address(
THR, compiler::target::Thread::
call_native_through_safepoint_entry_point_offset()));

// Calls R8 in a safepoint and clobbers R4 and NOTFP.
ASSERT(branch == R8);
static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0,
"NOTFP should be a reserved register");
__ blx(temp1);
}
// Outline state transition. In AOT, for code size. In JIT, because we
// cannot trust that code will be executable.
__ ldr(temp1,
compiler::Address(
THR, compiler::target::Thread::
call_native_through_safepoint_entry_point_offset()));

// Calls R8 in a safepoint and clobbers R4 and NOTFP.
ASSERT(branch == R8);
static_assert((kReservedCpuRegisters & (1 << NOTFP)) != 0,
"NOTFP should be a reserved register");
__ blx(temp1);

if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
Expand Down
42 changes: 10 additions & 32 deletions runtime/vm/compiler/backend/il_arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1371,39 +1371,17 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {

__ StoreToOffset(temp1, FPREG, kSavedCallerPcSlotFromFp * kWordSize);

if (CanExecuteGeneratedCodeInSafepoint()) {
// Update information in the thread object and enter a safepoint.
__ LoadImmediate(temp1, compiler::target::Thread::exit_through_ffi());
__ TransitionGeneratedToNative(branch, FPREG, temp1,
/*enter_safepoint=*/true);

// We are entering runtime code, so the C stack pointer must be restored
// from the stack limit to the top of the stack.
__ mov(temp_csp, CSP);
__ mov(CSP, SP);

__ blr(branch);

// Restore the Dart stack pointer.
__ mov(SP, CSP);
__ mov(CSP, temp_csp);

// Update information in the thread object and leave the safepoint.
__ TransitionNativeToGenerated(temp1, /*leave_safepoint=*/true);
} else {
// We cannot trust that this code will be executable within a safepoint.
// Therefore we delegate the responsibility of entering/exiting the
// safepoint to a stub which in the VM isolate's heap, which will never
// lose execute permission.
__ ldr(temp1,
compiler::Address(
THR, compiler::target::Thread::
call_native_through_safepoint_entry_point_offset()));
// Update information in the thread object and enter a safepoint.
// Outline state transition. In AOT, for code size. In JIT, because we
// cannot trust that code will be executable.
__ ldr(temp1,
compiler::Address(
THR, compiler::target::Thread::
call_native_through_safepoint_entry_point_offset()));

// Calls R9 and clobbers R19 (along with volatile registers).
ASSERT(branch == R9);
__ blr(temp1);
}
// Calls R9 and clobbers R19 (along with volatile registers).
ASSERT(branch == R9);
__ blr(temp1);

if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
Expand Down

0 comments on commit 762c507

Please sign in to comment.