summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Forstall <Bruce_Forstall@msn.com>2018-12-05 23:48:11 -0800
committerBruce Forstall <Bruce_Forstall@msn.com>2018-12-05 23:48:11 -0800
commitc153d9fc6627e5bb35e31eea6038f64f2a006022 (patch)
tree1cd3d5b8d10af4d71c7766e0e4c4d7cf7ff9ec5a
parent8aa0869eb9153429091fdba49469d89ec33092cb (diff)
downloadcoreclr-c153d9fc6627e5bb35e31eea6038f64f2a006022.tar.gz
coreclr-c153d9fc6627e5bb35e31eea6038f64f2a006022.tar.bz2
coreclr-c153d9fc6627e5bb35e31eea6038f64f2a006022.zip
Fix arm32 local variable references
Arm32 has different addressing mode offset ranges for floating-point and integer instructions. In addition, the ranges aren't too large. So in functions with a frame pointer, we try to access some variables using the frame pointer and some with the stack pointer, to expand the total number of variables we can access without allocating a "reserved register" just used for constructing large offsets. This calculation was incorrect for struct variables that contained floats, as float fields require calculating using the floating point range, but we were calculating using the variable type (struct), instead of the instruction type (floating-point). In addition, we were not correctly calculating the frame pointer range using the actual variable offset plus "within variable" offset (struct member offset). Added a test that covers some of these cases. Fixes #19537
-rw-r--r--src/jit/compiler.cpp3
-rw-r--r--src/jit/compiler.h2
-rw-r--r--src/jit/compiler.hpp83
-rw-r--r--src/jit/emit.cpp3
-rw-r--r--src/jit/emitarm.cpp23
-rw-r--r--src/jit/emitarm.h2
-rw-r--r--src/jit/lclvars.cpp2
-rw-r--r--tests/arm/corefx_linux_test_exclusions.txt1
-rw-r--r--tests/arm/corefx_test_exclusions.txt1
-rw-r--r--tests/issues.targets6
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.cs417
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.csproj35
12 files changed, 518 insertions, 60 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index f394e02aea..11f6a398ed 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -4280,7 +4280,8 @@ bool Compiler::compRsvdRegCheck(FrameLayoutState curState)
// Always do the layout even if returning early. Callers might
// depend on us to do the layout.
unsigned frameSize = lvaFrameSize(curState);
- JITDUMP("\ncompRsvdRegCheck\n"
+ JITDUMP("\n"
+ "compRsvdRegCheck\n"
" frame size = %6d\n"
" compArgSize = %6d\n",
frameSize, compArgSize);
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index b973e11105..aa8172569d 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -3041,7 +3041,7 @@ public:
#endif
#ifdef _TARGET_ARM_
- int lvaFrameAddress(int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset);
+ int lvaFrameAddress(int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset, bool isFloatUsage);
#else
int lvaFrameAddress(int varNum, bool* pFPbased);
#endif
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index e798741ea4..47c4ef1e4d 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -2064,25 +2064,37 @@ inline int Compiler::lvaCachedGenericContextArgOffset()
return lvaCachedGenericContextArgOffs;
}
-/*****************************************************************************
- *
- * Return the stack framed offset of the given variable; set *FPbased to
- * true if the variable is addressed off of FP, false if it's addressed
- * off of SP. Note that 'varNum' can be a negated spill-temporary var index.
- *
- * mustBeFPBased - strong about whether the base reg is FP. But it is also
- * strong about not being FPBased after FINAL_FRAME_LAYOUT. i.e.,
- * it enforces SP based.
- *
- * addrModeOffset - is the addressing mode offset, for example: v02 + 0x10
- * So, V02 itself is at offset sp + 0x10 and then addrModeOffset is what gets
- * added beyond that.
- */
-
+//------------------------------------------------------------------------
+// lvaFrameAddress: Determine the stack frame offset of the given variable,
+// and how to generate an address to that stack frame.
+//
+// Arguments:
+// varNum - The variable to inquire about. Positive for user variables
+// or arguments, negative for spill-temporaries.
+// mustBeFPBased - [_TARGET_ARM_ only] True if the base register must be FP.
+// After FINAL_FRAME_LAYOUT, if false, it also requires SP base register.
+// pBaseReg - [_TARGET_ARM_ only] Out arg. *pBaseReg is set to the base
+// register to use.
+// addrModeOffset - [_TARGET_ARM_ only] The mode offset within the variable that we need to address.
+// For example, for a large struct local, and a struct field reference, this will be the offset
+// of the field. Thus, for V02 + 0x28, if V02 itself is at offset SP + 0x10
+// then addrModeOffset is what gets added beyond that, here 0x28.
+// isFloatUsage - [_TARGET_ARM_ only] True if the instruction being generated is a floating
+// point instruction. This requires using floating-point offset restrictions.
+// Note that a variable can be non-float, e.g., struct, but accessed as a
+// float local field.
+// pFPbased - [non-_TARGET_ARM_] Out arg. Set *FPbased to true if the
+// variable is addressed off of FP, false if it's addressed
+// off of SP.
+//
+// Return Value:
+// Returns the variable offset from the given base register.
+//
inline
#ifdef _TARGET_ARM_
int
- Compiler::lvaFrameAddress(int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset)
+ Compiler::lvaFrameAddress(
+ int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset, bool isFloatUsage)
#else
int
Compiler::lvaFrameAddress(int varNum, bool* pFPbased)
@@ -2090,17 +2102,15 @@ inline
{
assert(lvaDoneFrameLayout != NO_FRAME_LAYOUT);
- int offset;
- bool FPbased;
- bool fConservative = false;
- var_types type = TYP_UNDEF;
+ int varOffset;
+ bool FPbased;
+ bool fConservative = false;
if (varNum >= 0)
{
LclVarDsc* varDsc;
assert((unsigned)varNum < lvaCount);
varDsc = lvaTable + varNum;
- type = varDsc->TypeGet();
bool isPrespilledArg = false;
#if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED)
isPrespilledArg = varDsc->lvIsParam && compIsProfilerHookNeeded() &&
@@ -2144,7 +2154,7 @@ inline
}
#endif // DEBUG
- offset = varDsc->lvStkOffs;
+ varOffset = varDsc->lvStkOffs;
}
else // Its a spill-temp
{
@@ -2158,8 +2168,7 @@ inline
tmpDsc = codeGen->regSet.tmpFindNum(varNum, RegSet::TEMP_USAGE_USED);
}
assert(tmpDsc != nullptr);
- offset = tmpDsc->tdTempOffs();
- type = tmpDsc->tdTempType();
+ varOffset = tmpDsc->tdTempOffs();
}
else
{
@@ -2186,7 +2195,6 @@ inline
// : :
// ---------------------------------------------------
- type = compFloatingPointUsed ? TYP_FLOAT : TYP_INT;
fConservative = true;
if (!FPbased)
{
@@ -2197,7 +2205,7 @@ inline
#else
int outGoingArgSpaceSize = 0;
#endif
- offset = outGoingArgSpaceSize + max(-varNum * TARGET_POINTER_SIZE, (int)lvaGetMaxSpillTempSize());
+ varOffset = outGoingArgSpaceSize + max(-varNum * TARGET_POINTER_SIZE, (int)lvaGetMaxSpillTempSize());
}
else
{
@@ -2205,9 +2213,9 @@ inline
CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef _TARGET_ARM_
- offset = codeGen->genCallerSPtoInitialSPdelta() - codeGen->genCallerSPtoFPdelta();
+ varOffset = codeGen->genCallerSPtoInitialSPdelta() - codeGen->genCallerSPtoFPdelta();
#else
- offset = -(codeGen->genTotalFrameSize());
+ varOffset = -(codeGen->genTotalFrameSize());
#endif
}
}
@@ -2229,19 +2237,20 @@ inline
// we have already selected the instruction. MinOpts will always reserve R10, so
// for MinOpts always use SP-based offsets, using R10 as necessary, for simplicity.
- int spOffset = fConservative ? compLclFrameSize : offset + codeGen->genSPtoFPdelta();
- int actualOffset = spOffset + addrModeOffset;
- int encodingLimitUpper = varTypeIsFloating(type) ? 0x3FC : 0xFFF;
- int encodingLimitLower = varTypeIsFloating(type) ? -0x3FC : -0xFF;
+ int spVarOffset = fConservative ? compLclFrameSize : varOffset + codeGen->genSPtoFPdelta();
+ int actualSPOffset = spVarOffset + addrModeOffset;
+ int actualFPOffset = varOffset + addrModeOffset;
+ int encodingLimitUpper = isFloatUsage ? 0x3FC : 0xFFF;
+ int encodingLimitLower = isFloatUsage ? -0x3FC : -0xFF;
// Use SP-based encoding. During encoding, we'll pick the best encoding for the actual offset we have.
- if (opts.MinOpts() || (actualOffset <= encodingLimitUpper))
+ if (opts.MinOpts() || (actualSPOffset <= encodingLimitUpper))
{
- offset = spOffset;
+ varOffset = spVarOffset;
*pBaseReg = compLocallocUsed ? REG_SAVED_LOCALLOC_SP : REG_SPBASE;
}
// Use Frame Pointer (R11)-based encoding.
- else if ((encodingLimitLower <= offset) && (offset <= encodingLimitUpper))
+ else if ((encodingLimitLower <= actualFPOffset) && (actualFPOffset <= encodingLimitUpper))
{
*pBaseReg = REG_FPBASE;
}
@@ -2250,7 +2259,7 @@ inline
// the "reserved register", which will get used during encoding.
else
{
- offset = spOffset;
+ varOffset = spVarOffset;
*pBaseReg = compLocallocUsed ? REG_SAVED_LOCALLOC_SP : REG_SPBASE;
}
}
@@ -2263,7 +2272,7 @@ inline
*pFPbased = FPbased;
#endif
- return offset;
+ return varOffset;
}
inline bool Compiler::lvaIsParameter(unsigned varNum)
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index 655f0e2bc8..4a3afc1ab0 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -6734,7 +6734,8 @@ target_ssize_t emitter::emitGetInsSC(instrDesc* id)
regNumber baseReg;
int offs = id->idAddr()->iiaLclVar.lvaOffset();
#if defined(_TARGET_ARM_)
- int adr = emitComp->lvaFrameAddress(varNum, id->idIsLclFPBase(), &baseReg, offs);
+ int adr =
+ emitComp->lvaFrameAddress(varNum, id->idIsLclFPBase(), &baseReg, offs, CodeGen::instIsFP(id->idIns()));
int dsp = adr + offs;
if ((id->idIns() == INS_sub) || (id->idIns() == INS_subw))
dsp = -dsp;
diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp
index 1b8d2af77d..8098c60a41 100644
--- a/src/jit/emitarm.cpp
+++ b/src/jit/emitarm.cpp
@@ -3474,7 +3474,8 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
int disp;
unsigned undisp;
- base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs);
+ base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs,
+ CodeGen::instIsFP(ins));
disp = base + offs;
undisp = unsigned_abs(disp);
@@ -3495,7 +3496,7 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
else
{
regNumber rsvdReg = codeGen->rsGetRsvdReg();
- emitIns_genStackOffset(rsvdReg, varx, offs);
+ emitIns_genStackOffset(rsvdReg, varx, offs, /* isFloatUsage */ true);
emitIns_R_R(INS_add, EA_4BYTE, rsvdReg, reg2);
emitIns_R_R_I(ins, attr, reg1, rsvdReg, 0);
return;
@@ -3519,7 +3520,7 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
{
// Load disp into a register
regNumber rsvdReg = codeGen->rsGetRsvdReg();
- emitIns_genStackOffset(rsvdReg, varx, offs);
+ emitIns_genStackOffset(rsvdReg, varx, offs, /* isFloatUsage */ false);
fmt = IF_T2_E0;
}
}
@@ -3545,7 +3546,7 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
{
// Load disp into a register
regNumber rsvdReg = codeGen->rsGetRsvdReg();
- emitIns_genStackOffset(rsvdReg, varx, offs);
+ emitIns_genStackOffset(rsvdReg, varx, offs, /* isFloatUsage */ false);
emitIns_R_R_R(ins, attr, reg1, reg2, rsvdReg);
return;
}
@@ -3583,13 +3584,14 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
}
// generate the offset of &varx + offs into a register
-void emitter::emitIns_genStackOffset(regNumber r, int varx, int offs)
+void emitter::emitIns_genStackOffset(regNumber r, int varx, int offs, bool isFloatUsage)
{
regNumber regBase;
int base;
int disp;
- base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &regBase, offs);
+ base =
+ emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &regBase, offs, isFloatUsage);
disp = base + offs;
emitIns_R_S(INS_movw, EA_4BYTE, r, varx, offs);
@@ -3633,7 +3635,8 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int va
int disp;
unsigned undisp;
- base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs);
+ base = emitComp->lvaFrameAddress(varx, emitComp->funCurrentFunc()->funKind != FUNC_ROOT, &reg2, offs,
+ CodeGen::instIsFP(ins));
disp = base + offs;
undisp = unsigned_abs(disp);
@@ -3654,7 +3657,7 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int va
else
{
regNumber rsvdReg = codeGen->rsGetRsvdReg();
- emitIns_genStackOffset(rsvdReg, varx, offs);
+ emitIns_genStackOffset(rsvdReg, varx, offs, /* isFloatUsage */ true);
emitIns_R_R(INS_add, EA_4BYTE, rsvdReg, reg2);
emitIns_R_R_I(ins, attr, reg1, rsvdReg, 0);
return;
@@ -3676,7 +3679,7 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int va
{
// Load disp into a register
regNumber rsvdReg = codeGen->rsGetRsvdReg();
- emitIns_genStackOffset(rsvdReg, varx, offs);
+ emitIns_genStackOffset(rsvdReg, varx, offs, /* isFloatUsage */ false);
fmt = IF_T2_E0;
}
assert((fmt == IF_T1_J2) || (fmt == IF_T2_E0) || (fmt == IF_T2_H0) || (fmt == IF_T2_VLDST) || (fmt == IF_T2_K1));
@@ -6363,7 +6366,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
int varNum = id->idAddr()->iiaLclVar.lvaVarNum();
unsigned ofs = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), TARGET_POINTER_SIZE);
regNumber regBase;
- int adr = emitComp->lvaFrameAddress(varNum, true, &regBase, ofs);
+ int adr = emitComp->lvaFrameAddress(varNum, true, &regBase, ofs, /* isFloatUsage */ false); // no float GC refs
if (id->idGCref() != GCT_NONE)
{
emitGCvarLiveUpd(adr + ofs, varNum, id->idGCref(), dst);
diff --git a/src/jit/emitarm.h b/src/jit/emitarm.h
index fe24007ef9..1e79af0fed 100644
--- a/src/jit/emitarm.h
+++ b/src/jit/emitarm.h
@@ -267,7 +267,7 @@ void emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int
void emitIns_S(instruction ins, emitAttr attr, int varx, int offs);
-void emitIns_genStackOffset(regNumber r, int varx, int offs);
+void emitIns_genStackOffset(regNumber r, int varx, int offs, bool isFloatUsage);
void emitIns_S_R(instruction ins, emitAttr attr, regNumber ireg, int varx, int offs);
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index a8ffe7c859..9d63409ca2 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -6719,7 +6719,7 @@ void Compiler::lvaDumpFrameLocation(unsigned lclNum)
regNumber baseReg;
#ifdef _TARGET_ARM_
- offset = lvaFrameAddress(lclNum, compLocallocUsed, &baseReg, 0);
+ offset = lvaFrameAddress(lclNum, compLocallocUsed, &baseReg, 0, /* isFloatUsage */ false);
#else
bool EBPbased;
offset = lvaFrameAddress(lclNum, &EBPbased);
diff --git a/tests/arm/corefx_linux_test_exclusions.txt b/tests/arm/corefx_linux_test_exclusions.txt
index 46698e07b0..907f404840 100644
--- a/tests/arm/corefx_linux_test_exclusions.txt
+++ b/tests/arm/corefx_linux_test_exclusions.txt
@@ -11,7 +11,6 @@ System.Memory.Tests # https://github.com/dotnet/coreclr/issues/
System.Net.Http.Functional.Tests # https://github.com/dotnet/coreclr/issues/17739
System.Net.NameResolution.Functional.Tests # https://github.com/dotnet/coreclr/issues/21224 -- JitStressRegs=1
System.Net.NameResolution.Pal.Tests # https://github.com/dotnet/coreclr/issues/17740
-System.Numerics.Vectors.Tests # https://github.com/dotnet/coreclr/issues/19537
System.Runtime.Tests # https://github.com/dotnet/coreclr/issues/21223 -- JitStress=2
System.Text.Encodings.Web.Tests # https://github.com/dotnet/coreclr/issues/21113 -- minopts
System.Text.Json.Tests # https://github.com/dotnet/coreclr/issues/21112
diff --git a/tests/arm/corefx_test_exclusions.txt b/tests/arm/corefx_test_exclusions.txt
index 315a175658..3667a3b84a 100644
--- a/tests/arm/corefx_test_exclusions.txt
+++ b/tests/arm/corefx_test_exclusions.txt
@@ -11,6 +11,5 @@ System.IO.Ports.Tests # https://github.com/dotnet/coreclr/issues/
System.Management.Tests # https://github.com/dotnet/coreclr/issues/16001
System.Linq.Expressions.Tests # JitStress=1 https://github.com/dotnet/coreclr/issues/19457
System.Net.HttpListener.Tests # https://github.com/dotnet/coreclr/issues/17584
-System.Numerics.Vectors.Tests # https://github.com/dotnet/coreclr/issues/19537
System.Runtime.Numerics.Tests # https://github.com/dotnet/coreclr/issues/18362 -- JitStress=1 JitStress=2
System.Text.RegularExpressions.Tests # https://github.com/dotnet/coreclr/issues/17754 -- timeout -- JitMinOpts only
diff --git a/tests/issues.targets b/tests/issues.targets
index 1a62cc8849..22fbf01836 100644
--- a/tests/issues.targets
+++ b/tests/issues.targets
@@ -196,12 +196,6 @@
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/devdiv_902271/DevDiv_902271/*">
<Issue>times out</Issue>
</ExcludeList>
- <ExcludeList Include="$(XunitTestBinBase)/JIT/SIMD/Matrix4x4_r/*">
- <Issue>19537</Issue>
- </ExcludeList>
- <ExcludeList Include="$(XunitTestBinBase)/JIT/SIMD/Matrix4x4_ro/*">
- <Issue>19537</Issue>
- </ExcludeList>
</ItemGroup>
<!-- Arm64 All OS -->
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.cs b/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.cs
new file mode 100644
index 0000000000..9c0de17c87
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.cs
@@ -0,0 +1,417 @@
+// 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;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+// Test derived from dotnet/corefx src\System.Numerics.Vectors\src\System\Numerics\Matrix4x4.cs, op_Multiply().
+// This was an ARM32-specific bug for addressing local variables as floats. ARM32 floating-point instructions
+// have a different offset range than integer instructions. If the local variable itself is a struct, but the
+// instruction generated is a float local field, then we were computing the offset as integer, but the actual
+// instruction was float. In certain frame layouts, the range will be out of range in this case, but we will
+// not have allocated a "reserved" register which is used for generating large offsets.
+//
+// The key functions in the JIT that are related are Compiler::compRsvdRegCheck() and Compiler::lvaFrameAddress().
+
+public class Test
+{
+ public struct BigStruct
+ {
+ public float float1;
+ public float float2;
+ public float float3;
+ public float float4;
+ public float float5;
+ public float float6;
+ public float float7;
+ public float float8;
+ public float float9;
+ public float float10;
+ public float float11;
+ public float float12;
+ public float float13;
+ public float float14;
+ public float float15;
+ public float float16;
+ public float float17;
+ public float float18;
+ public float float19;
+ public float float20;
+ public float float21;
+ public float float22;
+ public float float23;
+ public float float24;
+ public float float25;
+ public float float26;
+ public float float27;
+ public float float28;
+ public float float29;
+ public float float30;
+ public float float31;
+ public float float32;
+ public float float33;
+ public float float34;
+ public float float35;
+ public float float36;
+ public float float37;
+ public float float38;
+ public float float39;
+ public float float40;
+ public float float41;
+ public float float42;
+ public float float43;
+ public float float44;
+ public float float45;
+ public float float46;
+ public float float47;
+ public float float48;
+ public float float49;
+ public float float50;
+ public float float51;
+ public float float52;
+ public float float53;
+ public float float54;
+ public float float55;
+ public float float56;
+ public float float57;
+ public float float58;
+ public float float59;
+ public float float60;
+ public float float61;
+ public float float62;
+ public float float63;
+ public float float64;
+ public float float65;
+ public float float66;
+ public float float67;
+ public float float68;
+ public float float69;
+ public float float70;
+ public float float71;
+ public float float72;
+ public float float73;
+ public float float74;
+ public float float75;
+ public float float76;
+ public float float77;
+ public float float78;
+ public float float79;
+ public float float80;
+ public float float81;
+ public float float82;
+ public float float83;
+ public float float84;
+ public float float85;
+ public float float86;
+ public float float87;
+ public float float88;
+ public float float89;
+ public float float90;
+ public float float91;
+ public float float92;
+ public float float93;
+ public float float94;
+ public float float95;
+ public float float96;
+ public float float97;
+ public float float98;
+ public float float99;
+ public float float100;
+ public float float101;
+ public float float102;
+ public float float103;
+ public float float104;
+ public float float105;
+ public float float106;
+ public float float107;
+ public float float108;
+ public float float109;
+ public float float110;
+ public float float111;
+ public float float112;
+ public float float113;
+ public float float114;
+ public float float115;
+ public float float116;
+ public float float117;
+ public float float118;
+ public float float119;
+ public float float120;
+ public float float121;
+ public float float122;
+ public float float123;
+ public float float124;
+ public float float125;
+ public float float126;
+ public float float127;
+ public float float128;
+ public float float129;
+ public float float130;
+ public float float131;
+ public float float132;
+ public float float133;
+ public float float134;
+ public float float135;
+ public float float136;
+ public float float137;
+ public float float138;
+ public float float139;
+ public float float140;
+ public float float141;
+ public float float142;
+ public float float143;
+ public float float144;
+ public float float145;
+ public float float146;
+ public float float147;
+ public float float148;
+ public float float149;
+ public float float150;
+ public float float151;
+ public float float152;
+ public float float153;
+ public float float154;
+ public float float155;
+ public float float156;
+ public float float157;
+ public float float158;
+ public float float159;
+ public float float160;
+ public float float161;
+ public float float162;
+ public float float163;
+ public float float164;
+ public float float165;
+ public float float166;
+ public float float167;
+ public float float168;
+ public float float169;
+ public float float170;
+ public float float171;
+ public float float172;
+ public float float173;
+ public float float174;
+ public float float175;
+ public float float176;
+ public float float177;
+ public float float178;
+ public float float179;
+ public float float180;
+ public float float181;
+ public float float182;
+ public float float183;
+ public float float184;
+ public float float185;
+ public float float186;
+ public float float187;
+ public float float188;
+ public float float189;
+ public float float190;
+ public float float191;
+ public float float192;
+ public float float193;
+ public float float194;
+ public float float195;
+ public float float196;
+ public float float197;
+ public float float198;
+ public float float199;
+ public float float200;
+ public float float201;
+ public float float202;
+ public float float203;
+ public float float204;
+ public float float205;
+ public float float206;
+ public float float207;
+ public float float208;
+ public float float209;
+ public float float210;
+ public float float211;
+ public float float212;
+ public float float213;
+ public float float214;
+ public float float215;
+ public float float216;
+ public float float217;
+ public float float218;
+ public float float219;
+ public float float220;
+ public float float221;
+ public float float222;
+ public float float223;
+ public float float224;
+ public float float225;
+ public float float226;
+ public float float227;
+ public float float228;
+ public float float229;
+ public float float230;
+ public float float231;
+ public float float232;
+ public float float233;
+ public float float234;
+ public float float235;
+ public float float236;
+ public float float237;
+ public float float238;
+ public float float239;
+ public float float240;
+ public float float241;
+ public float float242;
+ public float float243;
+ public float float244;
+ public float float245;
+ public float float246;
+ public float float247;
+ public float float248;
+ public float float249;
+ public float float250;
+ public float float251;
+ public float float252;
+ public float float253;
+ public float float254;
+ public float float255;
+ }
+
+ public struct Matrix4x4
+ {
+ public float M11;
+ public float M12;
+ public float M13;
+ public float M14;
+
+ public float M21;
+ public float M22;
+ public float M23;
+ public float M24;
+
+ public float M31;
+ public float M32;
+ public float M33;
+ public float M34;
+
+ public float M41;
+ public float M42;
+ public float M43;
+ public float M44;
+
+ /// <summary>
+ /// Constructs a Matrix4x4 from the given components.
+ /// </summary>
+ public Matrix4x4(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44)
+ {
+ this.M11 = m11;
+ this.M12 = m12;
+ this.M13 = m13;
+ this.M14 = m14;
+
+ this.M21 = m21;
+ this.M22 = m22;
+ this.M23 = m23;
+ this.M24 = m24;
+
+ this.M31 = m31;
+ this.M32 = m32;
+ this.M33 = m33;
+ this.M34 = m34;
+
+ this.M41 = m41;
+ this.M42 = m42;
+ this.M43 = m43;
+ this.M44 = m44;
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether the given two matrices are equal.
+ /// </summary>
+ /// <param name="value1">The first matrix to compare.</param>
+ /// <param name="value2">The second matrix to compare.</param>
+ /// <returns>True if the given matrices are equal; False otherwise.</returns>
+ public static bool Equals(Matrix4x4 value1, Matrix4x4 value2)
+ {
+ return (value1.M11 == value2.M11 && value1.M22 == value2.M22 && value1.M33 == value2.M33 && value1.M44 == value2.M44 && // Check diagonal element first for early out.
+ value1.M12 == value2.M12 && value1.M13 == value2.M13 && value1.M14 == value2.M14 && value1.M21 == value2.M21 &&
+ value1.M23 == value2.M23 && value1.M24 == value2.M24 && value1.M31 == value2.M31 && value1.M32 == value2.M32 &&
+ value1.M34 == value2.M34 && value1.M41 == value2.M41 && value1.M42 == value2.M42 && value1.M43 == value2.M43);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void AddHelper(ref BigStruct b)
+ {
+ b.float1 += 1.0F;
+ b.float255 += 2.0F;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static Matrix4x4 AddTest(Matrix4x4 value1, Matrix4x4 value2)
+ {
+ BigStruct b = new BigStruct();
+
+ b.float1 = value1.M11 + value2.M11;
+ b.float255 = value1.M12 + value2.M12;
+
+ AddHelper(ref b);
+
+ Matrix4x4 m;
+
+ m = value1;
+ m.M11 = b.float1 + b.float255;
+ m.M12 = b.float1 - b.float255;
+
+ return m;
+ }
+
+ /// <summary>
+ /// Returns a String representing this matrix instance.
+ /// </summary>
+ /// <returns>The string representation.</returns>
+ public override string ToString()
+ {
+ return string.Format("{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} }}",
+ M11, M12, M13, M14,
+ M21, M22, M23, M24,
+ M31, M32, M33, M34,
+ M41, M42, M43, M44);
+ }
+
+ public static int Main()
+ {
+ Matrix4x4 m1 = new Matrix4x4(1.0F,2.0F,3.0F,4.0F,
+ 5.0F,6.0F,7.0F,8.0F,
+ 9.0F,10.0F,11.0F,12.0F,
+ 13.0F,14.0F,15.0F,16.0F);
+ Matrix4x4 m2 = new Matrix4x4(13.0F,14.0F,15.0F,16.0F,
+ 9.0F,10.0F,11.0F,12.0F,
+ 5.0F,6.0F,7.0F,8.0F,
+ 1.0F,2.0F,3.0F,4.0F);
+
+ Matrix4x4 m3 = AddTest(m1,m2);
+
+ Matrix4x4 mresult = new Matrix4x4(33.0F,-3.0F,3.0F,4.0F,
+ 5.0F,6.0F,7.0F,8.0F,
+ 9.0F,10.0F,11.0F,12.0F,
+ 13.0F,14.0F,15.0F,16.0F);
+ if (Equals(m3,mresult))
+ {
+ Console.WriteLine("PASS");
+ return 100;
+ }
+ else
+ {
+ Console.WriteLine("FAIL: matrices don't match");
+ Console.WriteLine(" m3 = {0}", m3.ToString());
+ Console.WriteLine(" mresult = {0}", mresult.ToString());
+ return 1;
+ }
+ }
+
+ }
+}
+
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.csproj
new file mode 100644
index 0000000000..3f44b60d19
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_19537/GitHub_19537.csproj
@@ -0,0 +1,35 @@
+<?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>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{2649FAFE-07BF-4F93-8120-BA9A69285ABB}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <CLRTestPriority>1</CLRTestPriority>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+ <PropertyGroup>
+ <DebugType>None</DebugType>
+ <Optimize>True</Optimize>
+ <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildProjectName).cs" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>