
The documentation for pokeByteOff indicates that the following equality holds: pokeElemOff addr idx x = poke (addr `plusPtr` (idx * sizeOf x)) x Notably, this ignores alignment. It thus seems to imply that sizeOf must always be a multiple of alignment; otherwise, non-zero indices could access non-aligned addresses. Was this intentional? If so, I believe sizeOf and alignment should document the law. If not, then I believe the {poke,peek}ElemOff laws need to change to something like pokeElemOff addr idx x = poke (addr `plusPtr` (idx * lcm (sizeOf x) (alignment x))) x

I realize that these libraries are used on many architectures.
However, on modern x86 machines, that are less than ~7 years old, it
doesn't seem to matter all that much.
https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/
In the comments, looks like somewhat older processors take a 40%
performance hit, which isn't good, but it isn't awful.
IIRC, of the processors that are actually used, there are two where
access alignment really matters - ARM and PowerPC. If you're running
linux I believe it will handle the exception and do the unaligned
access. However, of course it's really slow to do this.
I'm not sure if it makes sense to change the law. Someone might be
relying on the behavior of instances that do not fulfill this law.
Perhaps in those cases it makes sense to just remove the instance.
-Michael
On Fri, Dec 15, 2017 at 9:33 PM, David Feuer
The documentation for pokeByteOff indicates that the following equality holds:
pokeElemOff addr idx x = poke (addr `plusPtr` (idx * sizeOf x)) x
Notably, this ignores alignment. It thus seems to imply that sizeOf must always be a multiple of alignment; otherwise, non-zero indices could access non-aligned addresses.
Was this intentional? If so, I believe sizeOf and alignment should document the law. If not, then I believe the {poke,peek}ElemOff laws need to change to something like
pokeElemOff addr idx x = poke (addr `plusPtr` (idx * lcm (sizeOf x) (alignment x))) x
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

On Fri, 15 Dec 2017, Michael Sloan wrote:
I'm not sure if it makes sense to change the law. Someone might be relying on the behavior of instances that do not fulfill this law. Perhaps in those cases it makes sense to just remove the instance.
Storable is intended for data exchange with code written in other languages. Thus we must use the same alignment rules as the system ABI.

2017-12-16 8:53 GMT+01:00 Henning Thielemann
Storable is intended for data exchange with code written in other languages. Thus we must use the same alignment rules as the system ABI.
As already mentioned above, Storable is meant as a *basic* building block for doing that, without a reference to any ABI whatsoever. Another use case for Storable is e.g. marshaling things into some form suitable for serialization, which might have nothing to do with any system ABI. So the conclusion that Storable must somehow obey some alignment rules is not correct: *You* as the author of a Storable instance for a give purpose have to care about alignment/packing/etc. Taking a step back: An ABI like https://software.intel.com/sites/default/files/article/402129/mpx-linux64-ab... specifies a lot of things: * OS interface * File formats * Handling of exceptions * Linking * The calling convention for C (data types, data layout, registers, stack frames, ...) * ... Even the calling convention for C++ is left out of most such ABI documents (perhaps available as a separate addendum), other languages are most of the time not even mentioned. *Some* calling convention has to be specified, otherwise you would have a hard time specifying the OS interface, and C is still the most natural choice for that. The FFI spec just gives you a small fraction of such an ABI: * A way to call C land, because you can't construct the right stack frame/register contents from Haskell land * A way to call back from C to Haskell * A very basic tooling to (un)marshal primitive data types and arrays of them (Storable) Note that Storable itself is intentionally not tied to the ABI, it could as well be used to e.g. marshal packed graphics data to OpenGL etc. The right approach IMHO is to have separate classes for the various ABIs: * StorableAMD64 * Storablex86 * StorableARM32 * StorableARM64 * ... Those classes would handle alignment/ordering issues for their respective platform, and we can have more instances for them, including sum types. Perhaps even a packed variant of these classes might be needed. In addition, perhaps an alias (subclass?) "StorableNative" of one of those classes represents the native ABI on the platform the program is running is needed, but then cross-compilation will be tricky. I'm very open to extending the FFI toolset, but let's not retroactively interpret stuff into already existing things, at the danger of breaking an unknown amount of code. Cheers, S.

