From ad48067429d3e4469a740ccc89267f4b4d14bf23 Mon Sep 17 00:00:00 2001 From: Carol Eidt Date: Wed, 17 Jan 2018 11:54:52 -0800 Subject: ARM: Fix morphing of struct passed on stack If a struct is passed on the stack, it must live on the stack, unless/until we support `GT_FIELD_LIST` for these args. This is unlikely to represent a significant code quality issue, since ARM supports many register args, and this has gone undetected thus far. This was exposed by tailcall stress on desktop. I've added a test that exposes the issue without tailcall stress (though it gets a different assert than the desktop failure). It seemed that `fgMorphMultiregStructArg()` was the best place to fix this - and I noted that this is called for any struct that is larger than a single register. So I updated the comments to reflect that. I thought about putting the test in the JIT\Regressions test directory, but I consider that it is addressing basic missing test coverage, so I added it to JIT\Methodical\structs. --- .../JIT/Methodical/structs/StructStackParams.cs | 187 +++++++++++++++++++++ .../Methodical/structs/StructStackParams.csproj | 37 ++++ 2 files changed, 224 insertions(+) create mode 100644 tests/src/JIT/Methodical/structs/StructStackParams.cs create mode 100644 tests/src/JIT/Methodical/structs/StructStackParams.csproj (limited to 'tests') diff --git a/tests/src/JIT/Methodical/structs/StructStackParams.cs b/tests/src/JIT/Methodical/structs/StructStackParams.cs new file mode 100644 index 0000000000..9a5634b48d --- /dev/null +++ b/tests/src/JIT/Methodical/structs/StructStackParams.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +// This tests passing structs that are less than 64-bits in size, but that +// don't match the size of a primitive type, and passes them as the 6th +// parameter so that they are likely to wind up on the stack for ABIs that +// pass structs by value. + +using System; +using System.Runtime.CompilerServices; + +// Struct that's greater than 32-bits, but not a multiple of 32-bits. +public struct MyStruct1 +{ + public byte f1; + public byte f2; + public short f3; + public short f4; +} + +// Struct that's less than 32-bits, but not the same size as any primitive type. +public struct MyStruct2 +{ + public byte f1; + public byte f2; + public byte f3; +} + +// Struct that's less than 64-bits, but not the same size as any primitive type. +public struct MyStruct3 +{ + public short f1; + public short f2; + public short f3; +} + +// Struct that's greater than 64-bits, but not a multiple of 64-bits. +public struct MyStruct4 +{ + public int f1; + public int f2; + public short f3; +} + +public class MyProgram +{ + const int Pass = 100; + const int Fail = -1; + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static byte GetByte(byte i) + { + return i; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static short GetShort(short i) + { + return i; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int GetInt(int i) + { + return i; + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int Check1(int w, int i1, int i2, int i3, int i4, int i5, int i6, int i7, MyStruct1 s1) + { + if ((w != 1) || (s1.f1 != i1) || (s1.f2 != i2) || (s1.f3 != i3) || (s1.f4 != i4)) + { + Console.WriteLine("FAIL"); + return Fail; + } + Console.WriteLine("PASS"); + return Pass; + } + + public static int TestStruct1() + { + MyStruct1 s1; + s1.f1 = GetByte(1); s1.f2 = GetByte(2); s1.f3 = GetShort(3); s1.f4 = GetShort(4); + int x = (s1.f1 * s1.f2 * s1.f3 * s1.f4); + int y = (s1.f1 - s1.f2) * (s1.f3 - s1.f4); + int z = (s1.f1 + s1.f2) * (s1.f3 + s1.f4); + int w = (x + y) / z; + + return Check1(w, 1, 2, 3, 4, 5, 6, 7, s1); + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int Check2(int w, int i1, int i2, int i3, int i4, int i5, int i6, int i7, MyStruct2 s2) + { + if ((w != 2) || (s2.f1 != i1) || (s2.f2 != i2) || (s2.f3 != i3) || (i4 != 4)) + { + Console.WriteLine("FAIL"); + return Fail; + } + Console.WriteLine("PASS"); + return Pass; + } + + public static int TestStruct2() + { + MyStruct2 s2; + s2.f1 = GetByte(1); s2.f2 = GetByte(2); s2.f3 = GetByte(3); + int x = s2.f1 * s2.f2 * s2.f3; + int y = (s2.f1 + s2.f2) * s2.f3; + int z = s2.f1 + s2.f2 + s2.f3; + int w = (x + y) / z; + + return Check2(w, 1, 2, 3, 4, 5, 6, 7, s2); + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int Check3(int w, int i1, int i2, int i3, int i4, int i5, int i6, int i7, MyStruct3 s3) + { + if ((w != 2) || (s3.f1 != i1) || (s3.f2 != i2) || (s3.f3 != i3) || (i4 != 4)) + { + Console.WriteLine("FAIL"); + return Fail; + } + Console.WriteLine("PASS"); + return Pass; + } + + public static int TestStruct3() + { + MyStruct3 s3; + s3.f1 = GetByte(1); s3.f2 = GetByte(2); s3.f3 = GetByte(3); + int x = s3.f1 * s3.f2 * s3.f3; + int y = (s3.f1 + s3.f2) * s3.f3; + int z = s3.f1 + s3.f2 + s3.f3; + int w = (x + y) / z; + + return Check3(w, 1, 2, 3, 4, 5, 6, 7, s3); + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static int Check4(int w, int i1, int i2, int i3, int i4, int i5, int i6, int i7, MyStruct4 s4) + { + if ((w != 2) || (s4.f1 != i1) || (s4.f2 != i2) || (s4.f3 != i3) || (i4 != 4)) + { + Console.WriteLine("FAIL"); + return Fail; + } + Console.WriteLine("PASS"); + return Pass; + } + + public static int TestStruct4() + { + MyStruct4 s4; + s4.f1 = GetInt(1); s4.f2 = GetInt(2); s4.f3 = GetShort(3); + int x = s4.f1 * s4.f2 * s4.f3; + int y = (s4.f1 + s4.f2) * s4.f3; + int z = s4.f1 + s4.f2 + s4.f3; + int w = (x + y) / z; + + return Check4(w, 1, 2, 3, 4, 5, 6, 7, s4); + } + + public static int Main() + { + int retVal = Pass; + if (TestStruct1() != Pass) + { + retVal = Fail; + } + if (TestStruct2() != Pass) + { + retVal = Fail; + } + if (TestStruct3() != Pass) + { + retVal = Fail; + } + if (TestStruct4() != Pass) + { + retVal = Fail; + } + return retVal; + } +} diff --git a/tests/src/JIT/Methodical/structs/StructStackParams.csproj b/tests/src/JIT/Methodical/structs/StructStackParams.csproj new file mode 100644 index 0000000000..6d58ab0227 --- /dev/null +++ b/tests/src/JIT/Methodical/structs/StructStackParams.csproj @@ -0,0 +1,37 @@ + + + + + Debug + AnyCPU + $(MSBuildProjectName) + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + + + + + + + + False + + + + + True + + + + + + + + + + + -- cgit v1.2.3