Skip to content

Commit 61fc02b

Browse files
Allow NecessaryTypeSymbol in generic dictionaries (#117315)
Before this change, whenever we needed to refer to a type from a generic dictionary, we used the fully loaded "allocated" form of it. But we can do the same optimization we do for non-generic code where we distinguish between MethodTables that we need for casting-like operations and MethodTables that are actually for allocation.
1 parent 496af72 commit 61fc02b

File tree

16 files changed

+165
-14
lines changed

16 files changed

+165
-14
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLoo
266266

267267
public ReadyToRunHelperId GetLdTokenHelperForType(TypeDesc type)
268268
{
269+
// This will not correctly answer questions for canonical forms because the answer would
270+
// be about whether a type loader template for the type exists.
271+
// We need to make a small exception for canonical definitions because of RuntimeAugments.GetCanonType
272+
Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.IsCanonicalDefinitionType(CanonicalFormKind.Any));
273+
269274
bool canPotentiallyConstruct = ConstructedEETypeNode.CreationAllowed(type)
270275
&& NodeFactory.DevirtualizationManager.CanReferenceConstructedMethodTable(type.NormalizeInstantiation());
271276
if (canPotentiallyConstruct)
@@ -393,12 +398,6 @@ public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, Re
393398
}
394399
}
395400

396-
// We don't have separate entries for necessary type handles to avoid possible duplication
397-
if (lookupKind == ReadyToRunHelperId.NecessaryTypeHandle)
398-
{
399-
lookupKind = ReadyToRunHelperId.TypeHandle;
400-
}
401-
402401
DictionaryLayoutNode dictionaryLayout;
403402
if (contextSource == GenericContextSource.MethodParameter)
404403
dictionaryLayout = _nodeFactory.GenericDictionaryLayout(contextMethod);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,20 @@ public override bool TryGetSlotForEntry(GenericLookupResult entry, out int slot)
212212
{
213213
slot = Array.IndexOf(_layout, entry);
214214

215+
// If we're looking for a necessary type handle and it doesn't exist, check for a constructed type handle.
216+
if (slot < 0 && entry is NecessaryTypeHandleGenericLookupResult necessaryLookup)
217+
{
218+
for (int i = 0; i < _layout.Length; i++)
219+
{
220+
if (_layout[i] is TypeHandleGenericLookupResult other
221+
&& other.Type == necessaryLookup.Type)
222+
{
223+
slot = i;
224+
return true;
225+
}
226+
}
227+
}
228+
215229
// If this is a slot we should discard, respond false
216230
if (slot < 0 && Array.IndexOf(_discardedSlots, entry) >= 0)
217231
return false;

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,59 @@ protected override bool EqualsImpl(GenericLookupResult obj)
215215
}
216216
}
217217

218+
/// <summary>
219+
/// Generic lookup result that points to an MethodTable.
220+
/// </summary>
221+
public sealed class NecessaryTypeHandleGenericLookupResult : GenericLookupResult
222+
{
223+
private TypeDesc _type;
224+
225+
protected override int ClassCode => -4882991;
226+
227+
public NecessaryTypeHandleGenericLookupResult(TypeDesc type)
228+
{
229+
Debug.Assert(type.IsRuntimeDeterminedSubtype, "Concrete type in a generic dictionary?");
230+
_type = type;
231+
}
232+
233+
public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultContext dictionary)
234+
{
235+
TypeDesc instantiatedType = _type.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation);
236+
237+
factory.TypeSystemContext.DetectGenericCycles(dictionary.Context, instantiatedType);
238+
239+
return factory.NecessaryTypeSymbol(instantiatedType);
240+
}
241+
242+
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
243+
{
244+
sb.Append("NecessaryTypeHandle_"u8);
245+
sb.Append(nameMangler.GetMangledTypeName(_type));
246+
}
247+
248+
public TypeDesc Type => _type;
249+
public override string ToString() => $"NecessaryTypeHandle: {_type}";
250+
251+
public override NativeLayoutVertexNode TemplateDictionaryNode(NodeFactory factory)
252+
{
253+
return factory.NativeLayout.TypeHandleDictionarySlot(_type);
254+
}
255+
256+
protected override int CompareToImpl(GenericLookupResult other, TypeSystemComparer comparer)
257+
{
258+
return comparer.Compare(_type, ((NecessaryTypeHandleGenericLookupResult)other)._type);
259+
}
260+
261+
protected override int GetHashCodeImpl()
262+
{
263+
return _type.GetHashCode();
264+
}
265+
266+
protected override bool EqualsImpl(GenericLookupResult obj)
267+
{
268+
return ((NecessaryTypeHandleGenericLookupResult)obj)._type == _type;
269+
}
270+
}
218271

