{
m_VNsForSmallIntConsts[i] = NoVN;
}
- // We will reserve chunk 0 to hold some special constants, like the constant NULL, the "exception" value, and the
- // "zero map."
+ // We will reserve chunk 0 to hold some special constants.
Chunk* specialConstChunk = new (m_alloc) Chunk(m_alloc, &m_nextChunkBase, TYP_REF, CEA_Const);
- specialConstChunk->m_numUsed +=
- SRC_NumSpecialRefConsts; // Implicitly allocate 0 ==> NULL, and 1 ==> Exception, 2 ==> ZeroMap.
+ specialConstChunk->m_numUsed += SRC_NumSpecialRefConsts;
ChunkNum cn = m_chunks.Push(specialConstChunk);
assert(cn == 0);
}
}
-// Returns the value number for zero of the given "typ".
-// It has an unreached() for a "typ" that has no zero value, such as TYP_VOID.
ValueNum ValueNumStore::VNZeroForType(var_types typ)
{
switch (typ)
return VNForNull();
case TYP_BYREF:
return VNForByrefCon(0);
- case TYP_STRUCT:
- return VNForZeroMap(); // Recursion!
#ifdef FEATURE_SIMD
case TYP_SIMD8:
case TYP_SIMD12:
case TYP_SIMD16:
case TYP_SIMD32:
- return VNForLongCon(0);
+ // We do not have the base type - a "fake" one will have to do. Note that we cannot
+ // use the HWIntrinsic "get_Zero" VNFunc here. This is because they only represent
+ // "fully zeroed" vectors, and here we may be loading one from memory, leaving upper
+ // bits undefined. So using "SIMD_Init" is "the next best thing", so to speak, and
+ // TYP_FLOAT is one of the more popular base types, so that's why we use it here.
+ return VNForFunc(typ, VNF_SIMD_Init, VNForFloatCon(0), VNForSimdType(genTypeSize(typ), TYP_FLOAT));
#endif // FEATURE_SIMD
// These should be unreached.
}
}
+ValueNum ValueNumStore::VNForZeroObj(CORINFO_CLASS_HANDLE structHnd)
+{
+ assert(structHnd != NO_CLASS_HANDLE);
+
+ ValueNum structHndVN = VNForHandle(ssize_t(structHnd), GTF_ICON_CLASS_HDL);
+ ValueNum zeroObjVN = VNForFunc(TYP_STRUCT, VNF_ZeroObj, structHndVN);
+
+ return zeroObjVN;
+}
+
// Returns the value number for one of the given "typ".
// It returns NoVN for a "typ" that has no one value, such as TYP_REF.
ValueNum ValueNumStore::VNOneForType(var_types typ)
}
}
+#ifdef FEATURE_SIMD
+ValueNum ValueNumStore::VNForSimdType(unsigned simdSize, var_types simdBaseType)
+{
+ ValueNum baseTypeVN = VNForIntCon(INT32(simdBaseType));
+ ValueNum sizeVN = VNForIntCon(simdSize);
+ ValueNum simdTypeVN = VNForFunc(TYP_REF, VNF_SimdType, sizeVN, baseTypeVN);
+
+ return simdTypeVN;
+}
+#endif // FEATURE_SIMD
+
class Object* ValueNumStore::s_specialRefConsts[] = {nullptr, nullptr, nullptr};
//----------------------------------------------------------------------------------------
return RecursiveVN;
}
- if (map == VNForZeroMap())
- {
- return VNZeroForType(type);
- }
- else if (IsVNFunc(map))
+ VNFuncApp funcApp;
+ if (GetVNFunc(map, &funcApp))
{
- VNFuncApp funcApp;
- GetVNFunc(map, &funcApp);
if (funcApp.m_func == VNF_MapStore)
{
// select(store(m, i, v), i) == v
m_fixedPointMapSels.Pop();
}
}
+ else if (funcApp.m_func == VNF_ZeroObj)
+ {
+ // For structs, we need to extract the handle from the selector.
+ if (type == TYP_STRUCT)
+ {
+ // We only expect field selectors here.
+ assert(GetHandleFlags(index) == GTF_ICON_FIELD_HDL);
+ CORINFO_FIELD_HANDLE fieldHnd = CORINFO_FIELD_HANDLE(ConstantValue<ssize_t>(index));
+ CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE;
+ m_pComp->eeGetFieldType(fieldHnd, &structHnd);
+
+ return VNForZeroObj(structHnd);
+ }
+
+ return VNZeroForType(type);
+ }
}
// We may have run out of budget and already assigned a result
{
printf("void");
}
- else
- {
- assert(vn == VNForZeroMap());
- printf("zeroMap");
- }
break;
case TYP_BYREF:
printf("byrefVal");
case VNF_CastOvf:
vnDumpCast(comp, vn);
break;
+ case VNF_ZeroObj:
+ vnDumpZeroObj(comp, &funcApp);
+ break;
default:
printf("%s(", VNFuncName(funcApp.m_func));
}
}
+void ValueNumStore::vnDumpZeroObj(Compiler* comp, VNFuncApp* zeroObj)
+{
+ printf("ZeroObj(");
+ comp->vnPrint(zeroObj->m_args[0], 0);
+ printf(": %s)", comp->eeGetClassName(CORINFO_CLASS_HANDLE(ConstantValue<ssize_t>(zeroObj->m_args[0]))));
+}
#endif // DEBUG
// Static fields, methods.
"$VN.Recursive", // -2 RecursiveVN
"$VN.No", // -1 NoVN
"$VN.Null", // 0 VNForNull()
- "$VN.ZeroMap", // 1 VNForZeroMap()
- "$VN.ReadOnlyHeap", // 2 VNForROH()
- "$VN.Void", // 3 VNForVoid()
- "$VN.EmptyExcSet" // 4 VNForEmptyExcSet()
+ "$VN.ReadOnlyHeap", // 1 VNForROH()
+ "$VN.Void", // 2 VNForVoid()
+ "$VN.EmptyExcSet" // 3 VNForEmptyExcSet()
};
// Returns the string name of "vn" when it is a reserved value number, nullptr otherwise
if (isZeroed)
{
// By default we will zero init these LclVars
- initVal = vnStore->VNZeroForType(typ);
+ initVal = (typ == TYP_STRUCT) ? vnStore->VNForZeroObj(varDsc->GetStructHnd())
+ : vnStore->VNZeroForType(typ);
}
else
{
// Should not have been recorded as updating the GC heap.
assert(!GetMemorySsaMap(GcHeap)->Lookup(tree));
- unsigned lclNum = lclVarTree->GetLclNum();
-
unsigned lclDefSsaNum = GetSsaNumForLocalVarDef(lclVarTree);
+
// Ignore vars that we excluded from SSA (for example, because they're address-exposed). They don't have
// SSA names in which to store VN's on defs. We'll yield unique VN's when we read from them.
- if (lvaInSsa(lclNum) && lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
+ if (lclDefSsaNum != SsaConfig::RESERVED_SSA_NUM)
{
+ LclVarDsc* lclVarDsc = lvaGetDesc(lclVarTree);
+
// Should not have been recorded as updating ByrefExposed.
assert(!GetMemorySsaMap(ByrefExposed)->Lookup(tree));
- ValueNum initBlkVN = ValueNumStore::NoVN;
- GenTree* initConst = rhs;
- if (isEntire && initConst->OperGet() == GT_CNS_INT)
+ ValueNum lclVarVN = ValueNumStore::NoVN;
+ if (isEntire && rhs->IsIntegralConst(0))
{
- unsigned initVal = 0xFF & (unsigned)initConst->AsIntConCommon()->IconValue();
- if (initVal == 0)
- {
- initBlkVN = vnStore->VNZeroForType(lclVarTree->TypeGet());
- }
+ // Note that it is possible to see pretty much any kind of type for the local
+ // (not just TYP_STRUCT) here because of the ASG(BLK(ADDR(LCL_VAR/FLD)), 0) form.
+ lclVarVN = (lclVarDsc->TypeGet() == TYP_STRUCT) ? vnStore->VNForZeroObj(lclVarDsc->GetStructHnd())
+ : vnStore->VNZeroForType(lclVarDsc->TypeGet());
+ }
+ else
+ {
+ // Non-zero block init is very rare so we'll use a simple, unique VN here.
+ lclVarVN = vnStore->VNForExpr(compCurBB, lclVarDsc->TypeGet());
}
- ValueNum lclVarVN = (initBlkVN != ValueNumStore::NoVN)
- ? initBlkVN
- : vnStore->VNForExpr(compCurBB, var_types(lvaTable[lclNum].lvType));
- lvaTable[lclNum].GetPerSsaData(lclDefSsaNum)->m_vnPair.SetBoth(lclVarVN);
+ lclVarDsc->GetPerSsaData(lclDefSsaNum)->m_vnPair.SetBoth(lclVarVN);
#ifdef DEBUG
if (verbose)
{
- printf("N%03u ", tree->gtSeqNum);
+ printf("Tree ");
Compiler::printTreeID(tree);
- printf(" ");
- gtDispNodeName(tree);
- printf(" V%02u/%d => ", lclNum, lclDefSsaNum);
+ printf(" assigned VN to local var V%02u/%d: ", lclVarTree->GetLclNum(), lclDefSsaNum);
vnPrint(lclVarVN, 1);
printf("\n");
}
if (encodeResultType)
{
- ValueNum vnSize = vnStore->VNForIntCon(simdNode->GetSimdSize());
- ValueNum vnBaseType = vnStore->VNForIntCon(INT32(simdNode->GetSimdBaseType()));
- ValueNum simdTypeVN = vnStore->VNForFunc(TYP_REF, VNF_SimdType, vnSize, vnBaseType);
+ ValueNum simdTypeVN = vnStore->VNForSimdType(simdNode->GetSimdSize(), simdNode->GetSimdBaseType());
resvnp.SetBoth(simdTypeVN);
#ifdef DEBUG
if (encodeResultType)
{
- ValueNum vnSize = vnStore->VNForIntCon(hwIntrinsicNode->GetSimdSize());
- ValueNum vnBaseType = vnStore->VNForIntCon(INT32(hwIntrinsicNode->GetSimdBaseType()));
- ValueNum simdTypeVN = vnStore->VNForFunc(TYP_REF, VNF_SimdType, vnSize, vnBaseType);
+ ValueNum simdTypeVN =
+ vnStore->VNForSimdType(hwIntrinsicNode->GetSimdSize(), hwIntrinsicNode->GetSimdBaseType());
resvnp.SetBoth(simdTypeVN);
#ifdef DEBUG
return ValueNum(SRC_Null);
}
- // The zero map is the map that returns a zero "for the appropriate type" when indexed at any index.
- static ValueNum VNForZeroMap()
- {
- // We reserve Chunk 0 for "special" VNs. Let SRC_ZeroMap (== 1) be the zero map.
- return ValueNum(SRC_ZeroMap);
- }
-
// The ROH map is the map for the "read-only heap". We assume that this is never mutated, and always
// has the same value number.
static ValueNum VNForROH()
// It has an unreached() for a "typ" that has no zero value, such as TYP_VOID.
ValueNum VNZeroForType(var_types typ);
+ // Returns the value number for a zero-initialized struct.
+ ValueNum VNForZeroObj(CORINFO_CLASS_HANDLE structHnd);
+
// Returns the value number for one of the given "typ".
// It returns NoVN for a "typ" that has no one value, such as TYP_REF.
ValueNum VNOneForType(var_types typ);
+#ifdef FEATURE_SIMD
+ // A helper function for constructing VNF_SimdType VNs.
+ ValueNum VNForSimdType(unsigned simdSize, var_types simdBaseType);
+#endif // FEATURE_SIMD
+
// Create or return the existimg value number representing a singleton exception set
// for the the exception value "x".
ValueNum VNExcSetSingleton(ValueNum x);
// Prints the cast's representation mirroring GT_CAST's dump format.
void vnDumpCast(Compiler* comp, ValueNum castVN);
+ // Requires "zeroObj" to be a VNF_ZeroObj. Prints its representation.
+ void vnDumpZeroObj(Compiler* comp, VNFuncApp* zeroObj);
+
// Returns the string name of "vnf".
static const char* VNFuncName(VNFunc vnf);
// Used in the implementation of the above.
enum SpecialRefConsts
{
SRC_Null,
- SRC_ZeroMap,
SRC_ReadOnlyHeap,
SRC_Void,
SRC_EmptyExcSet,
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(ZeroMap, 0, false, false, false) // The "ZeroMap": indexing at any index yields "zero of the desired type".
ValueNumFuncDef(PtrToLoc, 2, false, false, 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(PhiDef, 3, false, false, false) // Args: 0: local var # (or -1 for memory), 1: SSA #, 2: VN of definition.
// Wouldn't need this if I'd made memory a regular local variable...
ValueNumFuncDef(PhiMemoryDef, 2, false, false, false) // Args: 0: VN for basic block pointer, 1: VN of definition
+ValueNumFuncDef(ZeroObj, 1, false, false, false) // Zero-initialized struct. Args: 0: VN of the class handle.
ValueNumFuncDef(InitVal, 1, false, false, false) // An input arg, or init val of a local Args: 0: a constant VN.