Skip to content

Commit

Permalink
Improve the performance of the type loader through various tweaks (#8…
Browse files Browse the repository at this point in the history
…5743)

* Skip type validation by default in ReadyToRun images
- Technically, this is a breaking change, so I've provided a means for disabling the type validation skip
- The model is that the C# compile won't get these details wrong, so disable the checks when run through crossgen2. The idea is that we'll get these things checked during normal, non-R2R usage of the app, and publish won't check these details.

* Replace expensive lookups of generic parameter and nested class data with R2R optimized forms

* Store index of MethodDesc as well as ChunkIndex. Makes MethodDesc::GetTemporaryEntryPoint *much* faster

* Optimize the path for computing if a method is eligible for tiered compilation

* Remove CanShareVtableChunksFrom concept
- it was only needed to support NGen

* Fix up some more issues

* Bring back late virtual propagation in the presence of covariant overrides only

* Check correct flag on EEClass

* Drive by fix for GetRestoredSlot. We don't need the handling of unrestored slots anymore

* Fix composite build with new tables

* Uniquify the mangled names

* Add more __

* Initial pass at type skip verifation checker

* Fix logging and some correctness issues

* Enable the more of type checking
- Notably, the recursive stuff now works
- Also fix a bug in constraint checking involving open types in the type system

* Fix build breaks involving new feature of GenericParameterDesc

* Add documentation for R2R format changes
Fix command line parameter to be more reasonable, and allow logging on command
Fix the rest of issues noted in crossgen2 testing

* Fix implementation of CompareMethodContraints. instead of using IsGeneric map, check to see if the method is generic in the first place. It turns out we have an efficient way to check in every place that matters

* Fix nits noticed by Aaron

* Add some const correctness to the world

* Fix issues noted by Michal, as well as remaining constrain checking issues

* Code review details

* Code review from trylek
  • Loading branch information
davidwrighton committed Jun 13, 2023
1 parent 1087766 commit 8042fac
Show file tree
Hide file tree
Showing 51 changed files with 1,814 additions and 532 deletions.
30 changes: 30 additions & 0 deletions docs/design/coreclr/botr/readytorun-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ The following section types are defined and described later in this document:
| ManifestAssemblyMvids | 118 | Image (added in V5.3)
| CrossModuleInlineInfo | 119 | Image (added in V6.3)
| HotColdMap | 120 | Image (added in V8.0)
| MethodIsGenericMap | 121 | Assembly (Added in V9.0)
| EnclosingTypeMap | 122 | Assembly (Added in V9.0)
| TypeGenericInfoMap | 123 | Assembly (Added in V9.0)

## ReadyToRunSectionType.CompilerIdentifier

Expand Down Expand Up @@ -654,6 +657,33 @@ The methods in this table are sorted by their hot part runtime function indices,

This section may not exist if no method is split - this happens when the `--hot-cold-splitting` flag is not specified during compilation, or the compiler decides it should not split any methods.

## ReadyToRunSectionType.MethodIsGenericMap (v9.0+)
This optional section holds a bit vector to indicate if the MethodDefs contained within the assembly have generic parameters or not. This allows determining if a method is generic or not by querying a bit vector (which is fast, and efficient) as opposed to examining the GenericParameter table, or the signature of the Method.

The section begins with a single 32 bit integer indicating the number of bits in the bit vector. Following that integer is the actual bit vector of all of the data. The data is grouped into 8 bit bytes, where the least significant bit of the byte is the bit which represents the lowest MethodDef.

For instance, the first byte in the bit vector represents the MethodDefs 06000001 to 06000008, and the least signficant bit of that first byte is the bit representing the IsGeneric bit for MethodDef 06000001.

## ReadyToRunSectionType.EnclosingTypeMap (v9.0+)

This optional section allows for efficient O(1) lookup from the enclosed type to the type which encloses it without requiring the binary search that is necessary if using the ECMA 335 defined NestedClass table (which encodes exactly the same information). This section may only be included in the assembly if the assembly has fewer than 0xFFFE types defined within it.

The structure of this section is:
A single 16 bit unsigned integer listing the count of entries in the map.
This count is followed by a 16 bit unsigned integer for each TypeDef defined in the assembly. This typedef is the RID of the enclosing type, or 0 if the typedef is not enclosed by another type.

## ReadyToRunSectionType.TypeGenericInfoMap (v9.0+)
This optional section represents a condensed view of some generic details about types. This can make it more efficient to load types.

The structure of this section is:
A single 32 bit integer representing the number of entries in the map followed by a series of 4 bit entries, one per type. These 4 bit entries are grouped into bytes, where each byte holds 2 entries, and the entry in the most significant 4 bits of the byte is the entry representing a lower TypeDef RID.

