Bug 56602 - System.Numerics vector structures are incorrectly aligned on Android
Summary: System.Numerics vector structures are incorrectly aligned on Android
Alias: None
Product: Runtime
Classification: Mono
Component: JIT (show other bugs)
Version: master
Hardware: PC Mac OS
: --- normal
Target Milestone: Future Cycle (TBD)
Assignee: Rodrigo Kumpera
Depends on:
Reported: 2017-05-18 08:05 UTC by Cole Campbell
Modified: 2017-10-12 11:53 UTC (History)
7 users (show)

See Also:
Tags: bugpool
Is this bug a regression?: ---
Last known good build:

testcase (597 bytes, text/plain)
2017-06-09 01:30 UTC, Zoltan Varga

Description Cole Campbell 2017-05-18 08:05:57 UTC
The Vector2 and Vector3 structures are treated as if they were 16 bytes in size for alignment purposes, despite being 8 and 12 bytes in size, respectively. This is inconsistent with the behavior of the System.Numerics.Vectors.dll package on the CLR and desktop Mono, and results in data structure misalignments across platforms.

There is a GitHub issue discussing vector sizes here: https://github.com/dotnet/corefx/issues/17214

To reproduce, consider the following structure:

> struct VertexData
> {
>    public Vector3 Position;
>    public Vector2 TextureCoordinate;
> }
We can write a loop which examines the structure as a sequence of floats.

> var vertex = new VertexData(new Vector3(1, 2, 3), new Vector2(4, 5));
> unsafe
> {
>     var pvertex = (Single*)&vertex;
>     var count = Marshal.SizeOf(typeof(VertexData)) / sizeof(Single);
>     for (int i = 0; i < count; i++)
>     {
>         Console.WriteLine(*pvertex++);
>     }
> }
On desktop platforms using the System.Numerics.Vectors.dll NuGet package, the output is what you would expect: "1 2 3 4 5"

On Xamarin.Android using System.Numerics.dll, the same code produces the following: "1 2 3 0 4"

An extra float has been inserted after the Position field. The reported size of the structure has not changed; it is still 20 bytes according to Marshal.SizeOf(). In addition to the TextureCoordinate field being at the wrong offset, this also results in the final float being lost altogether.

I have not yet had a chance to confirm whether this is also the case on iOS, though I plan to do so in the near future.
Comment 1 Cole Campbell 2017-05-18 19:34:07 UTC
I can now confirm that when I run this code on iOS 10.3, I get the same result as I do on Android.
Comment 2 Zoltan Varga 2017-06-08 18:37:31 UTC
These structures are supposed to mirror the SIMD registers in the hardware, which are 16 bytes long. If we made them shorter, we couldn't use normal SIMD store instructions to store data into them, making the api useless.
Comment 3 Cole Campbell 2017-06-08 19:47:37 UTC
That is clearly not a problem on the CLR or on Mono's desktop runtime, where this library behaves as expected. Such an inconsistency in the layout of these data structures across platforms also renders the API useless for any use case involving interop.
Comment 4 Zoltan Varga 2017-06-09 01:30:15 UTC
Created attachment 22790 [details]
Comment 5 Zoltan Varga 2017-06-09 01:31:05 UTC
Compile the testcase using:

csc /r:System.Numerics.dll /unsafe bug-simd.cs

This also happens on desktop mono, at least with mono 5.0.
Comment 6 Cole Campbell 2017-06-09 02:07:52 UTC
My Ubuntu VM was running Mono 4.8.1, so I decided to check that again first:

cole@linux-dev-64:~/Dev/bug-simd$ mcs /unsafe bug-simd.cs /r:System.Numerics.dll
cole@linux-dev-64:~/Dev/bug-simd$ mono bug-simd.exe

This is consistent with the CLR on Windows using System.Numerics.Vectors.dll from NuGet.

So then I updated to Mono and tried again.

cole@linux-dev-64:~/Dev/bug-simd$ csc bug-simd.cs /r:System.Numerics.dll /unsafe
Microsoft (R) Visual C# Compiler version
Copyright (C) Microsoft Corporation. All rights reserved.

cole@linux-dev-64:~/Dev/bug-simd$ mono bug-simd.exe

So this behavior has changed in Mono 5.
Comment 7 Cole Campbell 2017-06-09 02:15:41 UTC
As I see it there are two separate issues here:

1) I can't consistently submit vertex data to OpenGL because the layout of vector types differs between the Microsoft CLR and Mono 5+.

2) Even if this is the "correct" way of laying them out due to SIMD alignment issues (and GitHub commenters were unable to convince Microsoft that this is the case, see the GitHub link above), shouldn't Marshal.SizeOf() report that all of these structures are 16 bytes in size?
Comment 8 Ludovic Henry 2017-10-06 22:25:15 UTC
I can reproduce with Mono (2017-10/a3943e28cf8).
Comment 9 Rodrigo Kumpera 2017-10-11 00:05:49 UTC
You're depending on internal implementation details of how the runtime lays out fields and how framework types are defined.

This is not something worth fixing. If you need strict control over memory layout, define the types themselves and convert to System.Numerics.Vectors on load.

Further API discussion on whether this should be part of the contract are to be conducted on corefx github repo and not on a bug report here since this involves the wide dotnet ecosystem.

Note You need to log in before you can comment on or make changes to this bug.