Skip to content

Commit acd76bc

Browse files
Allow preinitializing types with canonical forms (#79384)
* Allow preinitializing types with a canonical form * Allow preinitializing generics with TypeLoaderAwarePreinitializationPolicy We blanket disable canonical form preinitialization to keep things working by the policy instead. * Fix Linq test
1 parent e467a5f commit acd76bc

File tree

16 files changed

+276
-77
lines changed

16 files changed

+276
-77
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public CompilationBuilder UseDwarf5(bool value)
118118
protected PreinitializationManager GetPreinitializationManager()
119119
{
120120
if (_preinitializationManager == null)
121-
return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), enableInterpreter: false);
121+
return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), new TypePreinit.DisabledPreinitializationPolicy());
122122
return _preinitializationManager;
123123
}
124124

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericTypesTemplateMap.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,10 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
5050
Section nativeSection = nativeWriter.NewSection();
5151
nativeSection.Place(hashtable);
5252

53-
foreach (TypeDesc type in factory.MetadataManager.GetTypesWithConstructedEETypes())
53+
foreach (TypeDesc type in factory.MetadataManager.GetTypeTemplates())
5454
{
55-
if (!IsEligibleToHaveATemplate(type))
56-
continue;
57-
5855
// Type's native layout info
5956
NativeLayoutTemplateTypeLayoutVertexNode templateNode = factory.NativeLayout.TemplateTypeLayout(type);
60-
61-
// If this template isn't considered necessary, don't emit it.
62-
if (!templateNode.Marked)
63-
continue;
6457
Vertex nativeLayout = templateNode.SavedVertex;
6558

6659
// Hashtable Entry

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,8 @@ public sealed class NativeLayoutTemplateTypeLayoutVertexNode : NativeLayoutSaved
921921
private TypeDesc _type;
922922
private bool _isUniversalCanon;
923923

924+
public TypeDesc CanonType => _type.ConvertToCanonForm(CanonicalFormKind.Specific);
925+
924926
protected override string GetName(NodeFactory factory) => "NativeLayoutTemplateTypeLayoutVertexNode_" + factory.NameMangler.GetMangledTypeName(_type);
925927

926928
public NativeLayoutTemplateTypeLayoutVertexNode(NodeFactory factory, TypeDesc type)
@@ -989,7 +991,7 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto
989991
}
990992
}
991993

992-
if (context.PreinitializationManager.HasLazyStaticConstructor(_type))
994+
if (context.PreinitializationManager.HasLazyStaticConstructor(_type.ConvertToCanonForm(CanonicalFormKind.Specific)))
993995
{
994996
yield return new DependencyListEntry(context.MethodEntrypoint(_type.GetStaticConstructor().GetCanonMethodTarget(CanonicalFormKind.Specific)), "cctor for template");
995997
}
@@ -1188,7 +1190,7 @@ public override Vertex WriteVertex(NodeFactory factory)
11881190
layoutInfo.Append(BagElementKind.DictionaryLayout, dictionaryLayout.WriteVertex(factory));
11891191
}
11901192

