Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 70 additions & 6 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ private sealed class ScannedDevirtualizationManager : DevirtualizationManager
private HashSet<TypeDesc> _canonConstructedTypes = new HashSet<TypeDesc>();
private HashSet<TypeDesc> _unsealedTypes = new HashSet<TypeDesc>();
private Dictionary<TypeDesc, HashSet<TypeDesc>> _interfaceImplementators = new();
private Dictionary<TypeDesc, HashSet<TypeDesc>> _classSubclasses = new();
private HashSet<TypeDesc> _disqualifiedInterfaces = new();

public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes)
Expand Down Expand Up @@ -467,6 +468,11 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray<Depend
}
}

if (!type.IsInterface && type.HasBaseType)
{
RecordSubclass(type.BaseType, type);
}

if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
{
// If the interface is implemented on a template type, there might be
Expand Down Expand Up @@ -557,6 +563,20 @@ private void RecordImplementation(TypeDesc type, TypeDesc implType)
implList.Add(implType);
}

private void RecordSubclass(TypeDesc type, TypeDesc subType)
{
Debug.Assert(!type.IsInterface);
Debug.Assert(!subType.IsInterface);

HashSet<TypeDesc> subclasses;
if (!_classSubclasses.TryGetValue(type, out subclasses))
{
subclasses = new();
_classSubclasses[type] = subclasses;
}
subclasses.Add(subType);
}

public override bool IsEffectivelySealed(TypeDesc type)
{
// If we know we scanned a type that derives from this one, this for sure can't be reported as sealed.
Expand Down Expand Up @@ -607,18 +627,62 @@ public override TypeDesc[] GetImplementingClasses(TypeDesc type)
if (_disqualifiedInterfaces.Contains(type))
return null;

if (type.IsInterface && _interfaceImplementators.TryGetValue(type, out HashSet<TypeDesc> implementations))
if (type.IsInterface)
{
var types = new TypeDesc[implementations.Count];
int index = 0;
foreach (TypeDesc implementation in implementations)
if (_interfaceImplementators.TryGetValue(type, out HashSet<TypeDesc> implementations))
{
types[index++] = implementation;
var types = new TypeDesc[implementations.Count];
int index = 0;
foreach (TypeDesc implementation in implementations)
{
types[index++] = implementation;
}
return types;
}
}
else
{
List<TypeDesc> subClasses = new();
if (PopulateSubclassesList(type, subClasses))
{
return subClasses.ToArray();
}
return types;
}
return null;
}

private bool PopulateSubclassesList(TypeDesc type, List<TypeDesc> allSubclasses)
{
if (type.IsCanonicalSubtype(CanonicalFormKind.Any) || type.IsArray)
{
return false;
}

if (_classSubclasses.TryGetValue(type, out HashSet<TypeDesc> subclasses))
{
foreach (TypeDesc subclass in subclasses)
{
if (subclass.IsCanonicalSubtype(CanonicalFormKind.Any) || subclass.IsArray)
{
// We won't have the full view of all subclasses - bail out.
return false;
}

// We don't need to report abstract classes as "subclasses" but we still need
// to inspect their subclasses.
if (subclass is not MetadataType { IsAbstract: true })
{
allSubclasses.Add(subclass);
}

if (!PopulateSubclassesList(subclass, allSubclasses))
{
return false;
}
}
}
return true;
}
}

private sealed class ScannedInliningPolicy : IInliningPolicy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2210,12 +2210,6 @@ private int getExactClasses(CORINFO_CLASS_STRUCT_* baseType, int maxExactClasses
return 1;
}

if (!type.IsInterface)
{
// TODO: handle classes
return 0;
}

TypeDesc[] implClasses = _compilation.GetImplementingClasses(type);
if (implClasses == null || implClasses.Length > maxExactClasses)
{
Expand Down