jextract woes
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Jan 29 00:53:29 UTC 2020
On 29/01/2020 00:06, Michael Zucchi wrote:
>
>
> Well (not really knowing the internal details of how native method
> handles work / where the costs are involved etc) I had the idea of
> something like a "bindTo()" but for a method handle to remap it's
> callee address. I edited it out as it seemed a bit involved.
>
> FWIW I implemented the method I described but it's not great (its in
> my foreign-abi branch, khr package). I mean it works (in principle, I
> don't have any extensions on this machine) but you just move the
> boilerplate to your own function and it's just not going to scale for
> something like vulkan. I am thinking of making something more like
> this as the high level 'binding' api:
>
> binding class {
> extension class {
> methodhandle funca;
> blah funca(blah) {
> return (blah)funca.invokeExact(funca);
> }
> }
> extension makeextension(function<string,methodhandle>resolver) {
> return extension resolved.
> }
> }
>
> (maybe all extension methods in one, but more likely groups of them)
>
> I think you understand this but incase you missed it the entry points
> need to be potentially resolved separately for each platform and you
> can have many of those. And in vulkan, apart from it covering the
> whole (large) api, this extends further to other context-specific
> functions (calls on a platform, then a device, and so on, at least I
> think so, i'm pretty green on vulkan).
>
> I'm not sure how that can be done by extending the jextract generated
> class though (i mean it's final anyway right ;-)? Unless you mean by
> just calling the RuntimeHelper methods manually which sort of defeats
> the purpose.
I think that, for this kind of stuff you are in jextract API territory.
We could provide the plumbing for creating such 'dynamically-addressed'
MHs, but you will have to create them by hand (or prorammatically, if
you have a well-known way to list the extensions).
>
> If i follow your second paragraph correctly regarding alternative
> boostrapping, how does application state get into there, potentially
> multiple times?
bootstrapping is done only the first time a given method handle is
called - that is, the bootstrapping logic is the very logic that gives
you the method handle. This can do something simple, or something fairly
complex (e.g. obtain an address entry point from another native call and
then construct the handle from it). But once the bootstrap returns the
handle, the assumption is that the handle will be reused (and the VM can
assume it is a 'constant'). In other words, the VM sees that it needs to
either resolve a 'dynamic constant' (condy) or invoke a 'dynamic method'
(indy) and in the process of resolving such dynamic entities it will end
up calling user-defined bootstrapping logic (written in Java).
You can get quite wild with the kind of stuff you can do at this level
(and implementors of dynamic languages on top of the JVM indeed have :-)
) - but a lot of this stuff is, at least currently, not expressible in
terms of Java source code.
Maurizio
>
> Cheers,
> Z
>
> On 28/1/20 7:27 pm, Maurizio Cimadamore wrote:
>> Thanks for the headsup - this is indeed an interesting case. My
>> thinking here is: in classfile lingo, a native call can be thought of
>> as an invokedynamic instruction with a certain recipe which describes
>> the ingredient of the native call (layouts, entry point etc.). These
>> recipes are expressed in Java, and they are called "bootstrap
>> methods". When the invokedynamic instruction is first linked, the
>> bootstrap method is called, and a method handle representing the
>> computation associated with that callsite is then created.
>>
>> It seems like we need at least one alternate bootstrap method which,
>> instead of a constant address, takes an extra MethodHandle, which
>> acts as an address generator. This way, the potentially expensive
>> clGetExtensionFunctionAddressForPlatform function would only really
>> be executed once (when the method is first linked) after which you
>> get, as in the other, simpler case, a constant method handle the VM
>> can just call and optimize at will.
>>
>> The picture I painted is very possible assuming you can do (again)
>> classfile generation. In future, it might also becomes possible to
>> express invokedynamic calls using the Java source code (see [1]). So,
>> I think jextract should provide the knobs for you to do so (more
>> specifically the RuntimeHelper class).
>>
>> In the meantime, it would not be hard for you to create a class which
>> extends the jextract generated one, which adds the required
>> additional extension handles (where the address is computed by
>> calling the clGetExtensionFunctionAddressForPlatform handle). Or to
>> use the jextract API to autogenerate this extension class. But this
>> has to be domain-specific, so some extra coding would be required on
>> your part (regardless of whether you go the classfile route or the
>> sourcefile route).
>>
>> Maurizio
>>
>> [1] - https://www.youtube.com/watch?v=iSEjlLFCS3E
>>
>> On 28/01/2020 03:37, Michael Zucchi wrote:
>>> OpenCL extensions provide extra sets of calls and constants but
>>> there are no entry points provided in libOpenCL.so. They need to be
>>> resolved at runtime as they depend on the driver, aka 'platform'.
>>> OpenCL 1.0/1.1 has clGetExtensionFunctionAddress but opencl 1.2
>>> deprecated it in favour of clGetExtensionFunctionAddressForPlatform
>>> as they are really platform specific. Vulkan follows the same idea
>>> but it covers the whole api apart from the function resolution
>>> functions.
>>>
>>> (incase you try jextract on cl_ext.h or cl_gl.h it and it seems to
>>> work, libOpenCL can include extension entry point symbols, but it
>>> isn't portable and it can't work for cl-platform-specific routines.
>>> https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clGetExtensionFunctionAddressForPlatform.html)
>>>
>>> So there needs to be a way to instantiate a set of method handles
>>> whose entry addresses are resolved based on runtime state. This
>>> isn't possible now with jextract and also obviously conflicts with
>>> the static method/handle route currently employed.
>>>
>>> With the old jextract I could directly call Libraries.bind() with a
>>> custom name resolver (more code than with the low level api would
>>> require). With 'generate-abi' (my generator) I would just create a
>>> concrete class with the constants/handles/calls and a factory method
>>> or constructor which takes a name resolver. The jni code basically
>>> did this but in c-land.
>>>
>>> An easy low-level solution for jextract which maintains the current
>>> static class design would be to add an option to the method
>>> generation. Rather than method handles and methods which use them
>>> it has methodhandle factory methods that take an address or address
>>> resolution function*. The 'public' library interface would just use
>>> this to get a set of method handles it cares about and then invoke
>>> them directly in it's methods. This would force it to call
>>> invoke(exact) directly but that isn't *much* worse than the untyped
>>> method calls as of now and they are already exposed for such use
>>> anyway.
>>>
>>> I will re-implement what i had with zcl/jni on the branch using this
>>> type of approach (but with my generator) to see how it works.
>>>
>>> Z
>>>
>>> * For vulkan this is going to have be per-method since it everything
>>> a big generated header file, or multiple runs of jextract with
>>> specific methods filtered in.
>>> I want to look at it vulkan at some point, it's got some additional
>>> problems to tackle.
>>>
>>
>
More information about the panama-dev
mailing list