Arm64: Implement JMP call for HFA register arguments
authorBruce Forstall <brucefo@microsoft.com>
Sat, 9 Mar 2019 01:58:54 +0000 (17:58 -0800)
committerBruce Forstall <brucefo@microsoft.com>
Wed, 13 Mar 2019 01:02:55 +0000 (18:02 -0700)
Add the code to load up HFA register arguments into their correct registers
before a JMP call.

Removes remaining NYI.

Fixes #23147

Add a test case with several variants of HFA and JMP call.

src/jit/codegenarmarch.cpp
tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.cs.txt [new file with mode: 0644]
tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.il [new file with mode: 0644]
tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.ilproj [new file with mode: 0644]

index 5e50e92..5018013 100644 (file)
@@ -2675,47 +2675,68 @@ void CodeGen::genJmpMethod(GenTree* jmp)
         if (varDsc->lvRegNum != argReg)
         {
             var_types loadType = TYP_UNDEF;
-            if (varTypeIsStruct(varDsc))
-            {
-                // Must be <= 16 bytes or else it wouldn't be passed in registers
-                noway_assert(EA_SIZE_IN_BYTES(varDsc->lvSize()) <= MAX_PASS_MULTIREG_BYTES);
-                loadType = compiler->getJitGCType(varDsc->lvGcLayout[0]);
-            }
-            else
+
+            if (varDsc->lvIsHfaRegArg())
             {
-                loadType = compiler->mangleVarArgsType(genActualType(varDsc->TypeGet()));
-            }
-            emitAttr loadSize = emitActualTypeSize(loadType);
-            getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argReg, varNum, 0);
+                // Note that for HFA, the argument is currently marked address exposed so lvRegNum will always be
+                // REG_STK. We home the incoming HFA argument registers in the prolog. Then we'll load them back
+                // here, whether they are already in the correct registers or not. This is such a corner case that
+                // it is not worth optimizing it.
 
-            // Update argReg life and GC Info to indicate varDsc stack slot is dead and argReg is going live.
-            // Note that we cannot modify varDsc->lvRegNum here because another basic block may not be expecting it.
-            // Therefore manually update life of argReg.  Note that GT_JMP marks the end of the basic block
-            // and after which reg life and gc info will be recomputed for the new block in genCodeForBBList().
-            regSet.AddMaskVars(genRegMask(argReg));
-            gcInfo.gcMarkRegPtrVal(argReg, loadType);
+                assert(!compiler->info.compIsVarArgs);
 
-            if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
+                loadType           = varDsc->GetHfaType();
+                regNumber fieldReg = argReg;
+                emitAttr  loadSize = emitActualTypeSize(loadType);
+                unsigned  cSlots   = varDsc->lvHfaSlots();
+
+                for (unsigned ofs = 0, cSlot = 0; cSlot < cSlots; cSlot++, ofs += (unsigned)loadSize)
+                {
+                    getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, fieldReg, varNum, ofs);
+                    assert(genIsValidFloatReg(fieldReg)); // No GC register tracking for floating point registers.
+                    fieldReg = regNextOfType(fieldReg, loadType);
+                }
+            }
+            else
             {
-                if (varDsc->lvIsHfa())
+                if (varTypeIsStruct(varDsc))
+                {
+                    // Must be <= 16 bytes or else it wouldn't be passed in registers, except for HFA,
+                    // which can be bigger (and is handled above).
+                    noway_assert(EA_SIZE_IN_BYTES(varDsc->lvSize()) <= 16);
+                    loadType = compiler->getJitGCType(varDsc->lvGcLayout[0]);
+                }
+                else
                 {
-                    NYI_ARM64("CodeGen::genJmpMethod with multireg HFA arg");
+                    loadType = compiler->mangleVarArgsType(genActualType(varDsc->TypeGet()));
                 }
+                emitAttr loadSize = emitActualTypeSize(loadType);
+                getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argReg, varNum, 0);
+
+                // Update argReg life and GC Info to indicate varDsc stack slot is dead and argReg is going live.
+                // Note that we cannot modify varDsc->lvRegNum here because another basic block may not be expecting it.
+                // Therefore manually update life of argReg.  Note that GT_JMP marks the end of the basic block
+                // and after which reg life and gc info will be recomputed for the new block in genCodeForBBList().
+                regSet.AddMaskVars(genRegMask(argReg));
+                gcInfo.gcMarkRegPtrVal(argReg, loadType);
 
