diff options
Diffstat (limited to 'src/coreclr/jit/morph.cpp')
-rw-r--r-- | src/coreclr/jit/morph.cpp | 672 |
1 files changed, 365 insertions, 307 deletions
diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 5a0812c4e64..ea291fd21c3 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -4963,30 +4963,34 @@ unsigned Compiler::fgGetFieldMorphingTemp(GenTreeField* fieldNode) return lclNum; } -/***************************************************************************** - * - * Transform the given GT_FIELD tree for code generation. - */ - +//------------------------------------------------------------------------ +// fgMorphField: Fully morph a FIELD/FIELD_ADDR tree. +// +// Expands the field node into explicit additions and indirections. +// +// Arguments: +// tree - The FIELD/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) { - assert(tree->gtOper == GT_FIELD); + assert(tree->OperIs(GT_FIELD, GT_FIELD_ADDR)); - CORINFO_FIELD_HANDLE symHnd = tree->AsField()->gtFldHnd; - unsigned fldOffset = tree->AsField()->gtFldOffset; - GenTree* objRef = tree->AsField()->GetFldObj(); - bool fldMayOverlap = tree->AsField()->gtFldMayOverlap; - FieldSeq* fieldSeq = nullptr; + GenTreeField* fieldNode = tree->AsField(); + GenTree* objRef = fieldNode->GetFldObj(); - // Reset the flag because we may reuse the node. - tree->AsField()->gtFldMayOverlap = false; - - noway_assert(((objRef != nullptr) && (objRef->IsLocalAddrExpr() != nullptr)) || - ((tree->gtFlags & GTF_GLOB_REF) != 0)); + if (tree->OperIs(GT_FIELD)) + { + noway_assert(((objRef != nullptr) && (objRef->IsLocalAddrExpr() != nullptr)) || + ((tree->gtFlags & GTF_GLOB_REF) != 0)); + } #ifdef FEATURE_SIMD // if this field belongs to simd struct, translate it to simd intrinsic. - if (mac == nullptr) + if ((mac == nullptr) && tree->OperIs(GT_FIELD)) { if (IsBaselineSimdIsaSupported()) { @@ -5008,38 +5012,92 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) } #endif - // Create a default MorphAddrContext early so it doesn't go out of scope - // before it is used. - MorphAddrContext defMAC(MACK_Ind); + MorphAddrContext indMAC(MACK_Ind); + MorphAddrContext addrMAC(MACK_Addr); + bool isAddr = tree->OperIs(GT_FIELD_ADDR); - // Is this an instance data member? - if (objRef != nullptr) + if (fieldNode->IsInstance()) { - if (tree->gtFlags & GTF_IND_TLS_REF) + // NULL mac means we encounter the GT_FIELD/GT_FIELD_ADDR first (and don't know our parent). + if (mac == nullptr) { - NO_WAY("instance field can not be a TLS ref."); + // FIELD denotes a dereference of the field, equivalent to a MACK_Ind with zero offset. + mac = tree->OperIs(GT_FIELD) ? &indMAC : &addrMAC; } - /* We'll create the expression "*(objRef + mem_offs)" */ + tree = fgMorphExpandInstanceField(tree, mac); + } + else if (fieldNode->IsTlsStatic()) + { + tree = fgMorphExpandTlsFieldAddr(tree); + } + else + { + tree = fgMorphExpandStaticField(tree); + } - noway_assert(varTypeIsGC(objRef->TypeGet()) || objRef->TypeGet() == TYP_I_IMPL); + // Pass down the current mac; if non null we are computing an address + GenTree* result; + if (tree->OperIsSimple()) + { + result = fgMorphSmpOp(tree, mac); + DBEXEC(result != fieldNode, result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - /* - Now we have a tree like this: + // Quirk: preserve previous behavior with this NO_CSE. + if (isAddr && result->OperIs(GT_COMMA)) + { + result->SetDoNotCSE(); + } + } + else + { + result = fgMorphTree(tree, mac); + DBEXEC(result == fieldNode, result->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED); + } + + JITDUMP("\nFinal value of Compiler::fgMorphField after morphing:\n"); + DISPTREE(result); + + return result; +} + +//------------------------------------------------------------------------ +// fgMorphExpandInstanceField: Expand an instance field reference. +// +// Expands the field node into explicit additions and indirections, adding +// explicit null checks if necessary. +// +// Arguments: +// tree - The FIELD/FIELD_ADDR tree +// mac - The morphing context, used to elide adding null checks +// +// Return Value: +// The expanded "tree" of an arbitrary shape. +// +GenTree* Compiler::fgMorphExpandInstanceField(GenTree* tree, MorphAddrContext* mac) +{ + assert(tree->OperIs(GT_FIELD, GT_FIELD_ADDR) && tree->AsField()->IsInstance()); + + GenTree* objRef = tree->AsField()->GetFldObj(); + CORINFO_FIELD_HANDLE fieldHandle = tree->AsField()->gtFldHnd; + unsigned fieldOffset = tree->AsField()->gtFldOffset; + + noway_assert(varTypeIsI(genActualType(objRef))); + + /* Now we have a tree like this: +--------------------+ - | GT_FIELD | tree + | GT_FIELD[_ADDR] | tree +----------+---------+ | +--------------+-------------+ |tree->AsField()->GetFldObj()| +--------------+-------------+ - We want to make it like this (when fldOffset is <= MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT): +--------------------+ - | GT_IND/GT_OBJ | tree + | GT_IND/GT_OBJ | tree (for FIELD) +---------+----------+ | | @@ -5050,37 +5108,37 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) / \ / \ / \ - +-------------------+ +----------------------+ - | objRef | | fldOffset | - | | | (when fldOffset !=0) | - +-------------------+ +----------------------+ + +-------------------+ +----------------------+ + | objRef | | fldOffset | + | | | (when fldOffset !=0) | + +-------------------+ +----------------------+ or this (when fldOffset is > MAX_UNCHECKED_OFFSET_FOR_NULL_OBJECT): +--------------------+ - | GT_IND/GT_OBJ | tree + | GT_IND/GT_OBJ | tree (for FIELD) +----------+---------+ | +----------+---------+ - | GT_COMMA | comma2 + | GT_COMMA | comma2 +----------+---------+ | / \ / \ / \ / \ - +---------+----------+ +---------+----------+ - comma | GT_COMMA | | "+" (i.e. GT_ADD) | addr - +---------+----------+ +---------+----------+ - | | - / \ / \ - / \ / \ - / \ / \ - +-----+-----+ +-----+-----+ +---------+ +-----------+ - asg | GT_ASG | ind | GT_IND | | tmpLcl | | fldOffset | - +-----+-----+ +-----+-----+ +---------+ +-----------+ + +---------+----------+ +---------+----------+ + comma | GT_COMMA | | "+" (i.e. GT_ADD) | addr + +---------+----------+ +---------+----------+ + | | + / \ / \ + / \ / \ + / \ / \ + +-----+-----+ +-----+-----+ +---------+ +-----------+ + asg | GT_ASG | ind | GT_IND | | tmpLcl | | fldOffset | + +-----+-----+ +-----+-----+ +---------+ +-----------+ | | / \ | / \ | @@ -5089,325 +5147,324 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) | tmpLcl | | objRef | | tmpLcl | +-----------+ +-----------+ +-----------+ + */ - */ - - var_types objRefType = objRef->TypeGet(); - GenTree* addr = nullptr; - GenTree* comma = nullptr; + var_types objRefType = objRef->TypeGet(); + GenTree* addr = nullptr; + GenTree* comma = nullptr; + bool addExplicitNullCheck = false; - // NULL mac means we encounter the GT_FIELD first. This denotes a dereference of the field, - // and thus is equivalent to a MACK_Ind with zero offset. - if (mac == nullptr) + if (fgAddrCouldBeNull(objRef)) + { + if (!mac->m_allConstantOffsets || fgIsBigOffset(mac->m_totalOffset + fieldOffset)) { - mac = &defMAC; + addExplicitNullCheck = true; } - - bool addExplicitNullCheck = false; - - // Implicit byref locals and string literals are never null. - if (fgAddrCouldBeNull(objRef)) + else { - if (!mac->m_allConstantOffsets || fgIsBigOffset(mac->m_totalOffset + fldOffset)) - { - addExplicitNullCheck = true; - } - else - { - addExplicitNullCheck = mac->m_kind == MACK_Addr; - } + addExplicitNullCheck = mac->m_kind == MACK_Addr; } + } - if (addExplicitNullCheck) - { -#ifdef DEBUG - if (verbose) - { - printf("Before explicit null check morphing:\n"); - gtDispTree(tree); - } -#endif - - // - // Create the "comma" subtree - // - GenTree* asg = nullptr; - - unsigned lclNum; + if (addExplicitNullCheck) + { + JITDUMP("Before explicit null check morphing:\n"); + DISPTREE(tree); - if (!objRef->OperIs(GT_LCL_VAR) || lvaIsLocalImplicitlyAccessedByRef(objRef->AsLclVar()->GetLclNum())) - { - lclNum = fgGetFieldMorphingTemp(tree->AsField()); + // Create the "comma" subtree. + GenTree* asg = nullptr; + unsigned lclNum; - // Create the "asg" node - asg = gtNewTempAssign(lclNum, objRef); - } - else - { - lclNum = objRef->AsLclVarCommon()->GetLclNum(); - } + if (!objRef->OperIs(GT_LCL_VAR) || lvaIsLocalImplicitlyAccessedByRef(objRef->AsLclVar()->GetLclNum())) + { + lclNum = fgGetFieldMorphingTemp(tree->AsField()); - GenTree* lclVar = gtNewLclvNode(lclNum, objRefType); - GenTree* nullchk = gtNewNullCheck(lclVar, compCurBB); + // Create the "asg" node + asg = gtNewTempAssign(lclNum, objRef); + } + else + { + lclNum = objRef->AsLclVarCommon()->GetLclNum(); + } - if (asg != nullptr) - { - // Create the "comma" node. - comma = gtNewOperNode(GT_COMMA, TYP_VOID, asg, nullchk); - } - else - { - comma = nullchk; - } + GenTree* lclVar = gtNewLclvNode(lclNum, objRefType); + GenTree* nullchk = gtNewNullCheck(lclVar, compCurBB); - addr = gtNewLclvNode(lclNum, objRefType); // Use "tmpLcl" to create "addr" node. + if (asg != nullptr) + { + // Create the "comma" node. + comma = gtNewOperNode(GT_COMMA, TYP_VOID, asg, nullchk); } else { - addr = objRef; + comma = nullchk; } + addr = gtNewLclvNode(lclNum, objRefType); // Use "tmpLcl" to create "addr" node. + } + else + { + addr = objRef; + } + #ifdef FEATURE_READYTORUN - if (tree->AsField()->gtFieldLookup.addr != nullptr) + if (tree->AsField()->gtFieldLookup.addr != nullptr) + { + GenTree* offsetNode = nullptr; + if (tree->AsField()->gtFieldLookup.accessType == IAT_PVALUE) { - GenTree* offsetNode = nullptr; - if (tree->AsField()->gtFieldLookup.accessType == IAT_PVALUE) - { - offsetNode = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)tree->AsField()->gtFieldLookup.addr, - GTF_ICON_CONST_PTR, true); + offsetNode = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)tree->AsField()->gtFieldLookup.addr, + GTF_ICON_CONST_PTR, true); #ifdef DEBUG - offsetNode->gtGetOp1()->AsIntCon()->gtTargetHandle = (size_t)symHnd; + offsetNode->gtGetOp1()->AsIntCon()->gtTargetHandle = (size_t)fieldHandle; #endif - } - else - { - noway_assert(!"unexpected accessType for R2R field access"); - } - - var_types addType = (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF; - addr = gtNewOperNode(GT_ADD, addType, addr, offsetNode); } -#endif - - // We only need to attach the field offset information for class fields. - if ((objRefType == TYP_REF) && !fldMayOverlap) + else { - fieldSeq = GetFieldSeqStore()->Create(symHnd, fldOffset, FieldSeq::FieldKind::Instance); + noway_assert(!"unexpected accessType for R2R field access"); } - // Add the member offset to the object's address. - if (fldOffset != 0) - { - addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr, - gtNewIconNode(fldOffset, fieldSeq)); - } + addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr, offsetNode); + } +#endif + + // We only need to attach the field offset information for class fields. + FieldSeq* fieldSeq = nullptr; + if ((objRefType == TYP_REF) && !tree->AsField()->gtFldMayOverlap) + { + fieldSeq = GetFieldSeqStore()->Create(fieldHandle, fieldOffset, FieldSeq::FieldKind::Instance); + } - // Now let's set the "tree" as a GT_IND tree. + // Add the member offset to the object's address. + if (fieldOffset != 0) + { + addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr, + gtNewIconNode(fieldOffset, fieldSeq)); + } + if (addExplicitNullCheck) + { + // Create the "comma2" tree. + addr = gtNewOperNode(GT_COMMA, addr->TypeGet(), comma, addr); + } + + if (tree->OperIs(GT_FIELD)) + { tree->SetOper(GT_IND); - tree->AsOp()->gtOp1 = addr; + tree->AsIndir()->SetAddr(addr); + } + else // Otherwise, we have a FIELD_ADDR. + { + tree = addr; + } - if (addExplicitNullCheck) - { - // - // Create "comma2" node and link it to "tree". - // - GenTree* comma2 = gtNewOperNode(GT_COMMA, addr->TypeGet(), comma, addr); - tree->AsOp()->gtOp1 = comma2; - } + if (addExplicitNullCheck) + { + JITDUMP("After adding explicit null check:\n"); + DISPTREE(tree); + } -#ifdef DEBUG - if (verbose) + return tree; +} + +//------------------------------------------------------------------------ +// fgMorphExpandTlsFieldAddr: Expand a TLS field address. +// +// Expands ".tls"-style statics, produced by the C++/CLI compiler for +// "__declspec(thread)" variables. An overview of the underlying native +// mechanism can be found here: http://www.nynaeve.net/?p=180. +// +// Arguments: +// tree - The GT_FIELD_ADDR tree +// +// Return Value: +// The expanded tree - a GT_ADD. +// +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()); + + CORINFO_FIELD_HANDLE fieldHandle = tree->AsField()->gtFldHnd; + int fieldOffset = tree->AsField()->gtFldOffset; + + // Thread Local Storage static field reference + // + // Field ref is a TLS 'Thread-Local-Storage' reference + // + // Build this tree: ADD(I_IMPL) # + // / \. + // / CNS(fldOffset) + // / + // / + // / + // IND(I_IMPL) == [Base of this DLL's TLS] + // | + // ADD(I_IMPL) + // / \. + // / CNS(IdValue*4) or MUL + // / / \. + // IND(I_IMPL) / CNS(4) + // | / + // CNS(TLS_HDL,0x2C) IND + // | + // CNS(pIdAddr) + // + // # Denotes the original node + // + void** pIdAddr = nullptr; + unsigned IdValue = info.compCompHnd->getFieldThreadLocalStoreID(fieldHandle, (void**)&pIdAddr); + + // + // If we can we access the TLS DLL index ID value directly + // then pIdAddr will be NULL and + // IdValue will be the actual TLS DLL index ID + // + GenTree* dllRef = nullptr; + if (pIdAddr == nullptr) + { + if (IdValue != 0) { - if (addExplicitNullCheck) - { - printf("After adding explicit null check:\n"); - gtDispTree(tree); - } + dllRef = gtNewIconNode(IdValue * 4, TYP_I_IMPL); } -#endif } - else /* This is a static data member */ + else { - if (tree->gtFlags & GTF_IND_TLS_REF) - { - // Thread Local Storage static field reference - // - // Field ref is a TLS 'Thread-Local-Storage' reference - // - // Build this tree: IND(*) # - // | - // ADD(I_IMPL) - // / \. - // / CNS(fldOffset) - // / - // / - // / - // IND(I_IMPL) == [Base of this DLL's TLS] - // | - // ADD(I_IMPL) - // / \. - // / CNS(IdValue*4) or MUL - // / / \. - // IND(I_IMPL) / CNS(4) - // | / - // CNS(TLS_HDL,0x2C) IND - // | - // CNS(pIdAddr) - // - // # Denotes the original node - // - void** pIdAddr = nullptr; - unsigned IdValue = info.compCompHnd->getFieldThreadLocalStoreID(symHnd, (void**)&pIdAddr); - - // - // If we can we access the TLS DLL index ID value directly - // then pIdAddr will be NULL and - // IdValue will be the actual TLS DLL index ID - // - GenTree* dllRef = nullptr; - if (pIdAddr == nullptr) - { - if (IdValue != 0) - { - dllRef = gtNewIconNode(IdValue * 4, TYP_I_IMPL); - } - } - else - { - dllRef = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)pIdAddr, GTF_ICON_CONST_PTR, true); + dllRef = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)pIdAddr, GTF_ICON_CONST_PTR, true); - // Next we multiply by 4 - dllRef = gtNewOperNode(GT_MUL, TYP_I_IMPL, dllRef, gtNewIconNode(4, TYP_I_IMPL)); - } + // Next we multiply by 4 + dllRef = gtNewOperNode(GT_MUL, TYP_I_IMPL, dllRef, gtNewIconNode(4, TYP_I_IMPL)); + } #define WIN32_TLS_SLOTS (0x2C) // Offset from fs:[0] where the pointer to the slots resides - // Mark this ICON as a TLS_HDL, codegen will use FS:[cns] + // Mark this ICON as a TLS_HDL, codegen will use FS:[cns] + GenTree* tlsRef = gtNewIconHandleNode(WIN32_TLS_SLOTS, GTF_ICON_TLS_HDL); - GenTree* tlsRef = gtNewIconHandleNode(WIN32_TLS_SLOTS, GTF_ICON_TLS_HDL); + // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS + if ((tree->gtFlags & GTF_FLD_INITCLASS) != 0) + { + tree->gtFlags &= ~GTF_FLD_INITCLASS; + tlsRef->gtFlags |= GTF_ICON_INITCLASS; + } - // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS - if ((tree->gtFlags & GTF_FLD_INITCLASS) != 0) - { - tree->gtFlags &= ~GTF_FLD_INITCLASS; - tlsRef->gtFlags |= GTF_ICON_INITCLASS; - } + tlsRef = gtNewIndir(TYP_I_IMPL, tlsRef, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); - tlsRef = gtNewOperNode(GT_IND, TYP_I_IMPL, tlsRef); + if (dllRef != nullptr) + { + // Add the dllRef. + tlsRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, tlsRef, dllRef); + } - if (dllRef != nullptr) - { - /* Add the dllRef */ - tlsRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, tlsRef, dllRef); - } + // indirect to have tlsRef point at the base of the DLLs Thread Local Storage. + tlsRef = gtNewOperNode(GT_IND, TYP_I_IMPL, tlsRef); - /* indirect to have tlsRef point at the base of the DLLs Thread Local Storage */ - tlsRef = gtNewOperNode(GT_IND, TYP_I_IMPL, tlsRef); + // Add the TLS static field offset to the address. + assert(!tree->AsField()->gtFldMayOverlap); + FieldSeq* fieldSeq = GetFieldSeqStore()->Create(fieldHandle, fieldOffset, FieldSeq::FieldKind::SimpleStatic); + GenTree* offsetNode = gtNewIconNode(fieldOffset, fieldSeq); - // Add the TLS static field offset to the address. - assert(!fldMayOverlap); - fieldSeq = GetFieldSeqStore()->Create(symHnd, fldOffset, FieldSeq::FieldKind::SimpleStatic); - tlsRef = gtNewOperNode(GT_ADD, TYP_I_IMPL, tlsRef, gtNewIconNode(fldOffset, fieldSeq)); + tree->ChangeOper(GT_ADD); + tree->AsOp()->gtOp1 = tlsRef; + tree->AsOp()->gtOp2 = offsetNode; - // Final indirect to get to actual value of TLS static field + return tree; +} - tree->SetOper(GT_IND); - tree->AsOp()->gtOp1 = tlsRef; +//------------------------------------------------------------------------ +// fgMorphExpandStaticField: Expand a simple static field load. +// +// Transforms the field into an explicit indirection off of a constant +// address. +// +// Arguments: +// tree - The GT_FIELD tree +// +// Return Value: +// The expanded tree - a GT_IND. +// +GenTree* Compiler::fgMorphExpandStaticField(GenTree* tree) +{ + // Note we do not support "FIELD_ADDR"s for simple statics. + assert(tree->OperIs(GT_FIELD) && tree->AsField()->IsStatic()); - noway_assert(tree->gtFlags & GTF_IND_TLS_REF); - } - else - { - // Normal static field reference - // - // If we can we access the static's address directly - // then pFldAddr will be NULL and - // fldAddr will be the actual address of the static field - // - void** pFldAddr = nullptr; - void* fldAddr = info.compCompHnd->getFieldAddress(symHnd, (void**)&pFldAddr); + // If we can we access the static's address directly + // then pFldAddr will be NULL and + // fldAddr will be the actual address of the static field + // + CORINFO_FIELD_HANDLE fieldHandle = tree->AsField()->gtFldHnd; + void** pFldAddr = nullptr; + void* fldAddr = info.compCompHnd->getFieldAddress(fieldHandle, (void**)&pFldAddr); - // We should always be able to access this static field address directly - // - assert(pFldAddr == nullptr); + // We should always be able to access this static field address directly + // + assert(pFldAddr == nullptr); - // For boxed statics, this direct address will be for the box. We have already added - // the indirection for the field itself and attached the sequence, in importation. - bool isBoxedStatic = gtIsStaticFieldPtrToBoxedStruct(tree->TypeGet(), symHnd); - if (!isBoxedStatic) - { - // Only simple statics get importred as GT_FIELDs. - fieldSeq = GetFieldSeqStore()->Create(symHnd, reinterpret_cast<size_t>(fldAddr), - FieldSeq::FieldKind::SimpleStatic); - } + // For boxed statics, this direct address will be for the box. We have already added + // the indirection for the field itself and attached the sequence, in importation. + FieldSeq* fieldSeq = nullptr; + bool isBoxedStatic = gtIsStaticFieldPtrToBoxedStruct(tree->TypeGet(), fieldHandle); + if (!isBoxedStatic) + { + // Only simple statics get importred as GT_FIELDs. + fieldSeq = GetFieldSeqStore()->Create(fieldHandle, reinterpret_cast<size_t>(fldAddr), + FieldSeq::FieldKind::SimpleStatic); + } - // TODO-CQ: enable this optimization for 32 bit targets. - bool isStaticReadOnlyInited = false; + // TODO-CQ: enable this optimization for 32 bit targets. + bool isStaticReadOnlyInited = false; #ifdef TARGET_64BIT - if (tree->TypeIs(TYP_REF) && !isBoxedStatic) - { - bool pIsSpeculative = true; - if (info.compCompHnd->getStaticFieldCurrentClass(symHnd, &pIsSpeculative) != NO_CLASS_HANDLE) - { - isStaticReadOnlyInited = !pIsSpeculative; - } - } + if (tree->TypeIs(TYP_REF) && !isBoxedStatic) + { + bool pIsSpeculative = true; + if (info.compCompHnd->getStaticFieldCurrentClass(fieldHandle, &pIsSpeculative) != NO_CLASS_HANDLE) + { + isStaticReadOnlyInited = !pIsSpeculative; + } + } #endif // TARGET_64BIT - GenTreeFlags handleKind = GTF_EMPTY; - if (isBoxedStatic) - { - handleKind = GTF_ICON_STATIC_BOX_PTR; - } - else if (isStaticReadOnlyInited) - { - handleKind = GTF_ICON_CONST_PTR; - } - else - { - handleKind = GTF_ICON_STATIC_HDL; - } - GenTreeIntCon* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq); - INDEBUG(addr->gtTargetHandle = reinterpret_cast<size_t>(symHnd)); - - // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS, if we need to. - if (((tree->gtFlags & GTF_FLD_INITCLASS) != 0) && !isStaticReadOnlyInited) - { - tree->gtFlags &= ~GTF_FLD_INITCLASS; - addr->gtFlags |= GTF_ICON_INITCLASS; - } - - tree->SetOper(GT_IND); - tree->AsOp()->gtOp1 = addr; - - if (isBoxedStatic) - { - // The box for the static cannot be null, and is logically invariant, since it - // represents (a base for) the static's address. - tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); - } - else if (isStaticReadOnlyInited) - { - JITDUMP("Marking initialized static read-only field '%s' as invariant.\n", eeGetFieldName(symHnd)); - - // Static readonly field is not null at this point (see getStaticFieldCurrentClass impl). - tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); - } + GenTreeFlags handleKind = GTF_EMPTY; + if (isBoxedStatic) + { + handleKind = GTF_ICON_STATIC_BOX_PTR; + } + else if (isStaticReadOnlyInited) + { + handleKind = GTF_ICON_CONST_PTR; + } + else + { + handleKind = GTF_ICON_STATIC_HDL; + } + GenTreeIntCon* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq); + INDEBUG(addr->gtTargetHandle = reinterpret_cast<size_t>(fieldHandle)); - return fgMorphSmpOp(tree, /* mac */ nullptr); - } + // Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS, if we need to. + if (((tree->gtFlags & GTF_FLD_INITCLASS) != 0) && !isStaticReadOnlyInited) + { + tree->gtFlags &= ~GTF_FLD_INITCLASS; + addr->gtFlags |= GTF_ICON_INITCLASS; } - noway_assert(tree->OperIs(GT_IND)); + tree->SetOper(GT_IND); + tree->AsOp()->gtOp1 = addr; - // Pass down the current mac; if non null we are computing an address - GenTree* result = fgMorphSmpOp(tree, mac); + if (isBoxedStatic) + { + // The box for the static cannot be null, and is logically invariant, since it + // represents (a base for) the static's address. + tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); + } + else if (isStaticReadOnlyInited) + { + JITDUMP("Marking initialized static read-only field '%s' as invariant.\n", eeGetFieldName(fieldHandle)); - JITDUMP("\nFinal value of Compiler::fgMorphField after calling fgMorphSmpOp:\n"); - DISPTREE(result); + // Static readonly field is not null at this point (see getStaticFieldCurrentClass impl). + tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); + } - return result; + return tree; } //------------------------------------------------------------------------------ @@ -9361,6 +9418,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA break; case GT_FIELD: + case GT_FIELD_ADDR: return fgMorphField(tree, mac); case GT_INDEX_ADDR: |