-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[vm/ffi] Remove workaround for type feedback missing in FFI trampolines #44454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
When the test fails, a nested struct accessed on a larger struct which has a typed data as backing store, is returned as a struct with a pointer as backing store, rather than a typed data view. This happens because
On failing runs with
On succeeding runs with
This looks like speculative optimization failing to deoptimize when assumptions no longer hold. |
Working theory: Fragment FlowGraphBuilder::WrapTypedDataBaseInStruct(
const AbstractType& struct_type) {
const auto& struct_sub_class = Class::ZoneHandle(Z, struct_type.type_class());
struct_sub_class.EnsureIsFinalized(thread_);
const auto& lib_ffi = Library::Handle(Z, Library::FfiLibrary());
const auto& struct_class =
Class::Handle(Z, lib_ffi.LookupClass(Symbols::Struct()));
const auto& struct_addressof = Field::ZoneHandle(
Z, struct_class.LookupInstanceFieldAllowPrivate(Symbols::_addressOf()));
ASSERT(!struct_addressof.IsNull());
Fragment body;
LocalVariable* typed_data = MakeTemporary("typed_data_base");
body += AllocateObject(TokenPosition::kNoSource, struct_sub_class, 0);
body += LoadLocal(MakeTemporary("struct")); // Duplicate Struct.
body += LoadLocal(typed_data);
body += StoreInstanceField(struct_addressof,
StoreInstanceFieldInstr::Kind::kInitializing);
body += DropTempsPreserveTop(1); // Drop TypedData.
return body;
} runtime/vm/compiler/frontend/kernel_to_il.cc This allocates an object, stores a (Trying to add the guard fails, because FFI trampolines are force optimized.) body += StoreInstanceFieldGuarded(
struct_addressof, StoreInstanceFieldInstr::Kind::kInitializing); |
I'm unable to construct a case of this on master (without the CL):
It fails in the nested struct CL because we branch on |
This CL adds support for nested structs in FFI calls, callbacks, and memory loads and stores through the Struct classes itself. Nesting empty structs and nesting a structs in themselves (directly or indirectly) is reported as error. This feature is almost fully implemented in the CFE transformation. Because structs depend on the sizes of their nested structs, the structs now need to be processed in topological order. Field access to nested structs branches at runtime on making a derived Pointer if the backing memory of the outer struct was a Pointer or making a TypedDataView if the backing memory of the outer struct was a TypedData. Assigning to a nested struct is a byte for byte copy from the source. The only changes in the VM are contained in the native calling convention calculation which now recursively needs to reason about fundamental types instead of just 1 struct deep. 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: arm64 iOS. ABIs not tested: ia32 Android (emulator), x64 iOS (simulator), 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 #37271. Contains a temporary workaround for #44454. Change-Id: I5e5d10e09e5c3fc209f5f7e997efe17bd362214d 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/+/169221 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
Because we have a workaround, this is not crashing. |
As discussed in today's meeting we should separate allocation from sizeof calculation (similar to // In "dart:ffi"
abstract class Allocator {
Pointer<Void> allocate(int numBytes);
void free(Pointer<Void> pointer);
}
// In "dart:ffi"
extern Pointer<T> allocate<T extends Struct>(Allocator allocator);
// In "package:ffi/ffi.dart"
class MallocAllocator extends Allocator { ... }
class ZoneAllocator extends Allocator { ... }
final malloc = MallocAllocator();
// User code:
void main() {
Pointer<Foo> foop = allocate<Foo>(malloc);
...
free<Foo>(foop, malloc);
}
// User code (lowered to kernel):
void main() {
Pointer<Foo> foop = malloc.allocate(sizeOf<Foo>(malloc) /* <-- or rather it's lowered form */).cast<Foo>();
...
malloc.free(foop.cast<Void>());
} So we'll
|
fd2e9b9 Made trampolines explicit in the CFE. If we make the FFI trampoline actually return |
Update 2021-01-05: The issue is that the JIT trampolines do not report type feedback that
Struct. _addressOf
containsTypedData
on return values and arguments.This could be addressed by making the VM know the layout of Struct and its subtypes. However, that makes the tree shaker no longer understand those types and their constructor calls. So, we might not want to make those types and their constructors opaque to the CFE&TFA.
We have a workaround, so this does not crash.
===============================================================================
In https://dart-review.googlesource.com/c/sdk/+/169221 we add nested structs and copying of nested structs.
Edit: the CQ also caught this: https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket.appspot.com/8861204090372525392/+/steps/test_results/0/logs/new_test_failures__logs_/0
Test succeeds:
Test fails:
The text was updated successfully, but these errors were encountered: