Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/coreclr/jit/morph.cpp')
-rw-r--r--src/coreclr/jit/morph.cpp672
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: