diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 12abefc8b4875e..50e3b1bb5b1705 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -227,15 +227,6 @@ bool IntegralRange::Contains(int64_t value) const break; #endif // defined(FEATURE_HW_INTRINSICS) - case GT_FIELD: - { - if (node->AsField()->IsSpanLength()) - { - return {SymbolicIntegerValue::Zero, UpperBoundForType(rangeType)}; - } - break; - } - default: break; } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 76b30f063f0205..783a35a7499c85 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -9716,17 +9716,6 @@ void cTreeFlags(Compiler* comp, GenTree* tree) case GT_NO_OP: break; - case GT_FIELD: - if (tree->gtFlags & GTF_FLD_VOLATILE) - { - chars += printf("[FLD_VOLATILE]"); - } - if (tree->gtFlags & GTF_FLD_TGT_HEAP) - { - chars += printf("[FLD_TGT_HEAP]"); - } - break; - case GT_INDEX_ADDR: if (tree->gtFlags & GTF_INX_RNGCHK) { @@ -10330,7 +10319,7 @@ var_types Compiler::gtTypeForNullCheck(GenTree* tree) // void Compiler::gtChangeOperToNullCheck(GenTree* tree, BasicBlock* block) { - assert(tree->OperIs(GT_FIELD, GT_IND, GT_BLK)); + assert(tree->OperIs(GT_IND, GT_BLK)); tree->ChangeOper(GT_NULLCHECK); tree->ChangeType(gtTypeForNullCheck(tree)); tree->SetIndirExceptionFlags(this); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 4420a7662a36cc..bee14dc44713f4 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2803,12 +2803,12 @@ class Compiler GenTreeLclFld* gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset); GenTreeRetExpr* gtNewInlineCandidateReturnExpr(GenTreeCall* inlineCandidate, var_types type); - GenTreeField* gtNewFieldRef(var_types type, CORINFO_FIELD_HANDLE fldHnd, GenTree* obj = nullptr, DWORD offset = 0); + GenTreeFieldAddr* gtNewFieldAddrNode(var_types type, + CORINFO_FIELD_HANDLE fldHnd, + GenTree* obj = nullptr, + DWORD offset = 0); - GenTreeField* gtNewFieldAddrNode(var_types type, - CORINFO_FIELD_HANDLE fldHnd, - GenTree* obj = nullptr, - DWORD offset = 0); + GenTreeIndir* gtNewFieldIndirNode(var_types type, ClassLayout* layout, GenTreeFieldAddr* addr); GenTreeIndexAddr* gtNewIndexAddr(GenTree* arrayOp, GenTree* indexOp, @@ -5912,7 +5912,7 @@ class Compiler void fgAssignSetVarDef(GenTree* tree); private: - GenTree* fgMorphField(GenTree* tree, MorphAddrContext* mac); + GenTree* fgMorphFieldAddr(GenTree* tree, MorphAddrContext* mac); GenTree* fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* mac); GenTree* fgMorphExpandTlsFieldAddr(GenTree* tree); bool fgCanFastTailCall(GenTreeCall* call, const char** failReason); @@ -6013,7 +6013,7 @@ class Compiler Statement* fgMorphStmt; unsigned fgBigOffsetMorphingTemps[TYP_COUNT]; - unsigned fgGetFieldMorphingTemp(GenTreeField* type); + unsigned fgGetFieldMorphingTemp(GenTreeFieldAddr* fieldNode); //----------------------- Liveness analysis ------------------------------- @@ -8634,7 +8634,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX GenTree* impSIMDPopStack(); void setLclRelatedToSIMDIntrinsic(GenTree* tree); - bool areFieldsContiguous(GenTree* op1, GenTree* op2); + bool areFieldsContiguous(GenTreeIndir* op1, GenTreeIndir* op2); bool areLocalFieldsContiguous(GenTreeLclFld* first, GenTreeLclFld* second); bool areArrayElementsContiguous(GenTree* op1, GenTree* op2); bool areArgumentsContiguous(GenTree* op1, GenTree* op2); @@ -11080,7 +11080,6 @@ class GenTreeVisitor case GT_PUTARG_STK: case GT_RETURNTRAP: case GT_NOP: - case GT_FIELD: case GT_FIELD_ADDR: case GT_RETURN: case GT_RETFILT: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 72e5553ebb7c09..4050f5d221d1d6 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3985,7 +3985,6 @@ void GenTree::VisitOperands(TVisitor visitor) // Unary operators with an optional operand case GT_NOP: - case GT_FIELD: case GT_FIELD_ADDR: case GT_RETURN: case GT_RETFILT: diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp index a2636938ef446b..7be061365d2e2a 100644 --- a/src/coreclr/jit/forwardsub.cpp +++ b/src/coreclr/jit/forwardsub.cpp @@ -743,7 +743,7 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) // Don't substitute nodes args morphing doesn't handle into struct args. // if (fsv.IsCallArg() && fsv.GetNode()->TypeIs(TYP_STRUCT) && - !fwdSubNode->OperIs(GT_BLK, GT_FIELD, GT_LCL_VAR, GT_LCL_FLD, GT_MKREFANY)) + !fwdSubNode->OperIs(GT_BLK, GT_LCL_VAR, GT_LCL_FLD, GT_MKREFANY)) { JITDUMP(" use is a struct arg; fwd sub node is not OBJ/LCL_VAR/LCL_FLD/MKREFANY\n"); return false; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ae7f23375a2f82..47c9e88e65a5ce 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -264,7 +264,6 @@ void GenTree::InitNodeSize() GenTree::s_gtNodeSizes[GT_ARR_INDEX] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_ARR_OFFSET] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_RET_EXPR] = TREE_NODE_SZ_LARGE; - GenTree::s_gtNodeSizes[GT_FIELD] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_FIELD_ADDR] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_CMPXCHG] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_QMARK] = TREE_NODE_SZ_LARGE; @@ -319,7 +318,7 @@ void GenTree::InitNodeSize() static_assert_no_msg(sizeof(GenTreeConditional) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeCast) <= TREE_NODE_SZ_LARGE); // *** large node static_assert_no_msg(sizeof(GenTreeBox) <= TREE_NODE_SZ_LARGE); // *** large node - static_assert_no_msg(sizeof(GenTreeField) <= TREE_NODE_SZ_LARGE); // *** large node + static_assert_no_msg(sizeof(GenTreeFieldAddr) <= TREE_NODE_SZ_LARGE); // *** large node static_assert_no_msg(sizeof(GenTreeFieldList) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeColon) <= TREE_NODE_SZ_SMALL); static_assert_no_msg(sizeof(GenTreeCall) <= TREE_NODE_SZ_LARGE); // *** large node @@ -750,10 +749,6 @@ ClassLayout* GenTree::GetLayout(Compiler* compiler) const structHnd = compiler->impGetRefAnyClass(); break; - case GT_FIELD: - compiler->eeGetFieldType(AsField()->gtFldHnd, &structHnd); - break; - case GT_CALL: structHnd = AsCall()->gtRetClsHnd; break; @@ -2754,9 +2749,8 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) } break; - case GT_FIELD: case GT_FIELD_ADDR: - if (op1->AsField()->gtFldHnd != op2->AsField()->gtFldHnd) + if (op1->AsFieldAddr()->gtFldHnd != op2->AsFieldAddr()->gtFldHnd) { return false; } @@ -3308,9 +3302,8 @@ unsigned Compiler::gtHashValue(GenTree* tree) static_cast(reinterpret_cast(tree->AsBlk()->GetLayout()))); break; - case GT_FIELD: case GT_FIELD_ADDR: - hash = genTreeHashAdd(hash, tree->AsField()->gtFldHnd); + hash = genTreeHashAdd(hash, tree->AsFieldAddr()->gtFldHnd); break; // For the ones below no extra argument matters for comparison. @@ -6894,9 +6887,8 @@ ExceptionSetFlags GenTree::OperExceptions(Compiler* comp) return ExceptionSetFlags::IndexOutOfRangeException; - case GT_FIELD: case GT_FIELD_ADDR: - if (AsField()->IsInstance() && comp->fgAddrCouldBeNull(AsField()->GetFldObj())) + if (AsFieldAddr()->IsInstance() && comp->fgAddrCouldBeNull(AsFieldAddr()->GetFldObj())) { return ExceptionSetFlags::NullReferenceException; } @@ -7834,13 +7826,14 @@ GenTreeRetExpr* Compiler::gtNewInlineCandidateReturnExpr(GenTreeCall* inlineCand } //------------------------------------------------------------------------ -// gtNewFieldRef: Create a new GT_FIELD node. +// gtNewFieldAddrNode: Create a new GT_FIELD_ADDR node. // -// Normalizes struct types (for SIMD vectors). Sets GTF_GLOB_REF for fields -// that may be pointing into globally visible memory. +// TODO-ADDR: consider creating a variant of this which would skip various +// no-op constructs (such as struct fields with zero offsets), and fold +// others (LCL_VAR_ADDR + FIELD_ADDR => LCL_FLD_ADDR). // // Arguments: -// type - type for the field node +// type - type for the address node // fldHnd - the field handle // obj - the instance, an address // offset - the field offset @@ -7848,31 +7841,18 @@ GenTreeRetExpr* Compiler::gtNewInlineCandidateReturnExpr(GenTreeCall* inlineCand // Return Value: // The created node. // -GenTreeField* Compiler::gtNewFieldRef(var_types type, CORINFO_FIELD_HANDLE fldHnd, GenTree* obj, DWORD offset) +GenTreeFieldAddr* Compiler::gtNewFieldAddrNode(var_types type, CORINFO_FIELD_HANDLE fldHnd, GenTree* obj, DWORD offset) { - // GT_FIELD nodes are transformed into GT_IND nodes. - assert(GenTree::s_gtNodeSizes[GT_IND] <= GenTree::s_gtNodeSizes[GT_FIELD]); - - if (type == TYP_STRUCT) - { - CORINFO_CLASS_HANDLE structHnd; - eeGetFieldType(fldHnd, &structHnd); - type = impNormStructType(structHnd); - } + assert(varTypeIsI(genActualType(type))); - GenTreeField* fieldNode = new (this, GT_FIELD) GenTreeField(GT_FIELD, type, obj, fldHnd, offset); + GenTreeFieldAddr* fieldNode = new (this, GT_FIELD_ADDR) GenTreeFieldAddr(type, obj, fldHnd, offset); // If "obj" is the address of a local, note that a field of that struct local has been accessed. if ((obj != nullptr) && obj->IsLclVarAddr()) { - LclVarDsc* varDsc = lvaGetDesc(obj->AsLclVarCommon()); - + LclVarDsc* varDsc = lvaGetDesc(obj->AsLclVarCommon()); varDsc->lvFieldAccessed = 1; } - else - { - fieldNode->gtFlags |= GTF_GLOB_REF; - } if ((obj != nullptr) && fgAddrCouldBeNull(obj)) { @@ -7883,40 +7863,37 @@ GenTreeField* Compiler::gtNewFieldRef(var_types type, CORINFO_FIELD_HANDLE fldHn } //------------------------------------------------------------------------ -// gtNewFieldAddrNode: Create a new GT_FIELD_ADDR node. -// -// TODO-ADDR: consider creating a variant of this which would skip various -// no-op constructs (such as struct fields with zero offsets), and fold -// others (LCL_VAR_ADDR + FIELD_ADDR => LCL_FLD_ADDR). +// gtNewFieldIndirNode: Create a new field indirection node. // // Arguments: -// type - type for the address node -// fldHnd - the field handle -// obj - the instance, an address -// offset - the field offset +// type - Indirection's type +// layout - Indirection's struct layout +// add - The field address // // Return Value: // The created node. // -GenTreeField* Compiler::gtNewFieldAddrNode(var_types type, CORINFO_FIELD_HANDLE fldHnd, GenTree* obj, DWORD offset) +// Notes: +// This method exists to preserve previous behavior. New code should +// use "gtNewIndir"/"gtNewBlkIndir" directly. +// +GenTreeIndir* Compiler::gtNewFieldIndirNode(var_types type, ClassLayout* layout, GenTreeFieldAddr* addr) { - assert(varTypeIsI(genActualType(type))); + GenTreeIndir* indir = (type == TYP_STRUCT) ? gtNewBlkIndir(layout, addr, GTF_IND_NONFAULTING) + : gtNewIndir(type, addr, GTF_IND_NONFAULTING); - GenTreeField* fieldNode = new (this, GT_FIELD_ADDR) GenTreeField(GT_FIELD_ADDR, type, obj, fldHnd, offset); - - // If "obj" is the address of a local, note that a field of that struct local has been accessed. - if ((obj != nullptr) && obj->IsLclVarAddr()) + if (addr->IsInstance() && addr->GetFldObj()->OperIs(GT_LCL_ADDR)) { - LclVarDsc* varDsc = lvaGetDesc(obj->AsLclVarCommon()); - varDsc->lvFieldAccessed = 1; + indir->gtFlags &= ~GTF_GLOB_REF; } - - if ((obj != nullptr) && fgAddrCouldBeNull(obj)) + else { - fieldNode->gtFlags |= GTF_EXCEPT; + indir->gtFlags |= GTF_GLOB_REF; } - return fieldNode; + addr->gtFlags |= GTF_FLD_DEREFERENCED; + + return indir; } //------------------------------------------------------------------------ @@ -8284,7 +8261,7 @@ void GenTreeOp::CheckDivideByConstOptimized(Compiler* comp) // GenTree* Compiler::gtNewBlkOpNode(GenTree* dst, GenTree* srcOrFillVal, bool isVolatile) { - assert(varTypeIsStruct(dst) && (dst->OperIsIndir() || dst->OperIsLocal() || dst->OperIs(GT_FIELD))); + assert(varTypeIsStruct(dst) && (dst->OperIsIndir() || dst->OperIsLocal())); bool isCopyBlock = srcOrFillVal->TypeGet() == dst->TypeGet(); if (!isCopyBlock) // InitBlk @@ -8575,29 +8552,28 @@ GenTree* Compiler::gtClone(GenTree* tree, bool complexOK) return nullptr; } - if (tree->OperIs(GT_FIELD)) + if (tree->OperIs(GT_IND, GT_BLK) && tree->AsIndir()->Addr()->OperIs(GT_FIELD_ADDR)) { - GenTree* objp = nullptr; + GenTree* objp = nullptr; + GenTreeFieldAddr* addr = tree->AsIndir()->Addr()->AsFieldAddr(); - if (tree->AsField()->GetFldObj() != nullptr) + if (addr->IsInstance()) { - objp = gtClone(tree->AsField()->GetFldObj(), false); + objp = gtClone(addr->GetFldObj(), false); if (objp == nullptr) { return nullptr; } } - copy = gtNewFieldRef(tree->TypeGet(), tree->AsField()->gtFldHnd, objp, tree->AsField()->gtFldOffset); - copy->AsField()->gtFldMayOverlap = tree->AsField()->gtFldMayOverlap; + copy = gtNewFieldAddrNode(addr->TypeGet(), addr->gtFldHnd, objp, addr->gtFldOffset); + copy->AsFieldAddr()->gtFldMayOverlap = addr->gtFldMayOverlap; + copy->AsFieldAddr()->SetIsSpanLength(addr->IsSpanLength()); #ifdef FEATURE_READYTORUN - copy->AsField()->gtFieldLookup = tree->AsField()->gtFieldLookup; + copy->AsFieldAddr()->gtFieldLookup = addr->gtFieldLookup; #endif - - if (tree->AsField()->IsSpanLength()) - { - copy->AsField()->SetIsSpanLength(true); - } + ClassLayout* layout = tree->OperIs(GT_BLK) ? tree->AsBlk()->GetLayout() : nullptr; + copy = gtNewFieldIndirNode(tree->TypeGet(), layout, copy->AsFieldAddr()); } else if (tree->OperIs(GT_ADD, GT_SUB)) { @@ -8943,20 +8919,15 @@ GenTree* Compiler::gtCloneExpr( GenTreeBlk(GT_BLK, tree->TypeGet(), tree->AsBlk()->Addr(), tree->AsBlk()->GetLayout()); break; - case GT_FIELD: case GT_FIELD_ADDR: - copy = new (this, tree->OperGet()) - GenTreeField(tree->OperGet(), tree->TypeGet(), tree->AsField()->GetFldObj(), - tree->AsField()->gtFldHnd, tree->AsField()->gtFldOffset); - copy->AsField()->gtFldMayOverlap = tree->AsField()->gtFldMayOverlap; + copy = new (this, GT_FIELD_ADDR) + GenTreeFieldAddr(tree->TypeGet(), tree->AsFieldAddr()->GetFldObj(), tree->AsFieldAddr()->gtFldHnd, + tree->AsFieldAddr()->gtFldOffset); + copy->AsFieldAddr()->gtFldMayOverlap = tree->AsFieldAddr()->gtFldMayOverlap; #ifdef FEATURE_READYTORUN - copy->AsField()->gtFieldLookup = tree->AsField()->gtFieldLookup; + copy->AsFieldAddr()->gtFieldLookup = tree->AsFieldAddr()->gtFieldLookup; #endif - - if ((oper == GT_FIELD) && tree->AsField()->IsSpanLength()) - { - copy->AsField()->SetIsSpanLength(true); - } + copy->AsFieldAddr()->SetIsSpanLength(tree->AsFieldAddr()->IsSpanLength()); break; case GT_BOX: @@ -9742,7 +9713,6 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) // Unary operators with an optional operand case GT_NOP: - case GT_FIELD: case GT_FIELD_ADDR: case GT_RETURN: case GT_RETFILT: @@ -10734,21 +10704,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, _In_ _In_opt_ } FALLTHROUGH; - case GT_FIELD: - if (tree->gtFlags & GTF_IND_VOLATILE) - { - printf("V"); - --msgLength; - break; - } - if (tree->gtFlags & GTF_IND_UNALIGNED) - { - printf("U"); - --msgLength; - break; - } - goto DASH; - case GT_ASG: if (tree->OperIsInitBlkOp()) { @@ -12163,11 +12118,11 @@ void Compiler::gtDispTree(GenTree* tree, #endif // FEATURE_ARG_SPLIT #endif // FEATURE_PUT_STRUCT_ARG_STK - if (tree->OperIs(GT_FIELD, GT_FIELD_ADDR)) + if (tree->OperIs(GT_FIELD_ADDR)) { auto disp = [&]() { char buffer[256]; - printf(" %s", eeGetFieldName(tree->AsField()->gtFldHnd, true, buffer, sizeof(buffer))); + printf(" %s", eeGetFieldName(tree->AsFieldAddr()->gtFldHnd, true, buffer, sizeof(buffer))); }; disp(); } @@ -14212,20 +14167,17 @@ GenTree* Compiler::gtFoldBoxNullable(GenTree* tree) JITDUMP("\nReplacing BOX_NULLABLE(&x) %s null [%06u] with x.hasValue\n", GenTree::OpName(oper), dspTreeID(tree)); - GenTree* nullableHndNode = call->gtArgs.GetArgByIndex(0)->GetNode(); - CORINFO_CLASS_HANDLE nullableClassHnd = gtGetHelperArgClassHandle(nullableHndNode); - CORINFO_FIELD_HANDLE hasValueFieldHnd = info.compCompHnd->getFieldInClass(nullableClassHnd, 0); - - GenTree* srcAddr = call->gtArgs.GetArgByIndex(1)->GetNode(); - GenTree* fieldNode = gtNewFieldRef(TYP_BOOL, hasValueFieldHnd, srcAddr, OFFSETOF__CORINFO_NullableOfT__hasValue); + static_assert_no_msg(OFFSETOF__CORINFO_NullableOfT__hasValue == 0); + GenTree* srcAddr = call->gtArgs.GetArgByIndex(1)->GetNode(); + GenTree* hasValueNode = gtNewIndir(TYP_BOOL, srcAddr); if (op == op1) { - tree->AsOp()->gtOp1 = fieldNode; + tree->AsOp()->gtOp1 = hasValueNode; } else { - tree->AsOp()->gtOp2 = fieldNode; + tree->AsOp()->gtOp2 = hasValueNode; } cons->gtType = TYP_INT; @@ -14453,7 +14405,7 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions { isStructCopy = true; - if ((copySrc->gtOper != GT_BLK) && (copySrc->gtOper != GT_IND) && (copySrc->gtOper != GT_FIELD)) + if (!copySrc->OperIs(GT_IND, GT_BLK)) { // We don't know how to handle other cases, yet. JITDUMP(" bailing; unexpected copy source struct op with side effect %s\n", @@ -14502,10 +14454,9 @@ GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions } else { - // For struct types read the first byte of the - // source struct; there's no need to read the - // entire thing, and no place to put it. - assert(copySrc->OperIs(GT_BLK, GT_IND, GT_FIELD)); + // For struct types read the first byte of the source struct; there's + // no need to read the entire thing, and no place to put it. + assert(copySrc->OperIs(GT_BLK, GT_IND)); copyStmt->SetRootNode(copySrc); if (options == BR_REMOVE_AND_NARROW || options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE) @@ -16204,6 +16155,17 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) { return true; } + + // TODO-FIELD: delete this zero-diff quirk. + if (tree->OperIsIndir() && tree->AsIndir()->Addr()->OperIs(GT_FIELD_ADDR)) + { + GenTreeFieldAddr* addr = tree->AsIndir()->Addr()->AsFieldAddr(); + if (addr->IsInstance() && ((addr->gtFlags & GTF_FLD_DEREFERENCED) != 0) && + fgAddrCouldBeNull(addr->GetFldObj())) + { + return true; + } + } } // Expressions declared as CSE by (e.g.) hoisting code are considered to have relevant side @@ -17826,7 +17788,7 @@ unsigned GenTreeVecCon::ElementCount(unsigned simdSize, var_types simdBaseType) // field, or to "nullptr", in which case the handle for the field is the primary // selector. For instance fields, "base address" will be the object reference, // for statics - the address to which the field offset with the field sequence -// is added, see "impImportStaticFieldAccess" and "fgMorphField". +// is added, see "impImportStaticFieldAccess" and "fgMorphFieldAddr". // bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeq** pFldSeq, ssize_t* pOffset) { @@ -18019,19 +17981,6 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b break; } - case GT_FIELD: - { - // For fields, get the type from the field handle. - CORINFO_FIELD_HANDLE fieldHnd = obj->AsField()->gtFldHnd; - - if (fieldHnd != nullptr) - { - objClass = gtGetFieldClassHandle(fieldHnd, pIsExact, pIsNonNull); - } - - break; - } - case GT_CNS_INT: { if (tree->IsIconHandle(GTF_ICON_OBJ_HDL)) @@ -18246,6 +18195,10 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b objClass = gtGetFieldClassHandle(fldHandle, pIsExact, pIsNonNull); } } + else if (base->OperIs(GT_FIELD_ADDR)) + { + objClass = gtGetFieldClassHandle(base->AsFieldAddr()->gtFldHnd, pIsExact, pIsNonNull); + } break; } @@ -25973,14 +25926,6 @@ bool GenTree::IsNeverNegative(Compiler* comp) const return true; } } - else if (OperIs(GT_FIELD)) - { - if (AsField()->IsSpanLength()) - { - // This is an early exit, it doesn't cover all cases - return true; - } - } // TODO-Casts: extend IntegralRange to handle constants return IntegralRange::ForNode(const_cast(this), comp).IsNonNegative(); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index f376044e6a6b1e..04e6ccb4fd0efe 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -243,7 +243,7 @@ class AssertionInfo } }; -// GT_FIELD nodes will be lowered into more "code-gen-able" representations, like GT_IND's of addresses. +// GT_FIELD_ADDR nodes will be lowered into more "code-gen-able" representations, like ADD's of addresses. // For value numbering, we would like to preserve the aliasing information for class and static fields, // and so will annotate such lowered addresses with "field sequences", representing the "base" static or // class field and any additional struct fields. We only need to preserve the handle for the first field, @@ -476,8 +476,7 @@ enum GenTreeFlags : unsigned int GTF_MEMORYBARRIER_LOAD = 0x40000000, // GT_MEMORYBARRIER -- Load barrier GTF_FLD_TLS = 0x80000000, // GT_FIELD_ADDR -- field address is a Windows x86 TLS reference - GTF_FLD_VOLATILE = 0x40000000, // GT_FIELD -- same as GTF_IND_VOLATILE - GTF_FLD_TGT_HEAP = 0x10000000, // GT_FIELD -- same as GTF_IND_TGT_HEAP + GTF_FLD_DEREFERENCED = 0x40000000, // GT_FIELD_ADDR -- used to preserve previous behavior GTF_INX_RNGCHK = 0x80000000, // GT_INDEX_ADDR -- this array address should be range-checked GTF_INX_ADDR_NONNULL = 0x40000000, // GT_INDEX_ADDR -- this array address is not null @@ -1715,7 +1714,6 @@ struct GenTree case GT_LEA: case GT_RETFILT: case GT_NOP: - case GT_FIELD: case GT_FIELD_ADDR: return true; case GT_RETURN: @@ -3869,8 +3867,8 @@ struct GenTreeBox : public GenTreeUnOp } }; -// GenTreeField -- data member ref (GT_FIELD) -struct GenTreeField : public GenTreeUnOp +// GenTreeFieldAddr -- data member address (GT_FIELD_ADDR) +struct GenTreeFieldAddr : public GenTreeUnOp { CORINFO_FIELD_HANDLE gtFldHnd; DWORD gtFldOffset; @@ -3884,8 +3882,8 @@ struct GenTreeField : public GenTreeUnOp CORINFO_CONST_LOOKUP gtFieldLookup; #endif - GenTreeField(genTreeOps oper, var_types type, GenTree* obj, CORINFO_FIELD_HANDLE fldHnd, DWORD offs) - : GenTreeUnOp(oper, type, obj) + GenTreeFieldAddr(var_types type, GenTree* obj, CORINFO_FIELD_HANDLE fldHnd, DWORD offs) + : GenTreeUnOp(GT_FIELD_ADDR, type, obj) , gtFldHnd(fldHnd) , gtFldOffset(offs) , gtFldMayOverlap(false) @@ -3897,7 +3895,7 @@ struct GenTreeField : public GenTreeUnOp } #if DEBUGGABLE_GENTREE - GenTreeField() : GenTreeUnOp() + GenTreeFieldAddr() : GenTreeUnOp() { } #endif @@ -3909,13 +3907,6 @@ struct GenTreeField : public GenTreeUnOp return gtOp1; } - // True if this field is a volatile memory operation. - bool IsVolatile() const - { - assert(((gtFlags & GTF_FLD_VOLATILE) == 0) || OperIs(GT_FIELD)); - return (gtFlags & GTF_FLD_VOLATILE) != 0; - } - bool IsSpanLength() const { // This is limited to span length today rather than a more general "IsNeverNegative" @@ -3924,8 +3915,6 @@ struct GenTreeField : public GenTreeUnOp // Extending this support more in the future will require additional work and // considerations to help ensure it is correctly used since people may want // or intend to use this as more of a "point in time" feature like GTF_IND_NONNULL - - assert(OperIs(GT_FIELD)); return gtFldIsSpanLength; } diff --git a/src/coreclr/jit/gschecks.cpp b/src/coreclr/jit/gschecks.cpp index 1d5ec9371bcb76..17443fc6ab2d2c 100644 --- a/src/coreclr/jit/gschecks.cpp +++ b/src/coreclr/jit/gschecks.cpp @@ -167,7 +167,6 @@ Compiler::fgWalkResult Compiler::gsMarkPtrsAndAssignGroups(GenTree** pTree, fgWa case GT_ARR_OFFSET: case GT_MDARR_LENGTH: case GT_MDARR_LOWER_BOUND: - case GT_FIELD: newState.isUnderIndir = true; { diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 02c43006d2912f..dc4451faefd3d5 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -89,8 +89,7 @@ GTNODE(NULLCHECK , GenTreeIndir ,0,GTK_UNOP|GTK_NOVALUE) GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) // single-dimension (SZ) array length GTNODE(MDARR_LENGTH , GenTreeMDArr ,0,GTK_UNOP|GTK_EXOP) // multi-dimension (MD) array length of a specific dimension GTNODE(MDARR_LOWER_BOUND, GenTreeMDArr ,0,GTK_UNOP|GTK_EXOP) // multi-dimension (MD) array lower bound of a specific dimension -GTNODE(FIELD , GenTreeField ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Field load -GTNODE(FIELD_ADDR , GenTreeField ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Field address +GTNODE(FIELD_ADDR , GenTreeFieldAddr ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Field address GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // object allocator GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index 35b8d17afbb49b..11f6928b3e307d 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -66,7 +66,7 @@ GTSTRUCT_2(LclVar , GT_LCL_VAR, GT_STORE_LCL_VAR) GTSTRUCT_3(LclFld , GT_LCL_FLD, GT_STORE_LCL_FLD, GT_LCL_ADDR) GTSTRUCT_1(Cast , GT_CAST) GTSTRUCT_1(Box , GT_BOX) -GTSTRUCT_2(Field , GT_FIELD, GT_FIELD_ADDR) +GTSTRUCT_1(FieldAddr , GT_FIELD_ADDR) GTSTRUCT_1(Call , GT_CALL) GTSTRUCT_1(FieldList , GT_FIELD_LIST) GTSTRUCT_1(Colon , GT_COLON) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 75f71b552d0a30..d70b9d11e81d16 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -930,7 +930,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, BasicBlock* block /* = nullptr */ ) { - assert(varTypeIsStruct(dest) && (dest->OperIsLocal() || dest->OperIsIndir() || dest->OperIs(GT_FIELD))); + assert(varTypeIsStruct(dest) && (dest->OperIsLocal() || dest->OperIsIndir())); assert(dest->TypeGet() == src->TypeGet()); if (dest->TypeIs(TYP_STRUCT)) @@ -1207,7 +1207,14 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, case GT_IND: if (willDeref) { - return structVal->AsIndir()->Addr(); + GenTree* addr = structVal->AsIndir()->Addr(); + if (addr->OperIs(GT_FIELD_ADDR)) + { + // TODO-FIELD: delete this zero-diff quirk. + addr->gtFlags &= ~GTF_FLD_DEREFERENCED; + } + + return addr; } break; @@ -1217,19 +1224,6 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, case GT_LCL_FLD: return gtNewLclAddrNode(structVal->AsLclFld()->GetLclNum(), structVal->AsLclFld()->GetLclOffs(), TYP_BYREF); - case GT_FIELD: - { - GenTreeField* fieldNode = structVal->AsField(); - GenTreeField* fieldAddr = - new (this, GT_FIELD_ADDR) GenTreeField(GT_FIELD_ADDR, TYP_BYREF, fieldNode->GetFldObj(), - fieldNode->gtFldHnd, fieldNode->gtFldOffset); - fieldAddr->gtFldMayOverlap = fieldNode->gtFldMayOverlap; -#ifdef FEATURE_READYTORUN - fieldAddr->gtFieldLookup = fieldNode->gtFieldLookup; -#endif - return fieldAddr; - } - case GT_COMMA: impAppendTree(structVal->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); return impGetStructAddr(structVal->AsOp()->gtGetOp2(), structHnd, curLevel, willDeref); @@ -1348,7 +1342,7 @@ GenTree* Compiler::impNormStructVal(GenTree* structVal, CORINFO_CLASS_HANDLE str } while (blockNode->OperGet() == GT_COMMA); } - if (blockNode->OperIsBlk() || blockNode->OperIs(GT_FIELD)) + if (blockNode->OperIsBlk()) { // Sink the GT_COMMA below the blockNode addr. // That is GT_COMMA(op1, op2=blockNode) is transformed into @@ -3264,18 +3258,13 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, if (castResult == TypeCompareState::Must) { - const CORINFO_FIELD_HANDLE hasValueFldHnd = - info.compCompHnd->getFieldInClass(nullableCls, 0); - - assert(info.compCompHnd->getFieldOffset(hasValueFldHnd) == 0); - GenTree* objToBox = impPopStack().val; // Spill struct to get its address (to access hasValue field) objToBox = impGetStructAddr(objToBox, nullableCls, CHECK_SPILL_ALL, true); + static_assert_no_msg(OFFSETOF__CORINFO_NullableOfT__hasValue == 0); - impPushOnStack(gtNewFieldRef(TYP_BOOL, hasValueFldHnd, objToBox, 0), - typeInfo(TI_INT)); + impPushOnStack(gtNewIndir(TYP_BOOL, objToBox), typeInfo(TI_INT)); JITDUMP("\n Importing BOX; ISINST; BR_TRUE/FALSE as nullableVT.hasValue\n"); return 1 + sizeof(mdToken); @@ -8287,7 +8276,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { // If the value being produced comes from loading // via an underlying address, just null check the address. - if (op1->OperIs(GT_FIELD, GT_IND, GT_BLK)) + if (op1->OperIs(GT_IND, GT_BLK)) { gtChangeOperToNullCheck(op1, block); } @@ -9259,33 +9248,32 @@ void Compiler::impImportBlockCode(BasicBlock* block) obj = impGetStructAddr(obj, objType, CHECK_SPILL_ALL, true); } - if (isLoadAddress) - { - op1 = gtNewFieldAddrNode(varTypeIsGC(obj) ? TYP_BYREF : TYP_I_IMPL, resolvedToken.hField, - obj, fieldInfo.offset); - } - else - { - op1 = gtNewFieldRef(lclTyp, resolvedToken.hField, obj, fieldInfo.offset); - } + op1 = gtNewFieldAddrNode(varTypeIsGC(obj) ? TYP_BYREF : TYP_I_IMPL, resolvedToken.hField, obj, + fieldInfo.offset); #ifdef FEATURE_READYTORUN if (fieldInfo.fieldAccessor == CORINFO_FIELD_INSTANCE_WITH_BASE) { - op1->AsField()->gtFieldLookup = fieldInfo.fieldLookup; + op1->AsFieldAddr()->gtFieldLookup = fieldInfo.fieldLookup; } #endif - if (StructHasOverlappingFields(info.compCompHnd->getClassAttribs(resolvedToken.hClass))) { - op1->AsField()->gtFldMayOverlap = true; + op1->AsFieldAddr()->gtFldMayOverlap = true; } - if (!isLoadAddress && compIsForInlining() && - impInlineIsGuaranteedThisDerefBeforeAnySideEffects(nullptr, nullptr, obj, - impInlineInfo->inlArgInfo)) + if (!isLoadAddress) { - impInlineInfo->thisDereferencedFirst = true; + ClassLayout* layout; + lclTyp = TypeHandleToVarType(fieldInfo.fieldType, clsHnd, &layout); + op1 = gtNewFieldIndirNode(lclTyp, layout, op1->AsFieldAddr()); + + if (compIsForInlining() && + impInlineIsGuaranteedThisDerefBeforeAnySideEffects(nullptr, nullptr, obj, + impInlineInfo->inlArgInfo)) + { + impInlineInfo->thisDereferencedFirst = true; + } } } break; @@ -9382,14 +9370,13 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (!isLoadAddress) { - if (prefixFlags & PREFIX_VOLATILE) { op1->gtFlags |= GTF_ORDER_SIDEEFF; // Prevent this from being reordered if (!usesHelper) { - assert(op1->OperIs(GT_FIELD, GT_IND, GT_BLK)); + assert(op1->OperIs(GT_IND, GT_BLK)); op1->gtFlags |= GTF_IND_VOLATILE; } } @@ -9398,7 +9385,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { if (!usesHelper) { - assert(op1->OperIs(GT_FIELD, GT_IND, GT_BLK)); + assert(op1->OperIs(GT_IND, GT_BLK)); op1->gtFlags |= GTF_IND_UNALIGNED; } } @@ -9426,7 +9413,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CEE_STFLD: case CEE_STSFLD: { - bool isStoreStatic = (opcode == CEE_STSFLD); /* Get the CP_Fieldref index */ @@ -9442,7 +9428,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) eeGetFieldInfo(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo); - lclTyp = JITtype2varType(fieldInfo.fieldType); + ClassLayout* layout; + lclTyp = TypeHandleToVarType(fieldInfo.fieldType, fieldInfo.structType, &layout); if (compIsForInlining()) { @@ -9568,20 +9555,21 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CORINFO_FIELD_INSTANCE_WITH_BASE: #endif { - /* Create the data member node */ - op1 = gtNewFieldRef(lclTyp, resolvedToken.hField, obj, fieldInfo.offset); - DWORD typeFlags = info.compCompHnd->getClassAttribs(resolvedToken.hClass); - if (StructHasOverlappingFields(typeFlags)) - { - op1->AsField()->gtFldMayOverlap = true; - } + op1 = gtNewFieldAddrNode(varTypeIsGC(obj) ? TYP_BYREF : TYP_I_IMPL, resolvedToken.hField, obj, + fieldInfo.offset); #ifdef FEATURE_READYTORUN if (fieldInfo.fieldAccessor == CORINFO_FIELD_INSTANCE_WITH_BASE) { - op1->AsField()->gtFieldLookup = fieldInfo.fieldLookup; + op1->AsFieldAddr()->gtFieldLookup = fieldInfo.fieldLookup; } #endif + if (StructHasOverlappingFields(info.compCompHnd->getClassAttribs(resolvedToken.hClass))) + { + op1->AsFieldAddr()->gtFldMayOverlap = true; + } + + op1 = gtNewFieldIndirNode(lclTyp, layout, op1->AsFieldAddr()); if (compIsForInlining() && impInlineIsGuaranteedThisDerefBeforeAnySideEffects(op2, nullptr, obj, @@ -9597,9 +9585,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) op1 = gtNewFieldAddrNode(TYP_I_IMPL, resolvedToken.hField, nullptr, fieldInfo.offset); op1->gtFlags |= GTF_FLD_TLS; // fgMorphExpandTlsField will handle the transformation. - ClassLayout* layout; - lclTyp = TypeHandleToVarType(fieldInfo.fieldType, fieldInfo.structType, &layout); - op1 = (lclTyp == TYP_STRUCT) ? gtNewBlkIndir(layout, op1, GTF_IND_NONFAULTING) + op1 = (lclTyp == TYP_STRUCT) ? gtNewBlkIndir(layout, op1, GTF_IND_NONFAULTING) : gtNewIndir(lclTyp, op1, GTF_IND_NONFAULTING); break; #else @@ -9635,7 +9621,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) } else { - assert(op1->OperIs(GT_FIELD, GT_IND)); + assert(op1->OperIs(GT_IND)); if (prefixFlags & PREFIX_VOLATILE) { @@ -9653,8 +9639,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if ((lclTyp == TYP_REF) && (((fieldInfo.fieldFlags & CORINFO_FLG_FIELD_STATIC) != 0) || obj->TypeIs(TYP_REF))) { - static_assert_no_msg(GTF_FLD_TGT_HEAP == GTF_IND_TGT_HEAP); - op1->gtFlags |= GTF_FLD_TGT_HEAP; + op1->gtFlags |= GTF_IND_TGT_HEAP; } /* V4.0 allows assignment of i4 constant values to i8 type vars when IL verifier is bypassed (full @@ -12674,7 +12659,7 @@ void Compiler::impFixPredLists() // // Remarks: // This is a variant of GenTree::IsInvariant that is more suitable for use -// during import. Unlike that function, this one handles GT_FIELD nodes. +// during import. Unlike that function, this one handles GT_FIELD_ADDR nodes. // bool Compiler::impIsInvariant(const GenTree* tree) { @@ -12695,9 +12680,9 @@ bool Compiler::impIsInvariant(const GenTree* tree) bool Compiler::impIsAddressInLocal(const GenTree* tree, GenTree** lclVarTreeOut) { const GenTree* op = tree; - while (op->OperIs(GT_FIELD_ADDR) && op->AsField()->IsInstance()) + while (op->OperIs(GT_FIELD_ADDR) && op->AsFieldAddr()->IsInstance()) { - op = op->AsField()->GetFldObj(); + op = op->AsFieldAddr()->GetFldObj(); } if (op->OperIs(GT_LCL_ADDR)) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 1cd5cc5dd18ff2..95caad6b7839d8 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2896,8 +2896,10 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_FIELD_HANDLE lengthHnd = info.compCompHnd->getFieldInClass(clsHnd, 1); const unsigned lengthOffset = info.compCompHnd->getFieldOffset(lengthHnd); - GenTreeField* length = gtNewFieldRef(TYP_INT, lengthHnd, ptrToSpan, lengthOffset); - length->SetIsSpanLength(true); + GenTreeFieldAddr* lengthFieldAddr = + gtNewFieldAddrNode(genActualType(ptrToSpan), lengthHnd, ptrToSpan, lengthOffset); + lengthFieldAddr->SetIsSpanLength(true); + GenTree* length = gtNewFieldIndirNode(TYP_INT, nullptr, lengthFieldAddr); GenTree* boundsCheck = new (this, GT_BOUNDS_CHECK) GenTreeBoundsChk(index, length, SCK_RNGCHK_FAIL); @@ -2913,8 +2915,10 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_FIELD_HANDLE ptrHnd = info.compCompHnd->getFieldInClass(clsHnd, 0); const unsigned ptrOffset = info.compCompHnd->getFieldOffset(ptrHnd); - GenTree* data = gtNewFieldRef(TYP_BYREF, ptrHnd, ptrToSpanClone, ptrOffset); - GenTree* result = gtNewOperNode(GT_ADD, TYP_BYREF, data, index); + GenTreeFieldAddr* dataFieldAddr = + gtNewFieldAddrNode(genActualType(ptrToSpanClone), ptrHnd, ptrToSpanClone, ptrOffset); + GenTree* data = gtNewFieldIndirNode(TYP_BYREF, nullptr, dataFieldAddr); + GenTree* result = gtNewOperNode(GT_ADD, TYP_BYREF, data, index); // Prepare result var_types resultType = JITtype2varType(sig->retType); @@ -2957,9 +2961,12 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, CORINFO_FIELD_HANDLE lengthHnd = info.compCompHnd->getFieldInClass(clsHnd, 1); const unsigned lengthOffset = info.compCompHnd->getFieldOffset(lengthHnd); - GenTreeField* fieldRef = gtNewFieldRef(TYP_INT, lengthHnd, ptrToSpan, lengthOffset); - fieldRef->SetIsSpanLength(true); - return fieldRef; + GenTreeFieldAddr* lengthFieldAddr = + gtNewFieldAddrNode(genActualType(ptrToSpan), lengthHnd, ptrToSpan, lengthOffset); + lengthFieldAddr->SetIsSpanLength(true); + GenTree* lengthField = gtNewFieldIndirNode(TYP_INT, nullptr, lengthFieldAddr); + + return lengthField; } case NI_System_RuntimeTypeHandle_GetValueInternal: diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 2a36b09b96988d..c751c7b4d3ee5b 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -388,7 +388,7 @@ GenTree* Compiler::impExpandHalfConstEqualsSWAR( // // Arguments: // data - Pointer (LCL_VAR) to a data to vectorize -// lengthFld - Pointer (LCL_VAR or GT_FIELD) to Length field +// lengthFld - Pointer (LCL_VAR or GT_IND) to Length field // checkForNull - Check data for null // startsWith - Is it StartsWith or Equals? // cns - Constant data (array of 2-byte chars) @@ -787,8 +787,10 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* GenTreeLclVar* spanObjRefLcl = gtNewLclvNode(spanObjRef, TYP_BYREF); GenTreeLclVar* spanDataTmpLcl = gtNewLclvNode(spanDataTmp, TYP_BYREF); - GenTreeField* spanLength = gtNewFieldRef(TYP_INT, lengthHnd, gtClone(spanObjRefLcl), lengthOffset); - GenTreeField* spanData = gtNewFieldRef(TYP_BYREF, pointerHnd, spanObjRefLcl); + GenTreeFieldAddr* spanLengthAddr = gtNewFieldAddrNode(TYP_BYREF, lengthHnd, gtClone(spanObjRefLcl), lengthOffset); + GenTree* spanLength = gtNewFieldIndirNode(TYP_INT, nullptr, spanLengthAddr); + GenTreeFieldAddr* spanDataAddr = gtNewFieldAddrNode(TYP_BYREF, pointerHnd, spanObjRefLcl); + GenTree* spanData = gtNewFieldIndirNode(TYP_BYREF, nullptr, spanDataAddr); GenTree* unrolled = impExpandHalfConstEquals(spanDataTmpLcl, spanLength, false, startsWith, (WCHAR*)str, cnsLength, 0, cmpMode); diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index d70f534f969925..1284185219dd02 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -217,7 +217,7 @@ class LocalAddressVisitor final : public GenTreeVisitor { // During tree traversal every GenTree node produces a "value" that represents: // - the memory location associated with a local variable, including an offset - // accumulated from GT_LCL_FLD and GT_FIELD nodes. + // accumulated from GT_LCL_FLD and GT_FIELD_ADDR nodes. // - the address of local variable memory location, including an offset as well. // - an unknown value - the result of a node we don't know how to process. This // also includes the result of TYP_VOID nodes (or any other nodes that don't @@ -554,41 +554,60 @@ class LocalAddressVisitor final : public GenTreeVisitor { GenTree* const node = *use; - if (node->OperIs(GT_IND, GT_FIELD, GT_FIELD_ADDR)) - { - MorphStructField(node, user); - } - else if (node->OperIs(GT_LCL_FLD)) + switch (node->OperGet()) { - MorphLocalField(node, user); - } + case GT_IND: + case GT_BLK: + if (MorphStructField(node->AsIndir(), user)) + { + goto LOCAL_NODE; + } + break; - if (node->OperIsAnyLocal()) - { - unsigned const lclNum = node->AsLclVarCommon()->GetLclNum(); - LclVarDsc* const varDsc = m_compiler->lvaGetDesc(lclNum); + case GT_FIELD_ADDR: + if (MorphStructFieldAddress(node, 0) != BAD_VAR_NUM) + { + goto LOCAL_NODE; + } + break; - UpdateEarlyRefCount(lclNum, node, user); + case GT_LCL_FLD: + MorphLocalField(node, user); + goto LOCAL_NODE; - if (varDsc->lvIsStructField) + case GT_LCL_VAR: + case GT_LCL_ADDR: + LOCAL_NODE: { - // Promoted field, increase count for the parent lclVar. - // - assert(!m_compiler->lvaIsImplicitByRefLocal(lclNum)); - unsigned parentLclNum = varDsc->lvParentLcl; - UpdateEarlyRefCount(parentLclNum, node, user); - } + unsigned const lclNum = node->AsLclVarCommon()->GetLclNum(); + LclVarDsc* const varDsc = m_compiler->lvaGetDesc(lclNum); - if (varDsc->lvPromoted) - { - // Promoted struct, increase count for each promoted field. - // - for (unsigned childLclNum = varDsc->lvFieldLclStart; - childLclNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++childLclNum) + UpdateEarlyRefCount(lclNum, node, user); + + if (varDsc->lvIsStructField) { - UpdateEarlyRefCount(childLclNum, node, user); + // Promoted field, increase count for the parent lclVar. + // + assert(!m_compiler->lvaIsImplicitByRefLocal(lclNum)); + unsigned parentLclNum = varDsc->lvParentLcl; + UpdateEarlyRefCount(parentLclNum, node, user); + } + + if (varDsc->lvPromoted) + { + // Promoted struct, increase count for each promoted field. + // + for (unsigned childLclNum = varDsc->lvFieldLclStart; + childLclNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++childLclNum) + { + UpdateEarlyRefCount(childLclNum, node, user); + } } } + break; + + default: + break; } PushValue(use); @@ -678,12 +697,12 @@ class LocalAddressVisitor final : public GenTreeVisitor } case GT_FIELD_ADDR: - if (node->AsField()->IsInstance()) + if (node->AsFieldAddr()->IsInstance()) { assert(TopValue(1).Node() == node); - assert(TopValue(0).Node() == node->AsField()->GetFldObj()); + assert(TopValue(0).Node() == node->AsFieldAddr()->GetFldObj()); - if (!TopValue(1).AddOffset(TopValue(0), node->AsField()->gtFldOffset)) + if (!TopValue(1).AddOffset(TopValue(0), node->AsFieldAddr()->gtFldOffset)) { // The field object did not represent an address, or the latter overflowed. EscapeValue(TopValue(0), node); @@ -697,32 +716,6 @@ class LocalAddressVisitor final : public GenTreeVisitor } break; - case GT_FIELD: - if (node->AsField()->IsInstance()) - { - assert(TopValue(1).Node() == node); - assert(TopValue(0).Node() == node->AsField()->GetFldObj()); - - if (node->AsField()->IsVolatile()) - { - // Volatile indirections must not be removed so the address, if any, must be escaped. - EscapeValue(TopValue(0), node); - } - else if (!TopValue(1).Indir(TopValue(0), node->AsField()->gtFldOffset)) - { - // Either the address comes from a location value (e.g. FIELD(IND(...))) or the field - // offset has overflowed. - EscapeValue(TopValue(0), node); - } - - PopValue(); - } - else - { - assert(TopValue(0).Node() == node); - } - break; - case GT_BLK: case GT_IND: assert(TopValue(1).Node() == node); @@ -964,7 +957,7 @@ class LocalAddressVisitor final : public GenTreeVisitor unsigned lclNum = val.LclNum(); LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); - unsigned indirSize = GetIndirSize(node); + unsigned indirSize = node->AsIndir()->Size(); bool isWide; if ((indirSize == 0) || ((val.Offset() + indirSize) > UINT16_MAX)) @@ -1013,35 +1006,6 @@ class LocalAddressVisitor final : public GenTreeVisitor INDEBUG(val.Consume();) } - //------------------------------------------------------------------------ - // GetIndirSize: Return the size (in bytes) of an indirection node. - // - // Arguments: - // indir - the indirection node - // user - the node that uses the indirection - // - // Notes: - // This returns 0 for indirection of unknown size, i. e. IND - // nodes that are used as sources of STORE_DYN_BLKs. - // - unsigned GetIndirSize(GenTree* indir) - { - assert(indir->OperIs(GT_IND, GT_BLK, GT_FIELD)); - - if (indir->TypeGet() != TYP_STRUCT) - { - return genTypeSize(indir); - } - - if (indir->OperIs(GT_IND)) - { - // TODO-1stClassStructs: remove once "IND" nodes are no more. - return 0; - } - - return indir->GetLayout(m_compiler)->GetSize(); - } - //------------------------------------------------------------------------ // MorphLocalAddress: Change a tree that represents a local variable address // to a single LCL_VAR_ADDR or LCL_FLD_ADDR node. @@ -1088,27 +1052,13 @@ class LocalAddressVisitor final : public GenTreeVisitor // void MorphWideLocalIndir(const Value& val) { - assert(val.Node()->OperIsIndir() || val.Node()->OperIs(GT_FIELD)); + assert(val.Node()->OperIsIndir()); GenTree* node = val.Node(); - GenTree* addr = node->gtGetOp1(); + GenTree* addr = node->AsIndir()->Addr(); MorphLocalAddress(addr, val.LclNum(), val.Offset()); - if (node->OperIs(GT_FIELD)) - { - if (node->TypeIs(TYP_STRUCT)) - { - ClassLayout* layout = node->GetLayout(m_compiler); - node->SetOper(GT_BLK); - node->AsBlk()->Initialize(layout); - } - else - { - node->SetOper(GT_IND); - } - } - // GLOB_REF may not be set already in the "large offset" case. Add it. node->gtFlags |= GTF_GLOB_REF; } @@ -1288,11 +1238,11 @@ class LocalAddressVisitor final : public GenTreeVisitor // IndirTransform SelectLocalIndirTransform(const Value& val, GenTree* user, ClassLayout** pStructLayout) { - GenTree* indir = val.Node(); + GenTreeIndir* indir = val.Node()->AsIndir(); // We don't expect indirections that cannot be turned into local nodes here. assert(val.Offset() <= UINT16_MAX); - assert(indir->OperIs(GT_IND, GT_BLK, GT_FIELD) && ((indir->gtFlags & GTF_IND_VOLATILE) == 0)); + assert(indir->OperIs(GT_IND, GT_BLK) && !indir->IsVolatile()); if (IsUnused(indir, user)) { @@ -1370,29 +1320,15 @@ class LocalAddressVisitor final : public GenTreeVisitor return IndirTransform::LclFld; } - ClassLayout* indirLayout = nullptr; - - if (indir->OperIs(GT_FIELD)) - { - CORINFO_CLASS_HANDLE fieldClassHandle; - var_types fieldType = m_compiler->eeGetFieldType(indir->AsField()->gtFldHnd, &fieldClassHandle); - assert(fieldType == TYP_STRUCT); - - indirLayout = m_compiler->typGetObjLayout(fieldClassHandle); - } - else - { - indirLayout = indir->AsBlk()->GetLayout(); - } - - *pStructLayout = indirLayout; + ClassLayout* layout = indir->AsBlk()->GetLayout(); + *pStructLayout = layout; if (varDsc->TypeGet() != TYP_STRUCT) { return IndirTransform::LclFld; } - if ((val.Offset() == 0) && ClassLayout::AreCompatible(indirLayout, varDsc->GetLayout())) + if ((val.Offset() == 0) && ClassLayout::AreCompatible(layout, varDsc->GetLayout())) { return IndirTransform::LclVar; } @@ -1402,107 +1338,119 @@ class LocalAddressVisitor final : public GenTreeVisitor //------------------------------------------------------------------------ // MorphStructField: Reduces indirect access to a promoted local (e.g. - // FIELD(ADDR(LCL_VAR))) to a GT_LCL_VAR that references the struct field. + // IND(FIELD_ADDR(LCL_ADDR))) to a GT_LCL_VAR that references the field. // // Arguments: - // node - the GT_IND/GT_FIELD/GT_FIELD_ADDR node - // user - the node that uses the field + // node - the GT_IND/GT_BLK node + // user - "node"'s user // - // Notes: - // This does not do anything if the access does not denote a promoted - // struct field. + // Return Value: + // Whether the indirection node was replaced with a local one. // - void MorphStructField(GenTree* node, GenTree* user) + bool MorphStructField(GenTreeIndir* node, GenTree* user) { - assert(node->OperIs(GT_IND, GT_FIELD, GT_FIELD_ADDR)); + assert(node->OperIs(GT_IND, GT_BLK)); - GenTree* objRef = node->AsUnOp()->gtOp1; + GenTree* addr = node->Addr(); + if (node->IsVolatile() && (!addr->OperIs(GT_FIELD_ADDR) || ((addr->gtFlags & GTF_FLD_DEREFERENCED) == 0))) + { + // TODO-Bug: transforming volatile indirections like this is not legal. The above condition is a quirk. + return false; + } - // TODO-Bug: this code does not pay attention to "GTF_IND_VOLATILE". - if ((objRef != nullptr) && objRef->IsLclVarAddr()) + unsigned fieldLclNum = MorphStructFieldAddress(addr, node->Size()); + if (fieldLclNum == BAD_VAR_NUM) { - const LclVarDsc* varDsc = m_compiler->lvaGetDesc(objRef->AsLclVarCommon()); + return false; + } - if (varDsc->lvPromoted) + LclVarDsc* fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum); + var_types fieldType = fieldVarDsc->TypeGet(); + assert(fieldType != TYP_STRUCT); // Promoted LCL_VAR can't have a struct type. + + if (node->TypeGet() == fieldType) + { + GenTreeFlags lclVarFlags = node->gtFlags & (GTF_NODE_MASK | GTF_DONT_CSE); + + if ((user != nullptr) && user->OperIs(GT_ASG) && (user->AsOp()->gtOp1 == node)) { - unsigned fieldOffset = node->OperIs(GT_IND) ? 0 : node->AsField()->gtFldOffset; - unsigned fieldLclNum = m_compiler->lvaGetFieldLocal(varDsc, fieldOffset); + lclVarFlags |= GTF_VAR_DEF; + } + + node->ChangeOper(GT_LCL_VAR); + node->AsLclVar()->SetLclNum(fieldLclNum); + node->gtType = fieldType; + node->gtFlags = lclVarFlags; + return true; + } + + return false; + } + + //------------------------------------------------------------------------ + // MorphStructFieldAddress: Replace FIELD_ADDR(LCL_ADDR) with LCL_ADDR + // that references a promoted field. + // + // Arguments: + // node - the address node + // accessSize - load/store size if known, zero otherwise + // + // Return Value: + // Local number for the promoted field if the replacement was successful, + // BAD_VAR_NUM otherwise. + // + unsigned MorphStructFieldAddress(GenTree* node, unsigned accessSize) + { + unsigned offset = 0; + bool isSpanLength = false; + GenTree* addr = node; + if (addr->OperIs(GT_FIELD_ADDR) && addr->AsFieldAddr()->IsInstance()) + { + offset = addr->AsFieldAddr()->gtFldOffset; + isSpanLength = addr->AsFieldAddr()->IsSpanLength(); + addr = addr->AsFieldAddr()->GetFldObj(); + } + + if (addr->IsLclVarAddr()) + { + const LclVarDsc* varDsc = m_compiler->lvaGetDesc(addr->AsLclVarCommon()); + if (varDsc->lvPromoted) + { + unsigned fieldLclNum = m_compiler->lvaGetFieldLocal(varDsc, offset); if (fieldLclNum == BAD_VAR_NUM) { // Access a promoted struct's field with an offset that doesn't correspond to any field. // It can happen if the struct was cast to another struct with different offsets. - return; + return BAD_VAR_NUM; } - const LclVarDsc* fieldDsc = m_compiler->lvaGetDesc(fieldLclNum); - var_types fieldType = fieldDsc->TypeGet(); - assert(fieldType != TYP_STRUCT); // Promoted LCL_VAR can't have a struct type. + LclVarDsc* fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum); - if (node->OperIs(GT_FIELD_ADDR)) + // Retargeting the indirection to reference the promoted field would make it "wide", exposing + // the whole parent struct (with all of its fields). + if (accessSize > genTypeSize(fieldVarDsc)) { - node->ChangeOper(GT_LCL_ADDR); - node->AsLclFld()->SetLclNum(fieldLclNum); - node->AsLclFld()->SetLclOffs(0); + return BAD_VAR_NUM; } - else if (node->TypeGet() == fieldType) - { - GenTreeFlags lclVarFlags = node->gtFlags & (GTF_NODE_MASK | GTF_DONT_CSE); - if (node->OperIs(GT_FIELD) && node->AsField()->IsSpanLength()) - { - m_compiler->lvaGetDesc(fieldLclNum)->SetIsNeverNegative(true); - } - - if ((user != nullptr) && user->OperIs(GT_ASG) && (user->AsOp()->gtOp1 == node)) - { - lclVarFlags |= GTF_VAR_DEF; - } - - node->ChangeOper(GT_LCL_VAR); - node->AsLclVar()->SetLclNum(fieldLclNum); - node->gtType = fieldType; - node->gtFlags = lclVarFlags; - } - else // Here we will turn "FIELD/IND(LCL_ADDR_VAR)" into "OBJ/IND(LCL_ADDR_VAR)". + if (isSpanLength && (accessSize == genTypeSize(TYP_INT))) { - // This type mismatch is somewhat common due to how we retype fields of struct type that - // recursively simplify down to a primitive. E. g. for "struct { struct { int a } A, B }", - // the promoted local would look like "{ int a, B }", while the IR would contain "FIELD" - // nodes for the outer struct "A". - // - // TODO-1stClassStructs: delete this once "IND" nodes are no more. - if (node->OperIs(GT_IND) && node->TypeIs(TYP_STRUCT)) - { - return; - } - - ClassLayout* layout = node->TypeIs(TYP_STRUCT) ? node->GetLayout(m_compiler) : nullptr; - unsigned indSize = node->TypeIs(TYP_STRUCT) ? layout->GetSize() : genTypeSize(node); - if (indSize > genTypeSize(fieldType)) - { - // Retargeting this indirection to reference the promoted field would make it - // "wide", address-exposing the whole parent struct (with all of its fields). - return; - } - - if (node->TypeIs(TYP_STRUCT)) - { - node->SetOper(GT_BLK); - node->AsBlk()->Initialize(layout); - } - else - { - node->SetOper(GT_IND); - } - - objRef->AsLclFld()->SetLclNum(fieldLclNum); + fieldVarDsc->SetIsNeverNegative(true); } JITDUMP("Replacing the field in promoted struct with local var V%02u\n", fieldLclNum); m_stmtModified = true; + + node->ChangeOper(GT_LCL_ADDR); + node->AsLclFld()->SetLclNum(fieldLclNum); + node->AsLclFld()->SetLclOffs(0); + + return fieldLclNum; } } + + return BAD_VAR_NUM; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 0e415743f561db..23b3e2444d5fa6 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -242,11 +242,6 @@ void Compiler::fgPerNodeLocalVarLiveness(GenTree* tree) } break; - // These should have been morphed away to become GT_INDs: - case GT_FIELD: - unreached(); - break; - case GT_ASG: // An indirect store defines a memory location. if (!tree->AsOp()->gtGetOp1()->OperIsLocal()) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4cfbfb2eeb63e5..edd471128ed6d4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -4438,6 +4438,8 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) // same values are used in the bounds check and the actual dereference. // Also we allocate the temporary when the expression is sufficiently complex/expensive. // + // TODO-FIELD: review below comment. + // // Note that if the expression is a GT_FIELD, it has not yet been morphed so its true complexity is // not exposed. Without that condition there are cases of local struct fields that were previously, // needlessly, marked as GTF_GLOB_REF, and when that was fixed, there were some regressions that @@ -4449,7 +4451,7 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) // do this here. Likewise for implicit byrefs. if (((arrRef->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) != 0) || - gtComplexityExceeds(arrRef, MAX_ARR_COMPLEXITY) || arrRef->OperIs(GT_FIELD, GT_LCL_FLD) || + gtComplexityExceeds(arrRef, MAX_ARR_COMPLEXITY) || arrRef->OperIs(GT_LCL_FLD) || (arrRef->OperIs(GT_LCL_VAR) && lvaIsLocalImplicitlyAccessedByRef(arrRef->AsLclVar()->GetLclNum()))) { unsigned arrRefTmpNum = lvaGrabTemp(true DEBUGARG("arr expr")); @@ -4464,7 +4466,7 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) } if (((index->gtFlags & (GTF_ASG | GTF_CALL | GTF_GLOB_REF)) != 0) || - gtComplexityExceeds(index, MAX_ARR_COMPLEXITY) || index->OperIs(GT_FIELD, GT_LCL_FLD) || + gtComplexityExceeds(index, MAX_ARR_COMPLEXITY) || index->OperIs(GT_LCL_FLD) || (index->OperIs(GT_LCL_VAR) && lvaIsLocalImplicitlyAccessedByRef(index->AsLclVar()->GetLclNum()))) { unsigned indexTmpNum = lvaGrabTemp(true DEBUGARG("index expr")); @@ -4943,7 +4945,7 @@ GenTree* Compiler::fgMorphLocalVar(GenTree* tree) // Return Value: // The local number. // -unsigned Compiler::fgGetFieldMorphingTemp(GenTreeField* fieldNode) +unsigned Compiler::fgGetFieldMorphingTemp(GenTreeFieldAddr* fieldNode) { assert(fieldNode->IsInstance()); @@ -4979,29 +4981,24 @@ unsigned Compiler::fgGetFieldMorphingTemp(GenTreeField* fieldNode) } //------------------------------------------------------------------------ -// fgMorphField: Fully morph a FIELD/FIELD_ADDR tree. +// fgMorphFieldAddr: Fully morph a FIELD_ADDR tree. // -// Expands the field node into explicit additions and indirections. +// Expands the field node into explicit additions. // // Arguments: -// tree - The FIELD/FIELD_ADDR tree +// tree - The FIELD_ADDR tree // mac - The morphing context, used to elide adding null checks // // Return Value: // The fully morphed "tree". // -GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) +GenTree* Compiler::fgMorphFieldAddr(GenTree* tree, MorphAddrContext* mac) { - assert(tree->OperIs(GT_FIELD, GT_FIELD_ADDR)); + assert(tree->OperIs(GT_FIELD_ADDR)); - GenTreeField* fieldNode = tree->AsField(); - GenTree* objRef = fieldNode->GetFldObj(); - bool isAddr = tree->OperIs(GT_FIELD_ADDR); - - if (tree->OperIs(GT_FIELD)) - { - noway_assert(((objRef != nullptr) && objRef->OperIs(GT_LCL_ADDR)) || ((tree->gtFlags & GTF_GLOB_REF) != 0)); - } + GenTreeFieldAddr* fieldNode = tree->AsFieldAddr(); + GenTree* objRef = fieldNode->GetFldObj(); + bool isAddr = ((tree->gtFlags & GTF_FLD_DEREFERENCED) == 0); if (fieldNode->IsInstance()) { @@ -5035,20 +5032,19 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) DBEXEC(result == fieldNode, result->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED); } - JITDUMP("\nFinal value of Compiler::fgMorphField after morphing:\n"); + JITDUMP("\nFinal value of Compiler::fgMorphFieldAddr after morphing:\n"); DISPTREE(result); return result; } //------------------------------------------------------------------------ -// fgMorphExpandInstanceField: Expand an instance field reference. +// fgMorphExpandInstanceField: Expand an instance field address. // -// Expands the field node into explicit additions and indirections, adding -// explicit null checks if necessary. +// Expands the field node into explicit additions and nullchecks. // // Arguments: -// tree - The FIELD/FIELD_ADDR tree +// tree - The FIELD_ADDR tree // mac - The morphing context, used to elide adding null checks // // Return Value: @@ -5056,31 +5052,26 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) // GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* mac) { - assert(tree->OperIs(GT_FIELD, GT_FIELD_ADDR) && tree->AsField()->IsInstance()); + assert(tree->OperIs(GT_FIELD_ADDR) && tree->AsFieldAddr()->IsInstance()); - GenTree* objRef = tree->AsField()->GetFldObj(); - CORINFO_FIELD_HANDLE fieldHandle = tree->AsField()->gtFldHnd; - unsigned fieldOffset = tree->AsField()->gtFldOffset; + GenTree* objRef = tree->AsFieldAddr()->GetFldObj(); + CORINFO_FIELD_HANDLE fieldHandle = tree->AsFieldAddr()->gtFldHnd; + unsigned fieldOffset = tree->AsFieldAddr()->gtFldOffset; noway_assert(varTypeIsI(genActualType(objRef))); /* Now we have a tree like this: +--------------------+ - | GT_FIELD[_ADDR] | tree + | GT_FIELD_ADDR | tree +----------+---------+ | +--------------+-------------+ - |tree->AsField()->GetFldObj()| + | tree->GetFldObj() | +--------------+-------------+ We want to make it like this (when fldOffset is <= MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT): - +--------------------+ - | GT_IND/GT_BLK | tree (for FIELD) - +---------+----------+ - | - | +---------+----------+ | GT_ADD | addr +---------+----------+ @@ -5136,20 +5127,13 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m if (fgAddrCouldBeNull(objRef)) { - if (tree->OperIs(GT_FIELD)) - { - addExplicitNullCheck = fgIsBigOffset(fieldOffset); - } - else - { - // A non-null context here implies our [+ some offset] parent is an indirection, one that - // will implicitly null-check the produced address. - addExplicitNullCheck = (mac == nullptr) || fgIsBigOffset(mac->m_totalOffset + fieldOffset); + // A non-null context here implies our [+ some offset] parent is an indirection, one that + // will implicitly null-check the produced address. + addExplicitNullCheck = (mac == nullptr) || fgIsBigOffset(mac->m_totalOffset + fieldOffset); - if (!addExplicitNullCheck) - { - mac->m_used = true; - } + if (!addExplicitNullCheck) + { + mac->m_used = true; } } @@ -5164,7 +5148,7 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m if (!objRef->OperIs(GT_LCL_VAR) || lvaIsLocalImplicitlyAccessedByRef(objRef->AsLclVar()->GetLclNum())) { - lclNum = fgGetFieldMorphingTemp(tree->AsField()); + lclNum = fgGetFieldMorphingTemp(tree->AsFieldAddr()); // Create the "asg" node asg = gtNewTempAssign(lclNum, objRef); @@ -5201,12 +5185,12 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m } #ifdef FEATURE_READYTORUN - if (tree->AsField()->gtFieldLookup.addr != nullptr) + if (tree->AsFieldAddr()->gtFieldLookup.addr != nullptr) { GenTree* offsetNode = nullptr; - if (tree->AsField()->gtFieldLookup.accessType == IAT_PVALUE) + if (tree->AsFieldAddr()->gtFieldLookup.accessType == IAT_PVALUE) { - offsetNode = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)tree->AsField()->gtFieldLookup.addr, + offsetNode = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)tree->AsFieldAddr()->gtFieldLookup.addr, GTF_ICON_CONST_PTR, true); #ifdef DEBUG offsetNode->gtGetOp1()->AsIntCon()->gtTargetHandle = (size_t)fieldHandle; @@ -5223,7 +5207,7 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m // We only need to attach the field offset information for class fields. FieldSeq* fieldSeq = nullptr; - if ((objRefType == TYP_REF) && !tree->AsField()->gtFldMayOverlap) + if ((objRefType == TYP_REF) && !tree->AsFieldAddr()->gtFldMayOverlap) { fieldSeq = GetFieldSeqStore()->Create(fieldHandle, fieldOffset, FieldSeq::FieldKind::Instance); } @@ -5239,35 +5223,12 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m { // Create the "comma2" tree. addr = gtNewOperNode(GT_COMMA, addr->TypeGet(), comma, addr); - } - - if (tree->OperIs(GT_FIELD)) - { - if (tree->TypeIs(TYP_STRUCT)) - { - ClassLayout* layout = tree->GetLayout(this); - tree->SetOper(GT_BLK); - tree->AsBlk()->Initialize(layout); - } - else - { - tree->SetOper(GT_IND); - } - - tree->AsIndir()->SetAddr(addr); - } - else // Otherwise, we have a FIELD_ADDR. - { - tree = addr; - } - if (addExplicitNullCheck) - { JITDUMP("After adding explicit null check:\n"); - DISPTREE(tree); + DISPTREE(addr); } - return tree; + return addr; } //------------------------------------------------------------------------ @@ -5285,11 +5246,10 @@ GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* m // GenTree* Compiler::fgMorphExpandTlsFieldAddr(GenTree* tree) { - // Note we do not support "FIELD"s for TLS statics, for simplicity. - assert(tree->OperIs(GT_FIELD_ADDR) && tree->AsField()->IsTlsStatic()); + assert(tree->OperIs(GT_FIELD_ADDR) && tree->AsFieldAddr()->IsTlsStatic()); - CORINFO_FIELD_HANDLE fieldHandle = tree->AsField()->gtFldHnd; - int fieldOffset = tree->AsField()->gtFldOffset; + CORINFO_FIELD_HANDLE fieldHandle = tree->AsFieldAddr()->gtFldHnd; + int fieldOffset = tree->AsFieldAddr()->gtFldOffset; // Thread Local Storage static field reference // @@ -5356,7 +5316,7 @@ GenTree* Compiler::fgMorphExpandTlsFieldAddr(GenTree* tree) tlsRef = gtNewIndir(TYP_I_IMPL, tlsRef); // Add the TLS static field offset to the address. - assert(!tree->AsField()->gtFldMayOverlap); + assert(!tree->AsFieldAddr()->gtFldMayOverlap); FieldSeq* fieldSeq = GetFieldSeqStore()->Create(fieldHandle, fieldOffset, FieldSeq::FieldKind::SimpleStatic); GenTree* offsetNode = gtNewIconNode(fieldOffset, fieldSeq); @@ -8303,9 +8263,15 @@ GenTree* Compiler::getSIMDStructFromField(GenTree* tree, unsigned* simdSizeOut, bool ignoreUsedInSIMDIntrinsic /*false*/) { - if (tree->OperIs(GT_FIELD) && tree->AsField()->IsInstance()) + if (tree->OperIs(GT_IND)) { - GenTree* objRef = tree->AsField()->GetFldObj(); + GenTree* addr = tree->AsIndir()->Addr(); + if (!addr->OperIs(GT_FIELD_ADDR) || !addr->AsFieldAddr()->IsInstance()) + { + return nullptr; + } + + GenTree* objRef = addr->AsFieldAddr()->GetFldObj(); if (objRef->IsLclVarAddr()) { LclVarDsc* varDsc = lvaGetDesc(objRef->AsLclVarCommon()); @@ -8313,7 +8279,7 @@ GenTree* Compiler::getSIMDStructFromField(GenTree* tree, { CorInfoType simdBaseJitType = varDsc->GetSimdBaseJitType(); var_types simdBaseType = JITtype2varType(simdBaseJitType); - unsigned fieldOffset = tree->AsField()->gtFldOffset; + unsigned fieldOffset = addr->AsFieldAddr()->gtFldOffset; unsigned baseTypeSize = genTypeSize(simdBaseType); // Below condition is convervative. We don't actually need the two types to @@ -8591,9 +8557,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA } break; - case GT_FIELD: case GT_FIELD_ADDR: - return fgMorphField(tree, mac); + return fgMorphFieldAddr(tree, mac); case GT_INDEX_ADDR: return fgMorphIndexAddr(tree->AsIndexAddr()); diff --git a/src/coreclr/jit/objectalloc.cpp b/src/coreclr/jit/objectalloc.cpp index e9c6df3e2410a2..80ef539f272c7c 100644 --- a/src/coreclr/jit/objectalloc.cpp +++ b/src/coreclr/jit/objectalloc.cpp @@ -664,7 +664,6 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack* parent keepChecking = true; break; - case GT_FIELD: case GT_IND: // Address of the field/ind is not taken so the local doesn't escape. canLclVarEscapeViaParentStack = false; @@ -760,7 +759,6 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack* p keepChecking = true; break; - case GT_FIELD: case GT_IND: { // The new target could be *not* on the heap. diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index 9b8cafb75e8096..16619e53e67b65 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -816,7 +816,7 @@ class ReplaceVisitor : public GenTreeVisitor JITDUMP("Processing block operation [%06u] that involves replacements\n", Compiler::dspTreeID(asg)); - if (dstInvolvesReplacements && (src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_BLK, GT_FIELD) || src->IsConstInitVal())) + if (dstInvolvesReplacements && (src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_BLK) || src->IsConstInitVal())) { StatementList result; EliminateCommasInBlockOp(asg, &result); @@ -1055,12 +1055,12 @@ class ReplaceVisitor : public GenTreeVisitor void CopyIntoFields( Replacement* firstRep, Replacement* endRep, GenTreeLclVarCommon* dst, GenTree* src, StatementList* result) { - assert(src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_BLK, GT_FIELD)); + assert(src->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_BLK)); GenTreeFlags indirFlags = GTF_EMPTY; - if (src->OperIs(GT_BLK, GT_FIELD)) + if (src->OperIs(GT_BLK)) { - GenTree* addr = src->gtGetOp1(); + GenTree* addr = src->AsIndir()->Addr(); if (addr->OperIsLocal() && (addr->AsLclVarCommon()->GetLclNum() != dst->GetLclNum())) { @@ -1081,15 +1081,8 @@ class ReplaceVisitor : public GenTreeVisitor src->AsUnOp()->gtOp1 = m_compiler->gtNewLclvNode(addrLcl, addr->TypeGet()); } - if (src->OperIs(GT_BLK)) - { - indirFlags = - src->gtFlags & (GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INITCLASS); - } - else - { - indirFlags = src->gtFlags & GTF_IND_VOLATILE; - } + indirFlags = + src->gtFlags & (GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INITCLASS); } LclVarDsc* srcDsc = @@ -1131,22 +1124,17 @@ class ReplaceVisitor : public GenTreeVisitor } else { - if (src->OperIs(GT_FIELD)) - { - srcOffs += src->AsField()->gtFldOffset; - } - if ((rep == firstRep) && m_compiler->fgIsBigOffset(srcOffs) && - m_compiler->fgAddrCouldBeNull(src->gtGetOp1())) + m_compiler->fgAddrCouldBeNull(src->AsIndir()->Addr())) { - GenTree* addrForNullCheck = m_compiler->gtCloneExpr(src->gtGetOp1()); + GenTree* addrForNullCheck = m_compiler->gtCloneExpr(src->AsIndir()->Addr()); GenTreeIndir* indir = m_compiler->gtNewIndir(TYP_BYTE, addrForNullCheck); indir->gtFlags |= indirFlags; result->AddStatement(indir); UpdateEarlyRefCount(addrForNullCheck); } - GenTree* addr = m_compiler->gtCloneExpr(src->gtGetOp1()); + GenTree* addr = m_compiler->gtCloneExpr(src->AsIndir()->Addr()); UpdateEarlyRefCount(addr); if (srcOffs != 0) { @@ -1157,7 +1145,6 @@ class ReplaceVisitor : public GenTreeVisitor GenTree* dstLcl = m_compiler->gtNewLclvNode(rep->LclNum, rep->AccessType); srcFld = m_compiler->gtNewIndir(rep->AccessType, addr, indirFlags); - srcFld->gtFlags |= GTF_GLOB_REF; } result->AddStatement(m_compiler->gtNewAssignNode(dstLcl, srcFld)); @@ -1225,7 +1212,7 @@ class ReplaceVisitor : public GenTreeVisitor { bool any = false; GenTree* lhs = asg->gtGetOp1(); - assert(lhs->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_FIELD, GT_IND, GT_BLK)); + assert(lhs->OperIs(GT_LCL_VAR, GT_LCL_FLD, GT_IND, GT_BLK)); GenTree* rhs = asg->gtGetOp2(); diff --git a/src/coreclr/jit/simd.cpp b/src/coreclr/jit/simd.cpp index 67c5fa1dd0c3a9..c0294ad3f91fb6 100644 --- a/src/coreclr/jit/simd.cpp +++ b/src/coreclr/jit/simd.cpp @@ -473,23 +473,26 @@ void Compiler::setLclRelatedToSIMDIntrinsic(GenTree* tree) } //------------------------------------------------------------- -// Check if two field nodes reference at the same memory location. -// Notice that this check is just based on pattern matching. +// Check if two field address nodes reference at the same location. +// // Arguments: -// op1 - GenTree*. -// op2 - GenTree*. +// op1 - first field address +// op2 - second field address +// // Return Value: -// If op1's parents node and op2's parents node are at the same location, return true. Otherwise, return false - -bool areFieldsParentsLocatedSame(GenTree* op1, GenTree* op2) +// If op1's parents node and op2's parents node are at the same +// location, return true. Otherwise, return false +// +bool areFieldAddressesTheSame(GenTreeFieldAddr* op1, GenTreeFieldAddr* op2) { - assert(op1->OperGet() == GT_FIELD); - assert(op2->OperGet() == GT_FIELD); + assert(op1->OperIs(GT_FIELD_ADDR) && op2->OperIs(GT_FIELD_ADDR)); - GenTree* op1ObjRef = op1->AsField()->GetFldObj(); - GenTree* op2ObjRef = op2->AsField()->GetFldObj(); - while (op1ObjRef != nullptr && op2ObjRef != nullptr) + GenTree* op1ObjRef = op1->GetFldObj(); + GenTree* op2ObjRef = op2->GetFldObj(); + while ((op1ObjRef != nullptr) && (op2ObjRef != nullptr)) { + assert(varTypeIsI(genActualType(op1ObjRef)) && varTypeIsI(genActualType(op2ObjRef))); + if (op1ObjRef->OperGet() != op2ObjRef->OperGet()) { break; @@ -501,10 +504,18 @@ bool areFieldsParentsLocatedSame(GenTree* op1, GenTree* op2) return true; } - if (op1ObjRef->OperIs(GT_FIELD) && (op1ObjRef->AsField()->gtFldHnd == op2ObjRef->AsField()->gtFldHnd)) + if (op1ObjRef->OperIs(GT_IND)) + { + op1ObjRef = op1ObjRef->AsIndir()->Addr(); + op2ObjRef = op2ObjRef->AsIndir()->Addr(); + continue; + } + + if (op1ObjRef->OperIs(GT_FIELD_ADDR) && + (op1ObjRef->AsFieldAddr()->gtFldHnd == op2ObjRef->AsFieldAddr()->gtFldHnd)) { - op1ObjRef = op1ObjRef->AsField()->GetFldObj(); - op2ObjRef = op2ObjRef->AsField()->GetFldObj(); + op1ObjRef = op1ObjRef->AsFieldAddr()->GetFldObj(); + op2ObjRef = op2ObjRef->AsFieldAddr()->GetFldObj(); continue; } else @@ -517,28 +528,29 @@ bool areFieldsParentsLocatedSame(GenTree* op1, GenTree* op2) } //---------------------------------------------------------------------- -// Check whether two field are contiguous +// areFieldsContiguous: Check whether two fields are contiguous. +// // Arguments: -// first - GenTree*. The Type of the node should be TYP_FLOAT -// second - GenTree*. The Type of the node should be TYP_FLOAT +// op1 - The first field indirection +// op2 - The second field indirection +// // Return Value: -// if the first field is located before second field, and they are located contiguously, -// then return true. Otherwise, return false. - -bool Compiler::areFieldsContiguous(GenTree* first, GenTree* second) +// If the first field is located before second field, and they are +// located contiguously, then return true. Otherwise, return false. +// +bool Compiler::areFieldsContiguous(GenTreeIndir* op1, GenTreeIndir* op2) { - assert(first->OperGet() == GT_FIELD); - assert(second->OperGet() == GT_FIELD); - assert(first->gtType == TYP_FLOAT); - assert(second->gtType == TYP_FLOAT); - - var_types firstFieldType = first->gtType; - var_types secondFieldType = second->gtType; - - unsigned firstFieldEndOffset = first->AsField()->gtFldOffset + genTypeSize(firstFieldType); - unsigned secondFieldOffset = second->AsField()->gtFldOffset; - if (firstFieldEndOffset == secondFieldOffset && firstFieldType == secondFieldType && - areFieldsParentsLocatedSame(first, second)) + assert(op1->OperIs(GT_IND) && op2->OperIs(GT_IND)); + // TODO-1stClassStructs: delete once IND nodes are no more. + assert(!op1->TypeIs(TYP_STRUCT) && !op2->TypeIs(TYP_STRUCT)); + + var_types op1Type = op1->TypeGet(); + var_types op2Type = op2->TypeGet(); + GenTreeFieldAddr* op1Addr = op1->Addr()->AsFieldAddr(); + GenTreeFieldAddr* op2Addr = op2->Addr()->AsFieldAddr(); + unsigned op1EndOffset = op1Addr->gtFldOffset + genTypeSize(op1Type); + unsigned op2Offset = op2Addr->gtFldOffset; + if ((op1Type == op2Type) && (op1EndOffset == op2Offset) && areFieldAddressesTheSame(op1Addr, op2Addr)) { return true; } @@ -594,19 +606,25 @@ bool Compiler::areArrayElementsContiguous(GenTree* op1, GenTree* op2) GenTree* op1IndexNode = op1IndexAddr->Index(); GenTree* op2IndexNode = op2IndexAddr->Index(); if ((op1IndexNode->OperGet() == GT_CNS_INT && op2IndexNode->OperGet() == GT_CNS_INT) && - op1IndexNode->AsIntCon()->gtIconVal + 1 == op2IndexNode->AsIntCon()->gtIconVal) + (op1IndexNode->AsIntCon()->gtIconVal + 1 == op2IndexNode->AsIntCon()->gtIconVal)) { - if (op1ArrayRef->OperIs(GT_FIELD) && op2ArrayRef->OperIs(GT_FIELD) && - areFieldsParentsLocatedSame(op1ArrayRef, op2ArrayRef)) + if (op1ArrayRef->OperIs(GT_IND) && op2ArrayRef->OperIs(GT_IND)) { - return true; + GenTree* op1ArrayRefAddr = op1ArrayRef->AsIndir()->Addr(); + GenTree* op2ArrayRefAddr = op2ArrayRef->AsIndir()->Addr(); + if (op1ArrayRefAddr->OperIs(GT_FIELD_ADDR) && op2ArrayRefAddr->OperIs(GT_FIELD_ADDR) && + areFieldAddressesTheSame(op1ArrayRefAddr->AsFieldAddr(), op2ArrayRefAddr->AsFieldAddr())) + { + return true; + } } else if (op1ArrayRef->OperIs(GT_LCL_VAR) && op2ArrayRef->OperIs(GT_LCL_VAR) && - op1ArrayRef->AsLclVarCommon()->GetLclNum() == op2ArrayRef->AsLclVarCommon()->GetLclNum()) + (op1ArrayRef->AsLclVar()->GetLclNum() == op2ArrayRef->AsLclVar()->GetLclNum())) { return true; } } + return false; } @@ -630,19 +648,25 @@ bool Compiler::areArgumentsContiguous(GenTree* op1, GenTree* op2) assert(!op1->TypeIs(TYP_STRUCT)); - if (op1->OperIs(GT_IND) && op1->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR) && op2->OperIs(GT_IND) && - op2->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR)) - { - return areArrayElementsContiguous(op1, op2); - } - else if (op1->OperIs(GT_FIELD) && op2->OperIs(GT_FIELD)) + if (op1->OperIs(GT_IND) && op2->OperIs(GT_IND)) { - return areFieldsContiguous(op1, op2); + GenTree* op1Addr = op1->AsIndir()->Addr(); + GenTree* op2Addr = op2->AsIndir()->Addr(); + + if (op1Addr->OperIs(GT_INDEX_ADDR) && op2Addr->OperIs(GT_INDEX_ADDR)) + { + return areArrayElementsContiguous(op1, op2); + } + if (op1Addr->OperIs(GT_FIELD_ADDR) && op2Addr->OperIs(GT_FIELD_ADDR)) + { + return areFieldsContiguous(op1->AsIndir(), op2->AsIndir()); + } } else if (op1->OperIs(GT_LCL_FLD) && op2->OperIs(GT_LCL_FLD)) { return areLocalFieldsContiguous(op1->AsLclFld(), op2->AsLclFld()); } + return false; } @@ -659,19 +683,23 @@ bool Compiler::areArgumentsContiguous(GenTree* op1, GenTree* op2) // return the address node. // // TODO-CQ: -// Currently just supports GT_FIELD and GT_IND(GT_INDEX_ADDR), because we can only verify those nodes +// Currently just supports GT_IND(GT_INDEX_ADDR / GT_FIELD_ADDR), because we can only verify those nodes // are located contiguously or not. In future we should support more cases. // GenTree* Compiler::CreateAddressNodeForSimdHWIntrinsicCreate(GenTree* tree, var_types simdBaseType, unsigned simdSize) { + assert(tree->OperIs(GT_IND)); + GenTree* addr = tree->AsIndir()->Addr(); GenTree* byrefNode = nullptr; unsigned offset = 0; var_types baseType = tree->gtType; - if (tree->OperIs(GT_FIELD)) + if (addr->OperIs(GT_FIELD_ADDR)) { - GenTree* objRef = tree->AsField()->GetFldObj(); - if ((objRef != nullptr) && objRef->IsLclVarAddr()) + assert(addr->AsFieldAddr()->IsInstance()); + + GenTree* objRef = addr->AsFieldAddr()->GetFldObj(); + if (objRef->IsLclVarAddr()) { // If the field is directly from a struct, then in this case, // we should set this struct's lvUsedInSIMDIntrinsic as true, @@ -690,17 +718,15 @@ GenTree* Compiler::CreateAddressNodeForSimdHWIntrinsicCreate(GenTree* tree, var_ } } - byrefNode = gtCloneExpr(tree->AsField()->GetFldObj()); + // TODO-FIELD: this seems unnecessary. Simply "return addr;"? + byrefNode = gtCloneExpr(objRef); assert(byrefNode != nullptr); - offset = tree->AsField()->gtFldOffset; + offset = addr->AsFieldAddr()->gtFldOffset; } else { - assert(tree->OperIs(GT_IND) && tree->AsIndir()->Addr()->OperIs(GT_INDEX_ADDR)); - - GenTreeIndexAddr* indexAddr = tree->AsIndir()->Addr()->AsIndexAddr(); - GenTree* arrayRef = indexAddr->Arr(); - GenTree* index = indexAddr->Index(); + GenTree* arrayRef = addr->AsIndexAddr()->Arr(); + GenTree* index = addr->AsIndexAddr()->Index(); assert(index->IsCnsIntOrI()); GenTree* checkIndexExpr = nullptr; @@ -780,12 +806,16 @@ void Compiler::impMarkContiguousSIMDFieldAssignments(Statement* stmt) // Successfully found the pattern, mark the lclvar as UsedInSIMDIntrinsic setLclRelatedToSIMDIntrinsic(srcSimdLclAddr); - if (curDst->OperIs(GT_FIELD) && curDst->AsField()->IsInstance()) + if (curDst->OperIs(GT_IND) && curDst->AsIndir()->Addr()->OperIs(GT_FIELD_ADDR)) { - GenTree* objRef = curDst->AsField()->GetFldObj(); - if (objRef->IsLclVarAddr() && varTypeIsStruct(lvaGetDesc(objRef->AsLclFld()))) + GenTreeFieldAddr* addr = curDst->AsIndir()->Addr()->AsFieldAddr(); + if (addr->IsInstance()) { - setLclRelatedToSIMDIntrinsic(objRef); + GenTree* objRef = addr->GetFldObj(); + if (objRef->IsLclVarAddr() && varTypeIsStruct(lvaGetDesc(objRef->AsLclFld()))) + { + setLclRelatedToSIMDIntrinsic(objRef); + } } } }