diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 93f67680284498..ce7751a387830b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -857,10 +857,8 @@ enum CorInfoFlag CORINFO_FLG_OVERLAPPING_FIELDS = 0x00100000, // struct or class has fields that overlap (aka union) CORINFO_FLG_INTERFACE = 0x00200000, // it is an interface CORINFO_FLG_DONT_DIG_FIELDS = 0x00400000, // don't ask field info (used for types outside of AOT compilation version bubble) - CORINFO_FLG_CUSTOMLAYOUT = 0x00800000, // does this struct have custom layout? CORINFO_FLG_CONTAINS_GC_PTR = 0x01000000, // does the class contain a gc ptr ? CORINFO_FLG_DELEGATE = 0x02000000, // is this a subclass of delegate or multicast delegate ? - CORINFO_FLG_INDEXABLE_FIELDS = 0x04000000, // struct fields may be accessed via indexing (used for inline arrays) CORINFO_FLG_BYREF_LIKE = 0x08000000, // it is byref-like value type CORINFO_FLG_VARIANCE = 0x10000000, // MethodTable::HasVariance (sealed does *not* mean uncast-able) CORINFO_FLG_BEFOREFIELDINIT = 0x20000000, // Additional flexibility for when to run .cctor (see code:#ClassConstructionFlags) @@ -1946,6 +1944,25 @@ struct CORINFO_VarArgInfo // (The CORINFO_VARARGS_HANDLE counts as an arg) }; +struct CORINFO_FLATTENED_TYPE_FIELD +{ + // Primitive type, or CORINFO_TYPE_VALUECLASS for value classes marked as intrinsic. + CorInfoType type; + // For an intrinsic value class this is the handle for it. + CORINFO_CLASS_HANDLE intrinsicValueClassHnd; + // Offset of the field into the root struct. + unsigned offset; + // Field handle (only used for diagnostic purposes) + CORINFO_FIELD_HANDLE fieldHandle; +}; + +enum class FlattenTypeResult +{ + Success, + Partial, + Failure, +}; + #define SIZEOF__CORINFO_Object TARGET_POINTER_SIZE /* methTable */ #define CORINFO_Array_MaxLength 0x7FFFFFC7 @@ -2448,6 +2465,29 @@ class ICorStaticInfo int32_t num ) = 0; + //------------------------------------------------------------------------------ + // flattenType: Flatten a type into its primitive/intrinsic constituent + // fields. + // + // Parameters: + // clsHnd - Handle of the type. + // fields - [out] Pointer to entries to write. + // numFields - [in, out] Size of 'fields'. Updated to contain + // the number of entries written in 'fields'. + // significantPadding - [out] Whether data not covered by fields should + // be considered as significant or whether the JIT + // is allowed to discard it on copies. + // + // Returns: + // A result indicating whether the type was successfully flattened and + // whether the result is partial or not. + // + virtual FlattenTypeResult flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) = 0; + virtual bool checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char * modifier, diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index cf4c94498c33a1..c8c4e2a7f0d6d8 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -263,6 +263,12 @@ CORINFO_FIELD_HANDLE getFieldInClass( CORINFO_CLASS_HANDLE clsHnd, int32_t num) override; +FlattenTypeResult flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) override; + bool checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char* modifier, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index d539e04c6b168d..bdc9c7be36cf0c 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 4e6355a0-3844-45e2-8cef-082c18217e14 */ - 0x4e6355a0, - 0x3844, - 0x45e2, - {0x8c, 0xef, 0x8, 0x2c, 0x18, 0x21, 0x7e, 0x14} +constexpr GUID JITEEVersionIdentifier = { /* 878d63a7-ffc9-421f-81f7-db4729f0ed5c */ + 0x878d63a7, + 0xffc9, + 0x421f, + {0x81, 0xf7, 0xdb, 0x47, 0x29, 0xf0, 0xed, 0x5c} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index cb5db87194525c..4467d9de986e18 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -64,6 +64,7 @@ DEF_CLR_API(getClassAlignmentRequirement) DEF_CLR_API(getClassGClayout) DEF_CLR_API(getClassNumInstanceFields) DEF_CLR_API(getFieldInClass) +DEF_CLR_API(flattenType) DEF_CLR_API(checkMethodModifier) DEF_CLR_API(getNewHelper) DEF_CLR_API(getNewArrHelper) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index b819fc12fbce13..a361d3cfa18140 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -599,6 +599,18 @@ CORINFO_FIELD_HANDLE WrapICorJitInfo::getFieldInClass( return temp; } +FlattenTypeResult WrapICorJitInfo::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + API_ENTER(flattenType); + FlattenTypeResult temp = wrapHnd->flattenType(clsHnd, fields, numFields, significantPadding); + API_LEAVE(flattenType); + return temp; +} + bool WrapICorJitInfo::checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char* modifier, diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 64ae8d35e6fdf4..05e4fb649a56f3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -593,8 +593,13 @@ class LclVarDsc // fgRetypeImplicitByRefArgs and fgMarkDemotedImplicitByRefArgs to indicate whether // references to the arg are being rewritten as references to a promoted shadow local. unsigned char lvIsStructField : 1; // Is this local var a field of a promoted struct local? - unsigned char lvContainsHoles : 1; // True when we have a promoted struct that contains holes - unsigned char lvCustomLayout : 1; // True when this struct has "CustomLayout" + unsigned char lvContainsHoles : 1; // Is this a promoted struct whose fields do not cover the struct local? + + // True for a promoted struct that has significant padding in it. + // Significant padding is any data in the struct that is not covered by a + // promoted field and that the EE told us we need to preserve on block + // copies/inits. + unsigned char lvAnySignificantPadding : 1; unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call @@ -3561,8 +3566,8 @@ class Compiler struct lvaStructFieldInfo { CORINFO_FIELD_HANDLE fldHnd; - unsigned char fldOffset; - unsigned char fldOrdinal; + uint8_t fldOffset; + uint8_t fldOrdinal; var_types fldType; unsigned fldSize; CORINFO_CLASS_HANDLE fldTypeHnd; @@ -3579,8 +3584,7 @@ class Compiler CORINFO_CLASS_HANDLE typeHnd; bool canPromote; bool containsHoles; - bool customLayout; - bool fieldsSorted; + bool anySignificantPadding; unsigned char fieldCnt; lvaStructFieldInfo fields[MAX_NumOfFieldsInPromotableStruct]; @@ -3588,18 +3592,12 @@ class Compiler : typeHnd(typeHnd) , canPromote(false) , containsHoles(false) - , customLayout(false) - , fieldsSorted(false) + , anySignificantPadding(false) , fieldCnt(0) { } }; - struct lvaFieldOffsetCmp - { - bool operator()(const lvaStructFieldInfo& field1, const lvaStructFieldInfo& field2); - }; - // This class is responsible for checking validity and profitability of struct promotion. // If it is both legal and profitable, then TryPromoteStructVar promotes the struct and initializes // necessary information for fgMorphStructField to use. @@ -3619,12 +3617,7 @@ class Compiler bool CanPromoteStructVar(unsigned lclNum); bool ShouldPromoteStructVar(unsigned lclNum); void PromoteStructVar(unsigned lclNum); - void SortStructFields(); - - bool CanConstructAndPromoteField(lvaStructPromotionInfo* structPromotionInfo); - - lvaStructFieldInfo GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal); - bool TryPromoteStructField(lvaStructFieldInfo& outerFieldInfo); + var_types TryPromoteIntrinsicTypeAsPrimitive(CORINFO_CLASS_HANDLE clsHnd); private: Compiler* compiler; @@ -8610,6 +8603,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX return pTypeInfo->IsStruct() && isSIMDClass(pTypeInfo->GetClassHandleForValueClass()); } + bool isRuntimeIntrinsicsNamespace(const char* ns) + { + return strcmp(ns, "System.Runtime.Intrinsics") == 0; + } + bool isHWSIMDClass(CORINFO_CLASS_HANDLE clsHnd) { #ifdef FEATURE_HW_INTRINSICS @@ -8617,7 +8615,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX { const char* namespaceName = nullptr; (void)getClassNameFromMetadata(clsHnd, &namespaceName); - return strcmp(namespaceName, "System.Runtime.Intrinsics") == 0; + return isRuntimeIntrinsicsNamespace(namespaceName); } #endif // FEATURE_HW_INTRINSICS return false; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index e35d1d0cd666df..f499d370887e55 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4276,21 +4276,11 @@ inline static bool StructHasOverlappingFields(DWORD attribs) return ((attribs & CORINFO_FLG_OVERLAPPING_FIELDS) != 0); } -inline static bool StructHasCustomLayout(DWORD attribs) -{ - return ((attribs & CORINFO_FLG_CUSTOMLAYOUT) != 0); -} - inline static bool StructHasDontDigFieldsFlagSet(DWORD attribs) { return ((attribs & CORINFO_FLG_DONT_DIG_FIELDS) != 0); } -inline static bool StructHasIndexableFields(DWORD attribs) -{ - return ((attribs & CORINFO_FLG_INDEXABLE_FIELDS) != 0); -} - //------------------------------------------------------------------------------ // DEBUG_DESTROY_NODE: sets value of tree to garbage to catch extra references // diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp index f1865202e84add..82371f1e783b73 100644 --- a/src/coreclr/jit/forwardsub.cpp +++ b/src/coreclr/jit/forwardsub.cpp @@ -584,7 +584,8 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) bool found = false; for (GenTreeLclVarCommon* lcl : nextStmt->LocalsTreeList()) { - if (lcl->OperIs(GT_LCL_VAR) && (lcl->GetLclNum() == lclNum)) + bool isUse = ((lcl->gtFlags) & GTF_VAR_DEF) == 0; + if (lcl->OperIs(GT_LCL_VAR) && (lcl->GetLclNum() == lclNum) && isUse) { if (fsv.IsLastUse(lcl->AsLclVar())) { diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index c3f65a44bd74a1..9dfc3926f75458 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -11961,28 +11961,13 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) { for (unsigned index = 0; index < varDsc->lvFieldCnt; index++) { - unsigned fieldLclNum = varDsc->lvFieldLclStart + index; - LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum); - const char* fieldName; -#if !defined(TARGET_64BIT) - if (varTypeIsLong(varDsc)) - { - fieldName = (index == 0) ? "lo" : "hi"; - } - else -#endif // !defined(TARGET_64BIT) - { - CORINFO_CLASS_HANDLE typeHnd = varDsc->GetLayout()->GetClassHandle(); - CORINFO_FIELD_HANDLE fldHnd = - info.compCompHnd->getFieldInClass(typeHnd, fieldVarDsc->lvFldOrdinal); - fieldName = eeGetFieldName(fldHnd, true, buffer, sizeof(buffer)); - } + unsigned fieldLclNum = varDsc->lvFieldLclStart + index; + LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum); printf("\n"); printf(" "); printIndent(indentStack); - printf(" %-6s V%02u.%s (offs=0x%02x) -> ", varTypeName(fieldVarDsc->TypeGet()), - tree->AsLclVarCommon()->GetLclNum(), fieldName, fieldVarDsc->lvFldOffset); + printf(" %-6s %s -> ", varTypeName(fieldVarDsc->TypeGet()), fieldVarDsc->lvReason); gtDispLclVar(fieldLclNum); gtDispSsaName(fieldLclNum, tree->AsLclVarCommon()->GetSsaNum(this, index), isDef); diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 1284185219dd02..bd20b4347b8e50 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -290,6 +290,13 @@ class LocalAddressVisitor final : public GenTreeVisitor return m_offset; } + void ChangeLocal(unsigned newLclNum, unsigned newOffset) + { + assert(IsLocation() || IsAddress()); + m_lclNum = newLclNum; + m_offset = newOffset; + } + //------------------------------------------------------------------------ // Location: Produce a location value. // @@ -957,7 +964,7 @@ class LocalAddressVisitor final : public GenTreeVisitor unsigned lclNum = val.LclNum(); LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); - unsigned indirSize = node->AsIndir()->Size(); + unsigned indirSize = GetIndirSize(node); bool isWide; if ((indirSize == 0) || ((val.Offset() + indirSize) > UINT16_MAX)) @@ -990,6 +997,14 @@ class LocalAddressVisitor final : public GenTreeVisitor } } + bool updateRefCount = false; + if (isWide && varDsc->lvIsStructField && RephraseFieldAccess(val, varDsc, indirSize)) + { + ClrSafeInt endOffset = ClrSafeInt(val.Offset()) + ClrSafeInt(indirSize); + isWide = endOffset.IsOverflow() || (endOffset.Value() > m_compiler->lvaLclExactSize(val.LclNum())); + updateRefCount = true; + } + if (isWide) { m_compiler->lvaSetVarAddrExposed(varDsc->lvIsStructField @@ -1001,12 +1016,101 @@ class LocalAddressVisitor final : public GenTreeVisitor { MorphLocalIndir(val, user); } + + if (updateRefCount) + { + UpdateEarlyRefCount(val.LclNum(), node, user); + } } INDEBUG(val.Consume();) } //------------------------------------------------------------------------ + // RephraseFieldAccess: Try to rephrase an access of a promoted field as an + // access of a sibling field or of the parent struct. + // + // Arguments: + // val - The value + // varDsc - Field local variable descriptor + // indirSize - Size of indirection being read + // + // Returns: + // True if the field access was rephrased. + // + bool RephraseFieldAccess(Value& val, LclVarDsc* varDsc, unsigned indirSize) + { + // See if we can rephrase an access of a field in terms of a sibling + // field or the parent with better chance of profitable codegen. + unsigned parentLclNum = varDsc->lvParentLcl; + unsigned int siblingFieldLcl; + unsigned newOffset = 0; + + if (val.Offset() > 0) + { + ClrSafeInt newOffsetSafe = + ClrSafeInt(val.Offset()) + ClrSafeInt(varDsc->lvFldOffset); + if (newOffsetSafe.IsOverflow()) + { + return false; + } + + newOffset = newOffsetSafe.Value(); + LclVarDsc* parentVarDsc = m_compiler->lvaGetDesc(parentLclNum); + siblingFieldLcl = m_compiler->lvaGetFieldLocal(parentVarDsc, newOffset); + + if (siblingFieldLcl != BAD_VAR_NUM) + { + LclVarDsc* siblingVarDsc = m_compiler->lvaGetDesc(siblingFieldLcl); + if (indirSize == genTypeSize(siblingVarDsc)) + { + JITDUMP("Rephrasing access of V%02u [+%u] (%s) to sibling V%02u (%s)\n", val.LclNum(), val.Offset(), + varDsc->lvReason, siblingFieldLcl, siblingVarDsc->lvReason); + val.ChangeLocal(siblingFieldLcl, 0); + return true; + } + } + } + else + { + newOffset = varDsc->lvFldOffset; + } + + JITDUMP("Rephrasing access of V%02u [+%u] (%s) to parent V%02u [+%u]\n", val.LclNum(), val.Offset(), + varDsc->lvReason, parentLclNum, newOffset); + val.ChangeLocal(parentLclNum, newOffset); + return true; + } + + //------------------------------------------------------------------------ + // GetIndirSize: Return the size (in bytes) of an indirection node. + // + // Arguments: + // indir - the indirection node + // user - the node that uses the indirection + // + // Notes: + // This returns 0 for indirection of unknown size, i. e. IND + // nodes that are used as sources of STORE_DYN_BLKs. + // + unsigned GetIndirSize(GenTree* indir) + { + assert(indir->OperIs(GT_IND, GT_BLK)); + + if (indir->TypeGet() != TYP_STRUCT) + { + return genTypeSize(indir); + } + + if (indir->OperIs(GT_IND)) + { + // TODO-1stClassStructs: remove once "IND" nodes are no more. + return 0; + } + + return indir->GetLayout(m_compiler)->GetSize(); + } + // MorphLocalAddress: Change a tree that represents a local variable address // to a single LCL_VAR_ADDR or LCL_FLD_ADDR node. // @@ -1057,7 +1161,18 @@ class LocalAddressVisitor final : public GenTreeVisitor GenTree* node = val.Node(); GenTree* addr = node->AsIndir()->Addr(); - MorphLocalAddress(addr, val.LclNum(), val.Offset()); + // Rebase the local address off of the parent local if possible. This + // improves our chances of producing LCL_ADDR since IsValidLclAddr is + // true for more cases. + LclVarDsc* lclDsc = m_compiler->lvaGetDesc(val.LclNum()); + if (lclDsc->lvIsStructField) + { + MorphLocalAddress(addr, lclDsc->lvParentLcl, lclDsc->lvFldOffset + val.Offset()); + } + else + { + MorphLocalAddress(addr, val.LclNum(), val.Offset()); + } // GLOB_REF may not be set already in the "large offset" case. Add it. node->gtFlags |= GTF_GLOB_REF; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 0b9e98dbbb411f..e366c318a11fd4 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1640,22 +1640,6 @@ void Compiler::lvSetMinOptsDoNotEnreg() } } -//-------------------------------------------------------------------------------------------- -// lvaFieldOffsetCmp - a static compare function passed to jitstd::sort() by Compiler::StructPromotionHelper; -// compares fields' offsets. -// -// Arguments: -// field1 - pointer to the first field; -// field2 - pointer to the second field. -// -// Return value: -// 0 if the fields' offsets are equal, 1 if the first field has bigger offset, -1 otherwise. -// -bool Compiler::lvaFieldOffsetCmp::operator()(const lvaStructFieldInfo& field1, const lvaStructFieldInfo& field2) -{ - return field1.fldOffset < field2.fldOffset; -} - //------------------------------------------------------------------------ // StructPromotionHelper constructor. // @@ -1745,8 +1729,6 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE // lvaStructFieldInfo.fieldCnt is byte-sized assert(static_cast(MAX_NumOfFieldsInPromotableStruct) == MAX_NumOfFieldsInPromotableStruct); - bool containsGCpointers = false; - COMP_HANDLE compHandle = compiler->info.compCompHnd; unsigned structSize = compHandle->getClassSize(typeHnd); @@ -1755,28 +1737,9 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE return false; // struct is too large } - unsigned fieldCnt = compHandle->getClassNumInstanceFields(typeHnd); - if (fieldCnt == 0 || fieldCnt > MAX_NumOfFieldsInPromotableStruct) - { - return false; // struct must have between 1 and MAX_NumOfFieldsInPromotableStruct fields - } - - structPromotionInfo.fieldCnt = (unsigned char)fieldCnt; - DWORD typeFlags = compHandle->getClassAttribs(typeHnd); - - bool overlappingFields = StructHasOverlappingFields(typeFlags); - if (overlappingFields) - { - return false; - } - - if (StructHasIndexableFields(typeFlags)) - { - return false; - } + DWORD typeFlags = compHandle->getClassAttribs(typeHnd); - // Don't struct promote if we have an CUSTOMLAYOUT flag on an HFA type - if (StructHasCustomLayout(typeFlags) && compiler->IsHfa(typeHnd)) + if (StructHasOverlappingFields(typeFlags)) { return false; } @@ -1786,65 +1749,56 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE unsigned structAlignment = roundUp(compHandle->getClassAlignmentRequirement(typeHnd), TARGET_POINTER_SIZE); #endif // TARGET_ARM - // If we have "Custom Layout" then we might have an explicit Size attribute - // Managed C++ uses this for its structs, such C++ types will not contain GC pointers. - // - // The current VM implementation also incorrectly sets the CORINFO_FLG_CUSTOMLAYOUT - // whenever a managed value class contains any GC pointers. - // (See the comment for VMFLAG_NOT_TIGHTLY_PACKED in class.h) - // - // It is important to struct promote managed value classes that have GC pointers - // So we compute the correct value for "CustomLayout" here - // - if (StructHasCustomLayout(typeFlags) && ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0)) - { - structPromotionInfo.customLayout = true; - } + CORINFO_FLATTENED_TYPE_FIELD fields[MAX_NumOfFieldsInPromotableStruct]; + size_t numFields = ArrLen(fields); + bool significantPadding; + FlattenTypeResult result = compHandle->flattenType(typeHnd, fields, &numFields, &significantPadding); - if (StructHasDontDigFieldsFlagSet(typeFlags)) + if ((result != FlattenTypeResult::Success) || (numFields == 0)) { - return CanConstructAndPromoteField(&structPromotionInfo); + return false; } + structPromotionInfo.fieldCnt = (unsigned char)numFields; + unsigned fieldsSize = 0; - for (BYTE ordinal = 0; ordinal < fieldCnt; ++ordinal) + for (size_t i = 0; i < numFields; i++) { - CORINFO_FIELD_HANDLE fieldHnd = compHandle->getFieldInClass(typeHnd, ordinal); - structPromotionInfo.fields[ordinal] = GetFieldInfo(fieldHnd, ordinal); - const lvaStructFieldInfo& fieldInfo = structPromotionInfo.fields[ordinal]; - - noway_assert(fieldInfo.fldOffset < structSize); + const CORINFO_FLATTENED_TYPE_FIELD& field = fields[i]; + lvaStructFieldInfo& promField = structPromotionInfo.fields[i]; + promField.fldHnd = field.fieldHandle; + // Should be ensured by limit on struct size we have. + noway_assert(field.offset <= BYTE_MAX); + promField.fldOffset = static_cast(field.offset); + promField.fldOrdinal = static_cast(i); + promField.fldTypeHnd = NO_CLASS_HANDLE; - if (fieldInfo.fldSize == 0) + if (field.type == CORINFO_TYPE_VALUECLASS) { - // Not a scalar type. - return false; - } + var_types fldType = TryPromoteIntrinsicTypeAsPrimitive(field.intrinsicValueClassHnd); + if (fldType == TYP_UNDEF) + { + return false; + } - if ((fieldInfo.fldOffset % fieldInfo.fldSize) != 0) - { - // The code in Compiler::genPushArgList that reconstitutes - // struct values on the stack from promoted fields expects - // those fields to be at their natural alignment. - return false; + promField.fldType = fldType; + promField.fldTypeHnd = field.intrinsicValueClassHnd; } - - if (varTypeIsGC(fieldInfo.fldType)) + else { - containsGCpointers = true; + promField.fldType = JITtype2varType(field.type); } - // The end offset for this field should never be larger than our structSize. - noway_assert(fieldInfo.fldOffset + fieldInfo.fldSize <= structSize); + promField.fldSize = genTypeSize(promField.fldType); - fieldsSize += fieldInfo.fldSize; + noway_assert(promField.fldOffset + promField.fldSize <= structSize); #ifdef TARGET_ARM // On ARM, for struct types that don't use explicit layout, the alignment of the struct is // at least the max alignment of its fields. We take advantage of this invariant in struct promotion, // so verify it here. - if (fieldInfo.fldSize > structAlignment) + if (promField.fldSize > structAlignment) { // Don't promote vars whose struct types violates the invariant. (Alignment == size for primitives.) return false; @@ -1852,20 +1806,39 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE #endif // TARGET_ARM } - // If we saw any GC pointer or by-ref fields above then CORINFO_FLG_CONTAINS_GC_PTR or - // CORINFO_FLG_BYREF_LIKE has to be set! - noway_assert((containsGCpointers == false) || - ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0)); + // Now check for overlap and holes. Note that the fast flag check above + // will not discard potentially overlapping fields in recursively promoted + // child structs. + jitstd::sort(structPromotionInfo.fields, structPromotionInfo.fields + structPromotionInfo.fieldCnt, + [](const lvaStructFieldInfo& lhs, const lvaStructFieldInfo& rhs) { + return lhs.fldOffset < rhs.fldOffset; + }); - // Check if this promoted struct contains any holes. - assert(!overlappingFields); - if (fieldsSize != structSize) + unsigned lastEnd = 0; + for (int i = 0; i < structPromotionInfo.fieldCnt; i++) + { + lvaStructFieldInfo& fieldInf = structPromotionInfo.fields[i]; + if (fieldInf.fldOffset < lastEnd) + { + // Overlapping fields + return false; + } + + if (fieldInf.fldOffset > lastEnd) + { + structPromotionInfo.containsHoles = true; + } + + lastEnd = fieldInf.fldOffset + fieldInf.fldSize; + } + + if (lastEnd < structSize) { - // If sizes do not match it means we have an overlapping fields or holes. - // Overlapping fields were rejected early, so here it can mean only holes. structPromotionInfo.containsHoles = true; } + structPromotionInfo.anySignificantPadding = significantPadding && structPromotionInfo.containsHoles; + // Cool, this struct is promotable. structPromotionInfo.canPromote = true; @@ -1873,59 +1846,60 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE } //-------------------------------------------------------------------------------------------- -// CanConstructAndPromoteField - checks if we can construct field types without asking about them directly. +// TryPromoteIntrinsicTypeAsPrimitive - Attempt to promote an intrinsic type as a primitive type. // // Arguments: -// structPromotionInfo - struct promotion candidate information. +// typeHnd - struct handle to check. // // Return value: -// true if we can figure out the fields from available knowledge. +// true if the struct type can be promoted. // // Notes: -// This is needed for AOT R2R compilation when we can't cross compilation bubble borders -// so we should not ask about fields that are not directly referenced. If we do VM will have -// to emit a type check for this field type but it does not have enough information about it. -// As a workaround for performance critical corner case: struct with 1 gcref, we try to construct -// the field information from indirect observations. +// The last analyzed type is memorized to skip the check if we ask about the same time again next. +// However, it was not found profitable to memorize all analyzed types in a map. +// +// The check initializes only necessary fields in lvaStructPromotionInfo, +// so if the promotion is rejected early than most fields will be uninitialized. // -bool Compiler::StructPromotionHelper::CanConstructAndPromoteField(lvaStructPromotionInfo* structPromotionInfo) +var_types Compiler::StructPromotionHelper::TryPromoteIntrinsicTypeAsPrimitive(CORINFO_CLASS_HANDLE typeHnd) { - const CORINFO_CLASS_HANDLE typeHnd = structPromotionInfo->typeHnd; - const COMP_HANDLE compHandle = compiler->info.compCompHnd; - const DWORD typeFlags = compHandle->getClassAttribs(typeHnd); - if (structPromotionInfo->fieldCnt != 1) +#ifdef FEATURE_SIMD + if (compiler->usesSIMDTypes() && compiler->isSIMDorHWSIMDClass(typeHnd)) { - // Can't find out values for several fields. - return false; + unsigned simdSize; + CorInfoType simdBaseJitType = compiler->getBaseJitTypeAndSizeOfSIMDType(typeHnd, &simdSize); + // We will only promote fields of SIMD types that fit into a SIMD register. + if (simdBaseJitType != CORINFO_TYPE_UNDEF) + { + if ((simdSize >= compiler->minSIMDStructBytes()) && (simdSize <= compiler->maxSIMDStructBytes())) + { + return compiler->getSIMDTypeForSize(simdSize); + } + } } - if ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0) +#endif + + const char* namespaceName; + const char* className = compiler->info.compCompHnd->getClassNameFromMetadata(typeHnd, &namespaceName); + if ((strcmp(namespaceName, "System.Runtime.InteropServices") == 0) && (strcmp(className, "NFloat") == 0)) { - // Can't find out type of a non-gc field. - return false; + return (TARGET_POINTER_SIZE == 4) ? TYP_FLOAT : TYP_DOUBLE; } - const unsigned structSize = compHandle->getClassSize(typeHnd); - if (structSize != TARGET_POINTER_SIZE) +#ifdef TARGET_64BIT + // TODO-Quirk: Vector64 is an intrinsic type with one 64-bit field, so when + // compiler->usesSIMDTypes() == false, it used to be promoted as a long + // field. Retain this behavior for now. + if (compiler->isRuntimeIntrinsicsNamespace(namespaceName) && (strcmp(className, "Vector64`1") == 0)) { - return false; + return TYP_LONG; } +#endif - assert(!structPromotionInfo->containsHoles); - assert(!structPromotionInfo->customLayout); - lvaStructFieldInfo& fldInfo = structPromotionInfo->fields[0]; - - fldInfo.fldHnd = compHandle->getFieldInClass(typeHnd, 0); - - // We should not read it anymore. - fldInfo.fldTypeHnd = 0; - - fldInfo.fldOffset = 0; - fldInfo.fldOrdinal = 0; - fldInfo.fldSize = TARGET_POINTER_SIZE; - fldInfo.fldType = TYP_REF; - - structPromotionInfo->canPromote = true; - return true; + // TODO-CQ: This discards fields of certain struct types (like Int128) that + // could be recursively handled via flattenType (or we could special case + // it; it is an intrinsic type after all). + return TYP_UNDEF; } //-------------------------------------------------------------------------------------------- @@ -2033,7 +2007,6 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) #elif defined(UNIX_AMD64_ABI) else { - SortStructFields(); // Only promote if the field types match the registers, unless we have a single SIMD field. SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc); @@ -2114,9 +2087,9 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) structPromotionInfo.fieldCnt, varDsc->lvFieldAccessed); shouldPromote = false; } - else if (varDsc->lvIsMultiRegRet && structPromotionInfo.containsHoles && structPromotionInfo.customLayout) + else if (varDsc->lvIsMultiRegRet && structPromotionInfo.anySignificantPadding) { - JITDUMP("Not promoting multi-reg returned struct local V%02u with holes.\n", lclNum); + JITDUMP("Not promoting multi-reg returned struct local V%02u with significant padding.\n", lclNum); shouldPromote = false; } #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -2137,9 +2110,9 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) // multiple registers? if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs)) { - if (structPromotionInfo.containsHoles && structPromotionInfo.customLayout) + if (structPromotionInfo.anySignificantPadding) { - JITDUMP("Not promoting multi-reg struct local V%02u with holes.\n", lclNum); + JITDUMP("Not promoting multi-reg struct local V%02u with significant padding.\n", lclNum); shouldPromote = false; } else if ((structPromotionInfo.fieldCnt != 2) && @@ -2192,153 +2165,6 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) return shouldPromote; } -//-------------------------------------------------------------------------------------------- -// SortStructFields - sort the fields according to the increasing order of the field offset. -// -// Notes: -// This is needed because the fields need to be pushed on stack (when referenced as a struct) in offset order. -// -void Compiler::StructPromotionHelper::SortStructFields() -{ - if (!structPromotionInfo.fieldsSorted) - { - jitstd::sort(structPromotionInfo.fields, structPromotionInfo.fields + structPromotionInfo.fieldCnt, - lvaFieldOffsetCmp()); - structPromotionInfo.fieldsSorted = true; - } -} - -//-------------------------------------------------------------------------------------------- -// GetFieldInfo - get struct field information. -// Arguments: -// fieldHnd - field handle to get info for; -// ordinal - field ordinal. -// -// Return value: -// field information. -// -Compiler::lvaStructFieldInfo Compiler::StructPromotionHelper::GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal) -{ - lvaStructFieldInfo fieldInfo; - fieldInfo.fldHnd = fieldHnd; - - unsigned fldOffset = compiler->info.compCompHnd->getFieldOffset(fieldInfo.fldHnd); - fieldInfo.fldOffset = (BYTE)fldOffset; - - fieldInfo.fldOrdinal = ordinal; - CorInfoType corType = compiler->info.compCompHnd->getFieldType(fieldInfo.fldHnd, &fieldInfo.fldTypeHnd); - fieldInfo.fldType = JITtype2varType(corType); - fieldInfo.fldSize = genTypeSize(fieldInfo.fldType); - -#ifdef FEATURE_SIMD - // Check to see if this is a SIMD type. - // We will only check this if we have already found a SIMD type, which will be true if - // we have encountered any SIMD intrinsics. - if (compiler->usesSIMDTypes() && (fieldInfo.fldSize == 0) && compiler->isSIMDorHWSIMDClass(fieldInfo.fldTypeHnd)) - { - unsigned simdSize; - CorInfoType simdBaseJitType = compiler->getBaseJitTypeAndSizeOfSIMDType(fieldInfo.fldTypeHnd, &simdSize); - // We will only promote fields of SIMD types that fit into a SIMD register. - if (simdBaseJitType != CORINFO_TYPE_UNDEF) - { - if ((simdSize >= compiler->minSIMDStructBytes()) && (simdSize <= compiler->maxSIMDStructBytes())) - { - fieldInfo.fldType = compiler->getSIMDTypeForSize(simdSize); - fieldInfo.fldSize = simdSize; - } - } - } -#endif // FEATURE_SIMD - - if (fieldInfo.fldSize == 0) - { - TryPromoteStructField(fieldInfo); - } - - return fieldInfo; -} - -//-------------------------------------------------------------------------------------------- -// TryPromoteStructField - checks that this struct's field is a struct that can be promoted as scalar type -// aligned at its natural boundary. Promotes the field as a scalar if the check succeeded. -// -// Arguments: -// fieldInfo - information about the field in the outer struct. -// -// Return value: -// true if the internal struct was promoted. -// -bool Compiler::StructPromotionHelper::TryPromoteStructField(lvaStructFieldInfo& fieldInfo) -{ - if (fieldInfo.fldType != TYP_STRUCT) - { - return false; - } - - COMP_HANDLE compHandle = compiler->info.compCompHnd; - - // Do not promote if the struct field in turn has more than one field. - if (compHandle->getClassNumInstanceFields(fieldInfo.fldTypeHnd) != 1) - { - return false; - } - - // Do not promote if the single field is not aligned at its natural boundary within - // the struct field. - CORINFO_FIELD_HANDLE innerFieldHndl = compHandle->getFieldInClass(fieldInfo.fldTypeHnd, 0); - unsigned innerFieldOffset = compHandle->getFieldOffset(innerFieldHndl); - if (innerFieldOffset != 0) - { - return false; - } - - CorInfoType fieldCorType = compHandle->getFieldType(innerFieldHndl); - var_types fieldVarType = JITtype2varType(fieldCorType); - unsigned fieldSize = genTypeSize(fieldVarType); - - // Do not promote if the field is not a primitive type or is not properly aligned. - // - // TODO-CQ: Right now we only promote an actual SIMD typed field, which would cause - // a nested SIMD type to fail promotion. - if (fieldSize == 0 || fieldSize > TARGET_POINTER_SIZE) - { - JITDUMP("Promotion blocked: struct contains struct field with one field," - " but that field has invalid size.\n"); - return false; - } - - if (fieldSize != TARGET_POINTER_SIZE) - { - unsigned outerFieldOffset = compHandle->getFieldOffset(fieldInfo.fldHnd); - - if ((outerFieldOffset % fieldSize) != 0) - { - JITDUMP("Promotion blocked: struct contains struct field with one field," - " but the outer struct offset %u is not a multiple of the inner field size %u.\n", - outerFieldOffset, fieldSize); - return false; - } - } - - // Insist this wrapped field occupy all of its parent storage. - unsigned innerStructSize = compHandle->getClassSize(fieldInfo.fldTypeHnd); - - if (fieldSize != innerStructSize) - { - JITDUMP("Promotion blocked: struct contains struct field with one field," - " but that field is not the same size as its parent.\n"); - return false; - } - - // Retype the field as the type of the single field of the struct. - // This is a hack that allows us to promote such fields before we support recursive struct promotion - // (tracked by #10019). - fieldInfo.fldType = fieldVarType; - fieldInfo.fldSize = fieldSize; - - return true; -} - //-------------------------------------------------------------------------------------------- // PromoteStructVar - promote struct variable. // @@ -2355,11 +2181,11 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) assert(varDsc->GetLayout()->GetClassHandle() == structPromotionInfo.typeHnd); assert(structPromotionInfo.canPromote); - varDsc->lvFieldCnt = structPromotionInfo.fieldCnt; - varDsc->lvFieldLclStart = compiler->lvaCount; - varDsc->lvPromoted = true; - varDsc->lvContainsHoles = structPromotionInfo.containsHoles; - varDsc->lvCustomLayout = structPromotionInfo.customLayout; + varDsc->lvFieldCnt = structPromotionInfo.fieldCnt; + varDsc->lvFieldLclStart = compiler->lvaCount; + varDsc->lvPromoted = true; + varDsc->lvContainsHoles = structPromotionInfo.containsHoles; + varDsc->lvAnySignificantPadding = structPromotionInfo.anySignificantPadding; #ifdef DEBUG // Don't stress this in LCL_FLD stress. @@ -2374,8 +2200,6 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) } #endif - SortStructFields(); - for (unsigned index = 0; index < structPromotionInfo.fieldCnt; ++index) { const lvaStructFieldInfo* pFieldInfo = &structPromotionInfo.fields[index]; @@ -2392,10 +2216,10 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) // Now grab the temp for the field local. #ifdef DEBUG + char buf[200]; char fieldNameBuffer[128]; const char* fieldName = compiler->eeGetFieldName(pFieldInfo->fldHnd, false, fieldNameBuffer, sizeof(fieldNameBuffer)); - char buf[200]; sprintf_s(buf, sizeof(buf), "field V%02u.%s (fldOffset=0x%x)", lclNum, fieldName, pFieldInfo->fldOffset); // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument. @@ -7695,46 +7519,30 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r printf(" unsafe-buffer"); } } - if (varDsc->lvIsStructField) - { - LclVarDsc* parentvarDsc = lvaGetDesc(varDsc->lvParentLcl); -#if !defined(TARGET_64BIT) - if (varTypeIsLong(parentvarDsc)) - { - bool isLo = (lclNum == parentvarDsc->lvFieldLclStart); - printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, isLo ? "lo" : "hi", isLo ? 0 : genTypeSize(TYP_INT)); - } - else -#endif // !defined(TARGET_64BIT) - { - CORINFO_CLASS_HANDLE typeHnd = parentvarDsc->GetLayout()->GetClassHandle(); - CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(typeHnd, varDsc->lvFldOrdinal); - - char buffer[128]; - printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, eeGetFieldName(fldHnd, false, buffer, sizeof(buffer)), - varDsc->lvFldOffset); - - lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc); - switch (promotionType) - { - case PROMOTION_TYPE_NONE: - printf(" P-NONE"); - break; - case PROMOTION_TYPE_DEPENDENT: - printf(" P-DEP"); - break; - case PROMOTION_TYPE_INDEPENDENT: - printf(" P-INDEP"); - break; - } - } - } if (varDsc->lvReason != nullptr) { printf(" \"%s\"", varDsc->lvReason); } + if (varDsc->lvIsStructField) + { + LclVarDsc* parentVarDsc = lvaGetDesc(varDsc->lvParentLcl); + lvaPromotionType promotionType = lvaGetPromotionType(parentVarDsc); + switch (promotionType) + { + case PROMOTION_TYPE_NONE: + printf(" P-NONE"); + break; + case PROMOTION_TYPE_DEPENDENT: + printf(" P-DEP"); + break; + case PROMOTION_TYPE_INDEPENDENT: + printf(" P-INDEP"); + break; + } + } + printf("\n"); } diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 03c5b3c9168cfb..7a9d321fc9b080 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1726,7 +1726,7 @@ bool Compiler::fgComputeLifeUntrackedLocal(VARSET_TP& life, { // Do not consider this store dead if the parent local variable is an address exposed local or // if the struct has a custom layout and holes. - return !(varDsc.IsAddressExposed() || (varDsc.lvCustomLayout && varDsc.lvContainsHoles)); + return !varDsc.IsAddressExposed() && !varDsc.lvAnySignificantPadding; } return false; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index a583c739816904..40795ce2fb2ec1 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14901,12 +14901,12 @@ PhaseStatus Compiler::fgRetypeImplicitByRefArgs() } // Copy the struct promotion annotations to the new temp. - LclVarDsc* newVarDsc = lvaGetDesc(newLclNum); - newVarDsc->lvPromoted = true; - newVarDsc->lvFieldLclStart = varDsc->lvFieldLclStart; - newVarDsc->lvFieldCnt = varDsc->lvFieldCnt; - newVarDsc->lvContainsHoles = varDsc->lvContainsHoles; - newVarDsc->lvCustomLayout = varDsc->lvCustomLayout; + LclVarDsc* newVarDsc = lvaGetDesc(newLclNum); + newVarDsc->lvPromoted = true; + newVarDsc->lvFieldLclStart = varDsc->lvFieldLclStart; + newVarDsc->lvFieldCnt = varDsc->lvFieldCnt; + newVarDsc->lvContainsHoles = varDsc->lvContainsHoles; + newVarDsc->lvAnySignificantPadding = varDsc->lvAnySignificantPadding; #ifdef DEBUG newVarDsc->lvKeepType = true; #endif // DEBUG diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index 31e6d49fd8bc49..9c391d2de918b7 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -383,10 +383,9 @@ void MorphInitBlockHelper::TryInitFieldByField() return; } - if (destLclVar->lvCustomLayout && destLclVar->lvContainsHoles) + if (destLclVar->lvAnySignificantPadding) { - // TODO-1stClassStructs: there are no reasons for this pessimization, delete it. - JITDUMP(" dest has custom layout and contains holes.\n"); + JITDUMP(" dest has significant padding.\n"); return; } @@ -874,17 +873,17 @@ void MorphCopyBlockHelper::MorphStructCases() } // Can we use field by field assignment for the dest? - if (m_dstDoFldAsg && m_dstVarDsc->lvCustomLayout && m_dstVarDsc->lvContainsHoles) + if (m_dstDoFldAsg && m_dstVarDsc->lvAnySignificantPadding) { - JITDUMP(" dest contains custom layout and contains holes"); + JITDUMP(" dest has significant padding"); // C++ style CopyBlock with holes requiresCopyBlock = true; } // Can we use field by field assignment for the src? - if (m_srcDoFldAsg && m_srcVarDsc->lvCustomLayout && m_srcVarDsc->lvContainsHoles) + if (m_srcDoFldAsg && m_srcVarDsc->lvAnySignificantPadding) { - JITDUMP(" src contains custom layout and contains holes"); + JITDUMP(" src has significant padding"); // C++ style CopyBlock with holes requiresCopyBlock = true; } diff --git a/src/coreclr/jit/ssabuilder.cpp b/src/coreclr/jit/ssabuilder.cpp index 55c86e9bbfe827..16dfdc6dafb99b 100644 --- a/src/coreclr/jit/ssabuilder.cpp +++ b/src/coreclr/jit/ssabuilder.cpp @@ -1411,6 +1411,7 @@ void SsaBuilder::RenameVariables() assert(varDsc->lvTracked); if (varDsc->lvIsParam || m_pCompiler->info.compInitMem || varDsc->lvMustInit || + !m_pCompiler->fgVarNeedsExplicitZeroInit(lclNum, false, false) || VarSetOps::IsMember(m_pCompiler, m_pCompiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex)) { unsigned ssaNum = varDsc->lvPerSsaData.AllocSsaNum(m_allocator); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 6d3806f2987682..9cfa5f35e55436 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2004,15 +2004,8 @@ private uint getClassAttribsInternal(TypeDesc type) if (metadataType.IsByRefLike) result |= CorInfoFlag.CORINFO_FLG_BYREF_LIKE; - // The CLR has more complicated rules around CUSTOMLAYOUT, but this will do. - if (metadataType.IsExplicitLayout || (metadataType.IsSequentialLayout && metadataType.GetClassLayout().Size != 0) || metadataType.IsWellKnownType(WellKnownType.TypedReference)) - result |= CorInfoFlag.CORINFO_FLG_CUSTOMLAYOUT; - if (metadataType.IsUnsafeValueType) result |= CorInfoFlag.CORINFO_FLG_UNSAFE_VALUECLASS; - - if (metadataType.IsInlineArray) - result |= CorInfoFlag.CORINFO_FLG_INDEXABLE_FIELDS; } if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) @@ -2375,6 +2368,108 @@ private uint getClassNumInstanceFields(CORINFO_CLASS_STRUCT_* cls) throw new InvalidOperationException(); } + private FlattenTypeResult FlattenTypeHelper(MetadataType type, uint baseOffs, CORINFO_FLATTENED_TYPE_FIELD* fields, nuint maxFields, nuint* numFields, ref bool significantPadding) + { + if (type.IsExplicitLayout || (type.IsSequentialLayout && type.GetClassLayout().Size != 0) || type.IsInlineArray) + { + if (!type.ContainsGCPointers && !type.IsByRefLike) + { + significantPadding = true; + } + } + + foreach (FieldDesc fd in type.GetFields()) + { + if (fd.IsStatic) + continue; + + nuint firstFieldIndex = *numFields; + Debug.Assert(fd.Offset != FieldAndOffset.InvalidOffset); + + TypeDesc fieldType = fd.FieldType; + CorInfoType corInfoType = asCorInfoType(fieldType); + if (corInfoType == CorInfoType.CORINFO_TYPE_VALUECLASS) + { + if (fieldType.IsIntrinsic) + { + if (*numFields >= maxFields) + return FlattenTypeResult.Partial; + + CORINFO_FLATTENED_TYPE_FIELD* field = &fields[(*numFields)++]; + field->type = CorInfoType.CORINFO_TYPE_VALUECLASS; + field->intrinsicValueClassHnd = ObjectToHandle(fieldType); + field->fieldHandle = ObjectToHandle(fd); + field->offset = baseOffs + (uint)fd.Offset.AsInt; + } + else + { + Debug.Assert(fieldType is MetadataType); + FlattenTypeResult result = FlattenTypeHelper((MetadataType)fieldType, baseOffs + (uint)fd.Offset.AsInt, fields, maxFields, numFields, ref significantPadding); + if (result != FlattenTypeResult.Success) + return result; + } + } + else + { + if (*numFields >= maxFields) + return FlattenTypeResult.Partial; + + CORINFO_FLATTENED_TYPE_FIELD* field = &fields[(*numFields)++]; + field->type = corInfoType; + field->intrinsicValueClassHnd = null; + field->fieldHandle = ObjectToHandle(fd); + field->offset = baseOffs + (uint)fd.Offset.AsInt; + } + + if (type.IsInlineArray) + { + nuint fieldEnd = *numFields; + int elemSize = fieldType.GetElementSize().AsInt; + int arrSize = type.GetElementSize().AsInt; + + for (int elemOffset = elemSize; elemOffset < arrSize; elemOffset += elemSize) + { + for (nuint templateFieldIndex = firstFieldIndex; templateFieldIndex < fieldEnd; templateFieldIndex++) + { + if (*numFields >= maxFields) + return FlattenTypeResult.Partial; + + CORINFO_FLATTENED_TYPE_FIELD* field = &fields[(*numFields)++]; + *field = fields[templateFieldIndex]; + field->offset += (uint)elemOffset; + } + } + } + } + + return FlattenTypeResult.Success; + } + + private FlattenTypeResult flattenType(CORINFO_CLASS_STRUCT_* clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, UIntPtr* numFields, ref bool significantPadding) + { + TypeDesc type = HandleToObject(clsHnd); + + if (type is not MetadataType metadataType || !type.IsValueType) + return FlattenTypeResult.Failure; + + significantPadding = false; + nuint maxFields = *numFields; + *numFields = 0; + FlattenTypeResult result = FlattenTypeHelper(metadataType, 0, fields, maxFields, numFields, ref significantPadding); + +#if READYTORUN + // TODO: Do we need a version bubble check here if we're going to + // add this type layout check anyway? + if (NeedsTypeLayoutCheck(type)) + { + ISymbolNode node = _compilation.SymbolNodeFactory.CheckTypeLayout(type); + AddPrecodeFixup(node); + } +#endif + + return result; + } + private bool checkMethodModifier(CORINFO_METHOD_STRUCT_* hMethod, byte* modifier, bool fOptional) { throw new NotImplementedException("checkMethodModifier"); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 3c7f632ef8e7b0..7da46803f04c8d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -897,6 +897,21 @@ private static uint _getClassNumInstanceFields(IntPtr thisHandle, IntPtr* ppExce } } + [UnmanagedCallersOnly] + private static FlattenTypeResult _flattenType(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, UIntPtr* numFields, bool* significantPadding) + { + var _this = GetThis(thisHandle); + try + { + return _this.flattenType(clsHnd, fields, numFields, ref *significantPadding); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] private static byte _checkMethodModifier(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* hMethod, byte* modifier, byte fOptional) { @@ -2730,7 +2745,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 184); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 185); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2792,130 +2807,131 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[57] = (delegate* unmanaged)&_getClassGClayout; callbacks[58] = (delegate* unmanaged)&_getClassNumInstanceFields; callbacks[59] = (delegate* unmanaged)&_getFieldInClass; - callbacks[60] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[61] = (delegate* unmanaged)&_getNewHelper; - callbacks[62] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[63] = (delegate* unmanaged)&_getCastingHelper; - callbacks[64] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[65] = (delegate* unmanaged)&_getTypeForBox; - callbacks[66] = (delegate* unmanaged)&_getBoxHelper; - callbacks[67] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[68] = (delegate* unmanaged)&_getRuntimeTypePointer; - callbacks[69] = (delegate* unmanaged)&_isObjectImmutable; - callbacks[70] = (delegate* unmanaged)&_getStringChar; - callbacks[71] = (delegate* unmanaged)&_getObjectType; - callbacks[72] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[73] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[74] = (delegate* unmanaged)&_initClass; - callbacks[75] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[76] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[77] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[78] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[79] = (delegate* unmanaged)&_canCast; - callbacks[80] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[81] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[82] = (delegate* unmanaged)&_mergeClasses; - callbacks[83] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[84] = (delegate* unmanaged)&_isEnum; - callbacks[85] = (delegate* unmanaged)&_getParentType; - callbacks[86] = (delegate* unmanaged)&_getChildType; - callbacks[87] = (delegate* unmanaged)&_satisfiesClassConstraints; - callbacks[88] = (delegate* unmanaged)&_isSDArray; - callbacks[89] = (delegate* unmanaged)&_getArrayRank; - callbacks[90] = (delegate* unmanaged)&_getArrayIntrinsicID; - callbacks[91] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[92] = (delegate* unmanaged)&_canAccessClass; - callbacks[93] = (delegate* unmanaged)&_printFieldName; - callbacks[94] = (delegate* unmanaged)&_getFieldClass; - callbacks[95] = (delegate* unmanaged)&_getFieldType; - callbacks[96] = (delegate* unmanaged)&_getFieldOffset; - callbacks[97] = (delegate* unmanaged)&_getFieldInfo; - callbacks[98] = (delegate* unmanaged)&_getThreadLocalFieldInfo; - callbacks[99] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; - callbacks[100] = (delegate* unmanaged)&_isFieldStatic; - callbacks[101] = (delegate* unmanaged)&_getArrayOrStringLength; - callbacks[102] = (delegate* unmanaged)&_getBoundaries; - callbacks[103] = (delegate* unmanaged)&_setBoundaries; - callbacks[104] = (delegate* unmanaged)&_getVars; - callbacks[105] = (delegate* unmanaged)&_setVars; - callbacks[106] = (delegate* unmanaged)&_reportRichMappings; - callbacks[107] = (delegate* unmanaged)&_allocateArray; - callbacks[108] = (delegate* unmanaged)&_freeArray; - callbacks[109] = (delegate* unmanaged)&_getArgNext; - callbacks[110] = (delegate* unmanaged)&_getArgType; - callbacks[111] = (delegate* unmanaged)&_getExactClasses; - callbacks[112] = (delegate* unmanaged)&_getArgClass; - callbacks[113] = (delegate* unmanaged)&_getHFAType; - callbacks[114] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[115] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[116] = (delegate* unmanaged)&_FilterException; - callbacks[117] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[118] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[119] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[120] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[121] = (delegate* unmanaged)&_getEEInfo; - callbacks[122] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[123] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[124] = (delegate* unmanaged)&_printMethodName; - callbacks[125] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[126] = (delegate* unmanaged)&_getMethodHash; - callbacks[127] = (delegate* unmanaged)&_findNameOfToken; - callbacks[128] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[129] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[130] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[131] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[132] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[133] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[134] = (delegate* unmanaged)&_getHelperFtn; - callbacks[135] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[136] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[137] = (delegate* unmanaged)&_getMethodSync; - callbacks[138] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[139] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[140] = (delegate* unmanaged)&_embedClassHandle; - callbacks[141] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[142] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[143] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[144] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[145] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[146] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[147] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[148] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[149] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[150] = (delegate* unmanaged)&_getCallInfo; - callbacks[151] = (delegate* unmanaged)&_canAccessFamily; - callbacks[152] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[153] = (delegate* unmanaged)&_getClassDomainID; - callbacks[154] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[155] = (delegate* unmanaged)&_getObjectContent; - callbacks[156] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[157] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[158] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[159] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[160] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[161] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[162] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[163] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[164] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[165] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[166] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[167] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[168] = (delegate* unmanaged)&_allocMem; - callbacks[169] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[170] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[171] = (delegate* unmanaged)&_allocGCInfo; - callbacks[172] = (delegate* unmanaged)&_setEHcount; - callbacks[173] = (delegate* unmanaged)&_setEHinfo; - callbacks[174] = (delegate* unmanaged)&_logMsg; - callbacks[175] = (delegate* unmanaged)&_doAssert; - callbacks[176] = (delegate* unmanaged)&_reportFatalError; - callbacks[177] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[178] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[179] = (delegate* unmanaged)&_recordCallSite; - callbacks[180] = (delegate* unmanaged)&_recordRelocation; - callbacks[181] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[182] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[183] = (delegate* unmanaged)&_getJitFlags; + callbacks[60] = (delegate* unmanaged)&_flattenType; + callbacks[61] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[62] = (delegate* unmanaged)&_getNewHelper; + callbacks[63] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[64] = (delegate* unmanaged)&_getCastingHelper; + callbacks[65] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[66] = (delegate* unmanaged)&_getTypeForBox; + callbacks[67] = (delegate* unmanaged)&_getBoxHelper; + callbacks[68] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[69] = (delegate* unmanaged)&_getRuntimeTypePointer; + callbacks[70] = (delegate* unmanaged)&_isObjectImmutable; + callbacks[71] = (delegate* unmanaged)&_getStringChar; + callbacks[72] = (delegate* unmanaged)&_getObjectType; + callbacks[73] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[74] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[75] = (delegate* unmanaged)&_initClass; + callbacks[76] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[77] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[78] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[79] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[80] = (delegate* unmanaged)&_canCast; + callbacks[81] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[82] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[83] = (delegate* unmanaged)&_mergeClasses; + callbacks[84] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[85] = (delegate* unmanaged)&_isEnum; + callbacks[86] = (delegate* unmanaged)&_getParentType; + callbacks[87] = (delegate* unmanaged)&_getChildType; + callbacks[88] = (delegate* unmanaged)&_satisfiesClassConstraints; + callbacks[89] = (delegate* unmanaged)&_isSDArray; + callbacks[90] = (delegate* unmanaged)&_getArrayRank; + callbacks[91] = (delegate* unmanaged)&_getArrayIntrinsicID; + callbacks[92] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[93] = (delegate* unmanaged)&_canAccessClass; + callbacks[94] = (delegate* unmanaged)&_printFieldName; + callbacks[95] = (delegate* unmanaged)&_getFieldClass; + callbacks[96] = (delegate* unmanaged)&_getFieldType; + callbacks[97] = (delegate* unmanaged)&_getFieldOffset; + callbacks[98] = (delegate* unmanaged)&_getFieldInfo; + callbacks[99] = (delegate* unmanaged)&_getThreadLocalFieldInfo; + callbacks[100] = (delegate* unmanaged)&_getThreadLocalStaticBlocksInfo; + callbacks[101] = (delegate* unmanaged)&_isFieldStatic; + callbacks[102] = (delegate* unmanaged)&_getArrayOrStringLength; + callbacks[103] = (delegate* unmanaged)&_getBoundaries; + callbacks[104] = (delegate* unmanaged)&_setBoundaries; + callbacks[105] = (delegate* unmanaged)&_getVars; + callbacks[106] = (delegate* unmanaged)&_setVars; + callbacks[107] = (delegate* unmanaged)&_reportRichMappings; + callbacks[108] = (delegate* unmanaged)&_allocateArray; + callbacks[109] = (delegate* unmanaged)&_freeArray; + callbacks[110] = (delegate* unmanaged)&_getArgNext; + callbacks[111] = (delegate* unmanaged)&_getArgType; + callbacks[112] = (delegate* unmanaged)&_getExactClasses; + callbacks[113] = (delegate* unmanaged)&_getArgClass; + callbacks[114] = (delegate* unmanaged)&_getHFAType; + callbacks[115] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[116] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[117] = (delegate* unmanaged)&_FilterException; + callbacks[118] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[119] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[120] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[121] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[122] = (delegate* unmanaged)&_getEEInfo; + callbacks[123] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[124] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[125] = (delegate* unmanaged)&_printMethodName; + callbacks[126] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[127] = (delegate* unmanaged)&_getMethodHash; + callbacks[128] = (delegate* unmanaged)&_findNameOfToken; + callbacks[129] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[130] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[131] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[132] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[133] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[134] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[135] = (delegate* unmanaged)&_getHelperFtn; + callbacks[136] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[137] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[138] = (delegate* unmanaged)&_getMethodSync; + callbacks[139] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[140] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[141] = (delegate* unmanaged)&_embedClassHandle; + callbacks[142] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[143] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[144] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[145] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[146] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[147] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[148] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[149] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[150] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[151] = (delegate* unmanaged)&_getCallInfo; + callbacks[152] = (delegate* unmanaged)&_canAccessFamily; + callbacks[153] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[154] = (delegate* unmanaged)&_getClassDomainID; + callbacks[155] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[156] = (delegate* unmanaged)&_getObjectContent; + callbacks[157] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[158] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[159] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[160] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[161] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[162] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[163] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[164] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[165] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[166] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[167] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[168] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[169] = (delegate* unmanaged)&_allocMem; + callbacks[170] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[171] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[172] = (delegate* unmanaged)&_allocGCInfo; + callbacks[173] = (delegate* unmanaged)&_setEHcount; + callbacks[174] = (delegate* unmanaged)&_setEHinfo; + callbacks[175] = (delegate* unmanaged)&_logMsg; + callbacks[176] = (delegate* unmanaged)&_doAssert; + callbacks[177] = (delegate* unmanaged)&_reportFatalError; + callbacks[178] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[179] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[180] = (delegate* unmanaged)&_recordCallSite; + callbacks[181] = (delegate* unmanaged)&_recordRelocation; + callbacks[182] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[183] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[184] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index cedbbda36b7046..d9223d6c2320a6 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using Internal.NativeFormat; using Internal.Pgo; namespace Internal.JitInterface @@ -600,10 +601,8 @@ public enum CorInfoFlag : uint CORINFO_FLG_OVERLAPPING_FIELDS = 0x00100000, // struct or class has fields that overlap (aka union) CORINFO_FLG_INTERFACE = 0x00200000, // it is an interface CORINFO_FLG_DONT_DIG_FIELDS = 0x00400000, // don't try to ask about fields outside of AOT compilation version bubble - CORINFO_FLG_CUSTOMLAYOUT = 0x00800000, // does this struct have custom layout? CORINFO_FLG_CONTAINS_GC_PTR = 0x01000000, // does the class contain a gc ptr ? CORINFO_FLG_DELEGATE = 0x02000000, // is this a subclass of delegate or multicast delegate ? - CORINFO_FLG_INDEXABLE_FIELDS = 0x04000000, // struct fields may be accessed via indexing (used for inline arrays) CORINFO_FLG_BYREF_LIKE = 0x08000000, // it is byref-like value type CORINFO_FLG_VARIANCE = 0x10000000, // MethodTable::HasVariance (sealed does *not* mean uncast-able) CORINFO_FLG_BEFOREFIELDINIT = 0x20000000, // Additional flexibility for when to run .cctor (see code:#ClassConstructionFlags) @@ -1467,4 +1466,19 @@ public bool IsSet(CorJitFlag flag) return (_corJitFlags & (1UL << (int)flag)) != 0; } } + + public enum FlattenTypeResult + { + Success = 0, + Partial = 1, + Failure = 2, + } + + public unsafe struct CORINFO_FLATTENED_TYPE_FIELD + { + public CorInfoType type; + public CORINFO_CLASS_STRUCT_* intrinsicValueClassHnd; + public uint offset; + public CORINFO_FIELD_STRUCT_* fieldHandle; + } } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index b9f8aa66f259eb..4e90270fe50bd1 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -89,6 +89,7 @@ CORINFO_FIELD_INFO*,CORINFO_FIELD_INFO* CORINFO_THREAD_STATIC_BLOCKS_INFO*,CORINFO_THREAD_STATIC_BLOCKS_INFO* CORINFO_CALL_INFO*,CORINFO_CALL_INFO* CORINFO_DEVIRTUALIZATION_INFO*,CORINFO_DEVIRTUALIZATION_INFO* +CORINFO_FLATTENED_TYPE_FIELD*,CORINFO_FLATTENED_TYPE_FIELD* PatchpointInfo* DelegateCtorArgs*,ref DelegateCtorArgs ICorDynamicInfo*,IntPtr @@ -132,6 +133,7 @@ TypeCompareState CORINFO_InstructionSet,InstructionSet ICorJitInfo::PgoSource, PgoSource ICorJitInfo::PgoSource*, ref PgoSource +FlattenTypeResult ; Handle types CORINFO_MODULE_HANDLE,CORINFO_MODULE_STRUCT_* @@ -219,6 +221,7 @@ FUNCTIONS unsigned getClassGClayout(CORINFO_CLASS_HANDLE cls, uint8_t* gcPtrs) unsigned getClassNumInstanceFields(CORINFO_CLASS_HANDLE cls) CORINFO_FIELD_HANDLE getFieldInClass(CORINFO_CLASS_HANDLE clsHnd, int32_t num) + FlattenTypeResult flattenType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t* numFields, bool* significantPadding) bool checkMethodModifier(CORINFO_METHOD_HANDLE hMethod, const char * modifier, bool fOptional) CorInfoHelpFunc getNewHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, bool* pHasSideEffects) CorInfoHelpFunc getNewArrHelper(CORINFO_CLASS_HANDLE arrayCls) diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 62db21d2232e23..4f3e66647bb138 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -71,6 +71,7 @@ struct JitInterfaceCallbacks unsigned (* getClassGClayout)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, uint8_t* gcPtrs); unsigned (* getClassNumInstanceFields)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls); CORINFO_FIELD_HANDLE (* getFieldInClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd, int32_t num); + FlattenTypeResult (* flattenType)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t* numFields, bool* significantPadding); bool (* checkMethodModifier)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod, const char* modifier, bool fOptional); CorInfoHelpFunc (* getNewHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_METHOD_HANDLE callerHandle, bool* pHasSideEffects); CorInfoHelpFunc (* getNewArrHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE arrayCls); @@ -797,6 +798,18 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual FlattenTypeResult flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + CorInfoExceptionClass* pException = nullptr; + FlattenTypeResult temp = _callbacks->flattenType(_thisHandle, &pException, clsHnd, fields, numFields, significantPadding); + if (pException != nullptr) throw pException; + return temp; +} + virtual bool checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char* modifier, diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 61f81c83e0d51f..f1f36aaf66c3be 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -204,6 +204,22 @@ struct Agnostic_GetStaticFieldCurrentClass bool isSpeculative; }; +struct Agnostic_CORINFO_FLATTENED_TYPE_FIELD +{ + DWORDLONG intrinsicValueClassHnd; + DWORDLONG fieldHandle; + DWORD type; + DWORD offset; +}; + +struct Agnostic_FlattenTypeResult +{ + DWORD result; + DWORD fieldsBuffer; + DWORD numFields; + DWORD significantPadding; +}; + struct Agnostic_CORINFO_RESOLVED_TOKEN { Agnostic_CORINFO_RESOLVED_TOKENin inValue; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 19a0600b3f582b..0a15a12dc646d0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -85,6 +85,7 @@ LWM(GetObjectContent, DLDD, DD) LWM(GetStaticFieldCurrentClass, DLD, Agnostic_GetStaticFieldCurrentClass) LWM(GetFieldClass, DWORDLONG, DWORDLONG) LWM(GetFieldInClass, DLD, DWORDLONG) +LWM(FlattenType, DLD, Agnostic_FlattenTypeResult) LWM(GetFieldInfo, Agnostic_GetFieldInfo, Agnostic_CORINFO_FIELD_INFO) LWM(GetFieldOffset, DWORDLONG, DWORD) LWM(GetFieldThreadLocalStoreID, DWORDLONG, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 469ff6f6d56d9e..9d885a7a2ea4bf 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4850,6 +4850,72 @@ CORINFO_FIELD_HANDLE MethodContext::repGetFieldInClass(CORINFO_CLASS_HANDLE clsH return result; } +void MethodContext::recFlattenType(FlattenTypeResult result, CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t maxFields, size_t* numFields, bool* significantPadding) +{ + if (FlattenType == nullptr) + FlattenType = new LightWeightMap(); + + DLD key; + ZeroMemory(&key, sizeof(key)); + key.A = CastHandle(clsHnd); + key.B = (DWORD)maxFields; + + Agnostic_FlattenTypeResult value; + ZeroMemory(&value, sizeof(value)); + + value.result = (DWORD)result; + if (result == FlattenTypeResult::Failure) + { + value.fieldsBuffer = UINT_MAX; + } + else + { + Agnostic_CORINFO_FLATTENED_TYPE_FIELD* agnosticFields = new Agnostic_CORINFO_FLATTENED_TYPE_FIELD[*numFields]; + for (size_t i = 0; i < *numFields; i++) + { + agnosticFields[i] = SpmiRecordsHelper::StoreAgnostic_CORINFO_FLATTENED_TYPE_FIELD(fields[i]); + } + + value.fieldsBuffer = FlattenType->AddBuffer((unsigned char*)agnosticFields, (unsigned int)(sizeof(Agnostic_CORINFO_FLATTENED_TYPE_FIELD) * *numFields)); + value.numFields = (DWORD)*numFields; + value.significantPadding = *significantPadding ? 1 : 0; + + delete[] agnosticFields; + } + + FlattenType->Add(key, value); +} +void MethodContext::dmpFlattenType(DLD key, const Agnostic_FlattenTypeResult& value) +{ + printf("FlattenType key cls-%016" PRIX64 " fields-%d, value result=%d fields=%d", key.A, key.B, (DWORD)value.result, value.numFields); +} +FlattenTypeResult MethodContext::repFlattenType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t* numFields, bool* significantPadding) +{ + DLD key; + ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding + key.A = CastHandle(clsHnd); + key.B = (DWORD)*numFields; + + Agnostic_FlattenTypeResult value = LookupByKeyOrMiss(FlattenType, key, ": key cls-%016" PRIX64 " fields-%d", key.A, key.B); + + FlattenTypeResult result = (FlattenTypeResult)value.result; + if (result == FlattenTypeResult::Failure) + { + return result; + } + + Assert(value.numFields <= *numFields); // since it's part of the key + Agnostic_CORINFO_FLATTENED_TYPE_FIELD* valueFields = (Agnostic_CORINFO_FLATTENED_TYPE_FIELD*)FlattenType->GetBuffer(value.fieldsBuffer); + for (size_t i = 0; i < value.numFields; i++) + { + fields[i] = SpmiRecordsHelper::RestoreCORINFO_FLATTENED_TYPE_FIELD(valueFields[i]); + } + + *numFields = value.numFields; + *significantPadding = value.significantPadding != 0; + return result; +} + void MethodContext::recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE* structType, CORINFO_CLASS_HANDLE memberParent, diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 53b3b02f745eaa..c6277018caee81 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -624,6 +624,10 @@ class MethodContext void dmpGetFieldInClass(DLD key, DWORDLONG value); CORINFO_FIELD_HANDLE repGetFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num); + void recFlattenType(FlattenTypeResult result, CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t maxFields, size_t* numFields, bool* significantPadding); + void dmpFlattenType(DLD key, const Agnostic_FlattenTypeResult& value); + FlattenTypeResult repFlattenType(CORINFO_CLASS_HANDLE clsHnd, CORINFO_FLATTENED_TYPE_FIELD* fields, size_t* numFields, bool* significantPadding); + void recGetFieldType(CORINFO_FIELD_HANDLE field, CORINFO_CLASS_HANDLE* structType, CORINFO_CLASS_HANDLE memberParent, @@ -1188,6 +1192,7 @@ enum mcPackets Packet_GetThreadLocalStaticBlocksInfo = 208, Packet_GetRISCV64PassStructInRegisterFlags = 209, Packet_GetObjectContent = 210, + Packet_FlattenType = 211, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmidumphelper.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmidumphelper.cpp index 77cf5a77564618..2c64eb01888ea5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmidumphelper.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmidumphelper.cpp @@ -183,7 +183,6 @@ std::string SpmiDumpHelper::DumpCorInfoFlag(CorInfoFlag flags) AddFlag(CORINFO_FLG_ARRAY); AddFlag(CORINFO_FLG_OVERLAPPING_FIELDS); AddFlag(CORINFO_FLG_INTERFACE); - AddFlag(CORINFO_FLG_CUSTOMLAYOUT); AddFlag(CORINFO_FLG_CONTAINS_GC_PTR); AddFlag(CORINFO_FLG_DELEGATE); AddFlag(CORINFO_FLG_BYREF_LIKE); diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.h b/src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.h index 513e1b063e8b6b..a0f61cbf53432b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmirecordhelper.h @@ -109,6 +109,9 @@ class SpmiRecordsHelper static Agnostic_CORINFO_LOOKUP StoreAgnostic_CORINFO_LOOKUP(CORINFO_LOOKUP* pLookup); static CORINFO_LOOKUP RestoreCORINFO_LOOKUP(Agnostic_CORINFO_LOOKUP& agnosticLookup); + + static Agnostic_CORINFO_FLATTENED_TYPE_FIELD StoreAgnostic_CORINFO_FLATTENED_TYPE_FIELD(const CORINFO_FLATTENED_TYPE_FIELD& field); + static CORINFO_FLATTENED_TYPE_FIELD RestoreCORINFO_FLATTENED_TYPE_FIELD(const Agnostic_CORINFO_FLATTENED_TYPE_FIELD& field); }; inline Agnostic_CORINFO_RESOLVED_TOKENin SpmiRecordsHelper::CreateAgnostic_CORINFO_RESOLVED_TOKENin( @@ -542,4 +545,24 @@ inline CORINFO_LOOKUP SpmiRecordsHelper::RestoreCORINFO_LOOKUP(Agnostic_CORINFO_ return lookup; } +inline Agnostic_CORINFO_FLATTENED_TYPE_FIELD SpmiRecordsHelper::StoreAgnostic_CORINFO_FLATTENED_TYPE_FIELD(const CORINFO_FLATTENED_TYPE_FIELD& field) +{ + Agnostic_CORINFO_FLATTENED_TYPE_FIELD result; + result.type = (DWORD)field.type; + result.intrinsicValueClassHnd = CastHandle(field.intrinsicValueClassHnd); + result.offset = field.offset; + result.fieldHandle = CastHandle(field.fieldHandle); + return result; +} + +inline CORINFO_FLATTENED_TYPE_FIELD SpmiRecordsHelper::RestoreCORINFO_FLATTENED_TYPE_FIELD(const Agnostic_CORINFO_FLATTENED_TYPE_FIELD& field) +{ + CORINFO_FLATTENED_TYPE_FIELD result; + result.type = (CorInfoType)field.type; + result.intrinsicValueClassHnd = (CORINFO_CLASS_HANDLE)field.intrinsicValueClassHnd; + result.offset = field.offset; + result.fieldHandle = (CORINFO_FIELD_HANDLE)field.fieldHandle; + return result; +} + #endif diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 96249dde36c0da..cf29ce40c2adeb 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -682,6 +682,19 @@ CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass(CORINFO_CLASS_HANDLE clsH return temp; } +FlattenTypeResult interceptor_ICJI::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + mc->cr->AddCall("flattenType"); + size_t maxFields = *numFields; + FlattenTypeResult result = original_ICorJitInfo->flattenType(clsHnd, fields, numFields, significantPadding); + mc->recFlattenType(result, clsHnd, fields, maxFields, numFields, significantPadding); + return result; +} + bool interceptor_ICJI::checkMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, bool fOptional) { mc->cr->AddCall("checkMethodModifier"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 58dc80abec8e1b..aa02efcd750e70 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -494,6 +494,16 @@ CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass( return original_ICorJitInfo->getFieldInClass(clsHnd, num); } +FlattenTypeResult interceptor_ICJI::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + mcs->AddCall("flattenType"); + return original_ICorJitInfo->flattenType(clsHnd, fields, numFields, significantPadding); +} + bool interceptor_ICJI::checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char* modifier, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index a9dced511ddce6..2553bed8062402 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -434,6 +434,15 @@ CORINFO_FIELD_HANDLE interceptor_ICJI::getFieldInClass( return original_ICorJitInfo->getFieldInClass(clsHnd, num); } +FlattenTypeResult interceptor_ICJI::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + return original_ICorJitInfo->flattenType(clsHnd, fields, numFields, significantPadding); +} + bool interceptor_ICJI::checkMethodModifier( CORINFO_METHOD_HANDLE hMethod, const char* modifier, diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 9915b4699eeebc..dfd8213d3b5020 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -576,6 +576,16 @@ CORINFO_FIELD_HANDLE MyICJI::getFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT nu return jitInstance->mc->repGetFieldInClass(clsHnd, num); } +FlattenTypeResult MyICJI::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + jitInstance->mc->cr->AddCall("flattenType"); + return jitInstance->mc->repFlattenType(clsHnd, fields, numFields, significantPadding); +} + bool MyICJI::checkMethodModifier(CORINFO_METHOD_HANDLE hMethod, LPCSTR modifier, bool fOptional) { jitInstance->mc->cr->AddCall("checkMethodModifier"); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 62a4400c7d86c0..b0713bbaf77013 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -2216,6 +2216,139 @@ CEEInfo::getFieldInClass(CORINFO_CLASS_HANDLE clsHnd, INT num) return result; } +static FlattenTypeResult FlattenTypeHelper( + MethodTable* pMT, + unsigned baseOffs, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t maxFields, + size_t* numFields, + bool* significantPadding) +{ + STANDARD_VM_CONTRACT; + + EEClass* pClass = pMT->GetClass(); + if (pClass->IsNotTightlyPacked() && (!pClass->IsManagedSequential() || pClass->HasExplicitSize() || pClass->IsInlineArray())) + { + // Historically on the JIT side we did not consider types with GC + // pointers to have significant padding, even when they have explicit + // layout attributes. This retains the more liberal treatment and + // lets the JIT still optimize these cases. + if (!pMT->ContainsPointers() && pMT != g_TypedReferenceMT) + { + *significantPadding = true; + } + } + + ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS); + for (FieldDesc* pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next()) + { + CorElementType fieldType = pFD->GetFieldType(); + + size_t firstFieldIndex = *numFields; + + if (fieldType == ELEMENT_TYPE_VALUETYPE) + { + MethodTable* pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable(); + if (pFieldMT->IsIntrinsicType()) + { + if (*numFields >= maxFields) + { + return FlattenTypeResult::Partial; + } + + CORINFO_FLATTENED_TYPE_FIELD& field = fields[(*numFields)++]; + field.type = CorInfoType::CORINFO_TYPE_VALUECLASS; + field.intrinsicValueClassHnd = CORINFO_CLASS_HANDLE(pFieldMT); + field.fieldHandle = CORINFO_FIELD_HANDLE(pFD); + field.offset = baseOffs + pFD->GetOffset(); + } + else + { + FlattenTypeResult result = FlattenTypeHelper(pFieldMT, baseOffs + pFD->GetOffset(), fields, maxFields, numFields, significantPadding); + if (result != FlattenTypeResult::Success) + { + return result; + } + } + } + else + { + CorInfoType corInfoType = CEEInfo::asCorInfoType(fieldType); + _ASSERTE(corInfoType != CORINFO_TYPE_UNDEF); + + if (*numFields >= maxFields) + { + return FlattenTypeResult::Partial; + } + + CORINFO_FLATTENED_TYPE_FIELD& field = fields[(*numFields)++]; + field.type = corInfoType; + field.intrinsicValueClassHnd = CORINFO_CLASS_HANDLE(nullptr); + field.fieldHandle = CORINFO_FIELD_HANDLE(pFD); + field.offset = baseOffs + pFD->GetOffset(); + } + + if (pMT->GetClass()->IsInlineArray()) + { + size_t fieldEnd = *numFields; + uint32_t elemSize = pFD->GetSize(); + uint32_t arrSize = pMT->GetNumInstanceFieldBytes(); + + for (uint32_t elemOffset = elemSize; elemOffset < arrSize; elemOffset += elemSize) + { + for (size_t templateFieldIndex = firstFieldIndex; templateFieldIndex < fieldEnd; templateFieldIndex++) + { + if (*numFields >= maxFields) + { + return FlattenTypeResult::Partial; + } + + CORINFO_FLATTENED_TYPE_FIELD& field = fields[(*numFields)++]; + field = fields[templateFieldIndex]; + field.offset += elemOffset; + } + } + } + } + + return FlattenTypeResult::Success; +} + +FlattenTypeResult CEEInfo::flattenType( + CORINFO_CLASS_HANDLE clsHnd, + CORINFO_FLATTENED_TYPE_FIELD* fields, + size_t* numFields, + bool* significantPadding) +{ + STANDARD_VM_CONTRACT; + + FlattenTypeResult result = FlattenTypeResult::Failure; + + JIT_TO_EE_TRANSITION_LEAF(); + + TypeHandle typeHnd(clsHnd); + + if (typeHnd.IsNativeValueType()) + { + *numFields = 0; + *significantPadding = true; + result = FlattenTypeResult::Success; + } + else if (typeHnd.IsValueType()) + { + MethodTable* pMT = typeHnd.AsMethodTable(); + + size_t maxFields = *numFields; + *numFields = 0; + *significantPadding = false; + result = FlattenTypeHelper(pMT, 0, fields, maxFields, numFields, significantPadding); + } + + EE_TO_JIT_TRANSITION_LEAF(); + + return result; +} + mdMethodDef CEEInfo::getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod) { @@ -3743,22 +3876,12 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd) if (pMT->IsByRefLike()) ret |= CORINFO_FLG_BYREF_LIKE; - if ((pClass->IsNotTightlyPacked() && (!pClass->IsManagedSequential() || pClass->HasExplicitSize())) || - pMT == g_TypedReferenceMT || - VMClsHnd.IsNativeValueType()) - { - ret |= CORINFO_FLG_CUSTOMLAYOUT; - } - if (pClass->IsUnsafeValueClass()) ret |= CORINFO_FLG_UNSAFE_VALUECLASS; } if (pClass->HasExplicitFieldOffsetLayout() && pClass->HasOverlaidField()) ret |= CORINFO_FLG_OVERLAPPING_FIELDS; - if (pClass->IsInlineArray()) - ret |= CORINFO_FLG_INDEXABLE_FIELDS; - if (VMClsHnd.IsCanonicalSubtype()) ret |= CORINFO_FLG_SHAREDINST; @@ -3772,9 +3895,7 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd) ret |= CORINFO_FLG_DELEGATE; if (pClass->IsBeforeFieldInit()) - { ret |= CORINFO_FLG_BEFOREFIELDINIT; - } if (pClass->IsAbstract()) ret |= CORINFO_FLG_ABSTRACT;