[vm/ffi] Outlining state transitions in AOT

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: https://github.com/dart-lang/sdk/issues/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>
This commit is contained in:
Daco Harkes 2022-10-25 13:07:34 +00:00 committed by Commit Queue
parent c13676f2b7
commit 762c507553
2 changed files with 21 additions and 57 deletions

View file

@ -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);
// 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()));
__ 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);
}
// 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.");

View file

@ -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);
// 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()));
// 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()));
// 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.");