diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index f894aeacf1d55e..30aaa8c5be1af2 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -1141,12 +1141,12 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse if (curAssertion->op1.kind == O1K_EXACT_TYPE) { printf("Exact Type MT(%08X)", dspPtr(curAssertion->op2.u1.iconVal)); - assert(curAssertion->op2.u1.iconFlags != GTF_EMPTY); + assert(curAssertion->op2.HasIconFlag()); } else if (curAssertion->op1.kind == O1K_SUBTYPE) { printf("MT(%08X)", dspPtr(curAssertion->op2.u1.iconVal)); - assert(curAssertion->op2.u1.iconFlags != GTF_EMPTY); + assert(curAssertion->op2.HasIconFlag()); } else if ((curAssertion->op1.kind == O1K_BOUND_OPER_BND) || (curAssertion->op1.kind == O1K_BOUND_LOOP_BND) || @@ -1183,7 +1183,7 @@ void Compiler::optPrintAssertion(AssertionDsc* curAssertion, AssertionIndex asse } else { - if ((curAssertion->op2.u1.iconFlags & GTF_ICON_HDL_MASK) != 0) + if (curAssertion->op2.HasIconFlag()) { printf("[%08p]", dspPtr(curAssertion->op2.u1.iconVal)); } @@ -1495,12 +1495,12 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, vn = optConservativeNormalVN(op1); } - assertion.op1.vn = vn; - assertion.assertionKind = assertionKind; - assertion.op2.kind = O2K_CONST_INT; - assertion.op2.vn = ValueNumStore::VNForNull(); - assertion.op2.u1.iconVal = 0; - assertion.op2.u1.iconFlags = GTF_EMPTY; + assertion.op1.vn = vn; + assertion.assertionKind = assertionKind; + assertion.op2.kind = O2K_CONST_INT; + assertion.op2.vn = ValueNumStore::VNForNull(); + assertion.op2.u1.iconVal = 0; + assertion.op2.SetIconFlag(GTF_EMPTY); } // // Are we making an assertion about a local variable? @@ -1548,13 +1548,13 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, // where a class can be sealed, but they don't behave as exact types because casts to // non-base types sometimes still succeed. // - assertion.op1.kind = O1K_SUBTYPE; - assertion.op1.lcl.lclNum = lclNum; - assertion.op1.vn = optConservativeNormalVN(op1); - assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum(); - assertion.op2.u1.iconVal = op2->AsIntCon()->gtIconVal; - assertion.op2.vn = optConservativeNormalVN(op2); - assertion.op2.u1.iconFlags = op2->GetIconHandleFlag(); + assertion.op1.kind = O1K_SUBTYPE; + assertion.op1.lcl.lclNum = lclNum; + assertion.op1.vn = optConservativeNormalVN(op1); + assertion.op1.lcl.ssaNum = op1->AsLclVarCommon()->GetSsaNum(); + assertion.op2.u1.iconVal = op2->AsIntCon()->gtIconVal; + assertion.op2.vn = optConservativeNormalVN(op2); + assertion.op2.SetIconFlag(op2->GetIconHandleFlag()); // // Ok everything has been set and the assertion looks good @@ -1642,8 +1642,8 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, } #endif // TARGET_ARM - assertion.op2.u1.iconVal = iconVal; - assertion.op2.u1.iconFlags = op2->GetIconHandleFlag(); + assertion.op2.u1.iconVal = iconVal; + assertion.op2.SetIconFlag(op2->GetIconHandleFlag(), op2->AsIntCon()->gtFieldSeq); } else if (op2->gtOper == GT_CNS_LNG) { @@ -1792,7 +1792,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, /* iconFlags should only contain bits in GTF_ICON_HDL_MASK */ assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0); - assertion.op2.u1.iconFlags = iconFlags; + assertion.op2.SetIconFlag(iconFlags); } // JIT case else if (optIsTreeKnownIntValue(!optLocalAssertionProp, op2, &cnsValue, &iconFlags)) @@ -1804,7 +1804,7 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1, /* iconFlags should only contain bits in GTF_ICON_HDL_MASK */ assert((iconFlags & ~GTF_ICON_HDL_MASK) == 0); - assertion.op2.u1.iconFlags = iconFlags; + assertion.op2.SetIconFlag(iconFlags); } else { @@ -2105,13 +2105,11 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion) case O2K_CONST_INT: { // The only flags that can be set are those in the GTF_ICON_HDL_MASK. - assert((assertion->op2.u1.iconFlags & ~GTF_ICON_HDL_MASK) == 0); - switch (assertion->op1.kind) { case O1K_EXACT_TYPE: case O1K_SUBTYPE: - assert(assertion->op2.u1.iconFlags != GTF_EMPTY); + assert(assertion->op2.HasIconFlag()); break; case O1K_LCLVAR: assert((lvaGetDesc(assertion->op1.lcl.lclNum)->lvType != TYP_REF) || @@ -2130,7 +2128,7 @@ void Compiler::optDebugCheckAssertion(AssertionDsc* assertion) { // All handles should be represented by O2K_CONST_INT, // so no handle bits should be set here. - assert((assertion->op2.u1.iconFlags & GTF_ICON_HDL_MASK) == 0); + assert(!assertion->op2.HasIconFlag()); } break; @@ -2336,13 +2334,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) if (hasTestAgainstZero && vnStore->IsVNCompareCheckedBoundArith(op1VN)) { AssertionDsc dsc; - dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; - dsc.op1.kind = O1K_BOUND_OPER_BND; - dsc.op1.vn = op1VN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; + dsc.op1.kind = O1K_BOUND_OPER_BND; + dsc.op1.vn = op1VN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2353,13 +2351,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (vnStore->IsVNCompareCheckedBoundArith(relopVN)) { AssertionDsc dsc; - dsc.assertionKind = OAK_NOT_EQUAL; - dsc.op1.kind = O1K_BOUND_OPER_BND; - dsc.op1.vn = relopVN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = OAK_NOT_EQUAL; + dsc.op1.kind = O1K_BOUND_OPER_BND; + dsc.op1.vn = relopVN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2370,13 +2368,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (hasTestAgainstZero && vnStore->IsVNCompareCheckedBound(op1VN)) { AssertionDsc dsc; - dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; - dsc.op1.kind = O1K_BOUND_LOOP_BND; - dsc.op1.vn = op1VN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; + dsc.op1.kind = O1K_BOUND_LOOP_BND; + dsc.op1.vn = op1VN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2387,13 +2385,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (vnStore->IsVNCompareCheckedBound(relopVN)) { AssertionDsc dsc; - dsc.assertionKind = OAK_NOT_EQUAL; - dsc.op1.kind = O1K_BOUND_LOOP_BND; - dsc.op1.vn = relopVN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = OAK_NOT_EQUAL; + dsc.op1.kind = O1K_BOUND_LOOP_BND; + dsc.op1.vn = relopVN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2430,13 +2428,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (hasTestAgainstZero && vnStore->IsVNConstantBound(op1VN)) { AssertionDsc dsc; - dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; - dsc.op1.kind = O1K_CONSTANT_LOOP_BND; - dsc.op1.vn = op1VN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = relop->gtOper == GT_EQ ? OAK_EQUAL : OAK_NOT_EQUAL; + dsc.op1.kind = O1K_CONSTANT_LOOP_BND; + dsc.op1.vn = op1VN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(op2->TypeGet()); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2447,13 +2445,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (vnStore->IsVNConstantBound(relopVN)) { AssertionDsc dsc; - dsc.assertionKind = OAK_NOT_EQUAL; - dsc.op1.kind = O1K_CONSTANT_LOOP_BND; - dsc.op1.vn = relopVN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = OAK_NOT_EQUAL; + dsc.op1.kind = O1K_CONSTANT_LOOP_BND; + dsc.op1.vn = relopVN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2461,13 +2459,13 @@ AssertionInfo Compiler::optCreateJTrueBoundsAssertion(GenTree* tree) else if (vnStore->IsVNConstantBoundUnsigned(relopVN)) { AssertionDsc dsc; - dsc.assertionKind = OAK_NOT_EQUAL; - dsc.op1.kind = O1K_CONSTANT_LOOP_BND_UN; - dsc.op1.vn = relopVN; - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); - dsc.op2.u1.iconVal = 0; - dsc.op2.u1.iconFlags = GTF_EMPTY; + dsc.assertionKind = OAK_NOT_EQUAL; + dsc.op1.kind = O1K_CONSTANT_LOOP_BND_UN; + dsc.op1.vn = relopVN; + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.vn = vnStore->VNZeroForType(TYP_INT); + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); AssertionIndex index = optAddAssertion(&dsc); optCreateComplementaryAssertion(index, nullptr, nullptr); return index; @@ -2557,13 +2555,13 @@ AssertionInfo Compiler::optAssertionGenJtrue(GenTree* tree) dsc.op1.bnd.vnIdx = vnStore->VNForIntCon(con - 1); } - dsc.op1.vn = op1VN; - dsc.op1.kind = O1K_ARR_BND; - dsc.op1.bnd.vnLen = op1VN; - dsc.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair); - dsc.op2.kind = O2K_CONST_INT; - dsc.op2.u1.iconFlags = GTF_EMPTY; - dsc.op2.u1.iconVal = 0; + dsc.op1.vn = op1VN; + dsc.op1.kind = O1K_ARR_BND; + dsc.op1.bnd.vnLen = op1VN; + dsc.op2.vn = vnStore->VNConservativeNormalValue(op2->gtVNPair); + dsc.op2.kind = O2K_CONST_INT; + dsc.op2.u1.iconVal = 0; + dsc.op2.SetIconFlag(GTF_EMPTY); // when con is not zero, create an assertion on the arr.Length == con edge // when con is zero, create an assertion on the arr.Length != 0 edge @@ -3366,7 +3364,7 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion, case O2K_CONST_INT: // Don't propagate handles if we need to report relocs. - if (opts.compReloc && ((curAssertion->op2.u1.iconFlags & GTF_ICON_HDL_MASK) != 0)) + if (opts.compReloc && curAssertion->op2.HasIconFlag()) { return nullptr; } @@ -3381,11 +3379,11 @@ GenTree* Compiler::optConstantAssertionProp(AssertionDsc* curAssertion, assert(!varTypeIsSmall(tree) || (curAssertion->op2.u1.iconVal == optCastConstantSmall(curAssertion->op2.u1.iconVal, tree->TypeGet()))); - if (curAssertion->op2.u1.iconFlags & GTF_ICON_HDL_MASK) + if (curAssertion->op2.HasIconFlag()) { // Here we have to allocate a new 'large' node to replace the old one - newTree = gtNewIconHandleNode(curAssertion->op2.u1.iconVal, - curAssertion->op2.u1.iconFlags & GTF_ICON_HDL_MASK); + newTree = gtNewIconHandleNode(curAssertion->op2.u1.iconVal, curAssertion->op2.GetIconFlag(), + curAssertion->op2.u1.fieldSeq); // Make sure we don't retype const gc handles to TYP_I_IMPL // Although, it's possible for e.g. GTF_ICON_STATIC_HDL diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index c3e98d49e3cfe0..03d9f2b1564fe8 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7058,7 +7058,7 @@ class Compiler O1K_COUNT }; - enum optOp2Kind + enum optOp2Kind : uint16_t { O2K_INVALID, O2K_LCLVAR_COPY, @@ -7096,14 +7096,17 @@ class Compiler struct AssertionDscOp2 { optOp2Kind kind; // a const or copy assignment - ValueNum vn; + private: + uint16_t m_encodedIconFlags; // encoded icon gtFlags, don't use directly + public: + ValueNum vn; struct IntVal { ssize_t iconVal; // integer #if !defined(HOST_64BIT) unsigned padding; // unused; ensures iconFlags does not overlap lconVal #endif - GenTreeFlags iconFlags; // gtFlags + FieldSeq* fieldSeq; }; union { SsaVar lcl; @@ -7112,6 +7115,29 @@ class Compiler double dconVal; IntegralRange u2; }; + + bool HasIconFlag() + { + assert(m_encodedIconFlags <= 0xFF); + return m_encodedIconFlags != 0; + } + GenTreeFlags GetIconFlag() + { + // number of trailing zeros in GTF_ICON_HDL_MASK + const uint16_t iconMaskTzc = 24; + static_assert_no_msg((0xFF000000 == GTF_ICON_HDL_MASK) && (GTF_ICON_HDL_MASK >> iconMaskTzc) == 0xFF); + + GenTreeFlags flags = (GenTreeFlags)(m_encodedIconFlags << iconMaskTzc); + assert((flags & ~GTF_ICON_HDL_MASK) == 0); + return flags; + } + void SetIconFlag(GenTreeFlags flags, FieldSeq* fieldSeq = nullptr) + { + const uint16_t iconMaskTzc = 24; + assert((flags & ~GTF_ICON_HDL_MASK) == 0); + m_encodedIconFlags = flags >> iconMaskTzc; + u1.fieldSeq = fieldSeq; + } } op2; bool IsCheckedBoundArithBound() @@ -7220,7 +7246,7 @@ class Compiler { case O2K_IND_CNS_INT: case O2K_CONST_INT: - return ((op2.u1.iconVal == that->op2.u1.iconVal) && (op2.u1.iconFlags == that->op2.u1.iconFlags)); + return ((op2.u1.iconVal == that->op2.u1.iconVal) && (op2.GetIconFlag() == that->op2.GetIconFlag())); case O2K_CONST_LONG: return (op2.lconVal == that->op2.lconVal); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 140aa56a1d9a9a..d1f80d8220cc60 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -17622,6 +17622,12 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeq** pFldSe baseAddr = AsOp()->gtOp1; fldSeq = AsOp()->gtOp2->AsIntCon()->gtFieldSeq; offset = AsOp()->gtOp2->AsIntCon()->IconValue(); + + if ((fldSeq != nullptr) && (fldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress)) + { + // fldSeq represents a known address (not a small offset) - bail out. + return false; + } } else { @@ -17630,17 +17636,15 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeq** pFldSe } else if (IsIconHandle(GTF_ICON_STATIC_HDL)) { - baseAddr = this; - fldSeq = AsIntCon()->gtFieldSeq; - offset = AsIntCon()->IconValue(); + fldSeq = AsIntCon()->gtFieldSeq; + offset = AsIntCon()->IconValue(); + assert((fldSeq == nullptr) || (fldSeq->GetKind() == FieldSeq::FieldKind::SimpleStaticKnownAddress)); } else { return false; } - assert(baseAddr != nullptr); - if (fldSeq == nullptr) { return false; diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b78096914ebab2..de6f900cfafc5b 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -270,9 +270,10 @@ class FieldSeq public: enum class FieldKind : uintptr_t { - Instance = 0, // An instance field. - SimpleStatic = 1, // Simple static field - the handle represents a unique location. - SharedStatic = 2, // Static field on a shared generic type: "Class<__Canon>.StaticField". + Instance = 0, // An instance field. + SimpleStatic = 1, // Simple static field - the handle represents a unique location. + SimpleStaticKnownAddress = 2, // Simple static field - the handle represents a known location. + SharedStatic = 3, // Static field on a shared generic type: "Class<__Canon>.StaticField". }; private: @@ -310,7 +311,8 @@ class FieldSeq bool IsStaticField() const { - return (GetKind() == FieldKind::SimpleStatic) || (GetKind() == FieldKind::SharedStatic); + return (GetKind() == FieldKind::SimpleStatic) || (GetKind() == FieldKind::SharedStatic) || + (GetKind() == FieldKind::SimpleStaticKnownAddress); } bool IsSharedStaticField() const diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 6185804f898ff0..e1e08d6b0972bd 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -4381,12 +4381,16 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT } else { - bool hasConstAddr = (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_ADDRESS) || + bool hasKnownAddr = (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_ADDRESS) || (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_RVA_ADDRESS); ssize_t offset; - if (hasConstAddr) + if (hasKnownAddr) { + // Change SimpleStatic to SimpleStaticKnownAddress + assert(fieldKind == FieldSeq::FieldKind::SimpleStatic); + fieldKind = FieldSeq::FieldKind::SimpleStaticKnownAddress; + offset = reinterpret_cast(info.compCompHnd->getFieldAddress(pResolvedToken->hField)); assert(offset != 0); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index c1b827e623fd56..6d8310403237ca 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5408,7 +5408,7 @@ GenTree* Compiler::fgMorphExpandStaticField(GenTree* tree) { // Only simple statics get importred as GT_FIELDs. fieldSeq = GetFieldSeqStore()->Create(fieldHandle, reinterpret_cast(fldAddr), - FieldSeq::FieldKind::SimpleStatic); + FieldSeq::FieldKind::SimpleStaticKnownAddress); } // TODO-CQ: enable this optimization for 32 bit targets.