TypeGenericInfoMap entries have 4 bits representing 3 different sets of information.

1. What is the count of generic parameters (0, 1, 2, MoreThanTwo) (This is represented in the least significant 2 bits of the TypeGenericInfoMap entry)
2. Are there any constraints on the generic parameters? (This is the 3rd bit of the entry)
3. Do any of the generic parameters have co or contra variance? (This is the 4th bit of the entry)

# Native Format

Native format is set of encoding patterns that allow persisting type system data in a binary format that is
Expand Down
25 changes: 25 additions & 0 deletions src/coreclr/inc/readytorun.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ enum class ReadyToRunSectionType : uint32_t
ManifestAssemblyMvids = 118, // Added in V5.3
CrossModuleInlineInfo = 119, // Added in V6.2
HotColdMap = 120, // Added in V8.0
MethodIsGenericMap = 121, // Added in V9.0
EnclosingTypeMap = 122, // Added in V9.0
TypeGenericInfoMap = 123, // Added in V9.0

// If you add a new section consider whether it is a breaking or non-breaking change.
// Usually it is non-breaking, but if it is preferable to have older runtimes fail
Expand Down Expand Up @@ -121,6 +124,28 @@ enum class ReadyToRunImportSectionFlags : uint16_t
PCode = 0x0004, // Section contains pointers to code
};

// All values in this enum should within a nibble (4 bits).
enum class ReadyToRunTypeGenericInfo : uint8_t
{
GenericCountMask = 0x3,
HasConstraints = 0x4,
HasVariance = 0x8,
};

// All values in this enum should fit within 2 bits.
enum class ReadyToRunGenericInfoGenericCount : uint32_t
{
Zero = 0,
One = 1,
Two = 2,
MoreThanTwo = 3
};

enum class ReadyToRunEnclosingTypeMap
{
MaxTypeCount = 0xFFFE
};

