[foreign-memaccess] RFR 8223978: Add alignment support to layouts

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu May 16 10:38:46 UTC 2019


Also note - in C, pragma will basically force the compiler to align 
fields in the specified way (adding padding if needed). The Layout API 
is more lower level than that - alignments are essentially constraints, 
which are local to the element on which the constraint appear.

So, you could use alignment an shoot yourself in the foot:

Value vChar = Value.ofSignedInt(8);
Value vShort = Value.ofSignedInt(16);

Group g = Group.of(vChar.alignTo(16), vShort.alignTo(16));

Now, this is bogus, as the vShort will never be correctly aligned (given 
it's at an 8-bit offset from the start of the struct, which is also 
16-bit aligned).

This is why we check things when we construct layout paths, to detect 
pathological situations like these.

In C, pragma will probably have ended up generating something like this:

Group g = Group.of(vChar.alignTo(16), Padding.of(8), vShort.alignTo(16));

With Layout API you have to do things manually.

Now, we don't exclude that, in the future, we will also have an higher 
level API to do deep transformations on layouts, so that e.g. you can go 
from natural alignment to e.g. pack(2) alignment (with padding 
added/changed where needed); this is a similar problem to the one of 
converting a layout from big endian to little endian, which also require 
deep transformations.

The way I see in the API is that the instance methods in the Layout 
interface act 'locally'; whereas helper functions which can radically 
change the sub-structure of layouts are better defined elsewhere.

Maurizio

On 16/05/2019 11:25, Maurizio Cimadamore wrote:
>
> On 15/05/2019 21:07, Jorn Vernee wrote:
>>> * you can align a layout using Layout::alignTo(long) - this sets the
>>> additional constraint (provided is power of two, non-negative, and
>>> >=8)
>>
>> Why the power of two constraint? Shouldn't it just be a multiple of 8?
>>
>> This seems to preclude some packed structs, e.g. with elements with 3 
>> byte alignment:
>>
>>     #pragma pack(1)
>>
>>     struct Foo {
>>         char x;
>>         short y; // 1 byte alignment
>>         int z; // 3 byte alignment
>>     }; // size = 7 bytes
>>
>> In the layout API this is:
>>
>>     Value vChar = Value.ofSignedInt(8);
>>     Value vShort = Value.ofSignedInt(16);
>>     Value vInt = Value.ofSignedInt(32);
>>     Group g = Group.struct(vChar, vShort.alignTo(8), vInt.alignTo(24));
>>
>> But this throws an IAE because alignment of 24 is not allowed.
>
> I think you can still model that use case - essentially what you want 
> is to just align everything to 8. (in fact that's what pragma pack(1) 
> does).
>
> I don't think there's such a thing as 3-byte aligned memory access: if 
> you want to read 4 bytes on a 3-byte aligned address, that's just 
> unaligned read.
>
> In other words, nothing new here - we're kind of following what the 
> pragma pack allows you to do: the argument to pragma pack must also be 
> a power of 2 (1, 2, 4, 8 ...), see here [1, 2] - the only difference 
> in the Panama API is that everything has to be multiplied by 8, 
> because alignment (as sizes) are expressed in bits, to have more room 
> to add sub-byte alignment (bit-fields, etc.) later on.
>
> So, back to your example:
>
> Value vChar = Value.ofSignedInt(8);
> Value vShort = Value.ofSignedInt(16);
> Value vInt = Value.ofSignedInt(32);
> Group g = Group.struct(vChar.alignTo(8), vShort.alignTo(8), 
> vInt.alignTo(8));
>
> Maurizio
>
> [1] - https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=vs-2019
> [2] - 
> https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
>
>
>>
>> Jorn
>>
>> Maurizio Cimadamore schreef op 2019-05-15 19:57:
>>> Hi,
>>> this patch adds support to the API to express alignment constraints on
>>> layouts. Following the model John put forward in [1], I did the
>>> following:
>>>
>>> * there's a way to compute the 'natural alignment' of a layout
>>>
>>> * if no alignment is specified, alignment is the natural alignment
>>>
>>> * you can align a layout using Layout::alignTo(long) - this sets the
>>> additional constraint (provided is power of two, non-negative, and
>>> >=8)
>>>
>>> * When we obtain a VarHandle out of a Layout, at that point we are in
>>> the process of computing offsets; here we can catch e.g. if the offset
>>> of a given path doesn't match the specified alignment - in this case
>>> we fail fast, even before memory is accessed; this occurs e.g. if the
>>> layout path offset is not a multiple of its alignment, or if the
>>> alignment of the nested element is stricter than the one of the
>>> enclosing element.
>>>
>>> (in principle we could enforce these checks on layout creation, but
>>> given we have unresolved layouts in our radar, I think it's best to do
>>> minimal checks on layout creation and let the check fully kick in on
>>> path creation).
>>>
>>> * If VarHandle can be constructed, a dynamic check verifies that
>>> alignment of address passed to VH matches the layout requirements
>>>
>>> * MemoryScope::allocate also honors the alignment requirements - this
>>> works by up-allocating memory (to make sure a pointer with desired
>>> alignment exists in the allocated area) and then adjusting it after
>>> the fact. For alignments < 16bytes, nothing is done given that malloc,
>>> at least on x64 is guaranteed to respect that.
>>>
>>>
>>> I think this is powerful and yet relatively simple to understand,
>>> overall I quite like it.
>>>
>>> Webrev:
>>> http://cr.openjdk.java.net/~mcimadamore/panama/8223978/
>>>
>>> Maurizio


More information about the panama-dev mailing list