[foreign-abi] RFR: 8247937: Specialize downcall binding recipes using MethodHandle combinators [v3]
Jorn Vernee
jvernee at openjdk.java.net
Tue Jun 23 11:48:30 UTC 2020
> Hi,
>
> This PR adds specialization of downcall binding recipes using MethodHandles. The main addition to the code is the
> ProgrammableInvoker::specialize method, but there are also some refactorings which I will explain;
> The current code has a single `invoke` method that takes the high-level input arguments of the down call as an
> Object[]. This method calls the BindingInterpreter to pre-process arguments, and also move any low-level values
> produced by the pre-processing directly into the intermediate argument buffer that is later passed to the native code
> that copies the values into registers and then calls the target function. This `invoke` method is split into 2;
> `invokeInterpBindings`, which pre-processes the arguments and outputs any low-level values produced by the
> pre-processing into an Object[]. This Object[] is then passed to `invokeMoves`, which moves the low-level values into
> the intermediate argument buffer that is passed to native. This split is done so that we can replace the
> invokeInterpBindings call with a specialized MethodHandle chain based on the binding recipe instead, which is built on
> top a type handle of invokeMoves. This takes care of the binding recipe specialization. invokeMoves can later be
> replaced during C2 compilation with code that passes these low-level values into registers directly (but that is for
> another patch). BindingInterpreter now doesn't write values to the intermediate buffer directly, and so instead of
> passing functors to obtain a pointer to write a low-level value to, it now takes functors that handle the reading or
> writing (see StoreFunc and LoadFunc). Since the read/write methods in BindingInterpreter are now called from multiple
> places, I've moved them to SharedUtils.
> ---
>
> The process of specializing the binding recipe is as follows:
>
> We first calculate a low-level method type (the 'intrinsicType') based on the MOVE bindings of a particular recipe,
> this gives us a method type that takes only primitives as arguments, which represent the values to be copied into the
> various CPU registers before calling the native target function. We then get a low-level MethodHandle that calls
> invokeMoves, and adapt it to the intrinsic type. On top of that we build the specialized binding recipe method handle
> chain. For each argument, we iterate through the bindings in reverse, and for each binding insert a new filter MH onto
> the handle we already have. At the end we will end up with a handle that features the high-level arguments of our
> native function (MemorySegment, MemoryAddress, etc.). The same is done for the return bindings; the return value is
> repeatedly filtered according to the bindings. This currently doesn't work for multiple return values, in which case
> invokeMoves will return an Object[] instead of just a plain Object. This case is not specialized with method handles,
> but is instead handled solely by invokeInterpBindings. (we detect this in getBoundMethodHandle) In case the bindings
> need to do allocation, a NativeAllocationScope is created and passed in as the first argument, which is then forwarded
> to the various filter MH using mergeArguments (which is a wrapper for MethodHandles::permuteArguments that merges 2
> arguments given their indices). The allocation and cleanup of the NativeAllocationScope are handled with a tryFinally
> combinator.
> ---
>
> I've added a system property to disable the MethodHandle specialization so that the performance difference can be
> measured. The speed up I've seen on the CallOverhead benchmark between this and the current code is about 1.5x (but
> with the C2 intrinsics this gets us on par with JNI). The difference between specialization turned off and on is a
> little bigger than that, since the split of the 'invoke' method adds a bit of overhead as well, but it's still on
> overall gain compared to what we currently have. Finally, I've also fixed a minor problems with TestUpcall where the
> expected and actual values were reversed when calling asserts.
> ---
>
> If I've missed anything in the explanation, please feel free to ask!
>
> Thanks,
> Jorn
Jorn Vernee has updated the pull request incrementally with one additional commit since the last revision:
Fix whitespace errors
-------------
Changes:
- all: https://git.openjdk.java.net/panama-foreign/pull/212/files
- new: https://git.openjdk.java.net/panama-foreign/pull/212/files/1f5a6b27..638b8e0b
Webrevs:
- full: https://webrevs.openjdk.java.net/panama-foreign/212/webrev.02
- incr: https://webrevs.openjdk.java.net/panama-foreign/212/webrev.01-02
Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod
Patch: https://git.openjdk.java.net/panama-foreign/pull/212.diff
Fetch: git fetch https://git.openjdk.java.net/panama-foreign pull/212/head:pull/212
PR: https://git.openjdk.java.net/panama-foreign/pull/212
More information about the panama-dev
mailing list