//
// READYTORUN_IMPORT_SECTION describes image range with references to code or runtime data structures
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,25 @@ internal class RuntimeGenericParameterDesc : GenericParameterDesc
{
private readonly GenericParameterKind _kind;
private readonly int _index;
private readonly TypeSystemContext _context;
private readonly GenericVariance _variance;
private readonly TypeSystemEntity _associatedTypeOrMethod;

public RuntimeGenericParameterDesc(GenericParameterKind kind, int index, TypeSystemContext context, GenericVariance variance)
public RuntimeGenericParameterDesc(GenericParameterKind kind, int index, TypeSystemEntity associatedTypeOrMethod, GenericVariance variance)
{
_kind = kind;
_index = index;
_context = context;
_associatedTypeOrMethod = associatedTypeOrMethod;
_variance = variance;
}

public override GenericParameterKind Kind => _kind;

public override int Index => _index;

public override TypeSystemContext Context => _context;
public override TypeSystemContext Context => _associatedTypeOrMethod.Context;

public override GenericVariance Variance => _variance;

public override TypeSystemEntity AssociatedTypeOrMethod => _associatedTypeOrMethod;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public override Instantiation Instantiation
TypeDesc[] genericParameters = new TypeDesc[genericArgCount];
for (int i = 0; i < genericParameters.Length; i++)
{
genericParameters[i] = new RuntimeGenericParameterDesc(GenericParameterKind.Method, i, Context, GenericVariance.None);
var newGenericParameter = new RuntimeGenericParameterDesc(GenericParameterKind.Method, i, this, GenericVariance.None);
genericParameters[i] = newGenericParameter;
}
_instantiation = new Instantiation(genericParameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,46 @@ internal class NoMetadataType : DefType
// "_baseType == this" means "base type was not initialized yet"
private DefType _baseType;

public NoMetadataType(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, DefType genericTypeDefinitionAsDefType, Instantiation instantiation, int hashcode)
public unsafe NoMetadataType(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, int instantiationLength, ReadOnlySpan<Runtime.GenericVariance> runtimeVarianceData, int hashcode)
{
TypeDesc[] genericParameters;
if (instantiationLength == 0)
{
genericParameters = Array.Empty<TypeDesc>();
}
else
{
genericParameters = new TypeDesc[instantiationLength];
for (int i = 0; i < genericParameters.Length; i++)
{
GenericVariance variance = runtimeVarianceData.Length == 0 ? GenericVariance.None : runtimeVarianceData[i] switch
{
Runtime.GenericVariance.Contravariant => GenericVariance.Contravariant,
Runtime.GenericVariance.Covariant => GenericVariance.Covariant,
Runtime.GenericVariance.NonVariant or Runtime.GenericVariance.ArrayCovariant => GenericVariance.None,
_ => throw new NotImplementedException()
};
genericParameters[i] = new RuntimeGenericParameterDesc(GenericParameterKind.Type, i, this, variance);
}
}

Instantiation instantiation = new Instantiation(genericParameters);
Init(context, genericTypeDefinition, null, instantiation, hashcode);
}

public unsafe NoMetadataType(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, DefType genericTypeDefinitionAsDefType, Instantiation instantiation, int hashcode)
{
Init(context, genericTypeDefinition, genericTypeDefinitionAsDefType, instantiation, hashcode);
}

private void Init(TypeSystemContext context, RuntimeTypeHandle genericTypeDefinition, DefType genericTypeDefinitionAsDefType, Instantiation instantiation, int hashcode)
{
_hashcode = hashcode;
_context = context;
_genericTypeDefinition = genericTypeDefinition;
_genericTypeDefinitionAsDefType = genericTypeDefinitionAsDefType;
_genericTypeDefinitionAsDefType ??= this;

_instantiation = instantiation;

// Instantiation must either be:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,9 @@ public TypeDesc ResolveRuntimeTypeHandle(RuntimeTypeHandle rtth)
TypeDesc[] genericParameters = new TypeDesc[rtth.ToEETypePtr()->GenericParameterCount];
Runtime.GenericVariance* runtimeVariance = rtth.ToEETypePtr()->HasGenericVariance ?
rtth.ToEETypePtr()->GenericVariance : null;
for (int i = 0; i < genericParameters.Length; i++)
{
GenericVariance variance = runtimeVariance == null ? GenericVariance.None : runtimeVariance[i] switch
{
Runtime.GenericVariance.Contravariant => GenericVariance.Contravariant,
Runtime.GenericVariance.Covariant => GenericVariance.Covariant,
Runtime.GenericVariance.NonVariant or Runtime.GenericVariance.ArrayCovariant => GenericVariance.None,
_ => throw new NotImplementedException()
};
genericParameters[i] = genericParameters[i] = new RuntimeGenericParameterDesc(GenericParameterKind.Type, i, this, variance);
}
ReadOnlySpan<Runtime.GenericVariance> varianceData = new ReadOnlySpan<Runtime.GenericVariance>(runtimeVariance, runtimeVariance == null ? 0 : genericParameters.Length);

returnedType = new NoMetadataType(this, rtth, null, new Instantiation(genericParameters), rtth.GetHashCode());
returnedType = new NoMetadataType(this, rtth, genericParameters.Length, varianceData, rtth.GetHashCode());
}
}
else if (RuntimeAugments.IsGenericType(rtth))
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public enum ReadyToRunSectionType
ManifestAssemblyMvids = 118, // Added in 5.3
CrossModuleInlineInfo = 119, // Added in 6.3
HotColdMap = 120, // Added in 8.0
MethodIsGenericMap = 121, // Added in V9.0
EnclosingTypeMap = 122, // Added in V9.0
TypeGenericInfoMap = 123, // Added in V9.0

//
// NativeAOT ReadyToRun sections
Expand Down
25 changes: 23 additions & 2 deletions src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ namespace Internal.ReadyToRunConstants
public enum ReadyToRunFlags
{
READYTORUN_FLAG_PlatformNeutralSource = 0x00000001, // Set if the original IL assembly was platform-neutral
READYTORUN_FLAG_SkipTypeValidation = 0x00000002, // Set of methods with native code was determined using profile data
READYTORUN_FLAG_Partial = 0x00000004,
READYTORUN_FLAG_SkipTypeValidation = 0x00000002, // Runtime should trust that the metadata for the types defined in this module is correct
READYTORUN_FLAG_Partial = 0x00000004, // Set of methods with native code was determined using profile data
READYTORUN_FLAG_NonSharedPInvokeStubs = 0x00000008, // PInvoke stubs compiled into image are non-shareable (no secret parameter)
READYTORUN_FLAG_EmbeddedMSIL = 0x00000010, // MSIL is embedded in the composite R2R executable
READYTORUN_FLAG_Component = 0x00000020, // This is the header describing a component assembly of composite R2R
Expand Down Expand Up @@ -89,6 +89,27 @@ internal enum ReadyToRunCrossModuleInlineFlags : uint
InlinerRidShift = 1,
};

[Flags]
public enum ReadyToRunTypeGenericInfo : byte
{
GenericCountMask = 0x3,
HasConstraints = 0x4,
HasVariance = 0x8,
}

public enum ReadyToRunGenericInfoGenericCount : uint
{
Zero = 0,
One = 1,
Two = 2,
MoreThanTwo = 3
}

public enum ReadyToRunEnclosingTypeMap : uint
{
MaxTypeCount = 0xFFFE
}

public enum DictionaryEntryKind
{
EmptySlot = 0,
Expand Down
14 changes: 13 additions & 1 deletion src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,21 @@ private static bool CanCastGenericParameterTo(this GenericParameterDesc thisType
return true;
}

Instantiation typeInstantiation;
Instantiation methodInstantiation = default(Instantiation);
if (thisType.AssociatedTypeOrMethod is MethodDesc method)
{
typeInstantiation = method.OwningType.Instantiation;
methodInstantiation = method.Instantiation;
}
else
{
typeInstantiation = ((TypeDesc)thisType.AssociatedTypeOrMethod).Instantiation;
}
foreach (var typeConstraint in thisType.TypeConstraints)
{
if (typeConstraint.CanCastToInternal(otherType, protect))
TypeDesc instantiatedConstraint = typeConstraint.InstantiateSignature(typeInstantiation, methodInstantiation);
if (instantiatedConstraint.CanCastToInternal(otherType, protect))
{
return true;
}
Expand Down
31 changes: 31 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Common/GenericParameterDesc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public virtual string Name
/// </summary>
public abstract int Index { get; }

/// <summary>
/// The associated type or method which defines this Generic Parameter
/// </summary>
public abstract TypeSystemEntity AssociatedTypeOrMethod { get; }

/// <summary>
/// Gets a value indicating the variance of this generic parameter.
/// </summary>
Expand Down Expand Up @@ -120,6 +125,9 @@ public virtual IEnumerable<TypeDesc> TypeConstraints
}
}

/// <summary>
/// Does this generic parameter have the NotNullableValueType constraint flag
/// </summary>
public bool HasNotNullableValueTypeConstraint
{
get
Expand All @@ -128,6 +136,9 @@ public bool HasNotNullableValueTypeConstraint
}
}

