未验证 提交 a50d79f4 编写于 作者: S SingleAccretion 提交者: GitHub

Only use first field maps for shared statics (#66558)

* Use a handle for field sequences in VN

The field sequences are already canonical in the store,
so there is no need to have the somewhat involved code
in VN which essentially re-canonicalized them, we can
just use the pointer value (as a handle) to encode them.

Makes the future change of encoding some information in
the handle a little easier.

* Encode the statics

* Use shared-ness info in IsFieldAddr
上级 eaa1f05d
......@@ -10480,7 +10480,7 @@ void Compiler::gtDispFieldSeq(FieldSeqNode* pfsn)
while (pfsn != nullptr)
{
assert(pfsn != FieldSeqStore::NotAField()); // Can't exist in a field sequence list except alone
CORINFO_FIELD_HANDLE fldHnd = pfsn->m_fieldHnd;
CORINFO_FIELD_HANDLE fldHnd = pfsn->GetFieldHandleValue();
// First check the "pseudo" field handles...
if (fldHnd == FieldSeqStore::FirstElemPseudoField)
{
......@@ -10494,7 +10494,7 @@ void Compiler::gtDispFieldSeq(FieldSeqNode* pfsn)
{
printf("%s", eeGetFieldName(fldHnd));
}
pfsn = pfsn->m_next;
pfsn = pfsn->GetNext();
if (pfsn != nullptr)
{
printf(", ");
......@@ -16247,17 +16247,18 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeqNode** pF
fldSeq = AsOp()->gtOp2->AsIntCon()->gtFieldSeq;
baseAddr = AsOp()->gtOp1;
}
if (baseAddr != nullptr)
else
{
assert(!baseAddr->TypeIs(TYP_REF) || !comp->GetZeroOffsetFieldMap()->Lookup(baseAddr));
return false;
}
assert(!baseAddr->TypeIs(TYP_REF) || !comp->GetZeroOffsetFieldMap()->Lookup(baseAddr));
}
else if (IsCnsIntOrI() && IsIconHandle(GTF_ICON_STATIC_HDL))
{
assert(!comp->GetZeroOffsetFieldMap()->Lookup(this) && (AsIntCon()->gtFieldSeq != nullptr));
fldSeq = AsIntCon()->gtFieldSeq;
baseAddr = nullptr;
baseAddr = this;
}
else if (comp->GetZeroOffsetFieldMap()->Lookup(this, &fldSeq))
{
......@@ -16268,7 +16269,7 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeqNode** pF
return false;
}
assert(fldSeq != nullptr);
assert((fldSeq != nullptr) && (baseAddr != nullptr));
if ((fldSeq == FieldSeqStore::NotAField()) || fldSeq->IsPseudoField())
{
......@@ -16280,16 +16281,15 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pBaseAddr, FieldSeqNode** pF
// or a static field. To avoid the expense of calling "getFieldClass" here, we will instead
// rely on the invariant that TYP_REF base addresses can never appear for struct fields - we
// will effectively treat such cases ("possible" in unsafe code) as undefined behavior.
if (comp->eeIsFieldStatic(fldSeq->GetFieldHandle()))
if (fldSeq->IsStaticField())
{
// TODO-VNTypes: this code is out of sync w.r.t. boxed statics that are numbered with
// VNF_PtrToStatic and treated as "simple" while here we treat them as "complex".
// For shared statics, we must encode the logical instantiation argument.
if (fldSeq->IsSharedStaticField())
{
*pBaseAddr = baseAddr;
}
// TODO-VNTypes: we will always return the "baseAddr" here for now, but strictly speaking,
// we only need to do that if we have a shared field, to encode the logical "instantiation"
// argument. In all other cases, this serves no purpose and just leads to redundant maps.
*pBaseAddr = baseAddr;
*pFldSeq = fldSeq;
*pFldSeq = fldSeq;
return true;
}
......@@ -16657,17 +16657,12 @@ CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree)
}
if (fieldSeq != nullptr)
{
while (fieldSeq->m_next != nullptr)
{
fieldSeq = fieldSeq->m_next;
}
fieldSeq = fieldSeq->GetTail();
if (fieldSeq != FieldSeqStore::NotAField() && !fieldSeq->IsPseudoField())
{
CORINFO_FIELD_HANDLE fieldHnd = fieldSeq->m_fieldHnd;
CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &structHnd);
// With unsafe code and type casts
// this can return a primitive type and have nullptr for structHnd
// see runtime/issues/38541
// Note we may have a primitive here (and correctly fail to obtain the handle)
eeGetFieldType(fieldSeq->GetFieldHandle(), &structHnd);
}
}
}
......@@ -16939,6 +16934,8 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b
}
else if (base->OperGet() == GT_ADD)
{
// TODO-VNTypes: use "IsFieldAddr" here instead.
// This could be a static field access.
//
// See if op1 is a static field base helper call
......@@ -16954,20 +16951,15 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b
if (fieldSeq != nullptr)
{
while (fieldSeq->m_next != nullptr)
{
fieldSeq = fieldSeq->m_next;
}
assert(!fieldSeq->IsPseudoField());
fieldSeq = fieldSeq->GetTail();
// No benefit to calling gtGetFieldClassHandle here, as
// the exact field being accessed can vary.
CORINFO_FIELD_HANDLE fieldHnd = fieldSeq->m_fieldHnd;
CORINFO_CLASS_HANDLE fieldClass = nullptr;
CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass);
CORINFO_FIELD_HANDLE fieldHnd = fieldSeq->GetFieldHandle();
CORINFO_CLASS_HANDLE fieldClass = NO_CLASS_HANDLE;
var_types fieldType = eeGetFieldType(fieldHnd, &fieldClass);
assert(fieldCorType == CORINFO_TYPE_CLASS);
assert(fieldType == TYP_REF);
objClass = fieldClass;
}
}
......@@ -17321,18 +17313,18 @@ void GenTree::ParseArrayAddress(
noway_assert(!"fldSeqIter is NotAField() in ParseArrayAddress");
}
if (!FieldSeqStore::IsPseudoField(fldSeqIter->m_fieldHnd))
if (!FieldSeqStore::IsPseudoField(fldSeqIter->GetFieldHandleValue()))
{
if (*pFldSeq == nullptr)
{
*pFldSeq = fldSeqIter;
}
CORINFO_CLASS_HANDLE fldCls = nullptr;
noway_assert(fldSeqIter->m_fieldHnd != nullptr);
CorInfoType cit = comp->info.compCompHnd->getFieldType(fldSeqIter->m_fieldHnd, &fldCls);
noway_assert(fldSeqIter->GetFieldHandle() != NO_FIELD_HANDLE);
CorInfoType cit = comp->info.compCompHnd->getFieldType(fldSeqIter->GetFieldHandle(), &fldCls);
fieldOffsets += comp->compGetTypeSize(cit, fldCls);
}
fldSeqIter = fldSeqIter->m_next;
fldSeqIter = fldSeqIter->GetNext();
}
// Is there some portion of the "offset" beyond the first-elem offset and the struct field suffix we just computed?
......@@ -17694,16 +17686,16 @@ void GenTree::LabelIndex(Compiler* comp, bool isConst)
// Note that the value of the below field doesn't matter; it exists only to provide a distinguished address.
//
// static
FieldSeqNode FieldSeqStore::s_notAField(nullptr, nullptr);
FieldSeqNode FieldSeqStore::s_notAField(nullptr, nullptr, FieldSeqNode::FieldKind::Instance);
// FieldSeqStore methods.
FieldSeqStore::FieldSeqStore(CompAllocator alloc) : m_alloc(alloc), m_canonMap(new (alloc) FieldSeqNodeCanonMap(alloc))
{
}
FieldSeqNode* FieldSeqStore::CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd)
FieldSeqNode* FieldSeqStore::CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd, FieldSeqNode::FieldKind fieldKind)
{
FieldSeqNode fsn(fieldHnd, nullptr);
FieldSeqNode fsn(fieldHnd, nullptr, fieldKind);
FieldSeqNode* res = nullptr;
if (m_canonMap->Lookup(fsn, &res))
{
......@@ -17738,8 +17730,8 @@ FieldSeqNode* FieldSeqStore::Append(FieldSeqNode* a, FieldSeqNode* b)
// Extremely special case for ConstantIndex pseudo-fields -- appending consecutive such
// together collapse to one.
}
else if (a->m_next == nullptr && a->m_fieldHnd == ConstantIndexPseudoField &&
b->m_fieldHnd == ConstantIndexPseudoField)
else if (a->GetNext() == nullptr && a->GetFieldHandleValue() == ConstantIndexPseudoField &&
b->GetFieldHandleValue() == ConstantIndexPseudoField)
{
return b;
}
......@@ -17748,8 +17740,8 @@ FieldSeqNode* FieldSeqStore::Append(FieldSeqNode* a, FieldSeqNode* b)
// We should never add a duplicate FieldSeqNode
assert(a != b);
FieldSeqNode* tmp = Append(a->m_next, b);
FieldSeqNode fsn(a->m_fieldHnd, tmp);
FieldSeqNode* tmp = Append(a->GetNext(), b);
FieldSeqNode fsn(a->GetFieldHandleValue(), tmp, a->GetKind());
FieldSeqNode* res = nullptr;
if (m_canonMap->Lookup(fsn, &res))
{
......@@ -17774,19 +17766,38 @@ CORINFO_FIELD_HANDLE FieldSeqStore::FirstElemPseudoField =
CORINFO_FIELD_HANDLE FieldSeqStore::ConstantIndexPseudoField =
(CORINFO_FIELD_HANDLE)&FieldSeqStore::ConstantIndexPseudoFieldStruct;
bool FieldSeqNode::IsFirstElemFieldSeq()
FieldSeqNode::FieldSeqNode(CORINFO_FIELD_HANDLE fieldHnd, FieldSeqNode* next, FieldKind fieldKind) : m_next(next)
{
uintptr_t handleValue = reinterpret_cast<uintptr_t>(fieldHnd);
assert((handleValue & FIELD_KIND_MASK) == 0);
m_fieldHandleAndKind = handleValue | static_cast<uintptr_t>(fieldKind);
if (!FieldSeqStore::IsPseudoField(fieldHnd) && (fieldHnd != NO_FIELD_HANDLE))
{
assert(JitTls::GetCompiler()->eeIsFieldStatic(fieldHnd) == IsStaticField());
}
else
{
// Use the default for pseudo-fields.
assert(fieldKind == FieldKind::Instance);
}
}
bool FieldSeqNode::IsFirstElemFieldSeq() const
{
return m_fieldHnd == FieldSeqStore::FirstElemPseudoField;
return GetFieldHandleValue() == FieldSeqStore::FirstElemPseudoField;
}
bool FieldSeqNode::IsConstantIndexFieldSeq()
bool FieldSeqNode::IsConstantIndexFieldSeq() const
{
return m_fieldHnd == FieldSeqStore::ConstantIndexPseudoField;
return GetFieldHandleValue() == FieldSeqStore::ConstantIndexPseudoField;
}
bool FieldSeqNode::IsPseudoField() const
{
return m_fieldHnd == FieldSeqStore::FirstElemPseudoField || m_fieldHnd == FieldSeqStore::ConstantIndexPseudoField;
return (GetFieldHandleValue() == FieldSeqStore::FirstElemPseudoField) ||
(GetFieldHandleValue() == FieldSeqStore::ConstantIndexPseudoField);
}
#ifdef FEATURE_SIMD
......
......@@ -220,36 +220,71 @@ public:
}
};
/*****************************************************************************/
// GT_FIELD nodes will be lowered into more "code-gen-able" representations, like
// GT_IND's of addresses, or GT_LCL_FLD nodes. We'd like to preserve the more abstract
// information, and will therefore annotate such lowered nodes with FieldSeq's. A FieldSeq
// represents a (possibly) empty sequence of fields. The fields are in the order
// in which they are dereferenced. The first field may be an object field or a struct field;
// all subsequent fields must be struct fields.
struct FieldSeqNode
class FieldSeqNode
{
CORINFO_FIELD_HANDLE m_fieldHnd;
FieldSeqNode* m_next;
public:
enum class FieldKind : uintptr_t
{
Instance = 0, // An instance field, object or struct.
SimpleStatic = 1, // Simple static field - the handle represents a unique location.
SharedStatic = 2, // Static field on a shared generic type: "Class<__Canon>.StaticField".
};
private:
static const uintptr_t FIELD_KIND_MASK = 0b11;
static_assert_no_msg(sizeof(CORINFO_FIELD_HANDLE) == sizeof(uintptr_t));
uintptr_t m_fieldHandleAndKind;
FieldSeqNode* m_next;
public:
FieldSeqNode(CORINFO_FIELD_HANDLE fieldHnd, FieldSeqNode* next, FieldKind fieldKind);
FieldKind GetKind() const
{
return static_cast<FieldKind>(m_fieldHandleAndKind & FIELD_KIND_MASK);
}
FieldSeqNode(CORINFO_FIELD_HANDLE fieldHnd, FieldSeqNode* next) : m_fieldHnd(fieldHnd), m_next(next)
CORINFO_FIELD_HANDLE GetFieldHandle() const
{
assert(!IsPseudoField() && (GetFieldHandleValue() != NO_FIELD_HANDLE));
return GetFieldHandleValue();
}
CORINFO_FIELD_HANDLE GetFieldHandleValue() const
{
return CORINFO_FIELD_HANDLE(m_fieldHandleAndKind & ~FIELD_KIND_MASK);
}
// returns true when this is the pseudo #FirstElem field sequence
bool IsFirstElemFieldSeq();
bool IsFirstElemFieldSeq() const;
// returns true when this is the pseudo #ConstantIndex field sequence
bool IsConstantIndexFieldSeq();
bool IsConstantIndexFieldSeq() const;
// returns true when this is the the pseudo #FirstElem field sequence or the pseudo #ConstantIndex field sequence
bool IsPseudoField() const;
CORINFO_FIELD_HANDLE GetFieldHandle() const
bool IsStaticField() const
{
assert(!IsPseudoField() && (m_fieldHnd != nullptr));
return m_fieldHnd;
return (GetKind() == FieldKind::SimpleStatic) || (GetKind() == FieldKind::SharedStatic);
}
bool IsSharedStaticField() const
{
return GetKind() == FieldKind::SharedStatic;
}
FieldSeqNode* GetNext() const
{
return m_next;
}
FieldSeqNode* GetTail()
......@@ -262,16 +297,17 @@ struct FieldSeqNode
return tail;
}
// Make sure this provides methods that allow it to be used as a KeyFuncs type in SimplerHash.
// Make sure this provides methods that allow it to be used as a KeyFuncs type in JitHashTable.
// Note that there is a one-to-one relationship between the field handle and the field kind, so
// we do not need to mask away the latter for comparison purposes.
static int GetHashCode(FieldSeqNode fsn)
{
return static_cast<int>(reinterpret_cast<intptr_t>(fsn.m_fieldHnd)) ^
static_cast<int>(reinterpret_cast<intptr_t>(fsn.m_next));
return static_cast<int>(fsn.m_fieldHandleAndKind) ^ static_cast<int>(reinterpret_cast<intptr_t>(fsn.m_next));
}
static bool Equals(const FieldSeqNode& fsn1, const FieldSeqNode& fsn2)
{
return fsn1.m_fieldHnd == fsn2.m_fieldHnd && fsn1.m_next == fsn2.m_next;
return fsn1.m_fieldHandleAndKind == fsn2.m_fieldHandleAndKind && fsn1.m_next == fsn2.m_next;
}
};
......@@ -293,7 +329,8 @@ public:
FieldSeqStore(CompAllocator alloc);
// Returns the (canonical in the store) singleton field sequence for the given handle.
FieldSeqNode* CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd);
FieldSeqNode* CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd,
FieldSeqNode::FieldKind fieldKind = FieldSeqNode::FieldKind::Instance);
// This is a special distinguished FieldSeqNode indicating that a constant does *not*
// represent a valid field sequence. This is "infectious", in the sense that appending it
......@@ -556,6 +593,7 @@ enum GenTreeFlags : unsigned int
GTF_ICON_CIDMID_HDL = 0x0E000000, // GT_CNS_INT -- constant is a class ID or a module ID
GTF_ICON_BBC_PTR = 0x0F000000, // GT_CNS_INT -- constant is a basic block count pointer
GTF_ICON_STATIC_BOX_PTR = 0x10000000, // GT_CNS_INT -- constant is an address of the box for a STATIC_IN_HEAP field
GTF_ICON_FIELD_SEQ = 0x11000000, // <--------> -- constant is a FieldSeqNode* (used only as VNHandle)
// GTF_ICON_REUSE_REG_VAL = 0x00800000 // GT_CNS_INT -- GTF_REUSE_REG_VAL, defined above
GTF_ICON_FIELD_OFF = 0x00400000, // GT_CNS_INT -- constant is a field offset
......
......@@ -8217,15 +8217,18 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT
// be mutable, but the only current producer of such images, the C++/CLI compiler, does
// not appear to support mapping different fields to the same address. So we will say
// that "mutable overlapping RVA statics" are UB as well. If this ever changes, code in
// morph and value numbering will need to be updated to respect "gtFldMayOverlap" and
// "NotAField FldSeq".
// value numbering will need to be updated to respect "NotAField FldSeq".
// For statics that are not "boxed", the initial address tree will contain the field sequence.
// For those that are, we will attach it later, when adding the indirection for the box, since
// that tree will represent the true address.
bool isBoxedStatic = (pFieldInfo->fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) != 0;
FieldSeqNode* innerFldSeq =
!isBoxedStatic ? GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField) : FieldSeqStore::NotAField();
bool isBoxedStatic = (pFieldInfo->fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP) != 0;
bool isSharedStatic = (pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER) ||
(pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_READYTORUN_HELPER);
FieldSeqNode::FieldKind fieldKind =
isSharedStatic ? FieldSeqNode::FieldKind::SharedStatic : FieldSeqNode::FieldKind::SimpleStatic;
FieldSeqNode* innerFldSeq = !isBoxedStatic ? GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField, fieldKind)
: FieldSeqStore::NotAField();
GenTree* op1;
......@@ -8355,7 +8358,7 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT
if (isBoxedStatic)
{
FieldSeqNode* outerFldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
FieldSeqNode* outerFldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField, fieldKind);
op1->ChangeType(TYP_REF); // points at boxed object
op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, gtNewIconNode(TARGET_POINTER_SIZE, outerFldSeq));
......@@ -8368,20 +8371,19 @@ GenTree* Compiler::impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedT
else
{
op1 = gtNewOperNode(GT_IND, lclTyp, op1);
op1->gtFlags |= GTF_GLOB_REF | GTF_IND_NONFAULTING;
op1->gtFlags |= (GTF_GLOB_REF | GTF_IND_NONFAULTING);
}
}
return op1;
}
break;
}
}
if (isBoxedStatic)
{
FieldSeqNode* outerFldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField);
FieldSeqNode* outerFldSeq = GetFieldSeqStore()->CreateSingleton(pResolvedToken->hField, fieldKind);
op1 = gtNewOperNode(GT_IND, TYP_REF, op1);
op1->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL);
......
......@@ -1029,7 +1029,8 @@ private:
// TODO-ADDR: ObjectAllocator produces FIELD nodes with FirstElemPseudoField as field
// handle so we cannot use FieldSeqNode::GetFieldHandle() because it asserts on such
// handles. ObjectAllocator should be changed to create LCL_FLD nodes directly.
assert(!indir->OperIs(GT_FIELD) || (indir->AsField()->gtFldHnd == fieldSeq->GetTail()->m_fieldHnd));
assert(!indir->OperIs(GT_FIELD) ||
(indir->AsField()->gtFldHnd == fieldSeq->GetTail()->GetFieldHandleValue()));
}
else
{
......
......@@ -5888,11 +5888,27 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
{
assert(tree->gtOper == GT_FIELD);
CORINFO_FIELD_HANDLE symHnd = tree->AsField()->gtFldHnd;
unsigned fldOffset = tree->AsField()->gtFldOffset;
GenTree* objRef = tree->AsField()->GetFldObj();
bool fieldMayOverlap = false;
bool objIsLocal = false;
CORINFO_FIELD_HANDLE symHnd = tree->AsField()->gtFldHnd;
unsigned fldOffset = tree->AsField()->gtFldOffset;
GenTree* objRef = tree->AsField()->GetFldObj();
bool objIsLocal = false;
FieldSeqNode* fieldSeq = FieldSeqStore::NotAField();
if (!tree->AsField()->gtFldMayOverlap)
{
if (objRef != nullptr)
{
fieldSeq = GetFieldSeqStore()->CreateSingleton(symHnd, FieldSeqNode::FieldKind::Instance);
}
else
{
// Only simple statics get importred as GT_FIELDs.
fieldSeq = GetFieldSeqStore()->CreateSingleton(symHnd, FieldSeqNode::FieldKind::SimpleStatic);
}
}
// Reset the flag because we may reuse the node.
tree->AsField()->gtFldMayOverlap = false;
if (fgGlobalMorph && (objRef != nullptr) && (objRef->gtOper == GT_ADDR))
{
......@@ -5905,13 +5921,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
noway_assert(((objRef != nullptr) && (objRef->IsLocalAddrExpr() != nullptr)) ||
((tree->gtFlags & GTF_GLOB_REF) != 0));
if (tree->AsField()->gtFldMayOverlap)
{
fieldMayOverlap = true;
// Reset the flag because we may reuse the node.
tree->AsField()->gtFldMayOverlap = false;
}
#ifdef FEATURE_SIMD
// if this field belongs to simd struct, translate it to simd intrinsic.
if (mac == nullptr)
......@@ -6167,10 +6176,8 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
if (fldOffset != 0)
{
// Generate the "addr" node.
/* Add the member offset to the object's address */
FieldSeqNode* fieldSeq =
fieldMayOverlap ? FieldSeqStore::NotAField() : GetFieldSeqStore()->CreateSingleton(symHnd);
addr = gtNewOperNode(GT_ADD, (var_types)(objRefType == TYP_I_IMPL ? TYP_I_IMPL : TYP_BYREF), addr,
// Add the member offset to the object's address.
addr = gtNewOperNode(GT_ADD, (objRefType == TYP_I_IMPL) ? TYP_I_IMPL : TYP_BYREF, addr,
gtNewIconHandleNode(fldOffset, GTF_ICON_FIELD_OFF, fieldSeq));
}
......@@ -6284,8 +6291,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
if (fldOffset != 0)
{
FieldSeqNode* fieldSeq =
fieldMayOverlap ? FieldSeqStore::NotAField() : GetFieldSeqStore()->CreateSingleton(symHnd);
GenTree* fldOffsetNode = new (this, GT_CNS_INT) GenTreeIntCon(TYP_INT, fldOffset, fieldSeq);
/* Add the TLS static field offset to the address */
......@@ -6302,8 +6307,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
}
else
{
assert(!fieldMayOverlap);
// Normal static field reference
//
// If we can we access the static's address directly
......@@ -6319,9 +6322,11 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// 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);
FieldSeqNode* fldSeq =
!isBoxedStatic ? GetFieldSeqStore()->CreateSingleton(symHnd) : FieldSeqStore::NotAField();
bool isBoxedStatic = gtIsStaticFieldPtrToBoxedStruct(tree->TypeGet(), symHnd);
if (isBoxedStatic)
{
fieldSeq = FieldSeqStore::NotAField();
}
// TODO-CQ: enable this optimization for 32 bit targets.
bool isStaticReadOnlyInited = false;
......@@ -6361,7 +6366,7 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
{
handleKind = GTF_ICON_STATIC_HDL;
}
GenTree* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fldSeq);
GenTree* addr = gtNewIconHandleNode((size_t)fldAddr, handleKind, fieldSeq);
// Translate GTF_FLD_INITCLASS to GTF_ICON_INITCLASS, if we need to.
if (((tree->gtFlags & GTF_FLD_INITCLASS) != 0) && !isStaticReadOnlyInited)
......@@ -6397,7 +6402,7 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
static_assert_no_msg(GTF_FLD_INITCLASS == GTF_CLS_VAR_INITCLASS);
tree->SetOper(GT_CLS_VAR);
tree->AsClsVar()->gtClsVarHnd = symHnd;
tree->AsClsVar()->gtFieldSeq = fldSeq;
tree->AsClsVar()->gtFieldSeq = fieldSeq;
}
return tree;
......@@ -6424,8 +6429,6 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
assert(addr->TypeGet() == TYP_BYREF || addr->TypeGet() == TYP_I_IMPL || addr->TypeGet() == TYP_REF);
// Since we don't make a constant zero to attach the field sequence to, associate it with the "addr" node.
FieldSeqNode* fieldSeq =
fieldMayOverlap ? FieldSeqStore::NotAField() : GetFieldSeqStore()->CreateSingleton(symHnd);
fgAddFieldSeqForZeroOffset(addr, fieldSeq);
}
......@@ -13912,7 +13915,7 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul)
bool op2IsConstIndex = op2->OperGet() == GT_CNS_INT && op2->AsIntCon()->gtFieldSeq != nullptr &&
op2->AsIntCon()->gtFieldSeq->IsConstantIndexFieldSeq();
assert(!op2IsConstIndex || op2->AsIntCon()->gtFieldSeq->m_next == nullptr);
assert(!op2IsConstIndex || op2->AsIntCon()->gtFieldSeq->GetNext() == nullptr);
if (mult == 0)
{
......@@ -13958,7 +13961,7 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul)
if (op2->OperGet() == GT_CNS_INT && op2->AsIntCon()->gtFieldSeq != nullptr &&
op2->AsIntCon()->gtFieldSeq->IsConstantIndexFieldSeq())
{
assert(op2->AsIntCon()->gtFieldSeq->m_next == nullptr);
assert(op2->AsIntCon()->gtFieldSeq->GetNext() == nullptr);
GenTree* otherOp = op1;
if (otherOp->OperGet() == GT_NEG)
{
......@@ -14618,7 +14621,7 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree)
if (cns->gtOper == GT_CNS_INT && cns->AsIntCon()->gtFieldSeq != nullptr &&
cns->AsIntCon()->gtFieldSeq->IsConstantIndexFieldSeq())
{
assert(cns->AsIntCon()->gtFieldSeq->m_next == nullptr);
assert(cns->AsIntCon()->gtFieldSeq->GetNext() == nullptr);
op2->AsIntCon()->gtFieldSeq = cns->AsIntCon()->gtFieldSeq;
}
......
......@@ -1962,7 +1962,6 @@ class Object* ValueNumStore::s_specialRefConsts[] = {nullptr, nullptr, nullptr};
ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func)
{
assert(VNFuncArity(func) == 0);
assert(func != VNF_NotAField);
ValueNum resultVN;
......@@ -3956,7 +3955,7 @@ ValueNum ValueNumStore::VNApplySelectors(ValueNumKind vnk,
FieldSeqNode* fieldSeq,
size_t* wbFinalStructSize)
{
for (FieldSeqNode* field = fieldSeq; field != nullptr; field = field->m_next)
for (FieldSeqNode* field = fieldSeq; field != nullptr; field = field->GetNext())
{
assert(field != FieldSeqStore::NotAField());
assert(!field->IsPseudoField());
......@@ -4120,7 +4119,7 @@ ValueNum ValueNumStore::VNApplySelectorsAssign(
assert(fieldSeq != FieldSeqStore::NotAField());
assert(!fieldSeq->IsPseudoField());
if (fieldSeq->m_next == nullptr)
if (fieldSeq->GetNext() == nullptr)
{
JITDUMP(" VNApplySelectorsAssign:\n");
}
......@@ -4129,10 +4128,10 @@ ValueNum ValueNumStore::VNApplySelectorsAssign(
ValueNum fldHndVN = VNForFieldSelector(fieldSeq->GetFieldHandle(), &fieldType);
ValueNum valueAfter;
if (fieldSeq->m_next != nullptr)
if (fieldSeq->GetNext() != nullptr)
{
ValueNum fseqMap = VNForMapSelect(vnk, fieldType, map, fldHndVN);
valueAfter = VNApplySelectorsAssign(vnk, fseqMap, fieldSeq->m_next, value, dstIndType);
valueAfter = VNApplySelectorsAssign(vnk, fseqMap, fieldSeq->GetNext(), value, dstIndType);
}
else
{
......@@ -4156,11 +4155,36 @@ ValueNumPair ValueNumStore::VNPairApplySelectors(ValueNumPair map, FieldSeqNode*
return ValueNumPair(liberalVN, conservVN);
}
//------------------------------------------------------------------------
// IsVNNotAField: Is the value number a "NotAField" field sequence?
//
// Arguments:
// vn - the value number in question
//
// Return Value:
// Whether "vn" represents a "NotAField" field sequence.
//
// Notes:
// "NotAField" field sequences always get a "new, unique" number, since
// they represent unknown locations. Thus they get their own chunk kind,
// for which no actual memory is allocated to hold VNFunc data.
//
bool ValueNumStore::IsVNNotAField(ValueNum vn)
{
return m_chunks.GetNoExpand(GetChunkNum(vn))->m_attribs == CEA_NotAField;
}
//------------------------------------------------------------------------
// VNForFieldSeq: Get the value number representing a field sequence.
//
// Arguments:
// fieldSeq - the field sequence
//
// Return Value:
// "VNForNull" if the sequence is empty ("nullptr").
// "IsVNNotAField" VN if it is a "NotAField".
// "GTF_FIELD_SEQ_PTR" handle VN otherwise.
//
ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
{
if (fieldSeq == nullptr)
......@@ -4178,16 +4202,14 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
}
else
{
ssize_t fieldHndVal = ssize_t(fieldSeq->m_fieldHnd);
ValueNum fieldHndVN = VNForHandle(fieldHndVal, GTF_ICON_FIELD_HDL);
ValueNum seqNextVN = VNForFieldSeq(fieldSeq->m_next);
fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, fieldHndVN, seqNextVN);
// This encoding relies on the canonicality of field sequences.
fieldSeqVN = VNForHandle(reinterpret_cast<ssize_t>(fieldSeq), GTF_ICON_FIELD_SEQ);
}
#ifdef DEBUG
if (m_pComp->verbose)
{
printf(" FieldSeq");
printf(" ");
vnDump(m_pComp, fieldSeqVN);
printf(" is " FMT_VN "\n", fieldSeqVN);
}
......@@ -4196,6 +4218,15 @@ ValueNum ValueNumStore::VNForFieldSeq(FieldSeqNode* fieldSeq)
return fieldSeqVN;
}
//------------------------------------------------------------------------
// FieldSeqVNToFieldSeq: Decode the field sequence from a VN representing one.
//
// Arguments:
// vn - the value number, must be one obtained using "VNForFieldSeq"
//
// Return Value:
// The field sequence associated with "vn".
//
FieldSeqNode* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn)
{
if (vn == VNForNull())
......@@ -4203,54 +4234,32 @@ FieldSeqNode* ValueNumStore::FieldSeqVNToFieldSeq(ValueNum vn)
return nullptr;
}
assert(IsVNFunc(vn));
VNFuncApp funcApp;
GetVNFunc(vn, &funcApp);
if (funcApp.m_func == VNF_NotAField)
if (IsVNNotAField(vn))
{
return FieldSeqStore::NotAField();
}
assert(funcApp.m_func == VNF_FieldSeq);
const ssize_t fieldHndVal = ConstantValue<ssize_t>(funcApp.m_args[0]);
FieldSeqNode* head =
m_pComp->GetFieldSeqStore()->CreateSingleton(reinterpret_cast<CORINFO_FIELD_HANDLE>(fieldHndVal));
FieldSeqNode* tail = FieldSeqVNToFieldSeq(funcApp.m_args[1]);
return m_pComp->GetFieldSeqStore()->Append(head, tail);
assert(IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_FIELD_SEQ));
return reinterpret_cast<FieldSeqNode*>(ConstantValue<ssize_t>(vn));
}
ValueNum ValueNumStore::FieldSeqVNAppend(ValueNum fsVN1, ValueNum fsVN2)
//------------------------------------------------------------------------
// FieldSeqVNAppend: Append a field sequences to one represented as a VN.
//
// Arguments:
// innerFieldSeqVN - VN of the field sequence being appended to
// outerFieldSeq - the field sequence being appended
//
// Return Value:
// The value number representing [innerFieldSeq, outerFieldSeq].
//
ValueNum ValueNumStore::FieldSeqVNAppend(ValueNum innerFieldSeqVN, FieldSeqNode* outerFieldSeq)
{
if (fsVN1 == VNForNull())
{
return fsVN2;
}
assert(IsVNFunc(fsVN1));
VNFuncApp funcApp1;
GetVNFunc(fsVN1, &funcApp1);
FieldSeqNode* innerFieldSeq = FieldSeqVNToFieldSeq(innerFieldSeqVN);
FieldSeqNode* fullFieldSeq = m_pComp->GetFieldSeqStore()->Append(innerFieldSeq, outerFieldSeq);
if ((funcApp1.m_func == VNF_NotAField) || IsVNNotAField(fsVN2))
{
return VNForFieldSeq(FieldSeqStore::NotAField());
}
assert(funcApp1.m_func == VNF_FieldSeq);
ValueNum tailRes = FieldSeqVNAppend(funcApp1.m_args[1], fsVN2);
ValueNum fieldSeqVN = VNForFunc(TYP_REF, VNF_FieldSeq, funcApp1.m_args[0], tailRes);
#ifdef DEBUG
if (m_pComp->verbose)
{
printf(" fieldSeq " FMT_VN " is ", fieldSeqVN);
vnDump(m_pComp, fieldSeqVN);
printf("\n");
}
#endif
return fieldSeqVN;
return VNForFieldSeq(fullFieldSeq);
}
ValueNum ValueNumStore::ExtendPtrVN(GenTree* opA, GenTree* opB)
......@@ -4292,24 +4301,22 @@ ValueNum ValueNumStore::ExtendPtrVN(GenTree* opA, FieldSeqNode* fldSeq)
VNFuncApp consFuncApp;
assert(GetVNFunc(VNConservativeNormalValue(opA->gtVNPair), &consFuncApp) && consFuncApp.Equals(funcApp));
#endif
ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
res = VNForFunc(TYP_BYREF, VNF_PtrToLoc, funcApp.m_args[0], FieldSeqVNAppend(funcApp.m_args[1], fldSeqVN));
res = VNForFunc(TYP_BYREF, VNF_PtrToLoc, funcApp.m_args[0], FieldSeqVNAppend(funcApp.m_args[1], fldSeq));
}
else if (funcApp.m_func == VNF_PtrToStatic)
{
ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
res = VNForFunc(TYP_BYREF, VNF_PtrToStatic, funcApp.m_args[0], FieldSeqVNAppend(funcApp.m_args[1], fldSeqVN));
res = VNForFunc(TYP_BYREF, VNF_PtrToStatic, funcApp.m_args[0], FieldSeqVNAppend(funcApp.m_args[1], fldSeq));
}
else if (funcApp.m_func == VNF_PtrToArrElem)
{
ValueNum fldSeqVN = VNForFieldSeq(fldSeq);
res = VNForFunc(TYP_BYREF, VNF_PtrToArrElem, funcApp.m_args[0], funcApp.m_args[1], funcApp.m_args[2],
FieldSeqVNAppend(funcApp.m_args[3], fldSeqVN));
FieldSeqVNAppend(funcApp.m_args[3], fldSeq));
}
if (res != NoVN)
{
res = VNWithExc(res, opAvnx);
}
return res;
}
......@@ -5888,7 +5895,6 @@ bool ValueNumStore::IsVNFunc(ValueNum vn)
Chunk* c = m_chunks.GetNoExpand(GetChunkNum(vn));
switch (c->m_attribs)
{
case CEA_NotAField:
case CEA_Func0:
case CEA_Func1:
case CEA_Func2:
......@@ -5957,12 +5963,6 @@ bool ValueNumStore::GetVNFunc(ValueNum vn, VNFuncApp* funcApp)
funcApp->m_arity = 0;
return true;
}
case CEA_NotAField:
{
funcApp->m_func = VNF_NotAField;
funcApp->m_arity = 0;
return true;
}
default:
return false;
}
......@@ -6021,6 +6021,11 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
{
printf("NoVN");
}
else if (IsVNNotAField(vn) || (IsVNHandle(vn) && (GetHandleFlags(vn) == GTF_ICON_FIELD_SEQ)))
{
comp->gtDispAnyFieldSeq(FieldSeqVNToFieldSeq(vn));
printf(" ");
}
else if (IsVNHandle(vn))
{
ssize_t val = ConstantValue<ssize_t>(vn);
......@@ -6146,10 +6151,6 @@ void ValueNumStore::vnDump(Compiler* comp, ValueNum vn, bool isPtr)
// A few special cases...
switch (funcApp.m_func)
{
case VNF_FieldSeq:
case VNF_NotAField:
vnDumpFieldSeq(comp, &funcApp, true);
break;
case VNF_MapSelect:
vnDumpMapSelect(comp, &funcApp);
break;
......@@ -6253,56 +6254,6 @@ void ValueNumStore::vnDumpExcSeq(Compiler* comp, VNFuncApp* excSeq, bool isHead)
}
}
void ValueNumStore::vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead)
{
if (fieldSeq->m_func == VNF_NotAField)
{
printf("<NotAField>");
return;
}
assert(fieldSeq->m_func == VNF_FieldSeq); // Precondition.
// First arg is the field handle VN.
assert(IsVNConstant(fieldSeq->m_args[0]) && TypeOfVN(fieldSeq->m_args[0]) == TYP_I_IMPL);
ssize_t fieldHndVal = ConstantValue<ssize_t>(fieldSeq->m_args[0]);
bool hasTail = (fieldSeq->m_args[1] != VNForNull());
if (isHead && hasTail)
{
printf("(");
}
CORINFO_FIELD_HANDLE fldHnd = CORINFO_FIELD_HANDLE(fieldHndVal);
if (fldHnd == FieldSeqStore::FirstElemPseudoField)
{
printf("#FirstElem");
}
else if (fldHnd == FieldSeqStore::ConstantIndexPseudoField)
{
printf("#ConstantIndex");
}
else
{
const char* modName;
const char* fldName = m_pComp->eeGetFieldName(fldHnd, &modName);
printf("%s", fldName);
}
if (hasTail)
{
printf(", ");
assert(IsVNFunc(fieldSeq->m_args[1]));
VNFuncApp tail;
GetVNFunc(fieldSeq->m_args[1], &tail);
vnDumpFieldSeq(comp, &tail, false);
}
if (isHead && hasTail)
{
printf(")");
}
}
void ValueNumStore::vnDumpMapSelect(Compiler* comp, VNFuncApp* mapSelect)
{
assert(mapSelect->m_func == VNF_MapSelect); // Precondition.
......@@ -8055,7 +8006,7 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
ValueNum newFirstFieldValueVN = ValueNumStore::NoVN;
// Optimization: avoid traversting the maps for the value of the first field if
// we do not need it, which is the case if the rest of the field sequence is empty.
if (fldSeq->m_next == nullptr)
if (fldSeq->GetNext() == nullptr)
{
newFirstFieldValueVN = vnStore->VNApplySelectorsAssignTypeCoerce(storeVal, indType);
}
......@@ -8067,8 +8018,9 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree)
firstFieldValueSelectorVN);
// Construct the maps updating the struct fields in the sequence.
newFirstFieldValueVN = vnStore->VNApplySelectorsAssign(VNK_Liberal, firstFieldValueVN,
fldSeq->m_next, storeVal, indType);
newFirstFieldValueVN =
vnStore->VNApplySelectorsAssign(VNK_Liberal, firstFieldValueVN, fldSeq->GetNext(),
storeVal, indType);
}
// Finally, construct the new field map...
......@@ -8991,7 +8943,7 @@ void Compiler::fgValueNumberTree(GenTree* tree)
// Finally, account for the rest of the fields in the sequence.
valueVN =
vnStore->VNApplySelectors(VNK_Liberal, firstFieldValueVN, fldSeq->m_next, &structSize);
vnStore->VNApplySelectors(VNK_Liberal, firstFieldValueVN, fldSeq->GetNext(), &structSize);
}
else
{
......@@ -11151,7 +11103,7 @@ void Compiler::fgDebugCheckValueNumberedTree(GenTree* tree)
break;
}
fldSeq = fldSeq->m_next;
fldSeq = fldSeq->GetNext();
}
assert(zeroOffsetFldSeqFound);
......
......@@ -685,20 +685,13 @@ public:
bool srcIsUnsigned = false,
bool hasOverflowCheck = false);
// Returns true iff the VN represents an application of VNF_NotAField.
bool IsVNNotAField(ValueNum vn);
// PtrToLoc values need to express a field sequence as one of their arguments. VN for null represents
// empty sequence, otherwise, "FieldSeq(VN(FieldHandle), restOfSeq)".
ValueNum VNForFieldSeq(FieldSeqNode* fieldSeq);
// Requires that "vn" represents a field sequence, that is, is the result of a call to VNForFieldSeq.
// Returns the FieldSequence it represents.
FieldSeqNode* FieldSeqVNToFieldSeq(ValueNum vn);
// Both argument must represent field sequences; returns the value number representing the
// concatenation "fsVN1 || fsVN2".
ValueNum FieldSeqVNAppend(ValueNum fsVN1, ValueNum fsVN2);
ValueNum FieldSeqVNAppend(ValueNum innerFieldSeqVN, FieldSeqNode* outerFieldSeq);
// If "opA" has a PtrToLoc, PtrToArrElem, or PtrToStatic application as its value numbers, and "opB" is an integer
// with a "fieldSeq", returns the VN for the pointer form extended with the field sequence; or else NoVN.
......@@ -1020,9 +1013,9 @@ public:
// Prints, to standard out, a representation of "vn".
void vnDump(Compiler* comp, ValueNum vn, bool isPtr = false);
// Requires "fieldSeq" to be a field sequence VNFuncApp.
// Requires "fieldSeq" to be a field sequence VN.
// Prints a representation (comma-separated list of field names) on standard out.
void vnDumpFieldSeq(Compiler* comp, VNFuncApp* fieldSeq, bool isHead);
void vnDumpFieldSeq(Compiler* comp, ValueNum fieldSeqVN);
// Requires "mapSelect" to be a map select VNFuncApp.
// Prints a representation of a MapSelect operation on standard out.
......
......@@ -10,9 +10,6 @@ ValueNumFuncDef(MemOpaque, 1, false, false, false) // Args: 0: loop num
ValueNumFuncDef(MapStore, 4, false, false, false) // Args: 0: map, 1: index (e. g. field handle), 2: value being stored, 3: loop num.
ValueNumFuncDef(MapSelect, 2, false, false, false) // Args: 0: map, 1: key.
ValueNumFuncDef(FieldSeq, 2, false, false, false) // Sequence (VN of null == empty) of (VN's of) field handles.
ValueNumFuncDef(NotAField, 0, false, false, false) // Value number function for FieldSeqStore::NotAField.
ValueNumFuncDef(PtrToLoc, 2, false, true, false) // Pointer (byref) to a local variable. Args: VN's of: 0: var num, 1: FieldSeq.
ValueNumFuncDef(PtrToArrElem, 4, false, false, false) // Pointer (byref) to an array element. Args: 0: array elem type eq class var_types value, VN's of: 1: array, 2: index, 3: FieldSeq.
ValueNumFuncDef(PtrToStatic, 2, false, true, false) // Pointer (byref) to a static variable (or possibly a field thereof, if the static variable is a struct).
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册