From a35f0620007ca6f8f6c0223b7f6e0d7573a40121 Mon Sep 17 00:00:00 2001 From: Brian Sullivan Date: Mon, 5 Nov 2018 18:26:10 -0800 Subject: [PATCH] Fixes issue 18259 The problem here was that we have an indirection of a LclVar that was a pointer to an array element whose type is a struct. The following discussion refers to the test case GitHub_18259/GitHub_18259.cs We recorded that the struct field F1.F0 is assigned 1234u. With the bug all subsequent reads of vr7.F0 return this value. We miss the update to zero because we didn't add the Zero Field sequence value to the LclVar vr7 Added Test case GitHub_18259.cs Added Test case GitHub_20838.cs --- src/jit/optimizer.cpp | 12 +- src/jit/valuenum.cpp | 169 +++-- .../JitBlue/GitHub_18259/GitHub_18259.cs | 49 ++ .../JitBlue/GitHub_18259/GitHub_18259.csproj | 17 + .../JitBlue/GitHub_20838/GitHub_20838.cs | 628 ++++++++++++++++++ .../JitBlue/GitHub_20838/GitHub_20838.csproj | 17 + 6 files changed, 831 insertions(+), 61 deletions(-) create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.cs create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.csproj create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.cs create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.csproj diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp index 30a2f1e64417..1f57e053d08d 100644 --- a/src/jit/optimizer.cpp +++ b/src/jit/optimizer.cpp @@ -7757,14 +7757,16 @@ void Compiler::optComputeLoopSideEffectsOfBlock(BasicBlock* blk) ArrayInfo arrInfo; bool b = GetArrayInfoMap()->Lookup(addrArg, &arrInfo); assert(b); - CORINFO_CLASS_HANDLE elemType = + CORINFO_CLASS_HANDLE elemTypeEq = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType); - tree->gtVNPair.SetBoth( - vnStore->VNForFunc(TYP_BYREF, VNF_PtrToArrElem, - vnStore->VNForHandle(ssize_t(elemType), GTF_ICON_CLASS_HDL), + ValueNum elemTypeEqVN = + vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL); + ValueNum ptrToArrElemVN = + vnStore->VNForFunc(TYP_BYREF, VNF_PtrToArrElem, elemTypeEqVN, // The rest are dummy arguments. vnStore->VNForNull(), vnStore->VNForNull(), - vnStore->VNForNull())); + vnStore->VNForNull()); + tree->gtVNPair.SetBoth(ptrToArrElemVN); } } } diff --git a/src/jit/valuenum.cpp b/src/jit/valuenum.cpp index b090b80fc7e6..6238ba9f20e1 100644 --- a/src/jit/valuenum.cpp +++ b/src/jit/valuenum.cpp @@ -3520,8 +3520,7 @@ ValueNum ValueNumStore::VNApplySelectors(ValueNumKind vnk, printf(" VNApplySelectors:\n"); const char* modName; const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName); - printf(" VNForHandle(Fseq[%s]) is " FMT_VN ", fieldType is %s", fldName, fldHndVN, - varTypeName(fieldType)); + printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s", fldName, fldHndVN, varTypeName(fieldType)); if (varTypeIsStruct(fieldType)) { printf(", size = %d", structSize); @@ -3662,24 +3661,46 @@ ValueNum ValueNumStore::VNApplySelectorsAssign( } // Otherwise, fldHnd is a real field handle. - CORINFO_FIELD_HANDLE fldHnd = fieldSeq->m_fieldHnd; + CORINFO_FIELD_HANDLE fldHnd = fieldSeq->m_fieldHnd; + ValueNum fldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL); noway_assert(fldHnd != nullptr); CorInfoType fieldCit = m_pComp->info.compCompHnd->getFieldType(fldHnd); var_types fieldType = JITtype2varType(fieldCit); - ValueNum fieldHndVN = VNForHandle(ssize_t(fldHnd), GTF_ICON_FIELD_HDL); ValueNum elemAfter; if (fieldSeq->m_next) { - ValueNum fseqMap = VNForMapSelect(vnk, fieldType, map, fieldHndVN); +#ifdef DEBUG + if (m_pComp->verbose) + { + const char* modName; + const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName); + printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN, + varTypeName(fieldType)); + } +#endif + ValueNum fseqMap = VNForMapSelect(vnk, fieldType, map, fldHndVN); elemAfter = VNApplySelectorsAssign(vnk, fseqMap, fieldSeq->m_next, elem, indType, block); } else { +#ifdef DEBUG + if (m_pComp->verbose) + { + if (fieldSeq->m_next == nullptr) + { + printf(" VNApplySelectorsAssign:\n"); + } + const char* modName; + const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName); + printf(" VNForHandle(%s) is " FMT_VN ", fieldType is %s\n", fldName, fldHndVN, + varTypeName(fieldType)); + } +#endif elemAfter = VNApplySelectorsAssignTypeCoerce(elem, indType, block); } - ValueNum newMap = VNForMapStore(fieldType, map, fieldHndVN, elemAfter); + ValueNum newMap = VNForMapStore(fieldType, map, fldHndVN, elemAfter); return newMap; } } @@ -3726,13 +3747,9 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq) #ifdef DEBUG if (m_pComp->verbose) { - printf(" fieldHnd " FMT_VN " is ", fieldHndVN); - vnDump(m_pComp, fieldHndVN); - printf("\n"); - - printf(" fieldSeq " FMT_VN " is ", fieldSeqVN); + printf(" FieldSeq"); vnDump(m_pComp, fieldSeqVN); - printf("\n"); + printf(" is " FMT_VN "\n", fieldSeqVN); } #endif @@ -3804,7 +3821,7 @@ ValueNum ValueNumStore::ExtendPtrVN(GenTree* opA, GenTree* opB) FieldSeqNode* fldSeq = opB->gtIntCon.gtFieldSeq; if (fldSeq != nullptr) { - return ExtendPtrVN(opA, opB->gtIntCon.gtFieldSeq); + return ExtendPtrVN(opA, fldSeq); } } return NoVN; @@ -6109,7 +6126,7 @@ ValueNum Compiler::fgMemoryVNForLoopSideEffects(MemoryKind memoryKind, { const char* modName; const char* fldName = eeGetFieldName(fldHnd, &modName); - printf(" VNForHandle(Fseq[%s]) is " FMT_VN "\n", fldName, fldHndVN); + printf(" VNForHandle(%s) is " FMT_VN "\n", fldName, fldHndVN); } #endif // DEBUG @@ -6701,20 +6718,28 @@ void Compiler::fgValueNumberTree(GenTree* tree) { GenTreeLclVarCommon* lcl = tree->AsLclVarCommon(); unsigned lclNum = lcl->gtLclNum; + LclVarDsc* varDsc = &lvaTable[lclNum]; + // Do we have a Use (read) of the LclVar? + // if ((lcl->gtFlags & GTF_VAR_DEF) == 0 || (lcl->gtFlags & GTF_VAR_USEASG)) // If it is a "pure" def, will handled as part of the assignment. { - LclVarDsc* varDsc = &lvaTable[lcl->gtLclNum]; + bool generateUniqueVN = false; + FieldSeqNode* zeroOffsetFldSeq = nullptr; + + // When we have a TYP_BYREF LclVar it can have a zero offset field sequence that needs to be added + if (typ == TYP_BYREF) + { + GetZeroOffsetFieldMap()->Lookup(tree, &zeroOffsetFldSeq); + } + if (varDsc->lvPromoted && varDsc->lvFieldCnt == 1) { // If the promoted var has only one field var, treat like a use of the field var. lclNum = varDsc->lvFieldLclStart; } - // Initialize to the undefined value, so we know whether we hit any of the cases here. - lcl->gtVNPair = ValueNumPair(); - if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM) { // Not an SSA variable. @@ -6731,18 +6756,17 @@ void Compiler::fgValueNumberTree(GenTree* tree) else { // Assign odd cases a new, unique, VN. - lcl->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lcl->TypeGet())); + generateUniqueVN = true; } } else { - var_types varType = varDsc->TypeGet(); ValueNumPair wholeLclVarVNP = varDsc->GetPerSsaData(lcl->gtSsaNum)->m_vnPair; // Check for mismatched LclVar size // unsigned typSize = genTypeSize(genActualType(typ)); - unsigned varSize = genTypeSize(genActualType(varType)); + unsigned varSize = genTypeSize(genActualType(varDsc->TypeGet())); if (typSize == varSize) { @@ -6755,54 +6779,76 @@ void Compiler::fgValueNumberTree(GenTree* tree) // the indirection is reading less that the whole LclVar // create a new VN that represent the partial value // - ValueNumPair partialLclVarVNP = vnStore->VNPairForCast(wholeLclVarVNP, typ, varType); - lcl->gtVNPair = partialLclVarVNP; + ValueNumPair partialLclVarVNP = + vnStore->VNPairForCast(wholeLclVarVNP, typ, varDsc->TypeGet()); + lcl->gtVNPair = partialLclVarVNP; } else { assert(typSize > varSize); // the indirection is reading beyond the end of the field // - lcl->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, typ)); // return a new unique value - // number + generateUniqueVN = true; } } } - // Temporary, to make progress. - // TODO-CQ: This should become an assert again... - if (lcl->gtVNPair.GetLiberal() == ValueNumStore::NoVN) + + if (!generateUniqueVN) { - assert(lcl->gtVNPair.GetConservative() == ValueNumStore::NoVN); - - // We don't want to fabricate arbitrary value numbers to things we can't reason about. - // So far, we know about two of these cases: - // Case 1) We have a local var who has never been defined but it's seen as a use. - // This is the case of storeIndir(addr(lclvar)) = expr. In this case since we only - // take the address of the variable, this doesn't mean it's a use nor we have to - // initialize it, so in this very rare case, we fabricate a value number. - // Case 2) Local variables that represent structs which are assigned using CpBlk. - GenTree* nextNode = lcl->gtNext; - assert((nextNode->gtOper == GT_ADDR && nextNode->gtOp.gtOp1 == lcl) || - varTypeIsStruct(lcl->TypeGet())); - lcl->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, lcl->TypeGet())); + // There are a couple of cases where we haven't assigned a valid value number to 'lcl' + // + if (lcl->gtVNPair.GetLiberal() == ValueNumStore::NoVN) + { + // So far, we know about two of these cases: + // Case 1) We have a local var who has never been defined but it's seen as a use. + // This is the case of storeIndir(addr(lclvar)) = expr. In this case since we only + // take the address of the variable, this doesn't mean it's a use nor we have to + // initialize it, so in this very rare case, we fabricate a value number. + // Case 2) Local variables that represent structs which are assigned using CpBlk. + // + // Make sure we have either case 1 or case 2 + // + GenTree* nextNode = lcl->gtNext; + assert((nextNode->gtOper == GT_ADDR && nextNode->gtOp.gtOp1 == lcl) || + varTypeIsStruct(lcl->TypeGet())); + + // We will assign a unique value number for these + // + generateUniqueVN = true; + } } - assert(lcl->gtVNPair.BothDefined()); - } - // TODO-Review: For the short term, we have a workaround for copyblk/initblk. Those that use - // addrSpillTemp will have a statement like "addrSpillTemp = addr(local)." If we previously decided - // that this block operation defines the local, we will have labeled the "local" node as a DEF - // This flag propagates to the "local" on the RHS. So we'll assume that this is correct, - // and treat it as a def (to a new, unique VN). + if (!generateUniqueVN && (zeroOffsetFldSeq != nullptr)) + { + ValueNum addrExtended = vnStore->ExtendPtrVN(lcl, zeroOffsetFldSeq); + if (addrExtended != ValueNumStore::NoVN) + { + lcl->gtVNPair.SetBoth(addrExtended); + } + } + + if (generateUniqueVN) + { + ValueNum uniqVN = vnStore->VNForExpr(compCurBB, lcl->TypeGet()); + lcl->gtVNPair.SetBoth(uniqVN); + } + } else if ((lcl->gtFlags & GTF_VAR_DEF) != 0) { - LclVarDsc* varDsc = &lvaTable[lcl->gtLclNum]; + // We have a Def (write) of the LclVar + + // TODO-Review: For the short term, we have a workaround for copyblk/initblk. Those that use + // addrSpillTemp will have a statement like "addrSpillTemp = addr(local)." If we previously decided + // that this block operation defines the local, we will have labeled the "local" node as a DEF + // This flag propagates to the "local" on the RHS. So we'll assume that this is correct, + // and treat it as a def (to a new, unique VN). + // if (lcl->gtSsaNum != SsaConfig::RESERVED_SSA_NUM) { - lvaTable[lclNum] - .GetPerSsaData(lcl->gtSsaNum) - ->m_vnPair.SetBoth(vnStore->VNForExpr(compCurBB, lcl->TypeGet())); + ValueNum uniqVN = vnStore->VNForExpr(compCurBB, lcl->TypeGet()); + varDsc->GetPerSsaData(lcl->gtSsaNum)->m_vnPair.SetBoth(uniqVN); } + lcl->gtVNPair = ValueNumPair(); // Avoid confusion -- we don't set the VN of a lcl being defined. } } @@ -7298,11 +7344,14 @@ void Compiler::fgValueNumberTree(GenTree* tree) ValueNum inxVN = funcApp.m_args[2]; FieldSeqNode* fldSeq = vnStore->FieldSeqVNToFieldSeq(funcApp.m_args[3]); - // Does the child of the GT_IND 'arg' have an associated zero-offset field sequence? - FieldSeqNode* addrFieldSeq = nullptr; - if (GetZeroOffsetFieldMap()->Lookup(arg, &addrFieldSeq)) + if (arg->gtOper != GT_LCL_VAR) { - fldSeq = GetFieldSeqStore()->Append(addrFieldSeq, fldSeq); + // Does the child of the GT_IND 'arg' have an associated zero-offset field sequence? + FieldSeqNode* addrFieldSeq = nullptr; + if (GetZeroOffsetFieldMap()->Lookup(arg, &addrFieldSeq)) + { + fldSeq = GetFieldSeqStore()->Append(addrFieldSeq, fldSeq); + } } #ifdef DEBUG @@ -7374,6 +7423,7 @@ void Compiler::fgValueNumberTree(GenTree* tree) assert(staticOffset == nullptr); } #endif // DEBUG + // Get the first (instance or static) field from field seq. GcHeap[field] will yield // the "field map". if (fldSeq->IsFirstElemFieldSeq()) @@ -7681,6 +7731,10 @@ void Compiler::fgValueNumberTree(GenTree* tree) // Get the array element type equivalence class rep. CORINFO_CLASS_HANDLE elemTypeEq = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType); ValueNum elemTypeEqVN = vnStore->VNForHandle(ssize_t(elemTypeEq), GTF_ICON_CLASS_HDL); + JITDUMP(" VNForHandle(arrElemType: %s) is " FMT_VN "\n", + (arrInfo.m_elemType == TYP_STRUCT) ? eeGetClassName(arrInfo.m_elemStructType) + : varTypeName(arrInfo.m_elemType), + elemTypeEqVN) // We take the "VNNormalValue"s here, because if either has exceptional outcomes, they will be captured // as part of the value of the composite "addr" operation... @@ -7711,6 +7765,7 @@ void Compiler::fgValueNumberTree(GenTree* tree) } } #endif // DEBUG + // We now need to retrieve the value number for the array element value // and give this value number to the GT_IND node 'tree' // We do this whenever we have an rvalue, but we don't do it for a @@ -7800,6 +7855,7 @@ void Compiler::fgValueNumberTree(GenTree* tree) assert(staticOffset == nullptr); } #endif // DEBUG + // Get a field sequence for just the first field in the sequence // FieldSeqNode* firstFieldOnly = GetFieldSeqStore()->CreateSingleton(fldSeq2->m_fieldHnd); @@ -8361,6 +8417,7 @@ void Compiler::fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueN GenTree* indirectCellAddress = call->fgArgInfo->GetArgNode(0); assert(indirectCellAddress->IsCnsIntOrI() && indirectCellAddress->gtRegNum == REG_R2R_INDIRECT_PARAM); #endif // DEBUG + // For ARM indirectCellAddress is consumed by the call itself, so it should have added as an implicit argument // in morph. So we do not need to use EntryPointAddrAsArg0, because arg0 is already an entry point addr. useEntryPointAddrAsArg0 = false; diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.cs b/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.cs new file mode 100644 index 000000000000..86aa93811758 --- /dev/null +++ b/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.cs @@ -0,0 +1,49 @@ +// 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; + +// Test case for issue 18259 +// +// We were a missing check for ZeroOffsetFldSeq values on LclVar reads +// +// Debug: Outputs 0 +// Release: Outputs 1234 + +struct S1 +{ + public uint F0; + public S1(uint f0): this() { F0 = f0; } +} + +struct S2 +{ + public S1 F1; + public int F2; + public S2(S1 f1): this() { F1 = f1; F2 = 1; } +} + +public class Program +{ + static S2[] s_11 = new S2[]{new S2(new S1(1234u))}; // Assigns 1234 to F1.F0 + public static int Main() + { + ref S1 vr7 = ref s_11[0].F1; + vr7.F0 = vr7.F0; + + vr7.F0 = 0; // Bug: We fail to update the Map with the proper ValueNum here. + + if (vr7.F0 != 0) // Bug: We continue to return the old value for vr7.F0 + { + System.Console.WriteLine(vr7.F0); + System.Console.WriteLine("Failed"); + return 101; + } + else + { + System.Console.WriteLine("Passed"); + return 100; + } + } +} diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.csproj new file mode 100644 index 000000000000..d86ed9f3d765 --- /dev/null +++ b/tests/src/JIT/Regression/JitBlue/GitHub_18259/GitHub_18259.csproj @@ -0,0 +1,17 @@ + + + + + Release + AnyCPU + $(MSBuildProjectName) + Exe + + True + + + + + + + \ No newline at end of file diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.cs b/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.cs new file mode 100644 index 000000000000..2b0f4cb66f6b --- /dev/null +++ b/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.cs @@ -0,0 +1,628 @@ +// 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; + +// Test case for fix 20838 +// We are a missing check for ZeroOffsetFldSeq values on LclVar reads +// without the fix several cases in this test fail because value numbering +// gives us the old value instead of the modified value. +// +// CoreRun.exe GitHub_18259.exe +// Failed - Test_e0_S2_F3_F1() - 640 -- 512 + 128 +// Failed - Test_e0_S2_F4_F1() - 960 -- 512 + 256 + 128 + 64 +// Failed - Test_e1_S2_F3_F1() - 640 +// Failed - Test_e1_S2_F4_F1() - 960 + +struct S1 +{ + public int F1; + public int F2; + + public S1(int a1, int a2) { F1 = a1; F2 = a2; } +} + +struct S2 +{ + public S1 F3; + public S1 F4; + + public S2(S1 a1, S1 a2) { F3 = a1; F4 = a2; } +} + +public class Program +{ + + static S1 ss_S1a = new S1(101, 102); + static S1 ss_S1b = new S1(103, 104); + static S1 ss_S1c = new S1(105, 106); + static S1 ss_S1d = new S1(107, 108); + static S2 ss_S2a = new S2(ss_S1a, ss_S1b); + static S2 ss_S2b = new S2(ss_S1c, ss_S1d); + static S2[] sa_S2 = new S2[] { ss_S2a, ss_S2b }; + + static bool Test_e0_S2_F3_F1() + { + ref S2 ref_e0_S2 = ref sa_S2[0]; + ref S1 ref_e0_S2_F3 = ref sa_S2[0].F3; + ref int ref_e0_S2_F3_F1 = ref sa_S2[0].F3.F1; + + int result = 0; + + if (sa_S2[0].F3.F1 != 101) + { + result |= 1; + } + + if (ref_e0_S2_F3_F1 != 101) + { + result |= 2; + } + + if (ref_e0_S2_F3.F1 != 101) + { + result |= 4; + } + + if (ref_e0_S2.F3.F1 != 101) + { + result |= 8; + } + + if (ref_e0_S2_F3.F1 != 101) + { + result |= 16; + } + + ref_e0_S2_F3.F1 = 99; + + if (sa_S2[0].F3.F1 != 99) + { + result |= 32; + } + + if (ref_e0_S2_F3_F1 != 99) + { + result |= 64; + } + + if (ref_e0_S2_F3.F1 != 99) + { + result |= 128; + } + + if (ref_e0_S2.F3.F1 != 99) + { + result |= 256; + } + + if (ref_e0_S2_F3.F1 != 99) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e0_S2_F3_F1() - " + result); + return false; + } + + return true; + } + + static bool Test_e0_S2_F3_F2() + { + ref S2 ref_e0_S2 = ref sa_S2[0]; + ref S1 ref_e0_S2_F3 = ref sa_S2[0].F3; + ref int ref_e0_S2_F3_F2 = ref sa_S2[0].F3.F2; + + int result = 0; + + if (sa_S2[0].F3.F2 != 102) + { + result |= 1; + } + + if (ref_e0_S2_F3_F2 != 102) + { + result |= 2; + } + + if (ref_e0_S2_F3.F2 != 102) + { + result |= 4; + } + + if (ref_e0_S2.F3.F2 != 102) + { + result |= 8; + } + + if (ref_e0_S2_F3.F2 != 102) + { + result |= 16; + } + + ref_e0_S2_F3.F2 = 98; + + if (sa_S2[0].F3.F2 != 98) + { + result |= 32; + } + + if (ref_e0_S2_F3_F2 != 98) + { + result |= 64; + } + + if (ref_e0_S2_F3.F2 != 98) + { + result |= 128; + } + + if (ref_e0_S2.F3.F2 != 98) + { + result |= 256; + } + + if (ref_e0_S2_F3.F2 != 98) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e0_S2_F3_F2() - " + result); + return false; + } + + return true; + } + + static bool Test_e0_S2_F4_F1() + { + ref S2 ref_e0_S2 = ref sa_S2[0]; + ref S1 ref_e0_S2_F4 = ref sa_S2[0].F4; + ref int ref_e0_S2_F4_F1 = ref sa_S2[0].F4.F1; + + int result = 0; + + if (sa_S2[0].F4.F1 != 103) + { + result |= 1; + } + + if (ref_e0_S2_F4_F1 != 103) + { + result |= 2; + } + + if (ref_e0_S2_F4.F1 != 103) + { + result |= 4; + } + + if (ref_e0_S2.F4.F1 != 103) + { + result |= 8; + } + + if (ref_e0_S2_F4.F1 != 103) + { + result |= 16; + } + + ref_e0_S2_F4.F1 = 97; + + if (sa_S2[0].F4.F1 != 97) + { + result |= 32; + } + + if (ref_e0_S2_F4_F1 != 97) + { + result |= 64; + } + + if (ref_e0_S2_F4.F1 != 97) + { + result |= 128; + } + + if (ref_e0_S2.F4.F1 != 97) + { + result |= 256; + } + + if (ref_e0_S2_F4.F1 != 97) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e0_S2_F4_F1() - " + result); + return false; + } + + return true; + } + + static bool Test_e0_S2_F4_F2() + { + ref S2 ref_e0_S2 = ref sa_S2[0]; + ref S1 ref_e0_S2_F4 = ref sa_S2[0].F4; + ref int ref_e0_S2_F4_F2 = ref sa_S2[0].F4.F2; + + int result = 0; + + if (sa_S2[0].F4.F2 != 104) + { + result |= 1; + } + + if (ref_e0_S2_F4_F2 != 104) + { + result |= 2; + } + + if (ref_e0_S2_F4.F2 != 104) + { + result |= 4; + } + + if (ref_e0_S2.F4.F2 != 104) + { + result |= 8; + } + + if (ref_e0_S2_F4.F2 != 104) + { + result |= 16; + } + + ref_e0_S2_F4.F2 = 96; + + if (sa_S2[0].F4.F2 != 96) + { + result |= 32; + } + + if (ref_e0_S2_F4_F2 != 96) + { + result |= 64; + } + + if (ref_e0_S2_F4.F2 != 96) + { + result |= 128; + } + + if (ref_e0_S2.F4.F2 != 96) + { + result |= 256; + } + + if (ref_e0_S2_F4.F2 != 96) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e0_S2_F4_F2() - " + result); + return false; + } + + return true; + } + + static bool Test_e1_S2_F3_F1() + { + ref S2 ref_e1_S2 = ref sa_S2[1]; + ref S1 ref_e1_S2_F3 = ref sa_S2[1].F3; + ref int ref_e1_S2_F3_F1 = ref sa_S2[1].F3.F1; + + int result = 0; + + if (sa_S2[1].F3.F1 != 105) + { + result |= 1; + } + + if (ref_e1_S2_F3_F1 != 105) + { + result |= 2; + } + + if (ref_e1_S2_F3.F1 != 105) + { + result |= 4; + } + + if (ref_e1_S2.F3.F1 != 105) + { + result |= 8; + } + + if (ref_e1_S2_F3.F1 != 105) + { + result |= 16; + } + + ref_e1_S2_F3.F1 = 95; + + if (sa_S2[1].F3.F1 != 95) + { + result |= 32; + } + + if (ref_e1_S2_F3_F1 != 95) + { + result |= 64; + } + + if (ref_e1_S2_F3.F1 != 95) + { + result |= 128; + } + + if (ref_e1_S2.F3.F1 != 95) + { + result |= 256; + } + + if (ref_e1_S2_F3.F1 != 95) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e1_S2_F3_F1() - " + result); + return false; + } + + return true; + } + + static bool Test_e1_S2_F3_F2() + { + ref S2 ref_e1_S2 = ref sa_S2[1]; + ref S1 ref_e1_S2_F3 = ref sa_S2[1].F3; + ref int ref_e1_S2_F3_F2 = ref sa_S2[1].F3.F2; + + int result = 0; + + if (sa_S2[1].F3.F2 != 106) + { + result |= 1; + } + + if (ref_e1_S2_F3_F2 != 106) + { + result |= 2; + } + + if (ref_e1_S2_F3.F2 != 106) + { + result |= 4; + } + + if (ref_e1_S2.F3.F2 != 106) + { + result |= 8; + } + + if (ref_e1_S2_F3.F2 != 106) + { + result |= 16; + } + + ref_e1_S2_F3.F2 = 94; + + if (sa_S2[1].F3.F2 != 94) + { + result |= 32; + } + + if (ref_e1_S2_F3_F2 != 94) + { + result |= 64; + } + + if (ref_e1_S2_F3.F2 != 94) + { + result |= 128; + } + + if (ref_e1_S2.F3.F2 != 94) + { + result |= 256; + } + + if (ref_e1_S2_F3.F2 != 94) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e1_S2_F3_F2() - " + result); + return false; + } + + return true; + } + + static bool Test_e1_S2_F4_F1() + { + ref S2 ref_e1_S2 = ref sa_S2[1]; + ref S1 ref_e1_S2_F4 = ref sa_S2[1].F4; + ref int ref_e1_S2_F4_F1 = ref sa_S2[1].F4.F1; + + int result = 0; + + if (sa_S2[1].F4.F1 != 107) + { + result |= 1; + } + + if (ref_e1_S2_F4_F1 != 107) + { + result |= 2; + } + + if (ref_e1_S2_F4.F1 != 107) + { + result |= 4; + } + + if (ref_e1_S2.F4.F1 != 107) + { + result |= 8; + } + + if (ref_e1_S2_F4.F1 != 107) + { + result |= 16; + } + + ref_e1_S2_F4.F1 = 93; + + if (sa_S2[1].F4.F1 != 93) + { + result |= 32; + } + + if (ref_e1_S2_F4_F1 != 93) + { + result |= 64; + } + + if (ref_e1_S2_F4.F1 != 93) + { + result |= 128; + } + + if (ref_e1_S2.F4.F1 != 93) + { + result |= 256; + } + + if (ref_e1_S2_F4.F1 != 93) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e1_S2_F4_F1() - " + result); + return false; + } + + return true; + } + + static bool Test_e1_S2_F4_F2() + { + ref S2 ref_e1_S2 = ref sa_S2[1]; + ref S1 ref_e1_S2_F4 = ref sa_S2[1].F4; + ref int ref_e1_S2_F4_F2 = ref sa_S2[1].F4.F2; + + int result = 0; + + if (sa_S2[1].F4.F2 != 108) + { + result |= 1; + } + + if (ref_e1_S2_F4_F2 != 108) + { + result |= 2; + } + + if (ref_e1_S2_F4.F2 != 108) + { + result |= 4; + } + + if (ref_e1_S2.F4.F2 != 108) + { + result |= 8; + } + + if (ref_e1_S2_F4.F2 != 108) + { + result |= 16; + } + + ref_e1_S2_F4.F2 = 92; + + if (sa_S2[1].F4.F2 != 92) + { + result |= 32; + } + + if (ref_e1_S2_F4_F2 != 92) + { + result |= 64; + } + + if (ref_e1_S2_F4.F2 != 92) + { + result |= 128; + } + + if (ref_e1_S2.F4.F2 != 92) + { + result |= 256; + } + + if (ref_e1_S2_F4.F2 != 92) + { + result |= 512; + } + + if (result != 0) + { + Console.WriteLine("Failed - Test_e1_S2_F4_F2() - " + result); + return false; + } + + return true; + } + + public static int Main() + { + bool isPassing = true; + + isPassing &= Test_e0_S2_F3_F1(); + + isPassing &= Test_e0_S2_F3_F2(); + + isPassing &= Test_e0_S2_F4_F1(); + + isPassing &= Test_e0_S2_F4_F2(); + + isPassing &= Test_e1_S2_F3_F1(); + + isPassing &= Test_e1_S2_F3_F2(); + + isPassing &= Test_e1_S2_F4_F1(); + + isPassing &= Test_e1_S2_F4_F2(); + + if (isPassing) + { + Console.WriteLine("Passed"); + return 100; + } + else + { + Console.WriteLine("Failed"); + return 101; + } + } +} diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.csproj new file mode 100644 index 000000000000..d86ed9f3d765 --- /dev/null +++ b/tests/src/JIT/Regression/JitBlue/GitHub_20838/GitHub_20838.csproj @@ -0,0 +1,17 @@ + + + + + Release + AnyCPU + $(MSBuildProjectName) + Exe + + True + + + + + + + \ No newline at end of file