/// <summary>
/// Does this generic parameter have the ReferenceType constraint flag
/// </summary>
public bool HasReferenceTypeConstraint
{
get
Expand All @@ -136,6 +147,9 @@ public bool HasReferenceTypeConstraint
}
}

/// <summary>
/// Does this generic parameter have the DefaultConstructor constraint flag
/// </summary>
public bool HasDefaultConstructorConstraint
{
get
Expand All @@ -144,6 +158,20 @@ public bool HasDefaultConstructorConstraint
}
}

/// <summary>
/// Does this generic parameter have the AcceptByRefLike flag
/// </summary>
public bool HasAcceptByRefLikeConstraint
{
get
{
return (Constraints & GenericConstraints.AcceptByRefLike) != 0;
}
}

/// <summary>
/// Is this generic parameter Covariant
/// </summary>
public bool IsCovariant
{
get
Expand All @@ -152,6 +180,9 @@ public bool IsCovariant
}
}

/// <summary>
/// Is this generic parameter Contravariant
/// </summary>
public bool IsContravariant
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ protected override TypeFlags ComputeTypeFlags(TypeFlags mask)
flags |= TypeFlags.SignatureTypeVariable;
}

flags |= TypeFlags.AttributeCacheComputed;

return flags;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@

using System;
using System.Diagnostics;
using Internal.IL;
using Internal.TypeSystem;

namespace ILVerify
namespace Internal.TypeSystem
{
internal class SimpleArrayOfTRuntimeInterfacesAlgorithm : RuntimeInterfacesAlgorithm
public class SimpleArrayOfTRuntimeInterfacesAlgorithm : RuntimeInterfacesAlgorithm
{
private DefType[] _arrayRuntimeInterfaces;
private MetadataType[] _genericRuntimeInterfaces;
private ModuleDesc _systemModule;

private static readonly string[] s_genericRuntimeInterfacesNames =
private static readonly string[] s_genericRuntimeInterfacesNames =
{
"IEnumerable`1",
"ICollection`1",
Expand All @@ -28,7 +26,7 @@ public SimpleArrayOfTRuntimeInterfacesAlgorithm(ModuleDesc systemModule)
_systemModule = systemModule;

// initialize interfaces
_arrayRuntimeInterfaces = _systemModule.GetType("System", "Array")?.RuntimeInterfaces
_arrayRuntimeInterfaces = _systemModule.GetType("System", "Array")?.RuntimeInterfaces
?? Array.Empty<DefType>();

_genericRuntimeInterfaces = new MetadataType[s_genericRuntimeInterfacesNames.Length];
Expand Down

0 comments on commit 8042fac

Please sign in to comment.