1191-
if (factory.PreinitializationManager.HasLazyStaticConstructor(_type))
1193+
if (factory.PreinitializationManager.HasLazyStaticConstructor(_type.ConvertToCanonForm(CanonicalFormKind.Specific)))
11921194
{
11931195
MethodDesc cctorMethod = _type.GetStaticConstructor();
11941196
MethodDesc canonCctorMethod = cctorMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NonGCStaticsNode.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public NonGCStaticsNode(MetadataType type, PreinitializationManager preinitializ
3333

3434
protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory)
3535
{
36-
if (_preinitializationManager.HasLazyStaticConstructor(_type)
36+
if (HasCCtorContext
3737
|| _preinitializationManager.IsPreinitialized(_type))
3838
{
3939
// We have data to be emitted so this needs to be in an initialized data section
@@ -63,7 +63,7 @@ int ISymbolDefinitionNode.Offset
6363
get
6464
{
6565
// Make sure the NonGCStatics symbol always points to the beginning of the data.
66-
if (_preinitializationManager.HasLazyStaticConstructor(_type))
66+
if (HasCCtorContext)
6767
{
6868
return GetClassConstructorContextStorageSize(_type.Context.Target, _type);
6969
}
@@ -74,7 +74,25 @@ int ISymbolDefinitionNode.Offset
7474
}
7575
}
7676

77-
public bool HasCCtorContext => _preinitializationManager.HasLazyStaticConstructor(_type);
77+
public static bool TypeHasCctorContext(PreinitializationManager preinitializationManager, MetadataType type)
78+
{
79+
// If the type has a lazy static constructor, we need the cctor context.
80+
if (preinitializationManager.HasLazyStaticConstructor(type))
81+
return true;
82+
83+
// If the type has a canonical form and accessing the base from a canonical
84+
// context requires a cctor check, we need the cctor context.
85+
TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);
86+
if (canonType != type && preinitializationManager.HasLazyStaticConstructor(canonType))
87+
return true;
88+
89+
// Otherwise no cctor context needed.
90+
return false;
91+
}
92+
93+
public bool HasCCtorContext => TypeHasCctorContext(_preinitializationManager, _type);
94+
95+
public bool HasLazyStaticConstructor => _preinitializationManager.HasLazyStaticConstructor(_type);
7896

7997
public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type);
8098

@@ -124,7 +142,7 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo
124142

125143
// If the type has a class constructor, its non-GC statics section is prefixed
126144
// by System.Runtime.CompilerServices.StaticClassConstructionContext struct.
127-
if (factory.PreinitializationManager.HasLazyStaticConstructor(_type))
145+
if (HasCCtorContext)
128146
{
129147
int alignmentRequired = Math.Max(_type.NonGCStaticFieldAlignment.AsInt, GetClassConstructorContextAlignment(_type.Context.Target));
130148
int classConstructorContextStorageSize = GetClassConstructorContextStorageSize(factory.Target, _type);
@@ -138,7 +156,24 @@ protected override ObjectData GetDehydratableData(NodeFactory factory, bool relo
138156
// Emit the actual StaticClassConstructionContext
139157
MethodDesc cctorMethod = _type.GetStaticConstructor();
140158
builder.EmitPointerReloc(factory.ExactCallableAddress(cctorMethod));
141-
builder.EmitZeroPointer();
159+
160+
// If we're emitting the cctor context, but the type is actually preinitialized, emit the
161+
// cctor context as already executed.
162+
if (!HasLazyStaticConstructor)
163+
{
164+
// Constructor executed
165+
// TODO-NICE: introduce a named constant and also use it in the runner in CoreLib
166+
builder.EmitInt(1);
167+
}
168+
else
169+
{
170+
// Constructor didn't execute
171+
builder.EmitInt(0);
172+
}
173+
174+
// Emit padding if needed
175+
if (builder.TargetPointerSize == 8)
176+
builder.EmitInt(0);
142177
}
143178
else
144179
{

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ protected sealed override void OnMarked(NodeFactory factory)
107107
layout.EnsureEntry(_lookupSignature);
108108

109109
if ((_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) &&
110-
factory.PreinitializationManager.HasLazyStaticConstructor((TypeDesc)_target))
110+
TriggersLazyStaticConstructor(factory))
111111
{
112112
// If the type has a lazy static constructor, we also need the non-GC static base
113113
// because that's where the class constructor context is.
@@ -116,6 +116,12 @@ protected sealed override void OnMarked(NodeFactory factory)
116116
}
117117
}
118118

119+
private bool TriggersLazyStaticConstructor(NodeFactory factory)
120+
{
121+
TypeDesc type = (TypeDesc)_target;
122+
return factory.PreinitializationManager.HasLazyStaticConstructor(type.ConvertToCanonForm(CanonicalFormKind.Specific));
123+
}
124+
119125
public IEnumerable<DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation)
120126
{
121127
DependencyList result = new DependencyList();
@@ -129,13 +135,11 @@ public IEnumerable<DependencyListEntry> InstantiateDependencies(NodeFactory fact
129135
{
130136
// If the type has a lazy static constructor, we also need the non-GC static base
131137
// because that's where the class constructor context is.
132-
TypeDesc type = (TypeDesc)_target;
133-
134-
if (factory.PreinitializationManager.HasLazyStaticConstructor(type))
138+
if (TriggersLazyStaticConstructor(factory))
135139
{
136140
result.Add(
137141
new DependencyListEntry(
138-
factory.GenericLookup.TypeNonGCStaticBase(type).GetTarget(factory, lookupContext),
142+
factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target).GetTarget(factory, lookupContext),
139143
"Dictionary dependency"));
140144
}
141145
}
@@ -250,7 +254,7 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep
250254
// a template dictionary node.
251255
TypeDesc type = (TypeDesc)_target;
252256
Debug.Assert(templateLayout != null);
253-
if (factory.PreinitializationManager.HasLazyStaticConstructor(type))
257+
if (TriggersLazyStaticConstructor(factory))
254258
{
255259
GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(type);
256260
conditionalDependencies.Add(new CombinedDependencyListEntry(nonGcRegionLookup.TemplateDictionaryNode(factory),

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/StaticsInfoHashtableNode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static void AddStaticsInfoDependencies(ref DependencyList dependencies, N
5757
dependencies.Add(factory.TypeGCStaticsSymbol(metadataType), "GC statics indirection for StaticsInfoHashtable");
5858
}
5959

60-
if (metadataType.NonGCStaticFieldSize.AsInt > 0 || factory.PreinitializationManager.HasLazyStaticConstructor(type))
60+
if (metadataType.NonGCStaticFieldSize.AsInt > 0 || NonGCStaticsNode.TypeHasCctorContext(factory.PreinitializationManager, metadataType))
6161
{
6262
// The entry in the StaticsInfoHashtable points at the beginning of the static fields data, rather than the cctor
6363
// context offset.
@@ -98,7 +98,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
9898
{
9999
bag.AppendUnsigned(BagElementKind.GcStaticData, _nativeStaticsReferences.GetIndex(factory.TypeGCStaticsSymbol(metadataType)));
100100
}
101-
if (metadataType.NonGCStaticFieldSize.AsInt > 0 || factory.PreinitializationManager.HasLazyStaticConstructor(type))
101+
if (metadataType.NonGCStaticFieldSize.AsInt > 0 || NonGCStaticsNode.TypeHasCctorContext(factory.PreinitializationManager, metadataType))
102102
{
103103
bag.AppendUnsigned(BagElementKind.NonGcStaticData, _nativeStaticsReferences.GetIndex(factory.TypeNonGCStaticsSymbol(metadataType)));
104104
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM/ARMReadyToRunGenericHelperNode.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter enco
7070

7171
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
7272

73-
MetadataType target = (MetadataType)_target;
74-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
73+
if (!TriggersLazyStaticConstructor(factory))
7574
{
7675
encoder.EmitRET();
7776
}
@@ -99,7 +98,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter enco
9998
encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result);
10099

101100
MetadataType target = (MetadataType)_target;
102-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
101+
if (!TriggersLazyStaticConstructor(factory))
103102
{
104103
encoder.EmitRET();
105104
}
@@ -132,7 +131,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter enco
132131
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);
133132

134133
ISymbolNode helperEntrypoint;
135-
if (factory.PreinitializationManager.HasLazyStaticConstructor(target))
134+
if (TriggersLazyStaticConstructor(factory))
136135
{
137136
// There is a lazy class constructor. We need the non-GC static base because that's where the
138137
// class constructor context lives.

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunGenericHelperNode.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter en
8181

8282
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
8383

84-
MetadataType target = (MetadataType)_target;
85-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
84+
if (!TriggersLazyStaticConstructor(factory))
8685
{
8786
encoder.EmitRET();
8887
}
@@ -111,7 +110,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter en
111110
encoder.EmitLDR(encoder.TargetRegister.Result, encoder.TargetRegister.Result);
112111

113112
MetadataType target = (MetadataType)_target;
114-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
113+
if (!TriggersLazyStaticConstructor(factory))
115114
{
116115
encoder.EmitRET();
117116
}
@@ -144,7 +143,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter en
144143
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);
145144

146145
ISymbolNode helperEntrypoint;
147-
if (factory.PreinitializationManager.HasLazyStaticConstructor(target))
146+
if (TriggersLazyStaticConstructor(factory))
148147
{
149148
// There is a lazy class constructor. We need the non-GC static base because that's where the
150149
// class constructor context lives.

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunGenericHelperNode.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emit
7070

7171
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
7272

73-
MetadataType target = (MetadataType)_target;
74-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
73+
if (!TriggersLazyStaticConstructor(factory))
7574
{
7675
encoder.EmitRET();
7776
}
@@ -100,7 +99,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emit
10099
encoder.EmitLD(encoder.TargetRegister.Result, encoder.TargetRegister.Result, 0);
101100

102101
MetadataType target = (MetadataType)_target;
103-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
102+
if (!TriggersLazyStaticConstructor(factory))
104103
{
105104
encoder.EmitRET();
106105
}
@@ -133,7 +132,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emit
133132
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);
134133

135134
ISymbolNode helperEntrypoint;
136-
if (factory.PreinitializationManager.HasLazyStaticConstructor(target))
135+
if (TriggersLazyStaticConstructor(factory))
137136
{
138137
// There is a lazy class constructor. We need the non-GC static base because that's where the
139138
// class constructor context lives.

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunGenericHelperNode.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter enco
8282
{
8383
Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);
8484

85-
MetadataType target = (MetadataType)_target;
86-
87-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
85+
if (!TriggersLazyStaticConstructor(factory))
8886
{
8987
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
9088
encoder.EmitRET();
@@ -119,7 +117,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter enco
119117
AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
120118
encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);
121119

122-
if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
120+
if (!TriggersLazyStaticConstructor(factory))
123121
{
124122
encoder.EmitRET();
125123
}
@@ -153,7 +151,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter enco
153151
EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);
154152

155153
ISymbolNode helperEntrypoint;
156-
if (factory.PreinitializationManager.HasLazyStaticConstructor(target))
154+
if (TriggersLazyStaticConstructor(factory))
157155
{
158156
// There is a lazy class constructor. We need the non-GC static base because that's where the
159157
// class constructor context lives.

0 commit comments

Comments
 (0)