Skip to content

Commit 3caa97a

Browse files
BrzVladjkotas
andauthored
Fix resolving of default interface methods in crossgen2 (#126351)
When doing a static virtual call, we would only lookup implementations on the constrained type. If this resolution fails, we add the fallback of looking for DIMs on the implemented interfaces. Makes Perf_DateTimeCultureInfo.ToString tests 25x faster when having only interpreter fallback --------- Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent 983639b commit 3caa97a

4 files changed

Lines changed: 59 additions & 31 deletions

File tree

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,11 +526,11 @@ private void EmitMethodSpecificationSignature(MethodWithToken method,
526526
{
527527
Instantiation instantiation = method.Method.Instantiation;
528528
EmitUInt((uint)instantiation.Length);
529-
SignatureContext methodInstantiationsContext;
530-
if ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UpdateContext) != 0)
531-
methodInstantiationsContext = context;
532-
else
533-
methodInstantiationsContext = context.OuterContext;
529+
530+
// The runtime decoder (ZapSig::DecodeMethod) always uses pOrigModule
531+
// (the module from before any UpdateContext) for method instantiation
532+
// type arguments. Match that by always using the OuterContext here.
533+
SignatureContext methodInstantiationsContext = context.OuterContext;
534534

535535
for (int typeParamIndex = 0; typeParamIndex < instantiation.Length; typeParamIndex++)
536536
{

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,12 @@ public Import CheckTypeLayout(TypeDesc type)
500500

501501
private NodeCache<VirtualResolutionFixupSignature, Import> _virtualFunctionOverrideCache;
502502

503-
public Import CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod)
503+
public Import CheckVirtualFunctionOverride(MethodWithToken declMethod, TypeDesc implType, MethodWithToken implMethod, bool checkOnly = false)
504504
{
505-
return _virtualFunctionOverrideCache.GetOrAdd(_codegenNodeFactory.VirtualResolutionFixupSignature(
506-
_verifyTypeAndFieldLayout ? ReadyToRunFixupKind.Verify_VirtualFunctionOverride : ReadyToRunFixupKind.Check_VirtualFunctionOverride,
507-
declMethod, implType, implMethod));
505+
ReadyToRunFixupKind fixupKind = (checkOnly || !_verifyTypeAndFieldLayout) ?
506+
ReadyToRunFixupKind.Check_VirtualFunctionOverride :
507+
ReadyToRunFixupKind.Verify_VirtualFunctionOverride;
508+
return _virtualFunctionOverrideCache.GetOrAdd(_codegenNodeFactory.VirtualResolutionFixupSignature(fixupKind, declMethod, implType, implMethod));
508509
}
509510

510511
private NodeCache<ILBodyFixupSignature, Import> _ilBodyFixupsCache;

src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,19 +2025,31 @@ private void ceeInfoGetCallInfo(
20252025
originalMethod = methodOnUnderlyingType;
20262026
}
20272027

2028-
MethodDesc directMethod;
2029-
if (isStaticVirtual)
2028+
var dimResolution = DefaultInterfaceMethodResolution.None;
2029+
MethodDesc directMethod = constrainedType.TryResolveConstraintMethodApprox(exactType, originalMethod, out forceUseRuntimeLookup, ref dimResolution);
2030+
if (isStaticVirtual && directMethod != null)
20302031
{
2031-
directMethod = constrainedType.ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(originalMethod);
2032-
if (directMethod != null && !_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(directMethod))
2032+
if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(directMethod))
20332033
{
20342034
directMethod = null;
20352035
}
2036+
else if (dimResolution == DefaultInterfaceMethodResolution.DefaultImplementation)
2037+
{
2038+
// For static virtual calls resolved through a default interface method,
2039+
// emit a Check_VirtualFunctionOverride fixup so that the R2R code is
2040+
// rejected at runtime if the resolution changes (e.g. a DIM override
2041+
// is added in an assembly outside the version bubble).
2042+
MethodWithToken declMethodWithToken = new MethodWithToken(originalMethod, HandleToModuleToken(ref pResolvedToken), null, false, null);
2043+
2044+
ModuleToken moduleToken = _compilation.NodeFactory.Resolver.GetModuleTokenForMethod(directMethod, false, false);
2045+
Debug.Assert(!moduleToken.IsNull);
2046+
MethodWithToken implMethodWithToken = new MethodWithToken(directMethod, moduleToken, null, false, null);
2047+
2048+
AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckVirtualFunctionOverride(
2049+
declMethodWithToken, constrainedType, implMethodWithToken, true));
2050+
}
20362051
}
2037-
else
2038-
{
2039-
directMethod = constrainedType.TryResolveConstraintMethodApprox(exactType, originalMethod, out forceUseRuntimeLookup);
2040-
}
2052+
20412053
if (directMethod != null)
20422054
{
20432055
// Either
@@ -2054,11 +2066,14 @@ private void ceeInfoGetCallInfo(
20542066
useInstantiatingStub = directMethod.OwningType.IsValueType;
20552067

20562068
methodAfterConstraintResolution = directMethod;
2057-
Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface);
2069+
// When resolved to a default interface method, the owning type is the interface
2070+
Debug.Assert(!methodAfterConstraintResolution.OwningType.IsInterface || (isStaticVirtual && methodAfterConstraintResolution.Signature.IsStatic));
20582071
resolvedConstraint = true;
20592072
pResult->thisTransform = CORINFO_THIS_TRANSFORM.CORINFO_NO_THIS_TRANSFORM;
20602073

2061-
exactType = constrainedType;
2074+
exactType = (isStaticVirtual && dimResolution == DefaultInterfaceMethodResolution.DefaultImplementation)
2075+
? directMethod.OwningType
2076+
: constrainedType;
20622077
if (isStaticVirtual)
20632078
{
20642079
pResolvedToken.tokenType = CorInfoTokenKind.CORINFO_TOKENKIND_ResolvedStaticVirtualMethod;

src/coreclr/vm/jitinterface.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14507,24 +14507,36 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
1450714507
MethodDesc *pImplMethodRuntime;
1450814508
if (pDeclMethod->IsInterface())
1450914509
{
14510-
if (!thImpl.CanCastTo(pDeclMethod->GetMethodTable()))
14510+
if (pDeclMethod->IsStatic())
1451114511
{
14512-
MethodTable *pInterfaceTypeCanonical = pDeclMethod->GetMethodTable()->GetCanonicalMethodTable();
14513-
14514-
// Its possible for the decl method to need to be found on the only canonically compatible interface on the owning type
14515-
MethodTable::InterfaceMapIterator it = thImpl.GetMethodTable()->IterateInterfaceMap();
14516-
while (it.Next())
14512+
pImplMethodRuntime = thImpl.GetMethodTable()->ResolveVirtualStaticMethod(
14513+
pDeclMethod->GetMethodTable(),
14514+
pDeclMethod,
14515+
ResolveVirtualStaticMethodFlags::AllowNullResult |
14516+
ResolveVirtualStaticMethodFlags::AllowVariantMatches |
14517+
ResolveVirtualStaticMethodFlags::InstantiateResultOverFinalMethodDesc);
14518+
}
14519+
else
14520+
{
14521+
if (!thImpl.CanCastTo(pDeclMethod->GetMethodTable()))
1451714522
{
14518-
MethodTable *pItfInMap = it.GetInterface(thImpl.GetMethodTable());
14519-
if (pInterfaceTypeCanonical == pItfInMap->GetCanonicalMethodTable())
14523+
MethodTable *pInterfaceTypeCanonical = pDeclMethod->GetMethodTable()->GetCanonicalMethodTable();
14524+
14525+
// Its possible for the decl method to need to be found on the only canonically compatible interface on the owning type
14526+
MethodTable::InterfaceMapIterator it = thImpl.GetMethodTable()->IterateInterfaceMap();
14527+
while (it.Next())
1452014528
{
14521-
pDeclMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pDeclMethod, pItfInMap, FALSE, pDeclMethod->GetMethodInstantiation(), FALSE, TRUE);
14522-
break;
14529+
MethodTable *pItfInMap = it.GetInterface(thImpl.GetMethodTable());
14530+
if (pInterfaceTypeCanonical == pItfInMap->GetCanonicalMethodTable())
14531+
{
14532+
pDeclMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(pDeclMethod, pItfInMap, FALSE, pDeclMethod->GetMethodInstantiation(), FALSE, TRUE);
14533+
break;
14534+
}
1452314535
}
1452414536
}
14537+
DispatchSlot slot = thImpl.GetMethodTable()->FindDispatchSlotForInterfaceMD(pDeclMethod, /*throwOnConflict*/ FALSE);
14538+
pImplMethodRuntime = slot.GetMethodDesc();
1452514539
}
14526-
DispatchSlot slot = thImpl.GetMethodTable()->FindDispatchSlotForInterfaceMD(pDeclMethod, /*throwOnConflict*/ FALSE);
14527-
pImplMethodRuntime = slot.GetMethodDesc();
1452814540
}
1452914541
else
1453014542
{

0 commit comments

Comments
 (0)