219272
/// <summary>
220273
/// Generic lookup result that points to an MethodTable where if the type is Nullable&lt;X&gt; the MethodTable is X

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.GenericLookups.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ private void CreateNodeCaches()
2727
return new TypeHandleGenericLookupResult(type);
2828
});
2929

30+
_necessaryTypeSymbols = new NodeCache<TypeDesc, GenericLookupResult>(type =>
31+
{
32+
return new NecessaryTypeHandleGenericLookupResult(type);
33+
});
34+
3035
_unwrapNullableSymbols = new NodeCache<TypeDesc, GenericLookupResult>(type =>
3136
{
3237
return new UnwrapNullableTypeHandleGenericLookupResult(type);
@@ -95,6 +100,13 @@ public GenericLookupResult Type(TypeDesc type)
95100
return _typeSymbols.GetOrAdd(type);
96101
}
97102

103+
private NodeCache<TypeDesc, GenericLookupResult> _necessaryTypeSymbols;
104+
105+
public GenericLookupResult NecessaryType(TypeDesc type)
106+
{
107+
return _necessaryTypeSymbols.GetOrAdd(type);
108+
}
109+
98110
private NodeCache<TypeDesc, GenericLookupResult> _unwrapNullableSymbols;
99111

100112
public GenericLookupResult UnwrapNullableType(TypeDesc type)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,12 @@ public ReadyToRunGenericHelperNode(NodeFactory factory, ReadyToRunHelperId helpe
5151

5252
public static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyToRunHelperId id, object target)
5353
{
54-
// Necessary type handle is not something you can put in a dictionary - someone should have normalized to TypeHandle
55-
Debug.Assert(id != ReadyToRunHelperId.NecessaryTypeHandle);
56-
5754
switch (id)
5855
{
5956
case ReadyToRunHelperId.TypeHandle:
6057
return factory.GenericLookup.Type((TypeDesc)target);
58+
case ReadyToRunHelperId.NecessaryTypeHandle:
59+
return factory.GenericLookup.NecessaryType((TypeDesc)target);
6160
case ReadyToRunHelperId.TypeHandleForCasting:
6261
// Check that we unwrapped the cases that could be unwrapped to prevent duplicate entries
6362
Debug.Assert(factory.GenericLookup.Type((TypeDesc)target) != factory.GenericLookup.UnwrapNullableType((TypeDesc)target));
@@ -304,6 +303,7 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer
304303
switch (_id)
305304
{
306305
case ReadyToRunHelperId.TypeHandle:
306+
case ReadyToRunHelperId.NecessaryTypeHandle:
307307
case ReadyToRunHelperId.GetGCStaticBase:
308308
case ReadyToRunHelperId.GetNonGCStaticBase:
309309
case ReadyToRunHelperId.GetThreadStaticBase:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARMEmitter enco
188188

189189
// These are all simple: just get the thing from the dictionary and we're done
190190
case ReadyToRunHelperId.TypeHandle:
191+
case ReadyToRunHelperId.NecessaryTypeHandle:
191192
case ReadyToRunHelperId.MethodHandle:
192193
case ReadyToRunHelperId.FieldHandle:
193194
case ReadyToRunHelperId.MethodDictionary:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref ARM64Emitter en
186186

187187
// These are all simple: just get the thing from the dictionary and we're done
188188
case ReadyToRunHelperId.TypeHandle:
189+
case ReadyToRunHelperId.NecessaryTypeHandle:
189190
case ReadyToRunHelperId.MethodHandle:
190191
case ReadyToRunHelperId.FieldHandle:
191192
case ReadyToRunHelperId.MethodDictionary:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref LoongArch64Emit
188188

189189
// These are all simple: just get the thing from the dictionary and we're done
190190
case ReadyToRunHelperId.TypeHandle:
191+
case ReadyToRunHelperId.NecessaryTypeHandle:
191192
case ReadyToRunHelperId.MethodHandle:
192193
case ReadyToRunHelperId.FieldHandle:
193194
case ReadyToRunHelperId.MethodDictionary:

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64ReadyToRunGenericHelperNode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref RiscV64Emitter
185185

186186
// These are all simple: just get the thing from the dictionary and we're done
187187
case ReadyToRunHelperId.TypeHandle:
188+
case ReadyToRunHelperId.NecessaryTypeHandle:
188189
case ReadyToRunHelperId.MethodHandle:
189190
case ReadyToRunHelperId.FieldHandle:
190191
case ReadyToRunHelperId.MethodDictionary:

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter enco
196196

197197
// These are all simple: just get the thing from the dictionary and we're done
198198
case ReadyToRunHelperId.TypeHandle:
199+
case ReadyToRunHelperId.NecessaryTypeHandle:
199200
case ReadyToRunHelperId.MethodHandle:
200201
case ReadyToRunHelperId.FieldHandle:
201202
case ReadyToRunHelperId.MethodDictionary:

0 commit comments

Comments
 (0)