Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions src/tests/Interop/PInvoke/Int128/Int128Native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

static Int128 Int128Value = { };

struct StructWithInt128
{
int64_t messUpPadding;
Copy link
Contributor

@MichalPetryka MichalPetryka Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use int8_t to better validate 32bit platforms that expect 4 byte alignment instead of 8/16 ones?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't matter too much. The way alignment works in the various architectures we support/are likely to support, there won't be a meaningful difference. However, this is altogether messy enough that we may simply remove the ability to pass Int128 across the p/invoke boundary.

Copy link
Member

@tannergooding tannergooding Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that while blocking this is fine for .NET 7 given the short timeframe and that its currently busted. This won't be viable longer term. Int128 is an ABI primitive and much like Vector128<T> needs to eventually be supported.

Int128 value;
};

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE GetInt128(uint64_t upper, uint64_t lower)
{
Int128 result;
Expand Down Expand Up @@ -62,6 +68,190 @@ extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128(Int128 lhs, Int128 rhs)
return result;
}

extern "C" DLL_EXPORT StructWithInt128 STDMETHODCALLTYPE AddStructWithInt128(StructWithInt128 lhs, StructWithInt128 rhs)
{
StructWithInt128 result = {};
result.messUpPadding = lhs.messUpPadding;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result.value = lhs.value + rhs.value;
#else
result.value.lower = lhs.value.lower + rhs.value.lower;
uint64_t carry = (result.value.lower < lhs.value.lower) ? 1 : 0;
result.value.upper = lhs.value.upper + rhs.value.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT StructWithInt128 STDMETHODCALLTYPE AddStructWithInt128_1(int64_t dummy1, StructWithInt128 lhs, StructWithInt128 rhs)
{
StructWithInt128 result = {};
result.messUpPadding = lhs.messUpPadding;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result.value = lhs.value + rhs.value;
#else
result.value.lower = lhs.value.lower + rhs.value.lower;
uint64_t carry = (result.value.lower < lhs.value.lower) ? 1 : 0;
result.value.upper = lhs.value.upper + rhs.value.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT StructWithInt128 STDMETHODCALLTYPE AddStructWithInt128_9(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, int64_t dummy6, int64_t dummy7, int64_t dummy8, int64_t dummy9, StructWithInt128 lhs, StructWithInt128 rhs)
{
StructWithInt128 result = {};
result.messUpPadding = lhs.messUpPadding;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result.value = lhs.value + rhs.value;
#else
result.value.lower = lhs.value.lower + rhs.value.lower;
uint64_t carry = (result.value.lower < lhs.value.lower) ? 1 : 0;
result.value.upper = lhs.value.upper + rhs.value.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_1(int64_t dummy1, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_2(int64_t dummy1, int64_t dummy2, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_3(int64_t dummy1, int64_t dummy2, int64_t dummy3, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_4(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_5(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_6(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, int64_t dummy6, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_7(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, int64_t dummy6, int64_t dummy7, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_8(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, int64_t dummy6, int64_t dummy7, int64_t dummy8, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}

extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128_9(int64_t dummy1, int64_t dummy2, int64_t dummy3, int64_t dummy4, int64_t dummy5, int64_t dummy6, int64_t dummy7, int64_t dummy8, int64_t dummy9, Int128 lhs, Int128 rhs)
{
Int128 result;

#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
result = lhs + rhs;
#else
result.lower = lhs.lower + rhs.lower;
uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
result.upper = lhs.upper + rhs.upper + carry;
#endif

return result;
}


extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128s(const Int128* pValues, uint32_t count)
{
Int128 result = {};
Expand Down
84 changes: 84 additions & 0 deletions src/tests/Interop/PInvoke/Int128/Int128Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
using System.Runtime.InteropServices;
using Xunit;

struct StructWithInt128
{
public StructWithInt128(Int128 val) { value = val; messUpPadding = 0x101010; }
public long messUpPadding;
public Int128 value;
};

unsafe partial class Int128Native
{
[DllImport(nameof(Int128Native))]
Expand All @@ -25,6 +32,43 @@ unsafe partial class Int128Native
[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128(Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern StructWithInt128 AddStructWithInt128(StructWithInt128 lhs, StructWithInt128 rhs);

[DllImport(nameof(Int128Native))]
public static extern StructWithInt128 AddStructWithInt128_1(long dummy1, StructWithInt128 lhs, StructWithInt128 rhs);

[DllImport(nameof(Int128Native))]
public static extern StructWithInt128 AddStructWithInt128_9(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, long dummy6, long dummy7, long dummy8, long dummy9, StructWithInt128 lhs, StructWithInt128 rhs);

// Test alignment and proper register usage for Int128 with various amounts of other registers in use. These tests are designed to stress the calling convention of Arm64 and Unix X64.
[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_1(long dummy1, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_2(long dummy1, long dummy2, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_3(long dummy1, long dummy2, long dummy3, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_4(long dummy1, long dummy2, long dummy3, long dummy4, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_5(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_6(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, long dummy6, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_7(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, long dummy6, long dummy7, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_8(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, long dummy6, long dummy7, long dummy8, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128_9(long dummy1, long dummy2, long dummy3, long dummy4, long dummy5, long dummy6, long dummy7, long dummy8, long dummy9, Int128 lhs, Int128 rhs);

[DllImport(nameof(Int128Native))]
public static extern Int128 AddInt128s(Int128* pValues, int count);

Expand Down Expand Up @@ -77,5 +121,45 @@ private static void TestInt128()

Int128 value9 = Int128Native.AddInt128s(in values[0], values.Length);
Assert.Equal(new Int128(95, 100), value9);

// Test ABI alignment issues on Arm64 and Unix X64, both enregistered and while spilled to the stack
Int128 value10 = Int128Native.AddInt128_1(1, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value10);

Int128 value11 = Int128Native.AddInt128_2(1, 2, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value11);

Int128 value12 = Int128Native.AddInt128_3(1, 2, 3, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value12);

Int128 value13 = Int128Native.AddInt128_4(1, 2, 3, 4, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value13);

Int128 value14 = Int128Native.AddInt128_5(1, 2, 3, 4, 5, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value14);

Int128 value15 = Int128Native.AddInt128_6(1, 2, 3, 4, 5, 6, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value15);

Int128 value16 = Int128Native.AddInt128_7(1, 2, 3, 4, 5, 6, 7, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value16);

Int128 value17 = Int128Native.AddInt128_8(1, 2, 3, 4, 5, 6, 7, 8, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value17);

Int128 value18 = Int128Native.AddInt128_9(1, 2, 3, 4, 5, 6, 7, 8, 9, new Int128(25, 26), new Int128(27, 28));
Assert.Equal(new Int128(52, 54), value18);

// Test alignment within a structure
StructWithInt128 value19 = Int128Native.AddStructWithInt128(new StructWithInt128(new Int128(29, 30)), new StructWithInt128(new Int128(31, 32)));
Assert.Equal(new StructWithInt128(new Int128(60, 62)), value19);

// Test abi register alignment within a structure
StructWithInt128 value20 = Int128Native.AddStructWithInt128_1(1, new StructWithInt128(new Int128(29, 30)), new StructWithInt128(new Int128(31, 32)));
Assert.Equal(new StructWithInt128(new Int128(60, 62)), value20);

// Test abi alignment when spilled to the stack
StructWithInt128 value21 = Int128Native.AddStructWithInt128_9(1, 2, 3, 4, 5, 6, 7, 8, 9, new StructWithInt128(new Int128(29, 30)), new StructWithInt128(new Int128(31, 32)));
Assert.Equal(new StructWithInt128(new Int128(60, 62)), value21);
}
}