From c6ca9dc012bfd1a1fca7b6a2139d20368a258deb Mon Sep 17 00:00:00 2001 From: Sergey Andreenko Date: Sun, 6 Mar 2022 16:32:10 -0500 Subject: [PATCH] [crossgen2] Promote single byref aot. (#65682) * Rename `CORINFO_FLG_DONT_PROMOTE` to `CORINFO_FLG_DONT_DIG_FIELDS`. * Support promotion of `struct{ 1 gcref; }` outside of version bubble. --- src/coreclr/inc/corinfo.h | 2 +- src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/compiler.hpp | 4 +- src/coreclr/jit/lclvars.cpp | 100 ++++++++++++++++----- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 2 +- 6 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 99dd1fb..9395eec 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -841,7 +841,7 @@ 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_PROMOTE = 0x00400000, // don't try to promote fields (used for types outside of AOT compilation version bubble) + CORINFO_FLG_DONT_DIG_FIELDS = 0x00400000, // don't ask field info, AOT can't rely on it (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 ? diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 58fd74f..dc8bb8f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4149,6 +4149,8 @@ public: void PromoteStructVar(unsigned lclNum); void SortStructFields(); + bool CanConstructAndPromoteField(lvaStructPromotionInfo* structPromotionInfo); + lvaStructFieldInfo GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal); bool TryPromoteStructField(lvaStructFieldInfo& outerFieldInfo); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index fcbb8cc..0cf6f8f 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4480,9 +4480,9 @@ inline static bool StructHasCustomLayout(DWORD attribs) return ((attribs & CORINFO_FLG_CUSTOMLAYOUT) != 0); } -inline static bool StructHasNoPromotionFlagSet(DWORD attribs) +inline static bool StructHasDontDigFieldsFlagSet(DWORD attribs) { - return ((attribs & CORINFO_FLG_DONT_PROMOTE) != 0); + return ((attribs & CORINFO_FLG_DONT_DIG_FIELDS) != 0); } //------------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index a60eef7..df32fcf 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1685,13 +1685,6 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE structPromotionInfo.fieldCnt = (unsigned char)fieldCnt; DWORD typeFlags = compHandle->getClassAttribs(typeHnd); - if (StructHasNoPromotionFlagSet(typeFlags)) - { - // In AOT ReadyToRun compilation, don't try to promote fields of types - // outside of the current version bubble. - return false; - } - bool overlappingFields = StructHasOverlappingFields(typeFlags); if (overlappingFields) { @@ -1709,6 +1702,26 @@ 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; + } + + if (StructHasDontDigFieldsFlagSet(typeFlags)) + { + return CanConstructAndPromoteField(&structPromotionInfo); + } + unsigned fieldsSize = 0; for (BYTE ordinal = 0; ordinal < fieldCnt; ++ordinal) @@ -1760,21 +1773,6 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE noway_assert((containsGCpointers == false) || ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) != 0)); - // 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; - } - // Check if this promoted struct contains any holes. assert(!overlappingFields); if (fieldsSize != structSize) @@ -1791,6 +1789,62 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE } //-------------------------------------------------------------------------------------------- +// CanConstructAndPromoteField - checks if we can construct field types without asking about them directly. +// +// Arguments: +// structPromotionInfo - struct promotion candidate information. +// +// Return value: +// true if we can figure out the fields from available knowledge. +// +// 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 perfomance critical corner case: struct with 1 gcref, we try to construct +// the field information from indirect observations. +// +bool Compiler::StructPromotionHelper::CanConstructAndPromoteField(lvaStructPromotionInfo* structPromotionInfo) +{ + const CORINFO_CLASS_HANDLE typeHnd = structPromotionInfo->typeHnd; + const COMP_HANDLE compHandle = compiler->info.compCompHnd; + const DWORD typeFlags = compHandle->getClassAttribs(typeHnd); + if (structPromotionInfo->fieldCnt != 1) + { + // Can't find out values for several fields. + return false; + } + if ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0) + { + // Can't find out type of a non-gc field. + return false; + } + + const unsigned structSize = compHandle->getClassSize(typeHnd); + if (structSize != TARGET_POINTER_SIZE) + { + return false; + } + + 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_BYREF; + + structPromotionInfo->canPromote = true; + return true; +} + +//-------------------------------------------------------------------------------------------- // CanPromoteStructVar - checks if the struct can be promoted. // // Arguments: @@ -2761,7 +2815,7 @@ void Compiler::makeExtraStructQueries(CORINFO_CLASS_HANDLE structHandle, int lev assert(structHandle != NO_CLASS_HANDLE); (void)typGetObjLayout(structHandle); DWORD typeFlags = info.compCompHnd->getClassAttribs(structHandle); - if (StructHasNoPromotionFlagSet(typeFlags)) + if (StructHasDontDigFieldsFlagSet(typeFlags)) { // In AOT ReadyToRun compilation, don't query fields of types // outside of the current version bubble. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 379d1fe..f5febe7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -2028,7 +2028,7 @@ namespace Internal.JitInterface if (!_compilation.CompilationModuleGroup.VersionsWithType(type)) { // Prevent the JIT from drilling into types outside of the current versioning bubble - result |= CorInfoFlag.CORINFO_FLG_DONT_PROMOTE; + result |= CorInfoFlag.CORINFO_FLG_DONT_DIG_FIELDS; result &= ~CorInfoFlag.CORINFO_FLG_BEFOREFIELDINIT; } #endif diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 6274c55..f902979 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -594,7 +594,7 @@ namespace Internal.JitInterface 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_PROMOTE = 0x00400000, // don't try to promote fieds of types outside of AOT compilation version bubble + 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 ? -- 2.7.4