[foreign-memaccess] on value kinds
Jorn Vernee
jbvernee at xs4all.nl
Tue Jul 16 22:22:36 UTC 2019
On 2019-07-16 22:37, Maurizio Cimadamore wrote:
> On 16/07/2019 18:44, Jorn Vernee wrote:
>> Hi,
>>
>> There seem to be 2 problems at play:
>>
>> 1.) Being able to express more value kinds in the Layout API.
>> 2.) Having a way to tell SystemABI, for a given native function, how
>> the machinery should handle the Java arguments it gets passed.
>>
>> We have been deriving the information for 2. from layouts, at least in
>> the cases of our limited ABI support (C only), but we've already seen
>> this fall short in that past in the case of varargs, and now again.
>> I'm asking myself; how many times do we have to come back to the
>> Layout API to add support for expressing the semantics of language/ABI
>> XYZ? I'd like to be able to solve 2. separately from 1.
>
> I disagree here. Varargs is a completely different issue - which I
> also explored in the past in a separate writeup. The problem with
> varargs is not that layouts are not powerful enough, but that we are
> trying to get away to model them using Java varargs - which only works
> so far (e.g. downcalls, to some extent, not upcalls).
Yes, sorry. My point is that we're trying to derive ABI semantics from
other data, while maybe we need a more explicit way of expressing them.
It seemed to me that, from an ABI perspective, things like 'should this
argument be treated as a vararg?' and 'do we need to pass this float in
an x87 register?' belonged to the same category, that's what got me
started.
I'm mostly talking about the fact that we pass an isVarArgs flag as part
of NativeMethodType, and need to keep track of arguments being passed as
varargs for the Windows ABI. SystemABI also currently has no way to ask
for a pre-linked variant of a varargs function linked as fixed arity (on
the Java side), since there is no way to say 'this argument is a varargs
argument'. I think the strategy of trying to infer ABI semantics is
falling/will fall short. (we seem to agree here?)
> We could invent something else for expressing 'this argument is an x87
> value', but given we're already investing some API budget in layout
> descriptions, it seems natural to me to try and see whether the Layout
> abstraction can (maybe with some help) solve that problem too.
>
> Also, I think you make this argument too ABI-centric; yes, whether
> something is an x87 is important for linking a native function - but
> that's all. We also need some way to look at a struct layout (e.g.
> coming from jextract) and be able to tell whether a given struct field
> is associated with the x87 semantics as opposed to the quadfloat
> semantics (which will trigger very different memory read/write
> operations).
>
> Again, we could imagine that jextract will use some other side-channel
> to capture this info - but since having one abstraction seems better
> than having two (slightly overlapping) ones, I'd try to work with the
> abstraction we have and see if there's some way to solve the problem
> that way?
I'm not saying we can't encode 'this argument is an x87 value' in the
Layouts that we pass. In this specific case this seems like a good
choice.
I think not every ABI semantic can naturally be encoded in the set
parameter Layouts, so down the line it seems that we need some other way
to express additional semantics as well. e.g. an additional argument to
SystemABI::downcallHandle besides a tuple of parameter layouts.
>>
>> So far we seem to be on the same page, but to solve 2., I don't think
>> using the identity of some Layout constant is powerful/robust enough.
>> I'd really like to see something based on a public version of
>> ArgumentBinding, where for a certain Java argument we have a way of
>> telling the VM how this should be shuffled into a target
>> register/stack/other. That would allow users to implement their own
>> ABI support, and we can implement the current C ABI support on top of
>> that.
>
> Sure - that is similar to what we've discussed internally with respect
> to how we'd like to evolve the linkToNative support to eventually
> cover all the cases currently handled by the universal invoker.
Yes, but beyond what the current implementation does, I'd like to move
_all_ the ABI specific logic to the Java side. Which, on second thought,
is more than just argument shuffling, which we already have covered. We
also need a way to denote volatile registers in order to preserve them,
required stack alignment (I don't think this is being enforced
currently?), and argument shadow space (to support Windows). (We can
probably get a complete list of things we need to support by looking at
the JNI downcall/upcall code)
The point is that, after moving to the Java side, if we allow users to
specify the ABI specific logic directly there is less of a burden on the
Layout API to support expressing different ABI quirks. Though some ABI
quirks are good to express in the Layout API, like I think is the case
for x87 vs. quad.
> While what you propose seems appealing - it's not entirely clear to me
> how you 'build C ABI on top' - that is, let's assume we have some API
> whose functionalities are similar to ArgumentBinding - and let's
> assume that we're writing an higher layer that has to generate a
> sequence of these bindings given a description of the function. Seems
> to me we're back to the same problem: how do we 'infer' the binding
> list from the function description? Or, as you probably would prefer
> to put it, what's in a function description? If inside a description
> there's just layouts - we're back to square one - we need a way to go
> from layouts to the right set of bindings.
We'd not need to infer anything, we'd explicitly encode all the needed
information. One advantage is that we could choose what's in a function
description specifically geared towards supporting C. Support for
another language could choose to have it's own function descriptor with
more/less captured information. But the point in the scope of this
discussion is that we don't have to try too much to support all the C
ABI (and down the line other ABI) semantics with the Layout API.
I'm mostly trying to set apart our current goals from our future goals.
Sorry if this was not clear from my last email.
> As for my identity comparison idea, I eventually realized that relying
> on identity is not a great idea given we have methods which change it
> - e.g. Layout::withName, so that complicates things.
>
>
>
>> But, in the short term, we need a way of expressing a C function
>> signature so that the current SystemABI knows how to call it. Relying
>> on Layouts only seems to get us so far, so I think we need a separate
>> abstraction for expressing C functions (though probably partially
>> Layout based), which captures all the semantics needed for C
>> specifically. We could then choose to either encode the fact that
>> we're dealing with an x87 float or a quad precision float using a
>> separate flag in this new abstraction, or encoding it in the argument
>> Layouts e.g. by relying on annotations, or something else.
>>
>> Coming back to 1.; It seems fair to have the ability to express more
>> value kinds in the Layout API, like you say; why should we have 3
>> 'blessed' kinds? We could solve this problem by replacing the
>> ValueLayout::Kind enum with a simple (multi character) string, as a
>> sort of light-weight alternative to using annotations. Then we could
>> for example express an x87 double as e.g. 'd128', and a quad float as
>> 'q128', or something else as 'xyz128'. How the kind string is
>> interpreted is up to the code using the Layout.
>
> I think the fundamental question here is: what are layouts for? Are
> they just for alignment and size? If that's the case, you don't need
> any 'kind' info at all - it's just some (aligned) bits. Or do layouts
> also convey some semantics information? The distinction between
> signed/unsigned/floating seems to suggest so, but I think that
> distinction is overly simplistic - and any attempt to generalize it
> doesn't seem very different than just using 'neutral' layouts with a
> bunch of meta-information attached on to the side.
Agreed - and I think that's probably were we need to go (again), i.e.
add back annotations. But, as far as descriptor strings go, I think
something like `f64` is more readable than `v64(kind=float)`. And, if we
choose the former, I think it's fair that this 'f' is customizable to
support 'value kind Foo in language XYZ', and doesn't just support our
three blessed kinds :)
Jorn
> Note: I'm resisting the temptation to think about layouts as strings
> in a language :-)
>
> Maurizio
>>
>> What do you think?
>>
>> Jorn
>>
>> On 2019-07-15 23:18, Maurizio Cimadamore wrote:
>>> Hi,
>>> as I was (re)starting the works on the second step of the Panama
>>> pipeline (foreign function access), it occurred to me that one piece
>>> of the design for ValueLayout is not 100% flushed out. I'm referring
>>> to the different 'kinds' of value layouts available in the API:
>>>
>>> * signed int
>>> * unsigned int
>>> * floating point
>>>
>>> We made this distinction long ago - the intention was to capture
>>> important distinctions between different layouts in an explicit
>>> fashion. For instance, system ABI typically pass integer values via
>>> general register, while they pass floating point values via floating
>>> point or vector registers. So it seemed an important distinction to
>>> capture.
>>>
>>> When I later started to work on support for x87 types, I realized
>>> that
>>> it wasn't all that simple - a "long double" in SysV ABI is typically
>>> encoded as a 128 bit floating point using the x87 extended precision
>>> format [1], but so is a "binary128" which instead uses the quad
>>> precision format [2]. In other words, the kind/size pair does not
>>> unambiguously denote a specific type semantics. Moreover, x87 types
>>> only really make sense when it comes to the SysV ABI, and the
>>> implementation of that ABI will have to ask whether a certain layout
>>> is that of an x87 floating point value - which brings up the question
>>> on how are these special, platform-dependent layouts denoted in the
>>> first place?
>>>
>>> Since Panama layouts support annotations, we always had the
>>> annotation
>>> route available to us to distinguish between these different types -
>>> that is:
>>>
>>> f128[abi=x87]
>>>
>>> could denote an extended precision x87 value, whereas:
>>>
>>> f128[abi=quadfloat]
>>>
>>> could denote a 128 floating point value using the 'quad' float format
>>> (binary128).
>>>
>>> This is of course still a viable option - yes, the memory access API
>>> no longer have general purpose annotations, but it's easy enough to
>>> add them back in as part of the System ABI support, and then retrofit
>>> layout 'names' as a special kind of annotation - that's a move we
>>> have
>>> pulled in the past and we know it works.
>>>
>>> But looking at this problem with fresh eyes, I'm noting an asymmetry,
>>> one that John pointed out in the past: the set of kinds supported by
>>> ValueLayout seem somewhat arbitrary, fixed and non-extensible in ways
>>> other than using annotations. What is the advantage of being able to
>>> tell an 'int' from a 'float' if we can't tell a 'x87' double from a
>>> 'quad float' ? Why is the former distinction supported _natively_ by
>>> the layout descriptions, whereas the latter is only supported
>>> indirectly, via annotations?
>>>
>>> Of course, we know that former proposals, such as LDL [3], precisely
>>> for this reason, decided not to embed any semantics in their 'kinds'.
>>> That is, LDL really has only bits and group of bits - all the
>>> semantics is specified via annotations. This is a more symmetric
>>> approach - there are no 'blessed' kinds, everything happens through
>>> annotations. This is certainly a fine decision when designing a
>>> layout
>>> language with a given fixed grammar.
>>>
>>> But, using annotations inside layouts is also a very indirect
>>> approach. Can we do better?After all, it seems that, if we leverage
>>> the fact that layouts are API elements, or objects, we can formulate
>>> an alternate solution where:
>>>
>>> * value layouts _cannot be created_ you have to use one of the
>>> pre-baked constants - we have already discussed introducing layout
>>> constants in [4] anyway
>>> * among the constants, users will find some that are ABI-specific
>>> (e.g. there will be one constant for x87 values, one for quadfloat,
>>> and so forth).
>>> * testing 'is this layout a x87 layout?' reduces to an equality test
>>> (e.g. "layout == SYSV_X87")
>>>
>>> Something like this would allow us to have layouts which are
>>> _internally_ general enough to express system ABI specific types -
>>> but
>>> at the level of the public API, a layout for a 128-bit x87 value
>>> would
>>> be the same as the one for a quad float - there would be no way for
>>> the user to tell them apart, other than noting that the two layouts
>>> correspond to different pre-baked constants. And this is, perhaps, a
>>> good outcome - after all, the distinction between these two layouts
>>> is
>>> a _semantic_ distinction, not (strictly speaking) a layout one (in
>>> fact the layout is, in terms of size and alignment, indeed the same
>>> in
>>> both cases). Therefore, it is very likely that this semantic
>>> distinction will only be of interest to very critical component of
>>> the
>>> Panama runtime - and that most of the clients will not care much
>>> about
>>> the distinction (other than maybe occasionally testing for "is this
>>> an
>>> x87 layout").
>>>
>>> Thoughts?
>>>
>>> Maurizio
>>>
>>> [1] -
>>> https://en.wikipedia.org/wiki/Extended_precision#IEEE_754_extended_precision_formats
>>> [2] -
>>> https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
>>> [3] - http://cr.openjdk.java.net/~jrose/panama/minimal-ldl.html
>>> [4] -
>>> https://mail.openjdk.java.net/pipermail/panama-dev/2019-July/005908.html
More information about the panama-dev
mailing list