mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 13:08:01 +00:00
[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:
parent
c13676f2b7
commit
762c507553
|
@ -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.");
|
||||
|
|
|
@ -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.");
|
||||
|
|
Loading…
Reference in a new issue