-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[ffi] Implement leaf FFI calls #36707
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
Additional benefit is that you can pass some pointers down directly (of course assuming that native side is not going to store them somewhere), e.g. pass an internal pointer into typed data arrays or strings, because you know that GC can't move them. For example this opens possibility to just call |
Some informal benchmarking suggests that replacing functions like |
That is without implementing leaf-calls I presume @cskau-g. It might be worth exploring implementing leaf calls. Both for |
Exactly. My previous comment was to further back the need for leaf-calls. |
As discussed in todays meeting we might want to use optional parameter: |
Let's keep this issue open until the register spilling subtask has also been implemented. |
Is there any planned support for allowing to pass pointers to typed data to C functions when doing a leaf call? |
Yes, we want to support that eventually. It's an important functionality.
I am not sure what you mean here. Dart integers are immutable and (often) unboxed so pointer-to-an-integer does not really have a well defined meaning. |
What I mean is that it's fairly common for a C library to expect something like a Pointer or some other integer type, and the only way Dart can allocate such a pointer currently is with malloc, whereas C libraries tend to just allocate those integers on the stack. static late final _floatPtr = ffi.malloc<Float>(); Even though it's unsafe, I wish there was a way to allocate memory on the stack. final lib = DynamicLibrary.open("mylib.dll");
final nativeFunctionThatNeedsIntPointer = lib.lookupFunction<Void Function(Pointer<Uint32>), (void, int) Function()>("add_inplace") This code tells the FFI that instead of the Dart function accepting a Pointer as expected, Dart should allocate a temporary Uint32 on the stack whenever this function is called, pass a pointer to that Uint32 to the add_inplace, then return whatever value is now in the integer. This is safer than allocating memory on the stack, but gives you less control. Not that FFI is ever truly safe to begin with. |
@Keithcat1 I see what you mean. You want a pointer to the variable more or less, just like it is possible in C. Yeah, I also found myself wanting something like that when working with APIs which involve output parameters. This request is tracked by #42509 as you point out yourself. I don't think it is in any way tied to leafness of the call. |
Will I only be able to pass a Typed data pointer to C when using a leaf call, or can I also do this in a non-leaf call? Also, how would it work? |
Anything you can do with a leaf call you can do with a non-leaf call. Leaf calls is an optimisation where the VM foregoes some of the safety measures for the sake of speed. |
Are we not guaranteed GC doesn't move objects in a leaf call? In that case we can do more in a leaf call than a non-leaf call. |
Hm. That's a good question. I don't know if we strictly guarantee that as a part of the contract with leaf calls (and promise that that will never change)? I believe the "sales pitch" for leaf calls from the user's perspective so far has been what I mentioned above. |
Yes, we do. We don't enter a safepoint around leaf calls - which means GC can't happen inside. That's the whole idea behind leaf calls. User is also not allowed to call back into Dart (hence "leaf" in the name). That's highlighted in the comment above: #36707 (comment)
Only on leaf calls. As explained above we can't allow that on non-leaf calls. |
What would the syntax for passing a typed data pointer be? |
If I were to make a suggestion then I'd suggest the following rule: if parameter in the native signature is So something like this: final memcpy = DynamicLibrary.process().lookupFunction<
Pointer<Void> Function(Pointer<Void>, Pointer<Void>, Size),
Pointer<Void> Function(TypedData, TypedData, int)>('memcpy', isLeaf: true);
final size = 1024*1024;
final a = Uint8List(size);
final b = Uint8List(size);
memcpy(a, b, size); |
That prohibits passing |
I don't really understand why typed data pointers can't be passed when doing a non-leaf call. Go lets you pass slice pointers, provided the C function only holds on to the pointer until it returns. |
@Keithcat1 during a non-leaf call, the Dart GC might move a If what you say about Go is correct, then Go does not run the GC during a call to C. This is similar to what we have for leaf calls. We expect leaf calls to be short, so we don't want to waste cycles on telling the GC that it can run. But non-leaf calls can be long-running, so we there we would like the GC to be able to run. Does that answer your question? |
FWIW Go GC is currently non-moving. Even in moving GC you can make this work but it requires GC to support object pinning (support situations when certain objects can't move because they are pinned until after they are unpinned). Object pinning is a complexity that we want to avoid as much as possible (at least with the current GC architecture which assumes that any object in new space can be moved freely). |
I believe this was addressed in ba341d7. For extra features, or more optimizations for leaf calls please open new issues. Related open issues: |
Add support for FFI leaf calls:
InvokeMathCFunction
withFFICall
The text was updated successfully, but these errors were encountered: