Skip to content

Commit

Permalink
Rework JIT<->EE communication for struct promotion (#87917)
Browse files Browse the repository at this point in the history
Rework the communication so that the JIT asks the EE for struct field
related information relevant to promotion in a single JIT-EE call of a
new function called getTypeLayout. Crossgen2 can then more easily
provide the information that the JIT is allowed to depend on.

As a side effect, removes CORINFO_FLG_CUSTOMLAYOUT (which fixes #71711)
and CORINFO_FLG_DONT_DIG_FIELDS.

Fix #85511
Fix #71711
  • Loading branch information
jakobbotsch authored Jun 27, 2023
1 parent b4a9d88 commit 318c0e6
Show file tree
Hide file tree
Showing 31 changed files with 1,129 additions and 578 deletions.
81 changes: 79 additions & 2 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -857,8 +857,6 @@ enum CorInfoFlag
CORINFO_FLG_ARRAY = 0x00080000, // class is an array class (initialized differently)
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)
Expand Down Expand Up @@ -1934,6 +1932,46 @@ struct CORINFO_VarArgInfo
// (The CORINFO_VARARGS_HANDLE counts as an arg)
};

struct CORINFO_TYPE_LAYOUT_NODE
{
// Type handle if this is a SIMD type, i.e. for intrinsic types in
// System.Numerics and System.Runtime.Intrinsics namespaces. This handle
// should be used for SIMD type recognition ONLY. During prejitting the
// returned handle cannot safely be used for arbitrary JIT-EE calls. The
// safe operations on this handle are:
// - getClassNameFromMetadata
// - getClassSize
// - getHfaType
// - getTypeInstantiationArgument, but only under the assumption that the returned type handle
// is used for primitive type recognition via getTypeForPrimitiveNumericClass
CORINFO_CLASS_HANDLE simdTypeHnd;
// Field handle that should only be used for diagnostic purposes. During
// prejit we cannot allow arbitrary JIT-EE calls with this field handle, but it can be used
// for diagnostic purposes (e.g. to obtain the field name).
CORINFO_FIELD_HANDLE diagFieldHnd;
// Index of parent node in the tree
unsigned parent;
// Offset into the root type of the field
unsigned offset;
// Size of the type.
unsigned size;
// Number of fields for type == CORINFO_TYPE_VALUECLASS. This is the number of nodes added.
unsigned numFields;
// Type of the field.
CorInfoType type;
// For type == CORINFO_TYPE_VALUECLASS indicates whether the type has significant padding.
// That is, whether or not the JIT always needs to preserve data stored in
// the parts that are not covered by fields.
bool hasSignificantPadding;
};

enum class GetTypeLayoutResult
{
Success,
Partial,
Failure,
};

#define SIZEOF__CORINFO_Object TARGET_POINTER_SIZE /* methTable */

#define CORINFO_Array_MaxLength 0x7FFFFFC7
Expand Down Expand Up @@ -2403,6 +2441,45 @@ class ICorStaticInfo
int32_t num
) = 0;

//------------------------------------------------------------------------------
// getTypeLayout: Obtain a tree describing the layout of a type.
//
// Parameters:
// typeHnd - Handle of the type.
// treeNodes - [in, out] Pointer to tree node entries to write.
// numTreeNodes - [in, out] Size of 'treeNodes' on entry. Updated to contain
// the number of entries written in 'treeNodes'.
//
// Returns:
// A result indicating whether the type layout was successfully
// retrieved and whether the result is partial or not.
//
// Remarks:
// The type layout should be stored in preorder in 'treeNodes': the root
// node is always at index 0, and the first child of any node is at its
// own index + 1. The fields returned are NOT guaranteed to be ordered
// by offset.
//
// SIMD and HW SIMD types are returned as a single entry without any
// children. For those, CORINFO_TYPE_LAYOUT_NODE::simdTypeHnd is set, but
// can only be used in a very restricted capacity, see
// CORINFO_TYPE_LAYOUT_NODE. Note that this special treatment is only for
// fields; if typeHnd itself is a SIMD type this function will treat it
// like a normal struct type and expand its fields.
//
// IMPORTANT: except for GC pointers the fields returned to the JIT by
// this function should be considered as a hint only. The JIT CANNOT make
// assumptions in its codegen that the specified fields are actually part
// of the type when the code finally runs. This means the JIT should not
// make optimizations based on the field information returned by this
// function that would break if those fields were removed or shifted
// around.
//
virtual GetTypeLayoutResult getTypeLayout(
CORINFO_CLASS_HANDLE typeHnd,
CORINFO_TYPE_LAYOUT_NODE* treeNodes,
size_t* numTreeNodes) = 0;

