Skip to content

Commit

Permalink
Devirtualize more based on whole program analysis (#97812)
Browse files Browse the repository at this point in the history
Whole program view lets us figure out whether a virtual method has been overriden by another method, or whether it can be considered `final`. Add a pass to collect this information and pass it to codegen,

It kicks in less often than I expected (for linear enough hierarchies or not-too-much-derived types we could already do this with guarded devirtualization or type sealing), but I can still a bit over 100 method bodies affected by this in the Stage1 app.
  • Loading branch information
MichalStrehovsky authored Feb 1, 2024
1 parent 1e8f741 commit 4f0216f
Showing 1 changed file with 45 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,12 @@ private sealed class ScannedDevirtualizationManager : DevirtualizationManager
private HashSet<TypeDesc> _unsealedTypes = new HashSet<TypeDesc>();
private Dictionary<TypeDesc, HashSet<TypeDesc>> _implementators = new();
private HashSet<TypeDesc> _disqualifiedTypes = new();
private HashSet<MethodDesc> _overridenMethods = new();

public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes)
{
var vtables = new Dictionary<TypeDesc, List<MethodDesc>>();

foreach (var node in markedNodes)
{
TypeDesc type = node switch
Expand Down Expand Up @@ -528,6 +531,40 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray<Depend
added = _unsealedTypes.Add(baseType);
baseType = baseType.BaseType;
}

static List<MethodDesc> BuildVTable(NodeFactory factory, TypeDesc currentType, TypeDesc implType, List<MethodDesc> vtable)
{
if (currentType == null)
return vtable;

BuildVTable(factory, currentType.BaseType?.ConvertToCanonForm(CanonicalFormKind.Specific), implType, vtable);

IReadOnlyList<MethodDesc> slice = factory.VTable(currentType).Slots;
foreach (MethodDesc decl in slice)
{
vtable.Add(implType.GetClosestDefType()
.FindVirtualFunctionTargetMethodOnObjectType(decl));
}

return vtable;
}

baseType = canonType.BaseType?.ConvertToCanonForm(CanonicalFormKind.Specific);
if (!canonType.IsArray && baseType != null)
{
if (!vtables.TryGetValue(baseType, out List<MethodDesc> baseVtable))
vtables.Add(baseType, baseVtable = BuildVTable(factory, baseType, baseType, new List<MethodDesc>()));

if (!vtables.TryGetValue(canonType, out List<MethodDesc> vtable))
vtables.Add(canonType, vtable = BuildVTable(factory, canonType, canonType, new List<MethodDesc>()));

for (int i = 0; i < baseVtable.Count; i++)
{
if (baseVtable[i] != vtable[i])
_overridenMethods.Add(baseVtable[i]);
}
}

}
}
}
Expand Down Expand Up @@ -603,6 +640,14 @@ public override bool IsEffectivelySealed(TypeDesc type)
return true;
}

public override bool IsEffectivelySealed(MethodDesc method)
{
if (method.IsFinal || IsEffectivelySealed(method.OwningType))
return true;

return !_overridenMethods.Contains(method.GetCanonMethodTarget(CanonicalFormKind.Specific));
}

protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail)
{
MethodDesc result = base.ResolveVirtualMethod(declMethod, implType, out devirtualizationDetail);
Expand Down

0 comments on commit 4f0216f

Please sign in to comment.