diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index df083c7c3a7524..6b7032336bd2d2 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2103,8 +2103,13 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) // multiple registers? if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs)) { - if ((structPromotionInfo.fieldCnt != 2) && - !((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType))) + if (structPromotionInfo.containsHoles && structPromotionInfo.customLayout) + { + JITDUMP("Not promoting multi-reg struct local V%02u with holes.\n", lclNum); + shouldPromote = false; + } + else if ((structPromotionInfo.fieldCnt != 2) && + !((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType))) { JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true, #fields != 2 and it's " "not a single SIMD.\n", diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.cs b/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.cs new file mode 100644 index 00000000000000..fb3ccef4ade51b --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Note: In below test case, we were not honoring the fact that the explicit struct size +// of struct is 32 bytes while the only 2 fields it has is just 2 bytes. In such case, +// we would pass partial struct value. +using System; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +[StructLayout(LayoutKind.Explicit, Size = 32)] +public readonly unsafe struct SmallString +{ + [FieldOffset(0)] private readonly byte _length; + [FieldOffset(1)] private readonly byte _firstByte; + + public SmallString(string value) + { + fixed (char* srcPtr = value) + fixed (byte* destPtr = &_firstByte) + { + Encoding.ASCII.GetBytes(srcPtr, value.Length, destPtr, value.Length); + } + + _length = (byte)value.Length; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public byte Dump() + { + fixed (byte* ptr = &_firstByte) + { + byte* next = ptr + 1; + return *next; + } + } +} + +public static class Program +{ + static int result = 0; + public static int Main() + { + var value = new SmallString("foobar"); + + TheTest(value); + + return result; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void TheTest(SmallString foo) + { + Execute(foo); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static object Execute(SmallString foo) + { + byte value = foo.Dump(); + // 111 corresponds to the ASCII code of 2nd characted of string "foobar" i.e. ASCII value of 'o'. + if (value == 111) + { + result = 100; + } + return new StringBuilder(); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.csproj new file mode 100644 index 00000000000000..e822a8b10a5a6f --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_62597/Runtime_62597.csproj @@ -0,0 +1,13 @@ + + + Exe + True + + + None + True + + + + + \ No newline at end of file