mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Daco Harkes](/assets/img/avatar_default.png)
This CL adds passing structs by value in FFI trampolines. Nested structs and inline arrays are future work. C defines passing empty structs as undefined behavior, so that is not supported in this CL. Suggested review order: 1) commit message 2) ffi/marshaller (decisions for what is done in IL and what in MC) 3) frontend/kernel_to_il (IL construction) 4) backend/il (MC generation from IL) 5) rest in VM Overall architecture is that structs are split up into word-size chunks in IL when this is possible: 1 definition in IL per chunk, 1 Location in IL per chunk, and 1 NativeLocation for the backend per chunk. In some cases it is not possible or less convenient to split into chunks. In these cases TypedDataBase objects are stored into and loaded from directly in machine code. The various cases: - FFI call arguments which are not passed as pointers: pass individual chunks to FFI call which already have the right location. - FFI call arguments which are passed as pointers: Pass in TypedDataBase to FFI call, allocate space on the stack, and make a copy on the stack and pass the copies' address to the callee. - FFI call return value: pass in TypedData to FFI call, and copy result in machine code. - FFI callback arguments which are not passed as pointers: IL definition for each chunk, and populate a new TypedData with those chunks. - FFI callback arguments which are passed as pointer: IL definition for the pointer, and copying of contents in IL. - FFI return value when location is pointer: Copy data to callee result location in IL. - FFI return value when location is not a pointer: Copy data in machine code to the right registers. Some other notes about the implementation: - Due to Store/LoadIndexed loading doubles from float arrays, we use a int32 instead and use the BitCastInstr. - Linux ia32 uses `ret 4` when returning structs by value. This requires special casing in the FFI callback trampolines to either use `ret` or `ret 4` when returning. - The 1 IL definition, 1 Location, and 1 NativeLocation approach does not remove the need for special casing PairLocations in the machine code generation because they are 1 Location belonging to 1 definition. Because of the amount of corner cases in the calling conventions that need to be covered, the tests are generated, rather than hand-written. ABIs tested on CQ: x64 (Linux, MacOS, Windows), ia32 (Linux, Windows), arm (Android softFP, Linux hardFP), arm64 Android. ABIs tested locally through Flutter: ia32 Android (emulator), x64 iOS (simulator), arm64 iOS. ABIs not tested: arm iOS. TEST=runtime/bin/ffi_test/ffi_test_functions_generated.cc TEST=runtime/bin/ffi_test/ffi_test_functions.cc TEST=tests/{ffi,ffi_2}/function_structs_by_value_generated_test.dart TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_generated_tes TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_test.dart TEST=tests/{ffi,ffi_2}/vmspecific_static_checks_test.dart Closes https://github.com/dart-lang/sdk/issues/36730. Change-Id: I474d3a4ee1faadbe767ddadd1b696e24d8dc364c Cq-Include-Trybots: luci.dart.try:dart-sdk-linux-try,dart-sdk-mac-try,dart-sdk-win-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-mac-release-x64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,analyzer-analysis-server-linux-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/140290 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
67 lines
2.6 KiB
C++
67 lines
2.6 KiB
C++
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_STACK_FRAME_IA32_H_
|
|
#define RUNTIME_VM_STACK_FRAME_IA32_H_
|
|
|
|
#if !defined(RUNTIME_VM_STACK_FRAME_H_)
|
|
#error Do not include stack_frame_ia32.h directly; use stack_frame.h instead.
|
|
#endif
|
|
|
|
namespace dart {
|
|
|
|
/* IA32 Dart Frame Layout
|
|
|
|
| | <- TOS
|
|
Callee frame | ... |
|
|
| saved EBP | (EBP of current frame)
|
|
| saved PC | (PC of current frame)
|
|
+--------------------+
|
|
Current frame | ... T| <- ESP of current frame
|
|
| first local T|
|
|
| code object T| (current frame's code object)
|
|
| caller's EBP | <- EBP of current frame
|
|
| caller's ret addr | (PC of caller frame)
|
|
+--------------------+
|
|
Caller frame | last parameter | <- ESP of caller frame
|
|
| ... |
|
|
|
|
T against a slot indicates it needs to be traversed during GC.
|
|
*/
|
|
|
|
static const int kDartFrameFixedSize = 3; // PC marker, EBP, PC.
|
|
static const int kSavedPcSlotFromSp = -1;
|
|
|
|
static const int kFirstObjectSlotFromFp = -1; // Used by GC to traverse stack.
|
|
static const int kLastFixedObjectSlotFromFp = -1;
|
|
|
|
static const int kFirstLocalSlotFromFp = -2;
|
|
static const int kPcMarkerSlotFromFp = -1;
|
|
static const int kSavedCallerFpSlotFromFp = 0;
|
|
static const int kSavedCallerPcSlotFromFp = 1;
|
|
static const int kParamEndSlotFromFp = 1; // One slot past last parameter.
|
|
static const int kCallerSpSlotFromFp = 2;
|
|
static const int kLastParamSlotFromEntrySp = 1; // Skip return address.
|
|
|
|
// No pool pointer on IA32 (indicated by aliasing saved fp).
|
|
static const int kSavedCallerPpSlotFromFp = kSavedCallerFpSlotFromFp;
|
|
|
|
// Entry and exit frame layout.
|
|
static const int kExitLinkSlotFromEntryFp = -8;
|
|
|
|
// All arguments are passed on the stack, so none need to be saved. Therefore
|
|
// there is no frame for holding the saved arguments.
|
|
//
|
|
// If NativeCallbackTrampolines::Enabled(), then
|
|
// kNativeCallbackTrampolineStackDelta must be added as well.
|
|
constexpr intptr_t kCallbackSlotsBeforeSavedArguments = 0;
|
|
|
|
// For FFI calls passing in TypedData, we save it on the stack before entering
|
|
// a Dart frame. This denotes how to get to the backed up typed data.
|
|
static const int kFfiCallerTypedDataSlotFromFp = kCallerSpSlotFromFp;
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_STACK_FRAME_IA32_H_
|