RFC: Refactoring SystemABI
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Thu Nov 22 11:49:17 UTC 2018
tl;dr; I like it!
One minor nit is that I would keep UpcallHandle as simple as possible -
e.g. what you need is, I think, an interface:
interface UpcallHandle {
Object receiver();
void free()
MethodHandle target();
Pointer<?> entryPoint();
}
Something high level like this.
In other words, I'd completely remove things like:
1) native methods - each implementation should be free to decide how to
implement the various features.
2) how the stub is allocated is implementation specific; no need to
expose an API method for that (I believe the method is already unused in
current repo, or at least IntelliJ say so)
3) a method like getUpcalHandler(long addr) morally belongs to SystemABI
(I missed that in my proposal); in other words:
interface SystemABI {
...
Optional<UpcallHandler> lookupHandler(Pointer<?>);
}
This gives us more latitude in implementing this functionality - maybe
there's just an HashMap in SystemABI that for a given pointer address
gives you the corresponding upcall handle (after all, SystemABI sees all
creation requests!), no need to go native or call findBlob (although the
current impl is also fine).
4) I would make both NativeMethodType and SystemABI public, in the
toplevel java.foreign package
5) All the various XYZUpcallHandle/XYZInvokers/XYZSignatureShuffler,
should live in the SysV ABI impl package (these last two points can be
done in a final renaming step); on the other hand, the UpcallHandle
general interface (see above) should be a public interface in the
java.foreign package
6) I believe AbstractABI goes in the SystemV bucket too - in fact I
would consider even removing it and flattening the hierarchy
7) Style-wise, I would refactor the code in ScopeImpl a bit, so that it
relies on some helper function to (i) extract a native method type from
the @Callback interface and (ii) extract a MethodHandle out of that.
These could be methods in the Util class. Maybe they will come in handy
later.
8) In LayoutType I see you added a carrier() accessor (which has been
brought up before). I would stress in the Javadoc that this is the
_erased_ carrier.
9) It seems like we might use a Util helper function that creates a
Symbol with given name and pointer (as used by CallbackImplGenerator)
10) Pretty sure that now that we have NativeMethodType we should be able
to simplify some of the signatures in
DirectUpcallHandle/UniversalUpcallHandle/UniversalNativeInvoker/DIrectNativeInvoker
too (e.g. constructors) - as you did with VarargsInvoker
That's all I have for now.
Thanks!
Maurizio
> Updated webrev[1]
>
> - Added NativeMethodType and use it in SystemABI. This also allow us to have varargs downcall MH from SystemABI considering that different platform may handle varargs differently.
> - UpcallHandler is now named UpcallHandle, and is returned by SystemABI for upcallStub.
>
> Cheers,
> Henry
>
> [1] https://cr.openjdk.java.net/~henryjen/panama/SystemABI/webrev.01/webrev/
>
>> On Nov 16, 2018, at 2:05 PM, Jorn Vernee <jbvernee at xs4all.nl> wrote:
>>
>> Comments inline...
>>
>> Henry Jen schreef op 2018-11-16 18:50:
>>>> On Nov 16, 2018, at 8:23 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
>>>> Hi Jorn,
>>>> what you propose is a possible refinement; I've been noodling over this myself when writing the SystemABI interface in an attempt to find a way to reduce the number of parameters in the SystemABI calls, to make the API simpler. But I then decided to strive for minimality, at least for now.
>>>> That said, creating the "native" equivalent of MethodType is something that makes total sense, and we should explore this path further.
>>> I think that’s Function + MethodType, not sure if we need an
>>> abstraction on top of those two. Create another type to hold two
>>> information seems an overkill for me.
>> I dont't think MethodType is good enough. That would treat a Pointer<MyStruct> as just a Pointer, since MethodType uses Class. You can get the layout of the struct from the `Function`, but you wouldn't know which Java carrier type you should actually use for it.
>>
>> But LayoutTypeImpl saves the pointee type if you create a pointer type [1], so that retains the information.
>>
>>>> As for adding the 'binding' method directly in the Library.Symbol method, I feel less sanguine about it; if you have a method in the SystemABI interface like:
>>>> MethodHandle downcallHandle(Symbol s, FunctionType ft)
>>> I gave this a thought, I am leaning toward to keep it as is. Note that
>>> SystemABI gave the final method handle which should have all the
>>> arguments actually passed into the call, varargs should have been
>>> expanded. So FunctonType for SystemABI don’t really give us extra
>>> value except we need to compose such object from the argument binding.
>> Right. NativeInvoker.of, which creates VarargsInvoker, is currently being called in HeaderImplGenerator and CallbackImplGenerator, and they pass on the generics information by using j.l.r.Method. But if we want to move these calls into SystemABI as well then I think we want another way to pass on that information. Since not every binding operation will have a Method for it, just relying on Method will not work for that, and we can't rely on just MethodType for the same reason I gave above; it loses the generics information.
>>
>> There are also some differences between ABIs when it comes to varargs. For instance, the CallingSequence generation mechanism on windows needs to know exactly which arguments are being passed as varargs to determine which registers to use, but on SysV we don't care about that. I'm thinking maybe the creation of VarargsInvoker should be pushed behind SystemABI as well. Then windows can pass on which arguments are varargs in an independent implementation from the SysV ABI.
>>
>> With that, having varargs information being passed to SystemABI also makes sense, since SystemABI needs to decided if it wants to create a VarargsInvoker or not (and maybe which kind of VarargsInvoker).
>>
>> Jorn
>>
>> [1] : http://hg.openjdk.java.net/panama/dev/file/28b312ac55b5/src/java.base/share/classes/jdk/internal/foreign/memory/LayoutTypeImpl.java#l100
>>
>>> On the other hand, varargs method handle need to be composed at higher
>>> level, where I think Function + MethodType or FunctionType makes more
>>> sense. Should we have this in the SystemABI as a default method or
>>> have it as an factory method in Symbol, I think I prefer Symbol.
>>> Cheers,
>>> Henry
>>>> I guess that would achieve the same effects? That is, if we have a FunctionType then I would use it to simplify all signatures in the SystemABI interface, at which point creating a MH from a symbol is pretty straightforward.
>>>> Maurizio
>>>> On 16/11/2018 16:50, Jorn Vernee wrote:
>>>>> Hi,
>>>>> I like the idea of moving away from the dependency on `j.l.r.Method`, I see that in a lot of places you've started using `LayoutType<?> ret, LayoutType<?>... args`. I really like this, and I think it could use it's own wrapper type. I had experimented earlier with a draft of this:
>>>>> public class FunctionType {
>>>>> private final LayoutType<?> return;
>>>>> private final LayoutType<?>[] args;
>>>>> private final boolean isVararags;
>>>>> private FunctionType(LayoutType<?> return, LayoutType<?>[] args, boolean isVararags) {...}
>>>>> public static FunctionType make(LayoutType<?> ret, LayoutType<?>... args) {...}
>>>>> public static FunctionType makeVarargs(LayoutType<?> ret, LayoutType<?>... args) {...}
>>>>> public static FunctionType ofMethod(Method m, Function layout) {...}
>>>>> // public static FunctionType ofAnnotatedMethod(Method m) {...} // for later when we have per-method annotations
>>>>> public LayoutType<?> returnType() {...}
>>>>> public LayoutType<?>[] paramTypes() {...}
>>>>> public boolean isVarargs() {...}
>>>>> public MethodType toMethodType() {...}
>>>>> public Function toFunction(){...}
>>>>> }
>>>>> This could maybe later also be used as a way to bind individual symbols without having to create an interface to bind to:
>>>>> class Library.Symbol {
>>>>> ...
>>>>> public MethodHandle bindAsFunction(FunctionType type) {...}
>>>>> }
>>>>> This doesn't work when using `Class`, since you lose generic information; `Pointer<Byte>` -> `Pointer`, but `LayoutTypeImpl` saves this information and makes it accessible through `pointeeType()`, and the API for creating them is currently really nice; `LayoutType<?> lt = NativeTypes.UINT8.pointer();` (similar with Array as well).
>>>>> What do you think?
>>>>> Jorn
>>>>> Maurizio Cimadamore schreef op 2018-11-16 11:06:
>>>>>> Hi Henry,
>>>>>> the work looks like a good initial step. But I think you didn't go all
>>>>>> the way through with it. That is, the binder code (HeaderImplGenerator
>>>>>> and ScopeImpl) still rely on UpcallHandler/NativeInvoker. I think it
>>>>>> would be nice if we could completely decouple these things. After all,
>>>>>> everything that HeaderImplGenerator needs is a way to get a method
>>>>>> handle for a given downcall, and that can be done directly through
>>>>>> SystemABI, now.
>>>>>> For upcalls its a bit harder, given that for an upcall there's
>>>>>> currently some info associated to it:
>>>>>> * receiver object
>>>>>> * address of the generated stub
>>>>>> * target method handle
>>>>>> plus a way to 'free' the stub generated by the VM.
>>>>>> I think the best way to get there is to create an intermediate
>>>>>> abstraction that holds these info (plus a free() method), call it
>>>>>> 'UpcallHandle' (removed the final 'R') and then let the binder program
>>>>>> against this. Then, a Callback object is essentially an UpcallHandle +
>>>>>> a Java carrier (functional interface) + a scope.
>>>>>> But UpcallHandler as it is now, is not a mere abstraction describing
>>>>>> an upcall - it also embodies the strategy by which the upcall is
>>>>>> performed (e.g. direct vs. universal) in the same way a NativeInvoker
>>>>>> does. So I think that moving forward we would probably want to just
>>>>>> have SystemABI select the right NativeInvoker/UpcallHandler to do the
>>>>>> job - and these things don't have to be superclasses, or induce a
>>>>>> hierarchy of any kind - e.g. it would be ok for a DirectInvoker to be
>>>>>> completely unrelated from a UniversalInvoker. As long as the rest of
>>>>>> the binder only interacts with SystemABI, that's ok.
>>>>>> Maurizio
>>>>>> On 15/11/2018 22:44, Henry Jen wrote:
>>>>>>> Hi,
>>>>>>> Please have a look at the webrev[1] refactor the SystemABI APIs based on Mauricio’s proposal. SystemABI is suppose to encapsulate platform-specific details, this patch make NativeInvoker/UpcallHander and CalllingSequence to be implementation details, and thus can further be simplified/removed if we need to.
>>>>>>> The binder suppose to only interact with SystemABI, that’s not completed yet in this webrev, but follow up work should get us there.
>>>>>>> Concept of CallingConvention is introduced as an abstraction for now and is currently simply support default.
>>>>>>> Anyway, it’s just first step, and I would like to see if it make sense to us.
>>>>>>> Cheers,
>>>>>>> Henry
>>>>>>> [1] http://cr.openjdk.java.net/~henryjen/panama/SystemABI/webrev/
More information about the panama-dev
mailing list