diff options
author | Jeremy Koritzinsky <jkoritzinsky@gmail.com> | 2019-04-04 18:37:57 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-04 18:37:57 -0700 |
commit | df804273f7bebbe45cb51f22b748c31b5fbe60cf (patch) | |
tree | b83dee7b3cd863d56c0cec86e53c9f8dab542a35 /tests | |
parent | 126aaf4619adb085a210178ead14fa9439a5ebb6 (diff) | |
download | coreclr-df804273f7bebbe45cb51f22b748c31b5fbe60cf.tar.gz coreclr-df804273f7bebbe45cb51f22b748c31b5fbe60cf.tar.bz2 coreclr-df804273f7bebbe45cb51f22b748c31b5fbe60cf.zip |
Fix SystemV AMD64 Explicit structure classification (#22041)
* Don't bail out on enregistering explicit structs if there are no overlapping fields.
* Don't enregister if any unaligned fields.
* Enable passing explicit structs by-value by enregistering on systemv. Some edge cases are likely still broken, but just removing our blanket opt-out makes the current tests pass.
* Enable MarshalstructAsLayoutExp off-Windows.
* Start adding additional tests for explicit layout to try to catch edge cases in SystemV classification.
* Added a test that spans across multiple eightbytes and has an overlap in the second eightbyte.
* Change repro to use an array of floats and an int field in managed and use a float array for padding in native to force an SSE classification on the first byte.
* New algorithm to calculate eightbyte classification by going throw the structure byte-by-byte instead of field-by-field.
* Fix updating eightbyte classifications in the loop to actually used the iterated-upon variable.
* Consider each element of a fixed array as a separate field (to match native implementations).
* Implement correct SystemV classification for fixed buffers in non-blittable structures. Fixed buffers in blittable structures have the managed layout assign classifications, which still is buggy.
* Add tests.
* Correctly classify blittable fixed buffers. Move "is this field a fixed buffer" tracking into one of the unused bits in FieldDesc as code that isn't in marshalers needs to know about it.
* Handle the case where we have a struct that has no fields in an eightbyte that contains (i.e. no fields in the first eight bytes of the structure).
* PR feedback.
* Only look up FixedBufferAttribute when the type is a value class and the type of the field is a value type.
* Use heuristic to determine if a type is a fixed buffer for SystemV classification.
* Revert tracking if a field is a fixed buffer in the FieldDesc.
* Update comments.
* Classify aligned, nonoverlapping, float/double only structures as HFAs even if explicitly laid out
* Enable overlapping fields in HFAs. Update NativeType HFA to check for alignment.
I checked Godbolt to verify that HFAs for overlapping fields are allowed.
* Add HFA tests.
* Fix compile errors from HFA alignment check.
* Non-valuetypes will never have their managed layout used to classify SystemV eightbytes.
* Don't classify a struct with no zero-offset field as an HFA.
* Remove duplicate semicolon.
* PR feedback.
* Add test with 2-field double HFA.
* Clean up and add static asserts for struct size.
* Add define for static_assert_no_msg to the native test headers
* Fix build breaks.
* Remove unneeded "size = X bytes" comments. They were holdovers from the .NET Framework test tree.
* Use GetNumInstanceFieldBytes instead of GetLayoutInfo()->GetManagedSize()
* Fix build break.
* Centralize FieldMarshaler offsettting in ClassifyEightBytesWithNativeLayout.
* Fix signed/unsigned mismatch
* Fix condition to also detect arm64.
* Change ifdef to if defined.
* Remove duplicate declaration (broken in rebase)
* Add some logging in one of the unreproable OSX test failures.
* Mark System.Numerics.Vector as intrinsic and don't use the eightbyte classifier to enregister it.
* Also explicitly opt-out of HFAs for System.Numerics.Vector`1 for consistency.
* Update R2R required version to 3.0.
* Remove debugging prints.
Diffstat (limited to 'tests')
7 files changed, 470 insertions, 55 deletions
diff --git a/tests/src/Common/Platform/platformdefines.h b/tests/src/Common/Platform/platformdefines.h index 0c2008b9c2..c76983b355 100644 --- a/tests/src/Common/Platform/platformdefines.h +++ b/tests/src/Common/Platform/platformdefines.h @@ -27,6 +27,7 @@ #endif #include <wchar.h> +#define static_assert_no_msg(x) static_assert((x), #x) // // types and constants diff --git a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs index 262ec0b1ad..9cffdfaccc 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs +++ b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.cs @@ -15,7 +15,10 @@ public class Managed ByteStructPack2ExplicitId, ShortStructPack4ExplicitId, IntStructPack8ExplicitId, - LongStructPack16ExplicitId + LongStructPack16ExplicitId, + OverlappingLongFloatId, + OverlappingMultipleEightbyteId, + HFAId } [SecuritySafeCritical] @@ -34,7 +37,7 @@ public class Managed if (failures > 0) { Console.WriteLine("\nTEST FAILED!"); - return 101; + return 100 + failures; } else { @@ -209,6 +212,21 @@ public class Managed [DllImport("MarshalStructAsParam")] static extern LongStructPack16Explicit GetLongStruct(long l1, long l2); + [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsExpByValOverlappingLongFloat(OverlappingLongFloat str, long expected); + [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsExpByValOverlappingLongFloat(OverlappingLongFloat2 str, long expected); + + [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsExpByValOverlappingMultipleEightByte(OverlappingMultipleEightbyte str, float i1, float i2, float i3); + + [DllImport("MarshalStructAsParam")] + static extern float ProductHFA(ExplicitHFA hfa); + [DllImport("MarshalStructAsParam")] + static extern float ProductHFA(ExplicitFixedHFA hfa); + [DllImport("MarshalStructAsParam")] + static extern float ProductHFA(OverlappingHFA hfa); + #region Marshal Explicit struct method [SecuritySafeCritical] private static void MarshalStructAsParam_AsExpByVal(StructID id) @@ -351,7 +369,85 @@ public class Managed { failures++; } - break; + break; + case StructID.OverlappingLongFloatId: + OverlappingLongFloat overlappingLongFloat = new OverlappingLongFloat + { + l = 12345, + f = 12.45f + }; + Console.WriteLine("\tCalling MarshalStructAsParam_AsExpByValOverlappingLongFloat..."); + if (!MarshalStructAsParam_AsExpByValOverlappingLongFloat(overlappingLongFloat, overlappingLongFloat.l)) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsExpByValOverlappingLongFloat. Expected:True;Actual:False"); + failures++; + } + OverlappingLongFloat2 overlappingLongFloat2 = new OverlappingLongFloat2 + { + l = 12345, + f = 12.45f + }; + Console.WriteLine("\tCalling MarshalStructAsParam_AsExpByValOverlappingLongFloat (Reversed field order)..."); + if (!MarshalStructAsParam_AsExpByValOverlappingLongFloat(overlappingLongFloat2, overlappingLongFloat.l)) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsExpByValOverlappingLongFloat. Expected:True;Actual:False"); + failures++; + } + break; + case StructID.OverlappingMultipleEightbyteId: + Console.WriteLine("\tCalling MarshalStructAsParam_AsExpByValOverlappingMultipleEightByte..."); + OverlappingMultipleEightbyte overlappingMultipleEightbyte = new OverlappingMultipleEightbyte + { + arr = new float[3] { 1f, 400f, 623289f}, + i = 1234 + }; + if (!MarshalStructAsParam_AsExpByValOverlappingMultipleEightByte( + overlappingMultipleEightbyte, + overlappingMultipleEightbyte.arr[0], + overlappingMultipleEightbyte.arr[1], + overlappingMultipleEightbyte.arr[2])) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsExpByValOverlappingMultipleEightByte. Expected True;Actual:False"); + failures++; + } + break; + case StructID.HFAId: + OverlappingHFA hfa = new OverlappingHFA + { + hfa = new HFA + { + f1 = 2.0f, + f2 = 10.5f, + f3 = 15.2f, + f4 = 0.12f + } + }; + + float expected = hfa.hfa.f1 * hfa.hfa.f2 * hfa.hfa.f3 * hfa.hfa.f4; + float actual; + + Console.WriteLine("\tCalling ProductHFA with Explicit HFA."); + actual = ProductHFA(hfa.explicitHfa); + if (expected != actual) + { + Console.WriteLine($"\tFAILED! Expected {expected}. Actual {actual}"); + failures++; + } + Console.WriteLine("\tCalling ProductHFA with Explicit Fixed HFA."); + actual = ProductHFA(hfa.explicitFixedHfa); + if (expected != actual) + { + Console.WriteLine($"\tFAILED! Expected {expected}. Actual {actual}"); + failures++; + } + Console.WriteLine("\tCalling ProductHFA with Overlapping HFA."); + actual = ProductHFA(hfa); + if (expected != actual) + { + Console.WriteLine($"\tFAILED! Expected {expected}. Actual {actual}"); + failures++; + } + break; default: Console.WriteLine("\tThere is not the struct id"); failures++; @@ -1464,6 +1560,9 @@ public class Managed MarshalStructAsParam_AsExpByVal(StructID.ShortStructPack4ExplicitId); MarshalStructAsParam_AsExpByVal(StructID.IntStructPack8ExplicitId); MarshalStructAsParam_AsExpByVal(StructID.LongStructPack16ExplicitId); + MarshalStructAsParam_AsExpByVal(StructID.OverlappingLongFloatId); + MarshalStructAsParam_AsExpByVal(StructID.OverlappingMultipleEightbyteId); + MarshalStructAsParam_AsExpByVal(StructID.HFAId); } [SecuritySafeCritical] diff --git a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.csproj b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.csproj index 2a38e61fbe..4f69d9c080 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.csproj +++ b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutExp.csproj @@ -13,10 +13,6 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <DefineConstants>$(DefineConstants);STATIC</DefineConstants> <CLRTestPriority>1</CLRTestPriority> - - <!-- Test unsupported outside of windows --> - <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows> - <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild> </PropertyGroup> <!-- Default configurations to help VS understand the configurations --> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"></PropertyGroup> @@ -42,4 +38,4 @@ <ProjectReference Include="CMakeLists.txt"></ProjectReference> </ItemGroup> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> -</Project>
\ No newline at end of file +</Project> diff --git a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutSeq.cs b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutSeq.cs index 6eb77a44f1..20aa1dfb0f 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutSeq.cs +++ b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsLayoutSeq.cs @@ -27,7 +27,10 @@ public class Managed IntWithInnerSequentialId, SequentialWrapperId, SequentialDoubleWrapperId, - AggregateSequentialWrapperId + AggregateSequentialWrapperId, + FixedBufferClassificationTestId, + HFAId, + DoubleHFAId } private static void InitialArray(int[] iarr, int[] icarr) @@ -318,10 +321,22 @@ public class Managed static extern bool MarshalStructAsParam_AsSeqByValSequentialAggregateSequentialWrapper(AggregateSequentialWrapper wrapper); [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(FixedBufferClassificationTest str, float f); + [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(FixedBufferClassificationTestBlittable str, float f); + [DllImport("MarshalStructAsParam")] + static extern bool MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(FixedArrayClassificationTest str, float f); + [DllImport("MarshalStructAsParam")] static extern int GetStringLength(AutoString str); [DllImport("MarshalStructAsParam")] static extern HFA GetHFA(float f1, float f2, float f3, float f4); + + [DllImport("MarshalStructAsParam")] + static extern float ProductHFA(HFA hfa); + + [DllImport("MarshalStructAsParam")] + static extern double ProductDoubleHFA(DoubleHFA hfa); [DllImport("MarshalStructAsParam")] static extern ManyInts GetMultiplesOf(int i); @@ -617,6 +632,96 @@ public class Managed failures++; } break; + case StructID.FixedBufferClassificationTestId: + Console.WriteLine("\tCalling MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest with nonblittable struct..."); + unsafe + { + FixedBufferClassificationTest str = new FixedBufferClassificationTest(); + str.arr[0] = 123456; + str.arr[1] = 78910; + str.arr[2] = 1234; + str.f = new NonBlittableFloat(56.789f); + if (!MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(str, str.f.F)) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest. Expected:True;Actual:False"); + failures++; + } + } + + Console.WriteLine("\tCalling MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest with blittable struct..."); + unsafe + { + FixedBufferClassificationTestBlittable str = new FixedBufferClassificationTestBlittable(); + str.arr[0] = 123456; + str.arr[1] = 78910; + str.arr[2] = 1234; + str.f = 56.789f; + if (!MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(str, str.f)) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest. Expected:True;Actual:False"); + failures++; + } + } + + Console.WriteLine("\tCalling MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest with fixed array..."); + FixedArrayClassificationTest fixedArrayTest = new FixedArrayClassificationTest + { + arr = new Int32Wrapper[3] + { + new Int32Wrapper { i = 123456 }, + new Int32Wrapper { i = 78910 }, + new Int32Wrapper { i = 1234 } + }, + f = 56.789f + }; + if (!MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(fixedArrayTest, fixedArrayTest.f)) + { + Console.WriteLine("\tFAILED! Managed to Native failed in MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest. Expected:True;Actual:False"); + failures++; + } + break; + case StructID.HFAId: + { + HFA hfa = new HFA + { + f1 = 2.0f, + f2 = 10.5f, + f3 = 15.2f, + f4 = 0.12f + }; + + float expected = hfa.f1 * hfa.f2 * hfa.f3 * hfa.f4; + float actual; + + Console.WriteLine("\tCalling ProductHFA with HFA."); + actual = ProductHFA(hfa); + if (expected != actual) + { + Console.WriteLine($"\tFAILED! Expected {expected}. Actual {actual}"); + failures++; + } + break; + } + case StructID.DoubleHFAId: + { + DoubleHFA doubleHFA = new DoubleHFA + { + d1 = 123.456, + d2 = 456.789 + }; + + double expected = doubleHFA.d1 * doubleHFA.d2; + double actual; + + Console.WriteLine("\tCalling ProductDoubleHFA."); + actual = ProductDoubleHFA(doubleHFA); + if (expected != actual) + { + Console.WriteLine($"\tFAILED! Expected {expected}. Actual {actual}"); + failures++; + } + break; + } default: Console.WriteLine("\tThere is not the struct id"); failures++; @@ -2239,6 +2344,9 @@ public class Managed MarshalStructAsParam_AsSeqByVal(StructID.SequentialWrapperId); MarshalStructAsParam_AsSeqByVal(StructID.SequentialDoubleWrapperId); MarshalStructAsParam_AsSeqByVal(StructID.AggregateSequentialWrapperId); + MarshalStructAsParam_AsSeqByVal(StructID.FixedBufferClassificationTestId); + MarshalStructAsParam_AsSeqByVal(StructID.HFAId); + MarshalStructAsParam_AsSeqByVal(StructID.DoubleHFAId); } [SecuritySafeCritical] diff --git a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp index 9de3f78770..0f2582ab31 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp +++ b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.cpp @@ -1178,6 +1178,23 @@ extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE MarshalStructAsParam_AsExpByRefOutL return TRUE; } +////////////////////////////////////////////////////////////////////////////////////// +extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE MarshalStructAsParam_AsExpByValOverlappingLongFloat(OverlappingLongFloat str, LONG64 expected) +{ + return str.a == expected; +} + +extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE MarshalStructAsParam_AsExpByValOverlappingMultipleEightByte(OverlappingMultipleEightbyte str, float i1, float i2, float i3) +{ + return str.arr[0] == i1 && str.arr[1] == i2 && str.arr[2] == i3; +} + +extern "C" DLL_EXPORT BOOL STDMETHODCALLTYPE MarshalStructAsParam_AsSeqByValFixedBufferClassificationTest(FixedBufferClassificationTest str, float f) +{ + return str.f == f; +} + +//////////////////////////////////////////////////////////////////////////////////// extern "C" DLL_EXPORT int GetStringLength(AutoString str) { #ifdef _WIN32 @@ -1187,12 +1204,21 @@ extern "C" DLL_EXPORT int GetStringLength(AutoString str) #endif } +extern "C" DLL_EXPORT float STDMETHODCALLTYPE ProductHFA(HFA hfa) +{ + return hfa.f1 * hfa.f2 * hfa.f3 * hfa.f4; +} extern "C" DLL_EXPORT HFA STDMETHODCALLTYPE GetHFA(float f1, float f2, float f3, float f4) { return {f1, f2, f3, f4}; } +extern "C" DLL_EXPORT double STDMETHODCALLTYPE ProductDoubleHFA(DoubleHFA hfa) +{ + return hfa.d1 * hfa.d2; +} + extern "C" DLL_EXPORT ManyInts STDMETHODCALLTYPE GetMultiplesOf(int value) { ManyInts multiples = diff --git a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.h b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.h index dcedc46b1e..15d98aecba 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.h +++ b/tests/src/Interop/StructMarshalling/PInvoke/MarshalStructAsParamDLL.h @@ -56,12 +56,13 @@ typedef float FLOAT; typedef double DOUBLE; #endif -struct INNER2 // size = 12 bytes +struct INNER2 { INT f1; FLOAT f2; LPCSTR f3; }; + void ChangeINNER2(INNER2* p) { p->f1 = 77; @@ -161,7 +162,7 @@ bool IsCorrectInnerArraySequential(InnerArraySequential* p) } -union InnerArrayExplicit // size = 32 bytes +union InnerArrayExplicit { struct InnerSequential arr[2]; struct @@ -173,34 +174,48 @@ union InnerArrayExplicit // size = 32 bytes #ifdef WINDOWS - #ifdef _WIN64 - #pragma warning(push) - #pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union - union OUTER3 // size = 32 bytes - { - struct InnerSequential arr[2]; - struct - { - CHAR _unused0[24]; - LPCSTR f4; - }; - }; - #pragma warning(pop) - #else - struct OUTER3 // size = 28 bytes - { - struct InnerSequential arr[2]; - LPCSTR f4; - }; - #endif +#ifdef _WIN64 +#pragma warning(push) +#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union +union OUTER3 +{ + struct InnerSequential arr[2]; + struct + { + CHAR _unused0[24]; + LPCSTR f4; + }; +}; +static_assert_no_msg(sizeof(OUTER3) == 32); +#pragma warning(pop) +#else +struct OUTER3 +{ + struct InnerSequential arr[2]; + LPCSTR f4; +}; +static_assert_no_msg(sizeof(OUTER3) == 28); +#endif +#else // WINDOWS +#if defined(__x86_64__) || defined(__aarch64__) +union OUTER3 +{ + struct InnerSequential arr[2]; + struct + { + CHAR _unused0[24]; + LPCSTR f4; + }; +}; +static_assert_no_msg(sizeof(OUTER3) == 32); +#else +struct OUTER3 +{ + struct InnerSequential arr[2]; + LPCSTR f4; +}; +static_assert_no_msg(sizeof(OUTER3) == 28); #endif - -#ifndef WINDOWS - struct OUTER3 // size = 28 bytes - { - struct InnerSequential arr[2]; - LPCSTR f4; - }; #endif void PrintOUTER3(OUTER3* p, char const * name) @@ -326,7 +341,7 @@ bool IsCorrectCharSetUnicodeSequential(CharSetUnicodeSequential* p) } -struct NumberSequential // size = 64 bytes +struct NumberSequential { LONG64 i64; ULONG64 ui64; @@ -383,7 +398,7 @@ bool IsCorrectNumberSequential(NumberSequential* p) return true; } -struct S3 // size = 1032 bytes +struct S3 { BOOL flag; LPCSTR str; @@ -434,7 +449,7 @@ bool IsCorrectS3(S3* p) return true; } -struct S4 // size = 8 bytes +struct S4 { INT age; LPCSTR name; @@ -444,7 +459,7 @@ enum Enum1 e1 = 1, e2 = 3 }; -struct S5 // size = 8 bytes +struct S5 { struct S4 s4; Enum1 ef; @@ -471,7 +486,7 @@ bool IsCorrectS5(S5* str) return true; } -struct StringStructSequentialAnsi // size = 8 bytes +struct StringStructSequentialAnsi { LPCSTR first; LPCSTR last; @@ -518,7 +533,7 @@ void ChangeStringStructSequentialAnsi(StringStructSequentialAnsi* str) str->last = newLast; } -struct StringStructSequentialUnicode // size = 8 bytes +struct StringStructSequentialUnicode { LPCWSTR first; LPCWSTR last; @@ -571,7 +586,7 @@ void ChangeStringStructSequentialUnicode(StringStructSequentialUnicode* str) } -struct S8 // size = 32 bytes +struct S8 { LPCSTR name; BOOL gender; @@ -614,7 +629,7 @@ void ChangeS8(S8* str) str->mySByte = 64; } #pragma pack (8) -struct S_int // size = 4 bytes +struct S_int { INT i; }; @@ -622,19 +637,19 @@ struct S_int // size = 4 bytes struct S9; typedef void (*TestDelegate1)(struct S9 myStruct); -struct S9 // size = 8 bytes +struct S9 { INT i32; TestDelegate1 myDelegate1; }; -struct S101 // size = 8 bytes +struct S101 { INT i; struct S_int s_int; }; -struct S10 // size = 8 bytes +struct S10 { struct S101 s; }; @@ -661,13 +676,13 @@ void ChangeS10(S10* str) typedef int* LPINT; #endif -struct S11 // size = 8 bytes +struct S11 { LPINT i32; INT i; }; -union U // size = 8 bytes +union U { INT i32; UINT ui32; @@ -724,7 +739,7 @@ bool IsCorrectU(U* p) return true; } -struct ByteStructPack2Explicit // size = 2 bytes +struct ByteStructPack2Explicit { BYTE b1; BYTE b2; @@ -749,7 +764,7 @@ bool IsCorrectByteStructPack2Explicit(ByteStructPack2Explicit* p) -struct ShortStructPack4Explicit // size = 4 bytes +struct ShortStructPack4Explicit { SHORT s1; SHORT s2; @@ -773,7 +788,7 @@ bool IsCorrectShortStructPack4Explicit(ShortStructPack4Explicit* p) } -struct IntStructPack8Explicit // size = 8 bytes +struct IntStructPack8Explicit { INT i1; INT i2; @@ -796,7 +811,7 @@ bool IsCorrectIntStructPack8Explicit(IntStructPack8Explicit* p) return true; } -struct LongStructPack16Explicit // size = 16 bytes +struct LongStructPack16Explicit { LONG64 l1; LONG64 l2; @@ -836,6 +851,12 @@ struct HFA float f4; }; +struct DoubleHFA +{ + double d1; + double d2; +}; + struct ManyInts { int i1; @@ -888,3 +909,51 @@ struct AggregateSequentialWrapper InnerSequential sequential; SequentialWrapper wrapper2; }; + +union OverlappingLongFloat +{ + LONG64 a; + struct + { + char unused[4]; + float f; + }; +}; + +struct FixedBufferClassificationTest +{ + int arr[3]; + float f; +}; + +// use float padding to ensure that we match the SystemV Classification +// as if this field was not here (the case in the managed representation). +union OverlappingMultipleEightbyte +{ + float arr[3]; + struct + { + float padding[2]; + int i; + }; +}; + +union OverlappingMultipleEightbyteFirst +{ + float arr[3]; + struct + { + float padding; + int i; + }; +}; + +union OverlappingMultipleEightbyteMultiple +{ + float arr[3]; + struct + { + float padding; + int i[3]; + }; +}; diff --git a/tests/src/Interop/StructMarshalling/PInvoke/Struct.cs b/tests/src/Interop/StructMarshalling/PInvoke/Struct.cs index 7ade4c377c..59623ea2c4 100644 --- a/tests/src/Interop/StructMarshalling/PInvoke/Struct.cs +++ b/tests/src/Interop/StructMarshalling/PInvoke/Struct.cs @@ -313,6 +313,50 @@ public struct HFA public float f4; } +[StructLayout(LayoutKind.Explicit)] +public struct ExplicitHFA +{ + [FieldOffset(0)] + public float f1; + [FieldOffset(4)] + public float f2; + [FieldOffset(8)] + public float f3; + [FieldOffset(12)] + public float f4; +} + +[StructLayout(LayoutKind.Explicit)] +public unsafe struct ExplicitFixedHFA +{ + [FieldOffset(0)] + public float f1; + [FieldOffset(4)] + public float f2; + [FieldOffset(8)] + public fixed float fs[2]; +} + +[StructLayout(LayoutKind.Explicit)] +public struct OverlappingHFA +{ + [FieldOffset(0)] + public HFA hfa; + + [FieldOffset(0)] + public ExplicitHFA explicitHfa; + + [FieldOffset(0)] + public ExplicitFixedHFA explicitFixedHfa; +} + +[StructLayout(LayoutKind.Sequential)] +public struct DoubleHFA +{ + public double d1; + public double d2; +} + [StructLayout(LayoutKind.Sequential)] public struct ManyInts { @@ -369,3 +413,75 @@ public struct MultipleBool public bool b1; public bool b2; } + +[StructLayout(LayoutKind.Explicit)] +public struct OverlappingLongFloat +{ + [FieldOffset(0)] + public long l; + + [FieldOffset(4)] + public float f; +} + +[StructLayout(LayoutKind.Explicit)] +public struct OverlappingLongFloat2 +{ + [FieldOffset(4)] + public float f; + [FieldOffset(0)] + public long l; +} + +[StructLayout(LayoutKind.Explicit)] +public struct OverlappingMultipleEightbyte +{ + [FieldOffset(8)] + public int i; + [FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public float[] arr; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct FixedBufferClassificationTestBlittable +{ + public fixed int arr[3]; + public float f; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct FixedBufferClassificationTest +{ + public fixed int arr[3]; + public NonBlittableFloat f; +} + +// A non-blittable wrapper for a float value. +// Used to force a type with a float field to be non-blittable +// and take a different code path. +[StructLayout(LayoutKind.Sequential)] +public struct NonBlittableFloat +{ + public NonBlittableFloat(float f) + { + arr = new []{f}; + } + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + private float[] arr; + + public float F => arr[0]; +} + +public struct Int32Wrapper +{ + public int i; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct FixedArrayClassificationTest +{ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public Int32Wrapper[] arr; + public float f; +} |