virtual bool checkMethodModifier(
CORINFO_METHOD_HANDLE hMethod,
const char * modifier,
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ CORINFO_FIELD_HANDLE getFieldInClass(
CORINFO_CLASS_HANDLE clsHnd,
int32_t num) override;

GetTypeLayoutResult getTypeLayout(
CORINFO_CLASS_HANDLE typeHnd,
CORINFO_TYPE_LAYOUT_NODE* treeNodes,
size_t* numTreeNodes) override;

bool checkMethodModifier(
CORINFO_METHOD_HANDLE hMethod,
const char* modifier,
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED

constexpr GUID JITEEVersionIdentifier = { /* 88398e9f-093a-4212-85bc-bebb8c14cd24 */
0x88398e9f,
0x093a,
0x4212,
{0x85, 0xbc, 0xbe, 0xbb, 0x8c, 0x14, 0xcd, 0x24}
constexpr GUID JITEEVersionIdentifier = { /* 878d63a7-ffc9-421f-81f7-db4729f0ed5c */
0x878d63a7,
0xffc9,
0x421f,
{0x81, 0xf7, 0xdb, 0x47, 0x29, 0xf0, 0xed, 0x5c}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ DEF_CLR_API(getClassAlignmentRequirement)
DEF_CLR_API(getClassGClayout)
DEF_CLR_API(getClassNumInstanceFields)
DEF_CLR_API(getFieldInClass)
DEF_CLR_API(getTypeLayout)
DEF_CLR_API(checkMethodModifier)
DEF_CLR_API(getNewHelper)
DEF_CLR_API(getNewArrHelper)
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,17 @@ CORINFO_FIELD_HANDLE WrapICorJitInfo::getFieldInClass(
return temp;
}

GetTypeLayoutResult WrapICorJitInfo::getTypeLayout(
CORINFO_CLASS_HANDLE typeHnd,
CORINFO_TYPE_LAYOUT_NODE* treeNodes,
size_t* numTreeNodes)
{
API_ENTER(getTypeLayout);
GetTypeLayoutResult temp = wrapHnd->getTypeLayout(typeHnd, treeNodes, numTreeNodes);
API_LEAVE(getTypeLayout);
return temp;
}

bool WrapICorJitInfo::checkMethodModifier(
CORINFO_METHOD_HANDLE hMethod,
const char* modifier,
Expand Down
63 changes: 33 additions & 30 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -2055,7 +2060,6 @@ class Compiler
bool shouldUseVerboseSsa();
bool treesBeforeAfterMorph; // If true, print trees before/after morphing (paired by an intra-compilation id:
int morphNum; // This counts the trees that have been morphed, allowing us to label each uniquely.
bool doExtraSuperPmiQueries;
void makeExtraStructQueries(CORINFO_CLASS_HANDLE structHandle, int level); // Make queries recursively 'level' deep.

const char* VarNameToStr(VarName name)
Expand Down Expand Up @@ -3568,17 +3572,18 @@ class Compiler
// Info about struct type fields.
struct lvaStructFieldInfo
{
CORINFO_FIELD_HANDLE fldHnd;
unsigned char fldOffset;
unsigned char fldOrdinal;
var_types fldType;
unsigned fldSize;
CORINFO_CLASS_HANDLE fldTypeHnd;
// Class handle for SIMD type recognition, see CORINFO_TYPE_LAYOUT_NODE
// for more details on the restrictions.
CORINFO_CLASS_HANDLE fldSIMDTypeHnd = NO_CLASS_HANDLE;
uint8_t fldOffset = 0;
uint8_t fldOrdinal = 0;
var_types fldType = TYP_UNDEF;
unsigned fldSize = 0;

lvaStructFieldInfo()
: fldHnd(nullptr), fldOffset(0), fldOrdinal(0), fldType(TYP_UNDEF), fldSize(0), fldTypeHnd(nullptr)
{
}
#ifdef DEBUG
// Field handle for diagnostic purposes only. See CORINFO_TYPE_LAYOUT_NODE.
CORINFO_FIELD_HANDLE diagFldHnd = NO_FIELD_HANDLE;
#endif
};

// Info about a struct type, instances of which may be candidates for promotion.
Expand All @@ -3587,7 +3592,7 @@ class Compiler
CORINFO_CLASS_HANDLE typeHnd;
bool canPromote;
bool containsHoles;
bool customLayout;
bool anySignificantPadding;
bool fieldsSorted;
unsigned char fieldCnt;
lvaStructFieldInfo fields[MAX_NumOfFieldsInPromotableStruct];
Expand All @@ -3596,18 +3601,13 @@ class Compiler
: typeHnd(typeHnd)
, canPromote(false)
, containsHoles(false)
, customLayout(false)
, anySignificantPadding(false)
, fieldsSorted(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.
Expand All @@ -3629,10 +3629,8 @@ class Compiler
void PromoteStructVar(unsigned lclNum);
void SortStructFields();

bool CanConstructAndPromoteField(lvaStructPromotionInfo* structPromotionInfo);

lvaStructFieldInfo GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal);
bool TryPromoteStructField(lvaStructFieldInfo& outerFieldInfo);
var_types TryPromoteValueClassAsPrimitive(CORINFO_TYPE_LAYOUT_NODE* treeNodes, size_t maxTreeNodes, size_t index);
void AdvanceSubTree(CORINFO_TYPE_LAYOUT_NODE* treeNodes, size_t maxTreeNodes, size_t* index);

private:
Compiler* compiler;
Expand Down Expand Up @@ -8509,6 +8507,16 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
return info.compCompHnd->getTypeInstantiationArgument(cls, index);
}

bool isNumericsNamespace(const char* ns)
{
return strcmp(ns, "System.Numerics") == 0;
}

bool isRuntimeIntrinsicsNamespace(const char* ns)
{
return strcmp(ns, "System.Runtime.Intrinsics") == 0;
}

#ifdef FEATURE_SIMD
// Have we identified any SIMD types?
// This is currently used by struct promotion to avoid getting type information for a struct
Expand Down Expand Up @@ -8613,11 +8621,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
return isOpaqueSIMDType(varDsc->GetLayout());
}

bool isNumericsNamespace(const char* ns)
{
return strcmp(ns, "System.Numerics") == 0;
}

bool isSIMDClass(CORINFO_CLASS_HANDLE clsHnd)
{
if (isIntrinsicType(clsHnd))
Expand All @@ -8636,7 +8639,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;
Expand Down
10 changes: 0 additions & 10 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4544,16 +4544,6 @@ 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);
Expand Down
23 changes: 4 additions & 19 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11914,31 +11914,15 @@ void Compiler::gtDispLocal(GenTreeLclVarCommon* tree, IndentStack* indentStack)
}
else
{
char buffer[256];

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->GetLclNum(),
fieldName, fieldVarDsc->lvFldOffset);
printf(" %-6s %s -> ", varTypeName(fieldVarDsc->TypeGet()), fieldVarDsc->lvReason);
gtDispLclVar(fieldLclNum);
gtDispSsaName(fieldLclNum, tree->GetSsaNum(this, index), isDef);

Expand All @@ -11947,6 +11931,7 @@ void Compiler::gtDispLocal(GenTreeLclVarCommon* tree, IndentStack* indentStack)
printf(" ");
fieldVarDsc->PrintVarReg();
}

if (fieldVarDsc->lvTracked && fgLocalVarLivenessDone && tree->IsLastUse(index))
{
printf(" (last use)");
Expand Down
Loading

0 comments on commit 318c0e6

Please sign in to comment.