-                // Restore the second register.
-                argRegNext = genRegArgNext(argReg);
+                if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
+                {
+                    // Restore the second register.
+                    argRegNext = genRegArgNext(argReg);
 
-                loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]);
-                loadSize = emitActualTypeSize(loadType);
-                getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE);
+                    loadType = compiler->getJitGCType(varDsc->lvGcLayout[1]);
+                    loadSize = emitActualTypeSize(loadType);
+                    getEmitter()->emitIns_R_S(ins_Load(loadType), loadSize, argRegNext, varNum, TARGET_POINTER_SIZE);
 
-                regSet.AddMaskVars(genRegMask(argRegNext));
-                gcInfo.gcMarkRegPtrVal(argRegNext, loadType);
-            }
+                    regSet.AddMaskVars(genRegMask(argRegNext));
+                    gcInfo.gcMarkRegPtrVal(argRegNext, loadType);
+                }
 
-            if (compiler->lvaIsGCTracked(varDsc))
-            {
-                VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex);
+                if (compiler->lvaIsGCTracked(varDsc))
+                {
+                    VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex);
+                }
             }
         }
 
@@ -2723,6 +2744,7 @@ void CodeGen::genJmpMethod(GenTree* jmp)
         {
             // In case of a jmp call to a vararg method ensure only integer registers are passed.
             assert((genRegMask(argReg) & (RBM_ARG_REGS | RBM_ARG_RET_BUFF)) != RBM_NONE);
+            assert(!varDsc->lvIsHfaRegArg());
 
             fixedIntArgMask |= genRegMask(argReg);
 
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.cs.txt b/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.cs.txt
new file mode 100644 (file)
index 0000000..6799607
--- /dev/null
@@ -0,0 +1,342 @@
+// 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.
+
+using System;
+using System.Runtime.CompilerServices;
+
+// Test case to generate a function with a JMP call and register HFA arguments.
+// Also, make sure the HFA register arguments need to be re-loaded from
+// the stack into registers. Make another call before the final JMP call
+// to clear the argument registers. This was designed to test an NYI in
+// the ARM64 JIT compiler.
+//
+// Note that this is the C# code; to generate the actual test case, you
+// need to "ildasm" the generated EXE from this C# code, then manually convert the 
+// final call in each of the "jmp_test_*" functions to a JMP.
+// The test case code will be an IL file.
+
+namespace Test
+{
+    struct HFA_f2
+    {
+        public float f1;
+        public float f2;
+    }
+    struct HFA_f3
+    {
+        public float f1;
+        public float f2;
+        public float f3;
+    }
+    struct HFA_f4
+    {
+        public float f1;
+        public float f2;
+        public float f3;
+        public float f4;
+    }
+    struct HFA_d2
+    {
+        public double d1;
+        public double d2;
+    }
+    struct HFA_d3
+    {
+        public double d1;
+        public double d2;
+        public double d3;
+    }
+    struct HFA_d4
+    {
+        public double d1;
+        public double d2;
+        public double d3;
+        public double d4;
+    }
+
+    class X
+    {
+        public static float fx1;
+        public static float fx2;
+        public static float fx3;
+        public static float fx4;
+        public static double dx1;
+        public static double dx2;
+        public static double dx3;
+        public static double dx4;
+
+        // arm64 has 8 int and 8 float argument registers. Create a function
+        // call that requires using all of them.
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static void clear_argument_regs(
+                int i1,
+                int i2,
+                int i3,
+                int i4,
+                int i5,
+                int i6,
+                int i7,
+                int i8,
+                int i9, // make sure something is on the stack
+                double f1,
+                double f2,
+                double f3,
+                double f4,
+                double f5,
+                double f6,
+                double f7,
+                double f8,
+                double f9) // make sure something is on the stack
+        {
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_f2(HFA_f2 a)
+        {
+            if ((a.f1 == fx1) && (a.f2 == fx2))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_f3(HFA_f3 a)
+        {
+            if ((a.f1 == fx1) && (a.f2 == fx2) && (a.f3 == fx3))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_f4(HFA_f4 a)
+        {
+            if ((a.f1 == fx1) && (a.f2 == fx2) && (a.f3 == fx3) && (a.f4 == fx4))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_d2(HFA_d2 a)
+        {
+            if ((a.d1 == dx1) && (a.d2 == dx2))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_d3(HFA_d3 a)
+        {
+            if ((a.d1 == dx1) && (a.d2 == dx2) && (a.d3 == dx3))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int test_d4(HFA_d4 a)
+        {
+            if ((a.d1 == dx1) && (a.d2 == dx2) && (a.d3 == dx3) && (a.d4 == dx4))
+            {
+                // pass
+                return 100;
+            }
+            else
+            {
+                // fail
+                return 1;
+            }
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_f2(HFA_f2 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_f2(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_f3(HFA_f3 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_f3(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_f4(HFA_f4 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_f4(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_d2(HFA_d2 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_d2(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_d3(HFA_d3 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_d3(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        public static int jmp_test_d4(HFA_d4 a)
+        {
+            clear_argument_regs(1,2,3,4,5,6,7,8,9,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0);
+            return test_d4(a); // CONVERT THIS TO JMP CALL!
+        }
+
+        public static int Main()
+        {
+            fx1 = 1.1F;
+            fx2 = 2.2F;
+            fx3 = 3.3F;
+            fx4 = 4.4F;
+
+            dx1 = 1.0;
+            dx2 = 2.0;
+            dx3 = 3.0;
+            dx4 = 4.0;
+
+            HFA_f2 hf2 = new HFA_f2();
+            hf2.f1 = fx1;
+            hf2.f2 = fx2;
+
+            HFA_f3 hf3 = new HFA_f3();
+            hf3.f1 = fx1;
+            hf3.f2 = fx2;
+            hf3.f3 = fx3;
+
+            HFA_f4 hf4 = new HFA_f4();
+            hf4.f1 = fx1;
+            hf4.f2 = fx2;
+            hf4.f3 = fx3;
+            hf4.f4 = fx4;
+
+            HFA_d2 hd2 = new HFA_d2();
+            hd2.d1 = dx1;
+            hd2.d2 = dx2;
+
+            HFA_d3 hd3 = new HFA_d3();
+            hd3.d1 = dx1;
+            hd3.d2 = dx2;
+            hd3.d3 = dx3;
+
+            HFA_d4 hd4 = new HFA_d4();
+            hd4.d1 = dx1;
+            hd4.d2 = dx2;
+            hd4.d3 = dx3;
+            hd4.d4 = dx4;
+
+            int final_result = 100; // assume pass
+            int result;
+
+            result = jmp_test_f2(hf2);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_f2 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_f2 FAIL");
+                final_result = 1;
+            }
+            
+            result = jmp_test_f3(hf3);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_f3 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_f3 FAIL");
+                final_result = 1;
+            }
+            
+            result = jmp_test_f4(hf4);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_f4 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_f4 FAIL");
+                final_result = 1;
+            }
+
+            result = jmp_test_d2(hd2);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_d2 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_d2 FAIL");
+                final_result = 1;
+            }
+            
+            result = jmp_test_d3(hd3);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_d3 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_d3 FAIL");
+                final_result = 1;
+            }
+            
+            result = jmp_test_d4(hd4);
+            if (result == 100)
+            {
+                Console.WriteLine("jmp_test_d4 PASS");
+            }
+            else
+            {
+                Console.WriteLine("jmp_test_d4 FAIL");
+                final_result = 1;
+            }
+
+            return final_result;
+        }
+        
+    }
+
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.il b/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.il
new file mode 100644 (file)
index 0000000..1298444
--- /dev/null
@@ -0,0 +1,696 @@
+.assembly extern System.Runtime { }
+.assembly extern System.Console
+{
+}
+
+.assembly GitHub_23147
+{
+}
+.module GitHub_23147.exe
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_f2
+       extends [System.Runtime]System.ValueType
+{
+  .field public float32 f1
+  .field public float32 f2
+} // end of class Test.HFA_f2
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_f3
+       extends [System.Runtime]System.ValueType
+{
+  .field public float32 f1
+  .field public float32 f2
+  .field public float32 f3
+} // end of class Test.HFA_f3
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_f4
+       extends [System.Runtime]System.ValueType
+{
+  .field public float32 f1
+  .field public float32 f2
+  .field public float32 f3
+  .field public float32 f4
+} // end of class Test.HFA_f4
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_d2
+       extends [System.Runtime]System.ValueType
+{
+  .field public float64 d1
+  .field public float64 d2
+} // end of class Test.HFA_d2
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_d3
+       extends [System.Runtime]System.ValueType
+{
+  .field public float64 d1
+  .field public float64 d2
+  .field public float64 d3
+} // end of class Test.HFA_d3
+
+.class private sequential ansi sealed beforefieldinit Test.HFA_d4
+       extends [System.Runtime]System.ValueType
+{
+  .field public float64 d1
+  .field public float64 d2
+  .field public float64 d3
+  .field public float64 d4
+} // end of class Test.HFA_d4
+
+.class private auto ansi beforefieldinit Test.X
+       extends [System.Runtime]System.Object
+{
+  .field public static float32 fx1
+  .field public static float32 fx2
+  .field public static float32 fx3
+  .field public static float32 fx4
+  .field public static float64 dx1
+  .field public static float64 dx2
+  .field public static float64 dx3
+  .field public static float64 dx4
+  .method public hidebysig static void  clear_argument_regs(int32 i1,
+                                                            int32 i2,
+                                                            int32 i3,
+                                                            int32 i4,
+                                                            int32 i5,
+                                                            int32 i6,
+                                                            int32 i7,
+                                                            int32 i8,
+                                                            int32 i9,
+                                                            float64 f1,
+                                                            float64 f2,
+                                                            float64 f3,
+                                                            float64 f4,
+                                                            float64 f5,
+                                                            float64 f6,
+                                                            float64 f7,
+                                                            float64 f8,
+                                                            float64 f9) cil managed noinlining
+  {
+    // Code size       1 (0x1)
+    .maxstack  8
+    IL_0000:  ret
+  } // end of method X::clear_argument_regs
+
+  .method public hidebysig static int32  test_f2(valuetype Test.HFA_f2 a) cil managed noinlining
+  {
+    // Code size       31 (0x1f)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float32 Test.HFA_f2::f1
+    IL_0006:  ldsfld     float32 Test.X::fx1
+    IL_000b:  bne.un.s   IL_001d
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float32 Test.HFA_f2::f2
+    IL_0013:  ldsfld     float32 Test.X::fx2
+    IL_0018:  bne.un.s   IL_001d
+
+    IL_001a:  ldc.i4.s   100
+    IL_001c:  ret
+
+    IL_001d:  ldc.i4.1
+    IL_001e:  ret
+  } // end of method X::test_f2
+
+  .method public hidebysig static int32  test_f3(valuetype Test.HFA_f3 a) cil managed noinlining
+  {
+    // Code size       44 (0x2c)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float32 Test.HFA_f3::f1
+    IL_0006:  ldsfld     float32 Test.X::fx1
+    IL_000b:  bne.un.s   IL_002a
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float32 Test.HFA_f3::f2
+    IL_0013:  ldsfld     float32 Test.X::fx2
+    IL_0018:  bne.un.s   IL_002a
+
+    IL_001a:  ldarg.0
+    IL_001b:  ldfld      float32 Test.HFA_f3::f3
+    IL_0020:  ldsfld     float32 Test.X::fx3
+    IL_0025:  bne.un.s   IL_002a
+
+    IL_0027:  ldc.i4.s   100
+    IL_0029:  ret
+
+    IL_002a:  ldc.i4.1
+    IL_002b:  ret
+  } // end of method X::test_f3
+
+  .method public hidebysig static int32  test_f4(valuetype Test.HFA_f4 a) cil managed noinlining
+  {
+    // Code size       57 (0x39)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float32 Test.HFA_f4::f1
+    IL_0006:  ldsfld     float32 Test.X::fx1
+    IL_000b:  bne.un.s   IL_0037
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float32 Test.HFA_f4::f2
+    IL_0013:  ldsfld     float32 Test.X::fx2
+    IL_0018:  bne.un.s   IL_0037
+
+    IL_001a:  ldarg.0
+    IL_001b:  ldfld      float32 Test.HFA_f4::f3
+    IL_0020:  ldsfld     float32 Test.X::fx3
+    IL_0025:  bne.un.s   IL_0037
+
+    IL_0027:  ldarg.0
+    IL_0028:  ldfld      float32 Test.HFA_f4::f4
+    IL_002d:  ldsfld     float32 Test.X::fx4
+    IL_0032:  bne.un.s   IL_0037
+
+    IL_0034:  ldc.i4.s   100
+    IL_0036:  ret
+
+    IL_0037:  ldc.i4.1
+    IL_0038:  ret
+  } // end of method X::test_f4
+
+  .method public hidebysig static int32  test_d2(valuetype Test.HFA_d2 a) cil managed noinlining
+  {
+    // Code size       31 (0x1f)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float64 Test.HFA_d2::d1
+    IL_0006:  ldsfld     float64 Test.X::dx1
+    IL_000b:  bne.un.s   IL_001d
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float64 Test.HFA_d2::d2
+    IL_0013:  ldsfld     float64 Test.X::dx2
+    IL_0018:  bne.un.s   IL_001d
+
+    IL_001a:  ldc.i4.s   100
+    IL_001c:  ret
+
+    IL_001d:  ldc.i4.1
+    IL_001e:  ret
+  } // end of method X::test_d2
+
+  .method public hidebysig static int32  test_d3(valuetype Test.HFA_d3 a) cil managed noinlining
+  {
+    // Code size       44 (0x2c)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float64 Test.HFA_d3::d1
+    IL_0006:  ldsfld     float64 Test.X::dx1
+    IL_000b:  bne.un.s   IL_002a
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float64 Test.HFA_d3::d2
+    IL_0013:  ldsfld     float64 Test.X::dx2
+    IL_0018:  bne.un.s   IL_002a
+
+    IL_001a:  ldarg.0
+    IL_001b:  ldfld      float64 Test.HFA_d3::d3
+    IL_0020:  ldsfld     float64 Test.X::dx3
+    IL_0025:  bne.un.s   IL_002a
+
+    IL_0027:  ldc.i4.s   100
+    IL_0029:  ret
+
+    IL_002a:  ldc.i4.1
+    IL_002b:  ret
+  } // end of method X::test_d3
+
+  .method public hidebysig static int32  test_d4(valuetype Test.HFA_d4 a) cil managed noinlining
+  {
+    // Code size       57 (0x39)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      float64 Test.HFA_d4::d1
+    IL_0006:  ldsfld     float64 Test.X::dx1
+    IL_000b:  bne.un.s   IL_0037
+
+    IL_000d:  ldarg.0
+    IL_000e:  ldfld      float64 Test.HFA_d4::d2
+    IL_0013:  ldsfld     float64 Test.X::dx2
+    IL_0018:  bne.un.s   IL_0037
+
+    IL_001a:  ldarg.0
+    IL_001b:  ldfld      float64 Test.HFA_d4::d3
+    IL_0020:  ldsfld     float64 Test.X::dx3
+    IL_0025:  bne.un.s   IL_0037
+
+    IL_0027:  ldarg.0
+    IL_0028:  ldfld      float64 Test.HFA_d4::d4
+    IL_002d:  ldsfld     float64 Test.X::dx4
+    IL_0032:  bne.un.s   IL_0037
+
+    IL_0034:  ldc.i4.s   100
+    IL_0036:  ret
+
+    IL_0037:  ldc.i4.1
+    IL_0038:  ret
+  } // end of method X::test_d4
+
+  .method public hidebysig static int32  jmp_test_f2(valuetype Test.HFA_f2 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp       int32 Test.X::test_f2(valuetype Test.HFA_f2)
+  } // end of method X::jmp_test_f2
+
+  .method public hidebysig static int32  jmp_test_f3(valuetype Test.HFA_f3 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp       int32 Test.X::test_f3(valuetype Test.HFA_f3)
+  } // end of method X::jmp_test_f3
+
+  .method public hidebysig static int32  jmp_test_f4(valuetype Test.HFA_f4 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp       int32 Test.X::test_f4(valuetype Test.HFA_f4)
+  } // end of method X::jmp_test_f4
+
+  .method public hidebysig static int32  jmp_test_d2(valuetype Test.HFA_d2 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp       int32 Test.X::test_d2(valuetype Test.HFA_d2)
+  } // end of method X::jmp_test_d2
+
+  .method public hidebysig static int32  jmp_test_d3(valuetype Test.HFA_d3 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp int32 Test.X::test_d3(valuetype Test.HFA_d3)
+  } // end of method X::jmp_test_d3
+
+  .method public hidebysig static int32  jmp_test_d4(valuetype Test.HFA_d4 a) cil managed noinlining
+  {
+    // Code size       103 (0x67)
+    .maxstack  18
+    IL_0000:  ldc.i4.1
+    IL_0001:  ldc.i4.2
+    IL_0002:  ldc.i4.3
+    IL_0003:  ldc.i4.4
+    IL_0004:  ldc.i4.5
+    IL_0005:  ldc.i4.6
+    IL_0006:  ldc.i4.7
+    IL_0007:  ldc.i4.8
+    IL_0008:  ldc.i4.s   9
+    IL_000a:  ldc.r8     1.
+    IL_0013:  ldc.r8     2.
+    IL_001c:  ldc.r8     3.
+    IL_0025:  ldc.r8     4.
+    IL_002e:  ldc.r8     5.
+    IL_0037:  ldc.r8     6.
+    IL_0040:  ldc.r8     7.
+    IL_0049:  ldc.r8     8.
+    IL_0052:  ldc.r8     9.
+    IL_005b:  call       void Test.X::clear_argument_regs(int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          int32,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64,
+                                                          float64)
+    jmp int32 Test.X::test_d4(valuetype Test.HFA_d4)
+  } // end of method X::jmp_test_d4
+
+  .method public hidebysig static int32  Main() cil managed
+  {
+    .entrypoint
+    // Code size       579 (0x243)
+    .maxstack  2
+    .locals init (valuetype Test.HFA_f2 V_0,
+             valuetype Test.HFA_f3 V_1,
+             valuetype Test.HFA_f4 V_2,
+             valuetype Test.HFA_d2 V_3,
+             valuetype Test.HFA_d3 V_4,
+             valuetype Test.HFA_d4 V_5,
+             int32 V_6)
+    IL_0000:  ldc.r4     1.1
+    IL_0005:  stsfld     float32 Test.X::fx1
+    IL_000a:  ldc.r4     2.2
+    IL_000f:  stsfld     float32 Test.X::fx2
+    IL_0014:  ldc.r4     3.3
+    IL_0019:  stsfld     float32 Test.X::fx3
+    IL_001e:  ldc.r4     4.4000001
+    IL_0023:  stsfld     float32 Test.X::fx4
+    IL_0028:  ldc.r8     1.
+    IL_0031:  stsfld     float64 Test.X::dx1
+    IL_0036:  ldc.r8     2.
+    IL_003f:  stsfld     float64 Test.X::dx2
+    IL_0044:  ldc.r8     3.
+    IL_004d:  stsfld     float64 Test.X::dx3
+    IL_0052:  ldc.r8     4.
+    IL_005b:  stsfld     float64 Test.X::dx4
+    IL_0060:  ldloca.s   V_0
+    IL_0062:  initobj    Test.HFA_f2
+    IL_0068:  ldloca.s   V_0
+    IL_006a:  ldsfld     float32 Test.X::fx1
+    IL_006f:  stfld      float32 Test.HFA_f2::f1
+    IL_0074:  ldloca.s   V_0
+    IL_0076:  ldsfld     float32 Test.X::fx2
+    IL_007b:  stfld      float32 Test.HFA_f2::f2
+    IL_0080:  ldloca.s   V_1
+    IL_0082:  initobj    Test.HFA_f3
+    IL_0088:  ldloca.s   V_1
+    IL_008a:  ldsfld     float32 Test.X::fx1
+    IL_008f:  stfld      float32 Test.HFA_f3::f1
+    IL_0094:  ldloca.s   V_1
+    IL_0096:  ldsfld     float32 Test.X::fx2
+    IL_009b:  stfld      float32 Test.HFA_f3::f2
+    IL_00a0:  ldloca.s   V_1
+    IL_00a2:  ldsfld     float32 Test.X::fx3
+    IL_00a7:  stfld      float32 Test.HFA_f3::f3
+    IL_00ac:  ldloca.s   V_2
+    IL_00ae:  initobj    Test.HFA_f4
+    IL_00b4:  ldloca.s   V_2
+    IL_00b6:  ldsfld     float32 Test.X::fx1
+    IL_00bb:  stfld      float32 Test.HFA_f4::f1
+    IL_00c0:  ldloca.s   V_2
+    IL_00c2:  ldsfld     float32 Test.X::fx2
+    IL_00c7:  stfld      float32 Test.HFA_f4::f2
+    IL_00cc:  ldloca.s   V_2
+    IL_00ce:  ldsfld     float32 Test.X::fx3
+    IL_00d3:  stfld      float32 Test.HFA_f4::f3
+    IL_00d8:  ldloca.s   V_2
+    IL_00da:  ldsfld     float32 Test.X::fx4
+    IL_00df:  stfld      float32 Test.HFA_f4::f4
+    IL_00e4:  ldloca.s   V_3
+    IL_00e6:  initobj    Test.HFA_d2
+    IL_00ec:  ldloca.s   V_3
+    IL_00ee:  ldsfld     float64 Test.X::dx1
+    IL_00f3:  stfld      float64 Test.HFA_d2::d1
+    IL_00f8:  ldloca.s   V_3
+    IL_00fa:  ldsfld     float64 Test.X::dx2
+    IL_00ff:  stfld      float64 Test.HFA_d2::d2
+    IL_0104:  ldloca.s   V_4
+    IL_0106:  initobj    Test.HFA_d3
+    IL_010c:  ldloca.s   V_4
+    IL_010e:  ldsfld     float64 Test.X::dx1
+    IL_0113:  stfld      float64 Test.HFA_d3::d1
+    IL_0118:  ldloca.s   V_4
+    IL_011a:  ldsfld     float64 Test.X::dx2
+    IL_011f:  stfld      float64 Test.HFA_d3::d2
+    IL_0124:  ldloca.s   V_4
+    IL_0126:  ldsfld     float64 Test.X::dx3
+    IL_012b:  stfld      float64 Test.HFA_d3::d3
+    IL_0130:  ldloca.s   V_5
+    IL_0132:  initobj    Test.HFA_d4
+    IL_0138:  ldloca.s   V_5
+    IL_013a:  ldsfld     float64 Test.X::dx1
+    IL_013f:  stfld      float64 Test.HFA_d4::d1
+    IL_0144:  ldloca.s   V_5
+    IL_0146:  ldsfld     float64 Test.X::dx2
+    IL_014b:  stfld      float64 Test.HFA_d4::d2
+    IL_0150:  ldloca.s   V_5
+    IL_0152:  ldsfld     float64 Test.X::dx3
+    IL_0157:  stfld      float64 Test.HFA_d4::d3
+    IL_015c:  ldloca.s   V_5
+    IL_015e:  ldsfld     float64 Test.X::dx4
+    IL_0163:  stfld      float64 Test.HFA_d4::d4
+    IL_0168:  ldc.i4.s   100
+    IL_016a:  stloc.s    V_6
+    IL_016c:  ldloc.0
+    IL_016d:  call       int32 Test.X::jmp_test_f2(valuetype Test.HFA_f2)
+    IL_0172:  ldc.i4.s   100
+    IL_0174:  bne.un.s   IL_0182
+
+    IL_0176:  ldstr      "jmp_test_f2 PASS"
+    IL_017b:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_0180:  br.s       IL_018f
+
+    IL_0182:  ldstr      "jmp_test_f2 FAIL"
+    IL_0187:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_018c:  ldc.i4.1
+    IL_018d:  stloc.s    V_6
+    IL_018f:  ldloc.1
+    IL_0190:  call       int32 Test.X::jmp_test_f3(valuetype Test.HFA_f3)
+    IL_0195:  ldc.i4.s   100
+    IL_0197:  bne.un.s   IL_01a5
+
+    IL_0199:  ldstr      "jmp_test_f3 PASS"
+    IL_019e:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01a3:  br.s       IL_01b2
+
+    IL_01a5:  ldstr      "jmp_test_f3 FAIL"
+    IL_01aa:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01af:  ldc.i4.1
+    IL_01b0:  stloc.s    V_6
+    IL_01b2:  ldloc.2
+    IL_01b3:  call       int32 Test.X::jmp_test_f4(valuetype Test.HFA_f4)
+    IL_01b8:  ldc.i4.s   100
+    IL_01ba:  bne.un.s   IL_01c8
+
+    IL_01bc:  ldstr      "jmp_test_f4 PASS"
+    IL_01c1:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01c6:  br.s       IL_01d5
+
+    IL_01c8:  ldstr      "jmp_test_f4 FAIL"
+    IL_01cd:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01d2:  ldc.i4.1
+    IL_01d3:  stloc.s    V_6
+    IL_01d5:  ldloc.3
+    IL_01d6:  call       int32 Test.X::jmp_test_d2(valuetype Test.HFA_d2)
+    IL_01db:  ldc.i4.s   100
+    IL_01dd:  bne.un.s   IL_01eb
+
+    IL_01df:  ldstr      "jmp_test_d2 PASS"
+    IL_01e4:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01e9:  br.s       IL_01f8
+
+    IL_01eb:  ldstr      "jmp_test_d2 FAIL"
+    IL_01f0:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_01f5:  ldc.i4.1
+    IL_01f6:  stloc.s    V_6
+    IL_01f8:  ldloc.s    V_4
+    IL_01fa:  call       int32 Test.X::jmp_test_d3(valuetype Test.HFA_d3)
+    IL_01ff:  ldc.i4.s   100
+    IL_0201:  bne.un.s   IL_020f
+
+    IL_0203:  ldstr      "jmp_test_d3 PASS"
+    IL_0208:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_020d:  br.s       IL_021c
+
+    IL_020f:  ldstr      "jmp_test_d3 FAIL"
+    IL_0214:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_0219:  ldc.i4.1
+    IL_021a:  stloc.s    V_6
+    IL_021c:  ldloc.s    V_5
+    IL_021e:  call       int32 Test.X::jmp_test_d4(valuetype Test.HFA_d4)
+    IL_0223:  ldc.i4.s   100
+    IL_0225:  bne.un.s   IL_0233
+
+    IL_0227:  ldstr      "jmp_test_d4 PASS"
+    IL_022c:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_0231:  br.s       IL_0240
+
+    IL_0233:  ldstr      "jmp_test_d4 FAIL"
+    IL_0238:  call       void [System.Console]System.Console::WriteLine(string)
+    IL_023d:  ldc.i4.1
+    IL_023e:  stloc.s    V_6
+    IL_0240:  ldloc.s    V_6
+    IL_0242:  ret
+  } // end of method X::Main
+
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       7 (0x7)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
+    IL_0006:  ret
+  } // end of method X::.ctor
+
+} // end of class Test.X
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.ilproj b/tests/src/JIT/Regression/JitBlue/GitHub_23147/GitHub_23147.ilproj
new file mode 100644 (file)
index 0000000..fe2d403
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <CLRTestPriority>1</CLRTestPriority>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+  <PropertyGroup>
+    <DebugType>None</DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="GitHub_23147.il" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+  </PropertyGroup>
+</Project>