2017-12-16 6:33 GMT+01:00 David Feuer
The documentation for pokeByteOff indicates that the following equality holds:
pokeElemOff addr idx x = poke (addr `plusPtr` (idx * sizeOf x)) x
[...] Was this intentional?
Yep, that was intentional, think of pokeElemOff as indexing into an array. Note that the FFI intentionally does not specify how to (un)marshal structs, only basic types and arrays of them. Doing anything else would be a) language-specific and b) platform-ABI-specific. Storable is just meant as a basic building block to do such more involved things.
If so, I believe sizeOf and alignment should document the law. [...]
Perhaps, but what exactly do you feel is missing there?

On Sat, 16 Dec 2017, Sven Panne wrote:
2017-12-16 6:33 GMT+01:00 David Feuer
: The documentation for pokeByteOff indicates that the following equality holds: pokeElemOff addr idx x = poke (addr `plusPtr` (idx * sizeOf x)) x
[...] Was this intentional?
Yep, that was intentional, think of pokeElemOff as indexing into an array. Note that the FFI intentionally does not specify how to (un)marshal structs, only basic types and arrays of them.
I thought that arrays require alignment of their elements.

2017-12-16 15:16 GMT+01:00 Henning Thielemann : I thought that arrays require alignment of their elements. Yes, and if you start aligned, the pokeElemOff law keeps you aligned. If
you don't start aligned, Storable never magically aligns the first element,
anyway, so this must have been intentional (e.g. an array within packed
data).
In theory one could have an e.g. 7-byte data type with 8-byte alignment
requirements, but I think we can re-open the discussion when a processor
manufacturer is masochistic enough to do that. ;-)

On Sat, 16 Dec 2017, Sven Panne wrote:
2017-12-16 15:16 GMT+01:00 Henning Thielemann
: I thought that arrays require alignment of their elements. Yes, and if you start aligned, the pokeElemOff law keeps you aligned. If you don't start aligned, Storable never magically aligns the first element, anyway, so this must have been intentional (e.g. an array within packed data).
In theory one could have an e.g. 7-byte data type with 8-byte alignment requirements, but I think we can re-open the discussion when a processor manufacturer is masochistic enough to do that. ;-)
I more think of a custom struct with size 12 bytes consisting of a 64 bit and 32 bit word. It must be 8-byte aligned. You would have to align all elements at multiples of 8-byte and the address difference between two array elements is 16 not 12. On x86 Linux there would be no problem because a 12 byte struct containing a 64 bit word must already be padded to 16 byte. But that's an ABI definition and Storable wants to keep independent from that, right?

2017-12-16 16:45 GMT+01:00 Henning Thielemann : I more think of a custom struct with size 12 bytes consisting of a 64 bit
and 32 bit word. It must be 8-byte aligned. You would have to align all
elements at multiples of 8-byte and the address difference between two
array elements is 16 not 12. On x86 Linux there would be no problem because a 12 byte struct containing
a 64 bit word must already be padded to 16 byte. But that's an ABI
definition and Storable wants to keep independent from that, right? Yes, that's an ABI issue. Once again: Storable is *not* for structs, it
never has been and will never be (because there is no single correct
instance for composite struct-like types). Without any further assumptions,
the gap-less definition is the only one which makes sense. And without
instances for struct-like things, it even makes more sense.
So what you want is an instance of the proposed StorableAMD64 class. Taking
your example: An array without gaps of your 12byte struct would e.g. be
totally OK for OpenGL or a packed AMD64-conformant struct, so base
shouldn't choose one for you.

On Sat, 16 Dec 2017, Sven Panne wrote:
Yes, that's an ABI issue. Once again: Storable is *not* for structs, it never has been and will never be (because there is no single correct instance for composite struct-like types).
One of hsc2hs most important tasks is to generate Storable instances of Haskell records that shall be compatible to C structs. Is that one completely invalid?
participants (4)
-
David Feuer
-
Henning Thielemann
-
Michael Sloan
-
Sven Panne