From 61a278fdb44c5796dff07531cd585f4474c0e0ff Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 21:32:01 +0900 Subject: [PATCH 01/33] Devirtualize virtual methods that require an instantiating stub --- src/coreclr/vm/jitinterface.cpp | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index c0ec22f3e75a8f..4d167fb4a33624 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8762,15 +8762,6 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) info->detail = CORINFO_DEVIRTUALIZATION_FAILED_LOOKUP; return false; } - - // If we devirtualized into a default interface method on a generic type, we should actually return an - // instantiating stub but this is not happening. - // Making this work is tracked by https://github.com/dotnet/runtime/issues/9588 - if (pDevirtMD->GetMethodTable()->IsInterface() && pDevirtMD->HasClassInstantiation()) - { - info->detail = CORINFO_DEVIRTUALIZATION_FAILED_DIM; - return false; - } } else { @@ -8833,12 +8824,13 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) bool isArray = false; bool isGenericVirtual = false; - if (pApproxMT->IsInterface()) + if (pExactMT->IsInterface()) { - // As noted above, we can't yet handle generic interfaces - // with default methods. - _ASSERTE(!pDevirtMD->HasClassInstantiation()); - + if (pDevirtMD->HasClassInstantiation()) + { + info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)pExactMT; + info->instParamLookup.constLookup.accessType = IAT_VALUE; + } } else if (pBaseMT->IsInterface() && pObjMT->IsArray()) { @@ -8855,10 +8847,27 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) MethodDesc* pPrimaryMD = pDevirtMD; pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true); + if (pDevirtMD->IsSharedByGenericMethodInstantiations()) { - info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; + pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( + pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false); + + if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetClassInstantiation())) + { + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + + const bool requiresRuntimeLookup = TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetMethodInstantiation()); + if (requiresRuntimeLookup) + { + // TODO: Support for runtime lookup + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } } isGenericVirtual = true; From f01f4f478d436f287bae4cb753ddcccb8bc4b4f7 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 21:43:40 +0900 Subject: [PATCH 02/33] R2R support --- .../Compiler/DevirtualizationManager.cs | 16 ++++--- .../tools/Common/JitInterface/CorInfoImpl.cs | 43 ++++++++++++++++++- .../ReadyToRun/DevirtualizationManager.cs | 4 +- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index ac544ed02e3fea..1fb376fcb91274 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -148,6 +148,14 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType return null; case DefaultInterfaceMethodResolution.DefaultImplementation: +#if READYTORUN + if (declMethod != defaultInterfaceDispatchDeclMethod) + { + // Fail for variant default interface dispatch + devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; + return null; + } +#else if (dimMethod.OwningType.HasInstantiation || (declMethod != defaultInterfaceDispatchDeclMethod)) { // If we devirtualized into a default interface method on a generic type, we should actually return an @@ -158,6 +166,7 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; return null; } +#endif else { impl = dimMethod; @@ -219,13 +228,6 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType } } - if (impl != null && impl.HasInstantiation && impl.GetCanonMethodTarget(CanonicalFormKind.Specific).IsCanonicalMethod(CanonicalFormKind.Specific)) - { - // We don't support devirtualization of shared generic virtual methods yet. - devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; - impl = null; - } - return impl; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 5fc46db5a8978b..9ceed25708ae2e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1482,6 +1482,47 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN); } + + bool isArrayInterfaceDevirtualization = objType.IsArray && decl.OwningType.IsInterface; + bool isGenericVirtual = decl.HasInstantiation; + bool isGenericDim = impl.OwningType.IsInterface && !impl.IsAbstract && impl.RequiresInstMethodTableArg(); + + if ((isGenericVirtual || isGenericDim) && originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + + if (isGenericDim) + { +#if READYTORUN + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); +#else + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; +#endif + } + else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsCanonicalMethod(CanonicalFormKind.Any)) + { + bool requiresRuntimeLookup = originalImpl.IsSharedByGenericInstantiations; + if (requiresRuntimeLookup) + { + // TODO: Support for runtime lookup + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } +#if READYTORUN + MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodHandle, originalImplWithToken)); +#else + // TODO: Implement array interface and generic virtual method devirtualization constant lookup for NativeAOT + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; +#endif + } + #if READYTORUN // Testing has not shown that concerns about virtual matching are significant // Only generate verification for builds with the stress mode enabled @@ -1496,7 +1537,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #endif info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_SUCCESS; info->devirtualizedMethod = ObjectToHandle(impl); - info->tokenLookupContext = contextFromType(owningType); + info->tokenLookupContext = (isArrayInterfaceDevirtualization || isGenericVirtual) ? contextFromMethod(originalImpl) : contextFromType(owningType); return true; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs index 7d0275ff2eaaee..e6e329e5b32663 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs @@ -45,9 +45,7 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp // // Result method checking // 1. Ensure that the resolved result versions with the code, or is the decl method - // 2. Devirtualizing to a default interface method is not currently considered to be useful, and how to check for version - // resilience has not yet been analyzed. - // 3. When checking that the resolved result versions with the code, validate that all of the types + // 2. When checking that the resolved result versions with the code, validate that all of the types // From implType to the owning type of resolved result method also version with the code. bool declMethodCheckFailed; From b42c29153a94e34f1886e406e3ce396139a203f4 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 22:30:16 +0900 Subject: [PATCH 03/33] Bail shared MT for generic DIM as well --- src/coreclr/vm/jitinterface.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4d167fb4a33624..5e7d79fefc700b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8828,6 +8828,14 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) { if (pDevirtMD->HasClassInstantiation()) { + if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetClassInstantiation())) + { + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)pExactMT; info->instParamLookup.constLookup.accessType = IAT_VALUE; } From b96fc2e697f9f5f577707251d2b5ec6b7c0b5db5 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 22:48:30 +0900 Subject: [PATCH 04/33] Stop returning an instantiating stub --- src/coreclr/vm/jitinterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 5e7d79fefc700b..beb317e730e876 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8890,12 +8890,13 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) } info->tokenLookupContext = MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pDevirtMD); - pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; } else { info->tokenLookupContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); } + + pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; // Success! Pass back the results. // From de840c22fceb7c99f134a3b2739e93619704f77f Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:15:16 +0900 Subject: [PATCH 05/33] Nit --- src/coreclr/vm/jitinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index beb317e730e876..a2f0e472bcc7b6 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8824,7 +8824,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) bool isArray = false; bool isGenericVirtual = false; - if (pExactMT->IsInterface()) + if (pApproxMT->IsInterface()) { if (pDevirtMD->HasClassInstantiation()) { From 181e88ee4a73bc99ae31b7497191f4c559a92a16 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:17:46 +0900 Subject: [PATCH 06/33] Nit 2 --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 9ceed25708ae2e..30ba55c7869a07 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1500,7 +1500,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #if READYTORUN info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); #else - info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; return false; #endif } From 9331cc106570d5071f7a265ce8c8fee7697d54af Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:18:31 +0900 Subject: [PATCH 07/33] Remove redundant empty line --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 30ba55c7869a07..a80328f469daae 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1482,7 +1482,6 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN); } - bool isArrayInterfaceDevirtualization = objType.IsArray && decl.OwningType.IsInterface; bool isGenericVirtual = decl.HasInstantiation; bool isGenericDim = impl.OwningType.IsInterface && !impl.IsAbstract && impl.RequiresInstMethodTableArg(); From f6356869b188d30dfd72eda84f3f1f57984c43ae Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:28:35 +0900 Subject: [PATCH 08/33] Generic DIM devirt for NativeAOT --- .../Common/Compiler/DevirtualizationManager.cs | 13 ------------- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index 1fb376fcb91274..a8a83cedec3bb5 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -148,25 +148,12 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType return null; case DefaultInterfaceMethodResolution.DefaultImplementation: -#if READYTORUN if (declMethod != defaultInterfaceDispatchDeclMethod) { // Fail for variant default interface dispatch devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; return null; } -#else - if (dimMethod.OwningType.HasInstantiation || (declMethod != defaultInterfaceDispatchDeclMethod)) - { - // If we devirtualized into a default interface method on a generic type, we should actually return an - // instantiating stub but this is not happening. - // Making this work is tracked by https://github.com/dotnet/runtime/issues/9588 - - // In addition, we fail here for variant default interface dispatch - devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; - return null; - } -#endif else { impl = dimMethod; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index a80328f469daae..4cb789c2e59af1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1499,7 +1499,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #if READYTORUN info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); #else - info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM; + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); return false; #endif } From 5da42c52152ebd6b2bd1d6309f84581d06199a2b Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:29:05 +0900 Subject: [PATCH 09/33] Meh --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 4cb789c2e59af1..2adffdc85918c9 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1500,7 +1500,6 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); #else info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); - return false; #endif } else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsCanonicalMethod(CanonicalFormKind.Any)) From 66c8d6a5bff3a34958d1b8edcc2fb4370260ec49 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:39:53 +0900 Subject: [PATCH 10/33] More NativeAOT support --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 +--- src/coreclr/vm/jitinterface.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 2adffdc85918c9..baa36c4f0e56e5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1515,9 +1515,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodHandle, originalImplWithToken)); #else - // TODO: Implement array interface and generic virtual method devirtualization constant lookup for NativeAOT - info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); #endif } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a2f0e472bcc7b6..b104699e6c7600 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8895,7 +8895,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) { info->tokenLookupContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); } - + pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; // Success! Pass back the results. From 152c44a903f4cab79f15c5ec4c1088c6cc3012d6 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:49:54 +0900 Subject: [PATCH 11/33] Address an assertion --- .../tools/Common/JitInterface/CorInfoImpl.cs | 4 ++-- .../MethodDesc.RuntimeDetermined.cs | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index baa36c4f0e56e5..21c978b9de52c5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1502,9 +1502,9 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); #endif } - else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsCanonicalMethod(CanonicalFormKind.Any)) + else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsMethodSharedByGenericInstantiations) { - bool requiresRuntimeLookup = originalImpl.IsSharedByGenericInstantiations; + bool requiresRuntimeLookup = originalImpl.IsMethodSharedByGenericInstantiations; if (requiresRuntimeLookup) { // TODO: Support for runtime lookup diff --git a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs index 2a66359448f0a4..624e7e44b35a5b 100644 --- a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs +++ b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs @@ -69,6 +69,26 @@ public bool IsSharedByGenericInstantiations } } + /// + /// Gets a value indicating whether this is a shared method body. + /// Similar to but this doesn't consider the owning type's generic instantiation. + /// + public bool IsMethodSharedByGenericInstantiations + { + get + { + foreach (TypeDesc type in Instantiation) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + return true; + } + } + + return false; + } + } + /// /// Gets a value indicating whether this is a canonical method that will only become concrete /// at runtime (after supplying the generic context). From 466ba2c16d9511eeaa051cf8ca0f8018c12c0f76 Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 28 May 2026 23:50:36 +0900 Subject: [PATCH 12/33] Use IsSharedByGenericMethodInstantiations --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 4 ++-- .../RuntimeDetermined/MethodDesc.RuntimeDetermined.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 21c978b9de52c5..45174412090d75 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1502,9 +1502,9 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); #endif } - else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsMethodSharedByGenericInstantiations) + else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsSharedByGenericMethodInstantiations) { - bool requiresRuntimeLookup = originalImpl.IsMethodSharedByGenericInstantiations; + bool requiresRuntimeLookup = originalImpl.IsSharedByGenericMethodInstantiations; if (requiresRuntimeLookup) { // TODO: Support for runtime lookup diff --git a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs index 624e7e44b35a5b..528dac156da9a9 100644 --- a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs +++ b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs @@ -73,7 +73,7 @@ public bool IsSharedByGenericInstantiations /// Gets a value indicating whether this is a shared method body. /// Similar to but this doesn't consider the owning type's generic instantiation. /// - public bool IsMethodSharedByGenericInstantiations + public bool IsSharedByGenericMethodInstantiations { get { From dca1834326efd3835f3e6ff998ce4c2125bd3728 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 29 May 2026 01:06:36 +0900 Subject: [PATCH 13/33] Add a couple of tests --- .../devirtualization/github39419.cs | 36 +++++++++++++++++++ .../devirtualization/github39419.csproj | 11 ++++++ .../devirtualization/inline.cs | 26 ++++++++++++++ .../devirtualization/inline.csproj | 11 ++++++ .../devirtualization/simple.cs | 31 ++++++++++++++++ .../devirtualization/simple.csproj | 11 ++++++ .../devirtualization/singlenongeneric.cs | 24 +++++++++++++ .../devirtualization/singlenongeneric.csproj | 11 ++++++ .../devirtualization/singleoverriding.cs | 25 +++++++++++++ .../devirtualization/singleoverriding.csproj | 11 ++++++ .../singleoverridinggeneric.cs | 25 +++++++++++++ .../singleoverridinggeneric.csproj | 11 ++++++ .../devirtualization/singleoverstruct.cs | 24 +++++++++++++ .../devirtualization/singleoverstruct.csproj | 11 ++++++ .../devirtualization/singlevaluetypenondim.cs | 25 +++++++++++++ .../singlevaluetypenondim.csproj | 11 ++++++ .../devirtualization/singlevariance.cs | 24 +++++++++++++ .../devirtualization/singlevariance.csproj | 11 ++++++ .../devirtualization/snondimstruct.cs | 22 ++++++++++++ .../devirtualization/snondimstruct.csproj | 11 ++++++ .../devirtualization/staticshared.cs | 19 ++++++++++ .../devirtualization/staticshared.csproj | 11 ++++++ .../devirtualization/structs.cs | 31 ++++++++++++++++ .../devirtualization/structs.csproj | 11 ++++++ 24 files changed, 444 insertions(+) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs new file mode 100644 index 00000000000000..8a611b3fbef284 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface IM +{ + bool UseDefaultM { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => true; } + ValueTask M(T instance) => throw new NotImplementedException("M must be implemented if UseDefaultM is false"); + static ValueTask DefaultM(T instance) + { + return default; + } +} + +struct M : IM { } + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var m = new M(); + if (((IM)m).UseDefaultM) + { + IM.DefaultM(42); + return; + } + else + { + ((IM)m).M(42); + } + throw new UnreachableException(); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj new file mode 100644 index 00000000000000..6eeef117cf6b22 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs new file mode 100644 index 00000000000000..c43a13315f0bac --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I where T : IComparable +{ + T GetAt(int i, T[] tx) => tx[i]; +} + +class C : I +{ +} + +public static class Program +{ + private static string[] tx = new string[] { "test" }; + + [Fact] + public static void TestEntryPoint() + { + I c = new C(); + var dcs = c.GetAt(0, tx); + Assert.Equal("test", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj new file mode 100644 index 00000000000000..b8a4a14ab89dab --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs new file mode 100644 index 00000000000000..0d96d46bd09252 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class Dummy { } + +class C : I, I, I +{ + string I.DefaultTypeOf() => "C.Dummy"; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + var dos = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dos); + var dds = ((I)c).DefaultTypeOf(); + Assert.Equal("C.Dummy", dds); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj new file mode 100644 index 00000000000000..c0b27ba716c988 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs new file mode 100644 index 00000000000000..4deff94ccc5e94 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(string).Name; +} + +class C : I +{ +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj new file mode 100644 index 00000000000000..e2b70bcd7df723 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs new file mode 100644 index 00000000000000..b2bb95f36434e1 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class C : I +{ + public string DefaultTypeOf() => "C.String"; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj new file mode 100644 index 00000000000000..f4f4302b148d01 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs new file mode 100644 index 00000000000000..31d8ec3549d629 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class C : I +{ + public string DefaultTypeOf() => "C." + typeof(T).Name; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj new file mode 100644 index 00000000000000..43285ec1c5afc7 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs new file mode 100644 index 00000000000000..91560c952ed6fd --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class C : I +{ +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("Int32", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj new file mode 100644 index 00000000000000..e7d13cf00c25f6 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs new file mode 100644 index 00000000000000..d401949fc5f577 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf(); +} + +struct C : I +{ + public string DefaultTypeOf() => "C." + typeof(T).Name; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj new file mode 100644 index 00000000000000..8b76d9a4f2e880 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs new file mode 100644 index 00000000000000..114fcbcf8933d1 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class C : I +{ +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj new file mode 100644 index 00000000000000..2bd3f7a79a671c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs new file mode 100644 index 00000000000000..f384f31c02d756 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + + +struct C +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public string DefaultTypeOf() => typeof(T).Name; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = c.DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj new file mode 100644 index 00000000000000..c7f805adcc4b03 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs new file mode 100644 index 00000000000000..f33c8b8aaf0fcd --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +class C +{ + public static string DefaultTypeOf() => typeof(T).Name; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var dcs = C.DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj new file mode 100644 index 00000000000000..1620cacc449e07 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs new file mode 100644 index 00000000000000..bbb79c7c3f0313 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +interface I +{ + string DefaultTypeOf() => typeof(T).Name; +} + +class Dummy { } + +struct C : I, I, I +{ + string I.DefaultTypeOf() => "C.Dummy"; +} + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + var dos = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dos); + var dds = ((I)c).DefaultTypeOf(); + Assert.Equal("C.Dummy", dds); + } +} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj new file mode 100644 index 00000000000000..79889f0c9b5077 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + From 2582ba6065e7b4c5056ed05c39d9329476e65c1e Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 00:07:51 +0900 Subject: [PATCH 14/33] Address test name collisions --- .../{github39419.cs => dim_devirt_github39419.cs} | 0 ...thub39419.csproj => dim_devirt_github39419.csproj} | 2 +- .../{inline.cs => dim_devirt_inline.cs} | 0 .../{inline.csproj => dim_devirt_inline.csproj} | 2 +- .../{simple.cs => dim_devirt_simple.cs} | 0 .../{simple.csproj => dim_devirt_simple.csproj} | 2 +- ...lenongeneric.cs => dim_devirt_singlenongeneric.cs} | 0 ...eric.csproj => dim_devirt_singlenongeneric.csproj} | 2 +- ...leoverriding.cs => dim_devirt_singleoverriding.cs} | 0 .../dim_devirt_singleoverriding.csproj | 11 +++++++++++ ...neric.cs => dim_devirt_singleoverridinggeneric.cs} | 0 .../dim_devirt_singleoverridinggeneric.csproj | 11 +++++++++++ ...leoverstruct.cs => dim_devirt_singleoverstruct.cs} | 0 .../dim_devirt_singleoverstruct.csproj | 11 +++++++++++ ...enondim.cs => dim_devirt_singlevaluetypenondim.cs} | 0 .../dim_devirt_singlevaluetypenondim.csproj | 11 +++++++++++ ...singlevariance.cs => dim_devirt_singlevariance.cs} | 0 .../devirtualization/dim_devirt_singlevariance.csproj | 11 +++++++++++ .../{snondimstruct.cs => dim_devirt_snondimstruct.cs} | 0 .../devirtualization/dim_devirt_snondimstruct.csproj | 11 +++++++++++ .../{staticshared.cs => dim_devirt_staticshared.cs} | 0 .../devirtualization/dim_devirt_staticshared.csproj | 11 +++++++++++ .../{structs.cs => dim_devirt_structs.cs} | 0 .../devirtualization/dim_devirt_structs.csproj | 11 +++++++++++ .../devirtualization/singleoverriding.csproj | 11 ----------- .../devirtualization/singleoverridinggeneric.csproj | 11 ----------- .../devirtualization/singleoverstruct.csproj | 11 ----------- .../devirtualization/singlevaluetypenondim.csproj | 11 ----------- .../devirtualization/singlevariance.csproj | 11 ----------- .../devirtualization/snondimstruct.csproj | 11 ----------- .../devirtualization/staticshared.csproj | 11 ----------- .../devirtualization/structs.csproj | 11 ----------- 32 files changed, 92 insertions(+), 92 deletions(-) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{github39419.cs => dim_devirt_github39419.cs} (100%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{github39419.csproj => dim_devirt_github39419.csproj} (82%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{inline.cs => dim_devirt_inline.cs} (100%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{inline.csproj => dim_devirt_inline.csproj} (82%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{simple.cs => dim_devirt_simple.cs} (100%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{simple.csproj => dim_devirt_simple.csproj} (82%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singlenongeneric.cs => dim_devirt_singlenongeneric.cs} (100%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singlenongeneric.csproj => dim_devirt_singlenongeneric.csproj} (82%) rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singleoverriding.cs => dim_devirt_singleoverriding.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singleoverridinggeneric.cs => dim_devirt_singleoverridinggeneric.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singleoverstruct.cs => dim_devirt_singleoverstruct.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singlevaluetypenondim.cs => dim_devirt_singlevaluetypenondim.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{singlevariance.cs => dim_devirt_singlevariance.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{snondimstruct.cs => dim_devirt_snondimstruct.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{staticshared.cs => dim_devirt_staticshared.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{structs.cs => dim_devirt_structs.cs} (100%) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.csproj similarity index 82% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.csproj index 6eeef117cf6b22..30d4804022360c 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/github39419.csproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.csproj @@ -3,7 +3,7 @@ 1 - + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj similarity index 82% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj index b8a4a14ab89dab..30d4804022360c 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/inline.csproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj @@ -3,7 +3,7 @@ 1 - + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj similarity index 82% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj index c0b27ba716c988..30d4804022360c 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/simple.csproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj @@ -3,7 +3,7 @@ 1 - + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj similarity index 82% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj index e2b70bcd7df723..30d4804022360c 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlenongeneric.csproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj @@ -3,7 +3,7 @@ 1 - + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.cs rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj deleted file mode 100644 index f4f4302b148d01..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverriding.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj deleted file mode 100644 index 43285ec1c5afc7..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverridinggeneric.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj deleted file mode 100644 index e7d13cf00c25f6..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singleoverstruct.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj deleted file mode 100644 index 8b76d9a4f2e880..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevaluetypenondim.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj deleted file mode 100644 index 2bd3f7a79a671c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/singlevariance.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj deleted file mode 100644 index c7f805adcc4b03..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/snondimstruct.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj deleted file mode 100644 index 1620cacc449e07..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/staticshared.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj deleted file mode 100644 index 79889f0c9b5077..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/structs.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - From 800aa20f25514f2ba0abb72d8c9bc9f06ac5371f Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 10:45:17 +0900 Subject: [PATCH 15/33] Fix crossgen2 not producing a const lookup --- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 ++++-- .../MethodDesc.RuntimeDetermined.cs | 20 ------------------- src/coreclr/vm/jitinterface.cpp | 2 ++ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 45174412090d75..56aee7a65ce7d0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1502,9 +1502,11 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); #endif } - else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsSharedByGenericMethodInstantiations) + else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsSharedByGenericInstantiations) { - bool requiresRuntimeLookup = originalImpl.IsSharedByGenericMethodInstantiations; + // We are dealing with a generic virtual method whose owning type is exact. + // If the method is not exact, a runtime lookup would be required. + bool requiresRuntimeLookup = originalImpl.IsSharedByGenericInstantiations; if (requiresRuntimeLookup) { // TODO: Support for runtime lookup diff --git a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs index 528dac156da9a9..2a66359448f0a4 100644 --- a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs +++ b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs @@ -69,26 +69,6 @@ public bool IsSharedByGenericInstantiations } } - /// - /// Gets a value indicating whether this is a shared method body. - /// Similar to but this doesn't consider the owning type's generic instantiation. - /// - public bool IsSharedByGenericMethodInstantiations - { - get - { - foreach (TypeDesc type in Instantiation) - { - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - return true; - } - } - - return false; - } - } - /// /// Gets a value indicating whether this is a canonical method that will only become concrete /// at runtime (after supplying the generic context). diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b104699e6c7600..7c629b79b28ec7 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8869,6 +8869,8 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) return false; } + // We are dealing with a generic virtual method whose owning type is exact. + // If the method is not exact, a runtime lookup would be required. const bool requiresRuntimeLookup = TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetMethodInstantiation()); if (requiresRuntimeLookup) { From 31ff0062a401a9b8ad938337550e4390c3cae10e Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 11:47:14 +0900 Subject: [PATCH 16/33] Fix NativeAOT build --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 56aee7a65ce7d0..ffc98c161a4958 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1517,7 +1517,14 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodHandle, originalImplWithToken)); #else - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); + if (originalImpl.HasInstantiation) + { + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); + } + else + { + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); + } #endif } From 645e5dd18bd75f48b1d663de389789f33e9545f8 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 12:05:19 +0900 Subject: [PATCH 17/33] Pass a type dictionary fixup when the method doesn't have an instantiation --- .../tools/Common/JitInterface/CorInfoImpl.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index ffc98c161a4958..8a80145504b039 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1513,25 +1513,32 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; return false; } -#if READYTORUN - MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodHandle, originalImplWithToken)); -#else + if (originalImpl.HasInstantiation) { +#if READYTORUN + MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken)); + +#else info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); +#endif } else { +#if READYTORUN + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); + +#else info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); - } #endif + } } #if READYTORUN - // Testing has not shown that concerns about virtual matching are significant - // Only generate verification for builds with the stress mode enabled - if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout) + // Testing has not shown that concerns about virtual matching are significant + // Only generate verification for builds with the stress mode enabled + if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout) { if (!methodWithTokenDecl.Method.OwningType.IsValueType || !methodWithTokenImpl.Method.OwningType.IsValueType) { From 1f2aacc4b06b4540fdceecd6687d4d15addb1e2e Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 12:08:05 +0900 Subject: [PATCH 18/33] Nit --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 8a80145504b039..d6e25bf62d4b65 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1536,9 +1536,9 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) } #if READYTORUN - // Testing has not shown that concerns about virtual matching are significant - // Only generate verification for builds with the stress mode enabled - if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout) + // Testing has not shown that concerns about virtual matching are significant + // Only generate verification for builds with the stress mode enabled + if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout) { if (!methodWithTokenDecl.Method.OwningType.IsValueType || !methodWithTokenImpl.Method.OwningType.IsValueType) { From cad1140934ecc10f5fe95323214795745a5c79a4 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 14:04:53 +0900 Subject: [PATCH 19/33] More fixes to AOT --- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../MethodDesc.RuntimeDetermined.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index d6e25bf62d4b65..7108e3a791e76b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1514,7 +1514,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) return false; } - if (originalImpl.HasInstantiation) + if (impl.IsSharedByGenericMethodInstantiations) { #if READYTORUN MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); diff --git a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs index 2a66359448f0a4..528dac156da9a9 100644 --- a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs +++ b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs @@ -69,6 +69,26 @@ public bool IsSharedByGenericInstantiations } } + /// + /// Gets a value indicating whether this is a shared method body. + /// Similar to but this doesn't consider the owning type's generic instantiation. + /// + public bool IsSharedByGenericMethodInstantiations + { + get + { + foreach (TypeDesc type in Instantiation) + { + if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + return true; + } + } + + return false; + } + } + /// /// Gets a value indicating whether this is a canonical method that will only become concrete /// at runtime (after supplying the generic context). From 0c7102a6ee686d628c6d1a52471ebb60b40fa881 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 14:15:40 +0900 Subject: [PATCH 20/33] Use a better helper --- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../MethodDesc.RuntimeDetermined.cs | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 7108e3a791e76b..e72d3ff5996d8c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1514,7 +1514,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) return false; } - if (impl.IsSharedByGenericMethodInstantiations) + if (impl.RequiresInstMethodDescArg()) { #if READYTORUN MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); diff --git a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs index 528dac156da9a9..2a66359448f0a4 100644 --- a/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs +++ b/src/coreclr/tools/Common/TypeSystem/RuntimeDetermined/MethodDesc.RuntimeDetermined.cs @@ -69,26 +69,6 @@ public bool IsSharedByGenericInstantiations } } - /// - /// Gets a value indicating whether this is a shared method body. - /// Similar to but this doesn't consider the owning type's generic instantiation. - /// - public bool IsSharedByGenericMethodInstantiations - { - get - { - foreach (TypeDesc type in Instantiation) - { - if (type.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - return true; - } - } - - return false; - } - } - /// /// Gets a value indicating whether this is a canonical method that will only become concrete /// at runtime (after supplying the generic context). From 8f0275089389d21a66355b46b8417f1cba3303ac Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 14:27:37 +0900 Subject: [PATCH 21/33] Minor refactor --- .../tools/Common/JitInterface/CorInfoImpl.cs | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index e72d3ff5996d8c..1b9b130b489e24 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1482,57 +1482,42 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN); } - bool isArrayInterfaceDevirtualization = objType.IsArray && decl.OwningType.IsInterface; - bool isGenericVirtual = decl.HasInstantiation; - bool isGenericDim = impl.OwningType.IsInterface && !impl.IsAbstract && impl.RequiresInstMethodTableArg(); - - if ((isGenericVirtual || isGenericDim) && originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + if (!impl.AcquiresInstMethodTableFromThis()) { - // If we end up with a shared MethodTable that is not exact, - // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. - info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; - } + if (originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) + { + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } - if (isGenericDim) - { -#if READYTORUN - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); -#else - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); -#endif - } - else if ((isArrayInterfaceDevirtualization || isGenericVirtual) && impl.IsSharedByGenericInstantiations) - { - // We are dealing with a generic virtual method whose owning type is exact. - // If the method is not exact, a runtime lookup would be required. - bool requiresRuntimeLookup = originalImpl.IsSharedByGenericInstantiations; - if (requiresRuntimeLookup) + if (originalImpl.IsSharedByGenericInstantiations) { // TODO: Support for runtime lookup info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; return false; } + } - if (impl.RequiresInstMethodDescArg()) - { + if (impl.RequiresInstMethodDescArg()) + { #if READYTORUN - MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken)); + MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken)); #else - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); #endif - } - else - { + } + else if (impl.RequiresInstMethodTableArg()) + { #if READYTORUN - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); #else - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); #endif - } } #if READYTORUN @@ -1549,7 +1534,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #endif info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_SUCCESS; info->devirtualizedMethod = ObjectToHandle(impl); - info->tokenLookupContext = (isArrayInterfaceDevirtualization || isGenericVirtual) ? contextFromMethod(originalImpl) : contextFromType(owningType); + info->tokenLookupContext = impl.RequiresInstMethodDescArg() ? contextFromMethod(originalImpl) : contextFromType(owningType); return true; From e27aba9a9e19987c958e40d58380f818afab21a7 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 16:10:18 +0900 Subject: [PATCH 22/33] Handle unboxing stub --- src/coreclr/jit/importercalls.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f4ce0bd388072f..2fb882205ce189 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -9414,26 +9414,24 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, } } } - else + + if (instParam == nullptr && dcInfo->pMethSig->hasTypeArg()) { - if (dcInfo->pMethSig->hasTypeArg()) + if (((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD) { - if (((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD) - { - CORINFO_METHOD_HANDLE exactMethodHandle = - (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); + CORINFO_METHOD_HANDLE exactMethodHandle = + (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); - instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); - } - else - { - assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); + instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); + } + else + { + assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); - CORINFO_CLASS_HANDLE exactClassHandle = - (CORINFO_CLASS_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); + CORINFO_CLASS_HANDLE exactClassHandle = + (CORINFO_CLASS_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); - instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle); - } + instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle); } } From b77ad2d77e152682eeff5f8b2ef703ca368cd98b Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 30 May 2026 16:17:21 +0900 Subject: [PATCH 23/33] Bail out unboxing stub in R2R for now --- src/coreclr/jit/importercalls.cpp | 28 ++++++++++--------- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 ++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 2fb882205ce189..f4ce0bd388072f 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -9414,24 +9414,26 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, } } } - - if (instParam == nullptr && dcInfo->pMethSig->hasTypeArg()) + else { - if (((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD) + if (dcInfo->pMethSig->hasTypeArg()) { - CORINFO_METHOD_HANDLE exactMethodHandle = - (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); + if (((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD) + { + CORINFO_METHOD_HANDLE exactMethodHandle = + (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); - instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); - } - else - { - assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); + instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); + } + else + { + assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS); - CORINFO_CLASS_HANDLE exactClassHandle = - (CORINFO_CLASS_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); + CORINFO_CLASS_HANDLE exactClassHandle = + (CORINFO_CLASS_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); - instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle); + instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_CLASS_HDL, exactClassHandle); + } } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 1b9b130b489e24..b4281b43e2da9b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1502,6 +1502,12 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) if (impl.RequiresInstMethodDescArg()) { + if (unboxingStub) + { + // Bail out for now. We need an unboxing stub that points to an instantiated method. + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } #if READYTORUN MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null); info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken)); From de3bd27e3de8bc7413265f2c1c30b8f1051d9cd0 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sun, 31 May 2026 17:31:37 +0900 Subject: [PATCH 24/33] Clean up tests --- .../dim_devirt_github39419.cs | 36 --- .../devirtualization/dim_devirt_inline.cs | 26 -- .../devirtualization/dim_devirt_inline.csproj | 11 - .../devirtualization/dim_devirt_simple.cs | 31 -- .../devirtualization/dim_devirt_simple.csproj | 11 - .../dim_devirt_singlenongeneric.cs | 24 -- .../dim_devirt_singlenongeneric.csproj | 11 - .../dim_devirt_singleoverriding.cs | 25 -- .../dim_devirt_singleoverriding.csproj | 11 - .../dim_devirt_singleoverridinggeneric.cs | 25 -- .../dim_devirt_singleoverridinggeneric.csproj | 11 - .../dim_devirt_singleoverstruct.cs | 24 -- .../dim_devirt_singleoverstruct.csproj | 11 - .../dim_devirt_singlevaluetypenondim.cs | 25 -- .../dim_devirt_singlevaluetypenondim.csproj | 11 - .../dim_devirt_singlevariance.cs | 24 -- .../dim_devirt_singlevariance.csproj | 11 - .../dim_devirt_snondimstruct.cs | 22 -- .../dim_devirt_snondimstruct.csproj | 11 - .../dim_devirt_staticshared.cs | 19 -- .../dim_devirt_staticshared.csproj | 11 - .../devirtualization/dim_devirt_structs.cs | 31 -- .../dim_devirt_structs.csproj | 11 - .../devirtualization/dim_devirtualization.cs | 275 ++++++++++++++++++ ...419.csproj => dim_devirtualization.csproj} | 0 25 files changed, 275 insertions(+), 433 deletions(-) delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs delete mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs rename src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/{dim_devirt_github39419.csproj => dim_devirtualization.csproj} (100%) diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs deleted file mode 100644 index 8a611b3fbef284..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface IM -{ - bool UseDefaultM { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => true; } - ValueTask M(T instance) => throw new NotImplementedException("M must be implemented if UseDefaultM is false"); - static ValueTask DefaultM(T instance) - { - return default; - } -} - -struct M : IM { } - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var m = new M(); - if (((IM)m).UseDefaultM) - { - IM.DefaultM(42); - return; - } - else - { - ((IM)m).M(42); - } - throw new UnreachableException(); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs deleted file mode 100644 index c43a13315f0bac..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I where T : IComparable -{ - T GetAt(int i, T[] tx) => tx[i]; -} - -class C : I -{ -} - -public static class Program -{ - private static string[] tx = new string[] { "test" }; - - [Fact] - public static void TestEntryPoint() - { - I c = new C(); - var dcs = c.GetAt(0, tx); - Assert.Equal("test", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_inline.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs deleted file mode 100644 index 0d96d46bd09252..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class Dummy { } - -class C : I, I, I -{ - string I.DefaultTypeOf() => "C.Dummy"; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("String", dcs); - var dos = ((I)c).DefaultTypeOf(); - Assert.Equal("Object", dos); - var dds = ((I)c).DefaultTypeOf(); - Assert.Equal("C.Dummy", dds); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_simple.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs deleted file mode 100644 index 4deff94ccc5e94..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(string).Name; -} - -class C : I -{ -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlenongeneric.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs deleted file mode 100644 index b2bb95f36434e1..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class C : I -{ - public string DefaultTypeOf() => "C.String"; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("C.String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverriding.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs deleted file mode 100644 index 31d8ec3549d629..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class C : I -{ - public string DefaultTypeOf() => "C." + typeof(T).Name; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("C.String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverridinggeneric.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs deleted file mode 100644 index 91560c952ed6fd..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class C : I -{ -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("Int32", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singleoverstruct.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs deleted file mode 100644 index d401949fc5f577..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf(); -} - -struct C : I -{ - public string DefaultTypeOf() => "C." + typeof(T).Name; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("C.String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevaluetypenondim.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs deleted file mode 100644 index 114fcbcf8933d1..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class C : I -{ -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("Object", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_singlevariance.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs deleted file mode 100644 index f384f31c02d756..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - - -struct C -{ - [MethodImpl(MethodImplOptions.NoInlining)] - public string DefaultTypeOf() => typeof(T).Name; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = c.DefaultTypeOf(); - Assert.Equal("String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_snondimstruct.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs deleted file mode 100644 index f33c8b8aaf0fcd..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -class C -{ - public static string DefaultTypeOf() => typeof(T).Name; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var dcs = C.DefaultTypeOf(); - Assert.Equal("String", dcs); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_staticshared.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs deleted file mode 100644 index bbb79c7c3f0313..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Xunit; - -interface I -{ - string DefaultTypeOf() => typeof(T).Name; -} - -class Dummy { } - -struct C : I, I, I -{ - string I.DefaultTypeOf() => "C.Dummy"; -} - -public static class Program -{ - [Fact] - public static void TestEntryPoint() - { - var c = new C(); - var dcs = ((I)c).DefaultTypeOf(); - Assert.Equal("String", dcs); - var dos = ((I)c).DefaultTypeOf(); - Assert.Equal("Object", dos); - var dds = ((I)c).DefaultTypeOf(); - Assert.Equal("C.Dummy", dds); - } -} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj deleted file mode 100644 index 30d4804022360c..00000000000000 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_structs.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - 1 - - - - - - - - diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs new file mode 100644 index 00000000000000..5a0b517304adb8 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs @@ -0,0 +1,275 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +public static class Program +{ + [Fact] + public static void TestEntryPoint() + { + GitHubIssue39419RegressionCase.Run(); + ConstrainedGenericInlineCase.Run(); + MultiInterfaceDefaultMethodCases.Run(); + NonGenericDefaultMethodCase.Run(); + OverriddenDefaultMethodCases.Run(); + ValueTypeArgumentDefaultMethodCase.Run(); + StructNonDefaultImplementationCase.Run(); + ContravariantDefaultMethodCase.Run(); + DirectStructMethodCase.Run(); + StaticGenericMethodCase.Run(); + } +} + +static class GitHubIssue39419RegressionCase +{ + interface IM + { + bool UseDefaultM + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => true; + } + + ValueTask M(T instance) => throw new NotImplementedException("M must be implemented if UseDefaultM is false"); + + static ValueTask DefaultM(T instance) + { + return default; + } + } + + struct M : IM + { + } + + public static void Run() + { + var m = new M(); + if (((IM)m).UseDefaultM) + { + IM.DefaultM(42); + return; + } + + ((IM)m).M(42); + throw new UnreachableException(); + } +} + +static class ConstrainedGenericInlineCase +{ + interface I where T : IComparable + { + T GetAt(int i, T[] tx) => tx[i]; + } + + class C : I + { + } + + private static readonly string[] s_values = new string[] { "test" }; + + public static void Run() + { + I c = new C(); + var dcs = c.GetAt(0, s_values); + Assert.Equal("test", dcs); + } +} + +static class MultiInterfaceDefaultMethodCases +{ + interface I + { + string DefaultTypeOf() => typeof(T).Name; + } + + class Dummy + { + } + + class ClassImplementation : I, I, I + { + string I.DefaultTypeOf() => "C.Dummy"; + } + + struct StructImplementation : I, I, I + { + string I.DefaultTypeOf() => "C.Dummy"; + } + + public static void Run() + { + RunClassImplementation(); + RunStructImplementation(); + } + + static void RunClassImplementation() + { + var c = new ClassImplementation(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + var dos = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dos); + var dds = ((I)c).DefaultTypeOf(); + Assert.Equal("C.Dummy", dds); + } + + static void RunStructImplementation() + { + var c = new StructImplementation(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + var dos = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dos); + var dds = ((I)c).DefaultTypeOf(); + Assert.Equal("C.Dummy", dds); + } +} + +static class NonGenericDefaultMethodCase +{ + interface I + { + string DefaultTypeOf() => typeof(string).Name; + } + + class C : I + { + } + + public static void Run() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} + +static class OverriddenDefaultMethodCases +{ + interface I + { + string DefaultTypeOf() => typeof(T).Name; + } + class C : I + { + public string DefaultTypeOf() => "C.String"; + } + + class GenericC : I + { + public string DefaultTypeOf() => "C." + typeof(T).Name; + } + + public static void Run() + { + RunNonGenericOverride(); + RunGenericOverride(); + } + + static void RunNonGenericOverride() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } + + static void RunGenericOverride() + { + var c = new GenericC(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } +} + +static class ValueTypeArgumentDefaultMethodCase +{ + interface I + { + string DefaultTypeOf() => typeof(T).Name; + } + + class C : I + { + } + + public static void Run() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("Int32", dcs); + } +} + +static class StructNonDefaultImplementationCase +{ + interface I + { + string DefaultTypeOf(); + } + + struct C : I + { + public string DefaultTypeOf() => "C." + typeof(T).Name; + } + + public static void Run() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("C.String", dcs); + } +} + +static class ContravariantDefaultMethodCase +{ + interface I + { + string DefaultTypeOf() => typeof(T).Name; + } + + class C : I + { + } + + public static void Run() + { + var c = new C(); + var dcs = ((I)c).DefaultTypeOf(); + Assert.Equal("Object", dcs); + } +} + +static class DirectStructMethodCase +{ + struct C + { + [MethodImpl(MethodImplOptions.NoInlining)] + public string DefaultTypeOf() => typeof(T).Name; + } + + public static void Run() + { + var c = new C(); + var dcs = c.DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} + +static class StaticGenericMethodCase +{ + class C + { + public static string DefaultTypeOf() => typeof(T).Name; + } + + public static void Run() + { + var dcs = C.DefaultTypeOf(); + Assert.Equal("String", dcs); + } +} + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.csproj similarity index 100% rename from src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirt_github39419.csproj rename to src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.csproj From 41e0827642769378d6e08b8cc4d36611ec8aed1a Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 1 Jun 2026 21:36:50 +0900 Subject: [PATCH 25/33] Nit --- .../devirtualization/dim_devirtualization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs index 5a0b517304adb8..cb67af966d11cd 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Xunit; -public static class Program +public class Program { [Fact] public static void TestEntryPoint() From b55d5c1fc7627d8834d86fd2283d1899cb7a48d8 Mon Sep 17 00:00:00 2001 From: Steven He Date: Mon, 1 Jun 2026 23:35:58 +0900 Subject: [PATCH 26/33] Take canonical subtype into account as well --- src/coreclr/vm/jitinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 7c629b79b28ec7..ec39958752c9bc 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8856,7 +8856,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true); - if (pDevirtMD->IsSharedByGenericMethodInstantiations()) + if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetMethodInstantiation())) { pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false); From 0820190ac5b4428ba664c08212dfb8620b63f0d9 Mon Sep 17 00:00:00 2001 From: Steven He Date: Mon, 1 Jun 2026 23:40:40 +0900 Subject: [PATCH 27/33] Add a test case --- .../VirtualMethods/generic_virtual_methods.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/tests/JIT/Generics/VirtualMethods/generic_virtual_methods.cs b/src/tests/JIT/Generics/VirtualMethods/generic_virtual_methods.cs index 7aba5abe26bab3..cab401999094bb 100644 --- a/src/tests/JIT/Generics/VirtualMethods/generic_virtual_methods.cs +++ b/src/tests/JIT/Generics/VirtualMethods/generic_virtual_methods.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Runtime.CompilerServices; using Xunit; @@ -219,6 +220,11 @@ internal static void Test() Delegate m1 = test2.Foo>(); Delegate m2 = test2.Foo>>; Assert.Equal(m1, m2); + + IBase foo = new DerivedStruct(); + Delegate d = foo.Foo>(); + MethodInfo expected = typeof(DerivedStruct).GetMethod(nameof(DerivedStruct.Foo))!.MakeGenericMethod(typeof(List)); + Assert.Equal(expected, d.Method); } } @@ -245,6 +251,19 @@ public override Delegate Foo() } } +internal interface IBase +{ + Delegate Foo(); +} + +internal struct DerivedStruct : IBase +{ + public Delegate Foo() + { + return Foo; + } +} + internal static class IconContextBridgeNonShared { public static TMethod SameMethodSameClass(IBaseMethodCaller caller, TMethod value) From d47b38275c89a0fb0642c73dc2b1f759a6b7fefb Mon Sep 17 00:00:00 2001 From: Steven He Date: Tue, 2 Jun 2026 21:25:57 +0900 Subject: [PATCH 28/33] Refactor and properly handle unboxing stubs --- .../tools/Common/JitInterface/CorInfoImpl.cs | 16 +++- src/coreclr/vm/jitinterface.cpp | 85 ++++++++----------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index b4281b43e2da9b..907eb19889a0c2 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1482,6 +1482,9 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN); } + bool isArray = decl.OwningType.IsInterface && objType.IsArray; + bool isGenericVirtual = !isArray && decl.HasInstantiation; + if (!impl.AcquiresInstMethodTableFromThis()) { if (originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) @@ -1500,6 +1503,17 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) } } + if (isArray) + { +#if READYTORUN + // Array interface devirt is not yet supported by R2R. + info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; +#else + // NativeAOT handles arrays in a different way that doesn't require an instantiating stub. +#endif + } + if (impl.RequiresInstMethodDescArg()) { if (unboxingStub) @@ -1540,7 +1554,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #endif info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_SUCCESS; info->devirtualizedMethod = ObjectToHandle(impl); - info->tokenLookupContext = impl.RequiresInstMethodDescArg() ? contextFromMethod(originalImpl) : contextFromType(owningType); + info->tokenLookupContext = (isArray || isGenericVirtual) ? contextFromMethod(originalImpl) : contextFromType(owningType); return true; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index ec39958752c9bc..e4e59c1ed21ad7 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8823,82 +8823,67 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) MethodTable* pExactMT = pApproxMT; bool isArray = false; bool isGenericVirtual = false; - - if (pApproxMT->IsInterface()) - { - if (pDevirtMD->HasClassInstantiation()) - { - if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetClassInstantiation())) - { - // If we end up with a shared MethodTable that is not exact, - // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. - info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; - } - - info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)pExactMT; - info->instParamLookup.constLookup.accessType = IAT_VALUE; - } - } - else if (pBaseMT->IsInterface() && pObjMT->IsArray()) + + if (pBaseMT->IsInterface() && pObjMT->IsArray()) { isArray = true; } - else + else if (!pApproxMT->IsInterface()) { pExactMT = pDevirtMD->GetExactDeclaringType(pObjMT); } + MethodDesc* pInstantiatedMD = pDevirtMD; + // This is generic virtual method devirtualization. if (!isArray && pBaseMD->HasMethodInstantiation()) { MethodDesc* pPrimaryMD = pDevirtMD; + pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true); - - if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetMethodInstantiation())) - { - pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( - pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false); - - if (TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetClassInstantiation())) - { - // If we end up with a shared MethodTable that is not exact, - // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. - info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; - } - - // We are dealing with a generic virtual method whose owning type is exact. - // If the method is not exact, a runtime lookup would be required. - const bool requiresRuntimeLookup = TypeHandle::IsCanonicalSubtypeInstantiation(pDevirtMD->GetMethodInstantiation()); - if (requiresRuntimeLookup) - { - // TODO: Support for runtime lookup - info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; - return false; - } - } + + pInstantiatedMD = MethodDesc::FindOrCreateAssociatedMethodDesc( + pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false); isGenericVirtual = true; } - if (isArray || isGenericVirtual) + MethodDesc* pUnboxedDevirtMD = pDevirtMD->IsUnboxingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; + + if (!pUnboxedDevirtMD->AcquiresInstMethodTableFromThis()) { - if (pDevirtMD->IsInstantiatingStub()) + if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetClassInstantiation())) { - info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)pDevirtMD; - info->instParamLookup.constLookup.accessType = IAT_VALUE; + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + + if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetMethodInstantiation())) + { + // TODO: Support for runtime lookup + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; } + } - info->tokenLookupContext = MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pDevirtMD); + if (pUnboxedDevirtMD->RequiresInstMethodDescArg()) + { + info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pInstantiatedMD; + info->instParamLookup.constLookup.accessType = IAT_VALUE; } - else + else if (pUnboxedDevirtMD->RequiresInstMethodTableArg()) { - info->tokenLookupContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); + info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pExactMT; + info->instParamLookup.constLookup.accessType = IAT_VALUE; } pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; + info->tokenLookupContext = (isArray || isGenericVirtual) + ? MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pInstantiatedMD) + : MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); // Success! Pass back the results. // From fe252112fe4a1200447e3c4680af18ac6b7822da Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 11 Jun 2026 21:31:17 +0900 Subject: [PATCH 29/33] Cleanup getUnboxedEntry and getInstantiatedEntry --- src/coreclr/inc/corinfo.h | 25 ++-- src/coreclr/inc/icorjitinfoimpl_generated.h | 9 -- src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 - .../jit/ICorJitInfo_wrapper_generated.hpp | 21 ---- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/importercalls.cpp | 111 ++++++++---------- src/coreclr/jit/indirectcalltransformer.cpp | 2 +- src/coreclr/jit/inline.h | 3 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 22 ---- .../JitInterface/CorInfoImpl_generated.cs | 34 ------ .../tools/Common/JitInterface/CorInfoTypes.cs | 9 +- .../ThunkGenerator/ThunkInput.txt | 2 - .../aot/jitinterface/jitinterface_generated.h | 23 ---- .../tools/superpmi/superpmi-shared/agnostic.h | 7 -- .../tools/superpmi/superpmi-shared/lwmlist.h | 2 - .../superpmi-shared/methodcontext.cpp | 84 ------------- .../superpmi/superpmi-shared/methodcontext.h | 17 +-- .../superpmi-shim-collector/icorjitinfo.cpp | 22 ---- .../icorjitinfo_generated.cpp | 17 --- .../icorjitinfo_generated.cpp | 15 --- .../tools/superpmi/superpmi/icorjitinfo.cpp | 15 --- src/coreclr/vm/jitinterface.cpp | 84 ++----------- 23 files changed, 85 insertions(+), 453 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 5ac1524d19c6f1..98a03384d8b26d 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1583,12 +1583,15 @@ struct CORINFO_DEVIRTUALIZATION_INFO // [Out] results of resolveVirtualMethod. // - devirtualizedMethod is set to MethodDesc of devirt'ed method iff we were able to devirtualize. // invariant is `resolveVirtualMethod(...) == (devirtualizedMethod != nullptr)`. - // - tokenLookupContext is set to the wrapped context handle to use for token lookups after devirtualization. + // - tokenLookupContext is set to the wrapped context handle to use for token lookups and the instantiation + // parameter after devirtualization. // - details on the computation done by the jit host - // - If pResolvedTokenDevirtualizedMethod is not set to NULL and targeting an R2R image - // use it as the parameter to getCallInfo + // - resolvedTokenDevirtualizedMethod is used as the parameter to getCallInfo when targeting an R2R image. + // - resolvedTokenDevirtualizedUnboxedMethod is set when devirtualizedMethod is an unboxing stub. Its hMethod + // is the unboxed entry point, and the resolved token is used as the parameter to getCallInfo when targeting + // an R2R image. // - instParamLookup contains all the information necessary to pass the instantiation parameter for - // the devirtualized method. + // the devirtualized method or its unboxed entry point. // CORINFO_METHOD_HANDLE devirtualizedMethod; CORINFO_CONTEXT_HANDLE tokenLookupContext; @@ -2265,20 +2268,6 @@ class ICorStaticInfo // Returns false if devirtualization is not possible. virtual bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info) = 0; - // Get the unboxed entry point for a method, if possible. - virtual CORINFO_METHOD_HANDLE getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg - ) = 0; - - // Get the wrapped entry point for an instantiating stub, if possible. - // Sets methodArg for method instantiations, classArg for class instantiations. - virtual CORINFO_METHOD_HANDLE getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg - ) = 0; - // Get the other variant of an async method, if possible. // If this is a method with async calling convention: returns the corresponding task-returning method. // If this is a task-returning method: returns the corresponding method with async calling convention. diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 8fc80cda20018c..09d1608bfa134f 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -95,15 +95,6 @@ void getMethodVTableOffset( bool resolveVirtualMethod( CORINFO_DEVIRTUALIZATION_INFO* info) override; -CORINFO_METHOD_HANDLE getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) override; - -CORINFO_METHOD_HANDLE getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) override; - CORINFO_METHOD_HANDLE getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 85d8a8682bf489..bc9e29979e6989 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 59df85b8-c0fd-4e40-aea1-68cb2cd916cc */ - 0x59df85b8, - 0xc0fd, - 0x4e40, - {0xae, 0xa1, 0x68, 0xcb, 0x2c, 0xd9, 0x16, 0xcc} +constexpr GUID JITEEVersionIdentifier = { /* 14ca1721-a3ba-40a0-b8a6-92a939719e66 */ + 0x14ca1721, + 0xa3ba, + 0x40a0, + {0xb8, 0xa6, 0x92, 0xa9, 0x39, 0x71, 0x9e, 0x66} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 57781f560ce0d5..32abc2cd9596d2 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -21,8 +21,6 @@ DEF_CLR_API(getEHinfo) DEF_CLR_API(getMethodClass) DEF_CLR_API(getMethodVTableOffset) DEF_CLR_API(resolveVirtualMethod) -DEF_CLR_API(getUnboxedEntry) -DEF_CLR_API(getInstantiatedEntry) DEF_CLR_API(getAsyncOtherVariant) DEF_CLR_API(getDefaultComparerClass) DEF_CLR_API(getDefaultEqualityComparerClass) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 87e541b608f155..c775460f0c1bbc 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -181,27 +181,6 @@ bool WrapICorJitInfo::resolveVirtualMethod( return temp; } -CORINFO_METHOD_HANDLE WrapICorJitInfo::getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) -{ - API_ENTER(getUnboxedEntry); - CORINFO_METHOD_HANDLE temp = wrapHnd->getUnboxedEntry(ftn, requiresInstMethodTableArg); - API_LEAVE(getUnboxedEntry); - return temp; -} - -CORINFO_METHOD_HANDLE WrapICorJitInfo::getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) -{ - API_ENTER(getInstantiatedEntry); - CORINFO_METHOD_HANDLE temp = wrapHnd->getInstantiatedEntry(ftn, methodArg, classArg); - API_LEAVE(getInstantiatedEntry); - return temp; -} - CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index de116a31f9ebb1..9c834818a54890 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4977,7 +4977,7 @@ class Compiler { CORINFO_CONTEXT_HANDLE tokenLookupContext; // The class context or method context CORINFO_RESOLVED_TOKEN* pResolvedToken; // Resolved token for the target method, used by R2R. - CORINFO_RESOLVED_TOKEN* pUnboxedResolvedToken; // Resolved token for the unboxed entry, used by R2R. + CORINFO_RESOLVED_TOKEN* pUnboxedResolvedToken; // Resolved token and method handle for the unboxed entry. CORINFO_LOOKUP* pInstParamLookup; // All the information needed for the instantiation parameter lookup. CORINFO_SIG_INFO* pMethSig; // The devirted method signature. bool objIsNonNull; // True if the receiver is known non-null. diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 851e08288b3d35..258067957e2091 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -8161,7 +8161,6 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call, InlineCandidateInfo* pInfo = new (this, CMK_Inlining) InlineCandidateInfo; pInfo->guardedMethodHandle = methodHandle; - pInfo->guardedMethodUnboxedEntryHandle = nullptr; pInfo->guardedMethodInstParamLookup = {}; pInfo->guardedMethodResolvedToken = {}; pInfo->guardedMethodUnboxedResolvedToken = {}; @@ -8181,22 +8180,6 @@ void Compiler::addGuardedDevirtualizationCandidate(GenTreeCall* call, pInfo->guardedMethodUnboxedResolvedToken = *pUnboxedResolvedToken; } - // If the guarded class is a value class, look for an unboxed entry point. - // - if ((classAttr & CORINFO_FLG_VALUECLASS) != 0) - { - JITDUMP(" ... class is a value class, looking for unboxed entry\n"); - bool requiresInstMethodTableArg = false; - CORINFO_METHOD_HANDLE unboxedEntryMethodHandle = - info.compCompHnd->getUnboxedEntry(methodHandle, &requiresInstMethodTableArg); - - if (unboxedEntryMethodHandle != nullptr) - { - JITDUMP(" ... updating GDV candidate with unboxed entry info\n"); - pInfo->guardedMethodUnboxedEntryHandle = unboxedEntryMethodHandle; - } - } - call->AddGDVCandidateInfo(this, pInfo); } @@ -8472,9 +8455,9 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, if (call->IsGuardedDevirtualizationCandidate()) { InlineCandidateInfo* gdvCandidate = call->GetGDVCandidateInfo(candidateIndex); - if (gdvCandidate->guardedMethodUnboxedEntryHandle != nullptr) + if (gdvCandidate->guardedMethodUnboxedResolvedToken.hMethod != nullptr) { - fncHandle = gdvCandidate->guardedMethodUnboxedEntryHandle; + fncHandle = gdvCandidate->guardedMethodUnboxedResolvedToken.hMethod; } else { @@ -9411,61 +9394,61 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, { JITDUMP("Have a direct call to boxed entry point. Trying to optimize to call an unboxed entry point\n"); - // Note for some shared methods the unboxed entry point requires an extra parameter. - bool requiresInstMethodTableArg = false; - CORINFO_METHOD_HANDLE unboxedEntryMethod = - info.compCompHnd->getUnboxedEntry(derivedMethod, &requiresInstMethodTableArg); + CORINFO_METHOD_HANDLE const unboxedEntryMethod = + (dcInfo->pUnboxedResolvedToken == nullptr) ? nullptr : dcInfo->pUnboxedResolvedToken->hMethod; if (unboxedEntryMethod != nullptr) { - // Rewrite the call to target the unboxed entry on the box payload. Keep the heap box, - // since the callee may return an interior managed pointer into it; object stack allocation - // can later promote the box to the stack when escape analysis proves the receiver does not - // escape. - // - if (requiresInstMethodTableArg) - { - // Get the method table from the boxed object. - // - // TODO-CallArgs-REVIEW: Use thisObj here? Differs by gtEffectiveVal. - GenTree* const clonedThisArg = gtClone(thisArg->GetEarlyNode()); + CORINFO_SIG_INFO unboxedEntrySig; + info.compCompHnd->getMethodSig(unboxedEntryMethod, &unboxedEntrySig); + + bool canUseUnboxedEntry = true; - if (clonedThisArg == nullptr) + if (unboxedEntrySig.hasTypeArg()) + { + if (unboxedEntrySig.sigInst.methInstCount != 0) { - JITDUMP("unboxed entry needs MT arg, but `this` was too complex to clone. Deferring update.\n"); + assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == + CORINFO_CONTEXTFLAGS_METHOD); + CORINFO_METHOD_HANDLE exactMethodHandle = + (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); + + instParam = getLookupTree(dcInfo->pInstParamLookup, GTF_ICON_METHOD_HDL, exactMethodHandle); + JITDUMP("revising call to invoke unboxed entry with additional method desc arg\n"); } else { - JITDUMP("revising call to invoke unboxed entry with additional method table arg\n"); - - instParam = gtNewMethodTableLookup(clonedThisArg); - - // Update the 'this' pointer to refer to the box payload + // Get the method table from the boxed object. // - GenTree* const payloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL); - GenTree* const boxPayload = - gtNewOperNode(GT_ADD, TYP_BYREF, thisArg->GetEarlyNode(), payloadOffset); - - assert(thisObj == thisArg->GetEarlyNode()); - thisArg->SetEarlyNode(boxPayload); - call->gtCallMethHnd = unboxedEntryMethod; - INDEBUG(call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED); + // TODO-CallArgs-REVIEW: Use thisObj here? Differs by gtEffectiveVal. + GenTree* const clonedThisArg = gtClone(thisArg->GetEarlyNode()); - // Method attributes will differ because unboxed entry point is shared - // - const DWORD unboxedMethodAttribs = info.compCompHnd->getMethodAttribs(unboxedEntryMethod); - JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs, - unboxedMethodAttribs); - derivedMethod = unboxedEntryMethod; - pDerivedResolvedToken = dcInfo->pUnboxedResolvedToken; - derivedMethodAttribs = unboxedMethodAttribs; - Metrics.DevirtualizedCallUnboxedEntry++; + if (clonedThisArg == nullptr) + { + JITDUMP( + "unboxed entry needs MT arg, but `this` was too complex to clone. Deferring update.\n"); + canUseUnboxedEntry = false; + } + else + { + instParam = gtNewMethodTableLookup(clonedThisArg); + assert(thisObj == thisArg->GetEarlyNode()); + JITDUMP("revising call to invoke unboxed entry with additional method table arg\n"); + } } } else { JITDUMP("revising call to invoke unboxed entry\n"); + } + if (canUseUnboxedEntry) + { + // Rewrite the call to target the unboxed entry on the box payload. Keep the heap box, + // since the callee may return an interior managed pointer into it; object stack allocation + // can later promote the box to the stack when escape analysis proves the receiver does not + // escape. + // GenTree* const payloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL); GenTree* const boxPayload = gtNewOperNode(GT_ADD, TYP_BYREF, thisArg->GetEarlyNode(), payloadOffset); @@ -9473,6 +9456,17 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, thisArg->SetEarlyNode(boxPayload); call->gtCallMethHnd = unboxedEntryMethod; INDEBUG(call->gtCallDebugFlags |= GTF_CALL_MD_UNBOXED); + + if (unboxedEntrySig.hasTypeArg()) + { + // Method attributes will differ because unboxed entry point is shared. + // + const DWORD unboxedMethodAttribs = info.compCompHnd->getMethodAttribs(unboxedEntryMethod); + JITDUMP("Updating method attribs from 0x%08x to 0x%08x\n", derivedMethodAttribs, + unboxedMethodAttribs); + derivedMethodAttribs = unboxedMethodAttribs; + } + derivedMethod = unboxedEntryMethod; pDerivedResolvedToken = dcInfo->pUnboxedResolvedToken; Metrics.DevirtualizedCallUnboxedEntry++; @@ -10068,7 +10062,6 @@ void Compiler::impCheckCanInline(GenTreeCall* call, // pInfo->guardedClassHandle = nullptr; pInfo->guardedMethodHandle = nullptr; - pInfo->guardedMethodUnboxedEntryHandle = nullptr; pInfo->guardedMethodInstParamLookup = {}; pInfo->guardedMethodResolvedToken = {}; pInfo->guardedMethodUnboxedResolvedToken = {}; diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index 892a2c5b430586..195c3ad5dc877c 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -1007,7 +1007,7 @@ class IndirectCallTransformer // If the devirtualizer was unable to transform the call to invoke the unboxed entry, the inline info // we set up may be invalid. We won't be able to inline anyways. So demote the call as an inline candidate. // - CORINFO_METHOD_HANDLE unboxedMethodHnd = inlineInfo->guardedMethodUnboxedEntryHandle; + CORINFO_METHOD_HANDLE unboxedMethodHnd = inlineInfo->guardedMethodUnboxedResolvedToken.hMethod; if ((unboxedMethodHnd != nullptr) && (methodHnd != unboxedMethodHnd)) { // Demote this call to a non-inline candidate diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index 595acc9ecb174f..065f7de3eaaa20 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -595,10 +595,9 @@ struct InlineCandidateInfo : public HandleHistogramProfileCandidateInfo { CORINFO_CLASS_HANDLE guardedClassHandle; CORINFO_METHOD_HANDLE guardedMethodHandle; - CORINFO_METHOD_HANDLE guardedMethodUnboxedEntryHandle; CORINFO_LOOKUP guardedMethodInstParamLookup; CORINFO_RESOLVED_TOKEN guardedMethodResolvedToken; // Only used by R2R - CORINFO_RESOLVED_TOKEN guardedMethodUnboxedResolvedToken; // Only used by R2R + CORINFO_RESOLVED_TOKEN guardedMethodUnboxedResolvedToken; // hMethod is the unboxed entry; token data is used by R2R unsigned likelihood; CORINFO_METHOD_INFO methInfo; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 5638e885771ee5..80250efc4e5b39 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1540,28 +1540,6 @@ static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInter } } - private CORINFO_METHOD_STRUCT_* getUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, ref bool requiresInstMethodTableArg) - { - MethodDesc result = null; - requiresInstMethodTableArg = false; - - MethodDesc method = HandleToObject(ftn); - if (method.IsUnboxingThunk()) - { - result = method.GetUnboxedMethod(); - requiresInstMethodTableArg = method.RequiresInstMethodTableArg(); - } - - return result != null ? ObjectToHandle(result) : null; - } - - private CORINFO_METHOD_STRUCT_* getInstantiatedEntry(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_STRUCT_** methodArg, CORINFO_CLASS_STRUCT_** classArg) - { - *methodArg = null; - *classArg = null; - return null; - } - private CORINFO_METHOD_STRUCT_* getAsyncOtherVariant(CORINFO_METHOD_STRUCT_* ftn, ref bool variantIsThunk) { MethodDesc method = HandleToObject(ftn); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 2ec1fc175a8d91..74da04da013a53 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -37,8 +37,6 @@ static ICorJitInfoCallbacks() s_callbacks.getMethodClass = &_getMethodClass; s_callbacks.getMethodVTableOffset = &_getMethodVTableOffset; s_callbacks.resolveVirtualMethod = &_resolveVirtualMethod; - s_callbacks.getUnboxedEntry = &_getUnboxedEntry; - s_callbacks.getInstantiatedEntry = &_getInstantiatedEntry; s_callbacks.getAsyncOtherVariant = &_getAsyncOtherVariant; s_callbacks.getDefaultComparerClass = &_getDefaultComparerClass; s_callbacks.getDefaultEqualityComparerClass = &_getDefaultEqualityComparerClass; @@ -220,8 +218,6 @@ static ICorJitInfoCallbacks() public delegate* unmanaged getMethodClass; public delegate* unmanaged getMethodVTableOffset; public delegate* unmanaged resolveVirtualMethod; - public delegate* unmanaged getUnboxedEntry; - public delegate* unmanaged getInstantiatedEntry; public delegate* unmanaged getAsyncOtherVariant; public delegate* unmanaged getDefaultComparerClass; public delegate* unmanaged getDefaultEqualityComparerClass; @@ -639,36 +635,6 @@ private static byte _resolveVirtualMethod(IntPtr thisHandle, IntPtr* ppException } } - [UnmanagedCallersOnly] - private static CORINFO_METHOD_STRUCT_* _getUnboxedEntry(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, bool* requiresInstMethodTableArg) - { - var _this = GetThis(thisHandle); - try - { - return _this.getUnboxedEntry(ftn, ref *requiresInstMethodTableArg); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - - [UnmanagedCallersOnly] - private static CORINFO_METHOD_STRUCT_* _getInstantiatedEntry(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_STRUCT_** methodArg, CORINFO_CLASS_STRUCT_** classArg) - { - var _this = GetThis(thisHandle); - try - { - return _this.getInstantiatedEntry(ftn, methodArg, classArg); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; - } - } - [UnmanagedCallersOnly] private static CORINFO_METHOD_STRUCT_* _getAsyncOtherVariant(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* ftn, bool* variantIsThunk) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index f173125897d053..14284d26860edc 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1176,10 +1176,15 @@ public unsafe struct CORINFO_DEVIRTUALIZATION_INFO // [Out] results of resolveVirtualMethod. // - devirtualizedMethod is set to MethodDesc of devirt'ed method iff we were able to devirtualize. // invariant is `resolveVirtualMethod(...) == (devirtualizedMethod != nullptr)`. - // - tokenLookupContext is set to the wrapped context handle to use for token lookups after devirtualization. + // - tokenLookupContext is set to the wrapped context handle to use for token lookups and the instantiation + // parameter after devirtualization. // - detail describes the computation done by the jit host + // - resolvedTokenDevirtualizedMethod is used as the parameter to getCallInfo when targeting an R2R image. + // - resolvedTokenDevirtualizedUnboxedMethod is set when devirtualizedMethod is an unboxing stub. Its hMethod + // is the unboxed entry point, and the resolved token is used as the parameter to getCallInfo when targeting + // an R2R image. // - instParamLookup contains all the information necessary to pass the instantiation parameter for - // the devirtualized method. + // the devirtualized method or its unboxed entry point. // public CORINFO_METHOD_STRUCT_* devirtualizedMethod; public CORINFO_CONTEXT_STRUCT* tokenLookupContext; diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 384ca10f881504..fe14f4d9bc0e5c 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -190,8 +190,6 @@ FUNCTIONS CORINFO_CLASS_HANDLE getMethodClass( CORINFO_METHOD_HANDLE method ); void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info); - CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); - CORINFO_METHOD_HANDLE getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg); CORINFO_METHOD_HANDLE getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 90ec07a3b446bc..0049a99f1e1dd8 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -28,8 +28,6 @@ struct JitInterfaceCallbacks CORINFO_CLASS_HANDLE (* getMethodClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method); void (* getMethodVTableOffset)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool (* resolveVirtualMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_DEVIRTUALIZATION_INFO* info); - CORINFO_METHOD_HANDLE (* getUnboxedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); - CORINFO_METHOD_HANDLE (* getInstantiatedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodArg, CORINFO_CLASS_HANDLE* classArg); CORINFO_METHOD_HANDLE (* getAsyncOtherVariant)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); CORINFO_CLASS_HANDLE (* getDefaultComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); @@ -376,27 +374,6 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } - virtual CORINFO_METHOD_HANDLE getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) -{ - CorInfoExceptionClass* pException = nullptr; - CORINFO_METHOD_HANDLE temp = _callbacks->getUnboxedEntry(_thisHandle, &pException, ftn, requiresInstMethodTableArg); - if (pException != nullptr) throw pException; - return temp; -} - - virtual CORINFO_METHOD_HANDLE getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) -{ - CorInfoExceptionClass* pException = nullptr; - CORINFO_METHOD_HANDLE temp = _callbacks->getInstantiatedEntry(_thisHandle, &pException, ftn, methodArg, classArg); - if (pException != nullptr) throw pException; - return temp; -} - virtual CORINFO_METHOD_HANDLE getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 85a7a8bf540eed..82bbc093be6f70 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -694,13 +694,6 @@ struct Agnostic_ResolveVirtualMethodResult Agnostic_CORINFO_LOOKUP instParamLookup; }; -struct Agnostic_GetInstantiatedEntryResult -{ - DWORDLONG methodHandle; - DWORDLONG classHandle; - DWORDLONG result; -}; - struct ResolveTokenValue { Agnostic_CORINFO_RESOLVED_TOKENout tokenOut; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index b464feef60b3f6..957c92a4aa30f7 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -138,8 +138,6 @@ LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG) LWM(GetTypeForBox, DWORDLONG, DWORDLONG) LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD) LWM(GetTypeForPrimitiveNumericClass, DWORDLONG, DWORD) -LWM(GetUnboxedEntry, DWORDLONG, DLD); -LWM(GetInstantiatedEntry, DWORDLONG, Agnostic_GetInstantiatedEntryResult); LWM(GetAsyncOtherVariant, DWORDLONG, DLD); LWM(GetUnBoxHelper, DWORDLONG, DWORD) LWM(GetRuntimeTypePointer, DWORDLONG, DWORDLONG) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index ec6b3b9e1ef4ac..85dec9a6b527ca 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3318,90 +3318,6 @@ bool MethodContext::repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info return result.returnValue; } -void MethodContext::recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg, - CORINFO_METHOD_HANDLE result) -{ - if (GetUnboxedEntry == nullptr) - { - GetUnboxedEntry = new LightWeightMap(); - } - - DWORDLONG key = CastHandle(ftn); - DLD value; - value.A = CastHandle(result); - if (requiresInstMethodTableArg != nullptr) - { - value.B = (DWORD)*requiresInstMethodTableArg ? 1 : 0; - } - else - { - value.B = 0; - } - GetUnboxedEntry->Add(key, value); - DEBUG_REC(dmpGetUnboxedEntry(key, value)); -} - -void MethodContext::dmpGetUnboxedEntry(DWORDLONG key, DLD value) -{ - printf("GetUnboxedEntry ftn-%016" PRIX64 ", result-%016" PRIX64 ", requires-inst-%u", key, value.A, value.B); -} - -CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg) -{ - DWORDLONG key = CastHandle(ftn); - - DLD value = LookupByKeyOrMiss(GetUnboxedEntry, key, ": key %016" PRIX64 "", key); - - DEBUG_REP(dmpGetUnboxedEntry(key, value)); - - if (requiresInstMethodTableArg != nullptr) - { - *requiresInstMethodTableArg = (value.B == 1); - } - return (CORINFO_METHOD_HANDLE)(value.A); -} - -void MethodContext::recGetInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE methodHandle, - CORINFO_CLASS_HANDLE classHandle, - CORINFO_METHOD_HANDLE result) -{ - if (GetInstantiatedEntry == nullptr) - { - GetInstantiatedEntry = new LightWeightMap(); - } - - DWORDLONG key = CastHandle(ftn); - Agnostic_GetInstantiatedEntryResult value; - value.methodHandle = CastHandle(methodHandle); - value.classHandle = CastHandle(classHandle); - value.result = CastHandle(result); - - GetInstantiatedEntry->Add(key, value); - DEBUG_REC(dmpGetUnboxedEntry(key, value)); -} - -void MethodContext::dmpGetInstantiatedEntry(DWORDLONG key, const Agnostic_GetInstantiatedEntryResult& value) -{ - printf("GetUnboxedEntry ftn-%016" PRIX64 ", methodHnd-%016" PRIX64 ", classHnd-%016" PRIX64 ", result-%016" PRIX64 "\n", - key, value.methodHandle, value.classHandle, value.result); -} - -CORINFO_METHOD_HANDLE MethodContext::repGetInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodHandle, CORINFO_CLASS_HANDLE* classHandle) -{ - DWORDLONG key = CastHandle(ftn); - - Agnostic_GetInstantiatedEntryResult value = LookupByKeyOrMiss(GetInstantiatedEntry, key, ": key %016" PRIX64 "", key); - - DEBUG_REP(dmpGetInstantiatedEntryEntry(key, value)); - - *methodHandle = (CORINFO_METHOD_HANDLE)value.methodHandle; - *classHandle = (CORINFO_CLASS_HANDLE)value.classHandle; - - return (CORINFO_METHOD_HANDLE)(value.result); -} - void MethodContext::recGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool variantIsThunk, CORINFO_METHOD_HANDLE result) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 449a18a91caeb5..e4cef62c08791a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -446,19 +446,6 @@ class MethodContext void dmpResolveVirtualMethod(const Agnostic_ResolveVirtualMethodKey& key, const Agnostic_ResolveVirtualMethodResult& value); bool repResolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info); - void recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg, CORINFO_METHOD_HANDLE result); - void dmpGetUnboxedEntry(DWORDLONG key, DLD value); - CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); - - void recGetInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE methodHandle, - CORINFO_CLASS_HANDLE classHandle, - CORINFO_METHOD_HANDLE result); - void dmpGetInstantiatedEntry(DWORDLONG key, const Agnostic_GetInstantiatedEntryResult& value); - CORINFO_METHOD_HANDLE repGetInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodHandle, - CORINFO_CLASS_HANDLE* classHandle); - void recGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool variantIsThunk, CORINFO_METHOD_HANDLE result); void dmpGetAsyncOtherVariant(DWORDLONG key, DLD value); CORINFO_METHOD_HANDLE repGetAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk); @@ -1149,7 +1136,7 @@ enum mcPackets Packet_GetDefaultEqualityComparerClass = 162, Packet_CompareTypesForCast = 163, Packet_CompareTypesForEquality = 164, - Packet_GetUnboxedEntry = 165, + //Packet_GetUnboxedEntry = 165, Packet_GetClassNameFromMetadata = 166, Packet_GetTypeInstantiationArgument = 167, Packet_GetTypeForPrimitiveNumericClass = 168, @@ -1212,7 +1199,7 @@ enum mcPackets Packet_GetClassAssemblyName = 225, Packet_GetSZArrayHelperEnumeratorClass = 226, Packet_GetMethodInstantiationArgument = 227, - Packet_GetInstantiatedEntry = 228, + //Packet_GetInstantiatedEntry = 228, Packet_NotifyInstructionSetUsage = 229, Packet_GetAsyncInfo = 230, Packet_GetAsyncResumptionStub = 231, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 6d0cadcd5616ae..04082c6ac0c406 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -241,28 +241,6 @@ bool interceptor_ICJI::resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info return result; } -// Get the unboxed entry point for a method, if possible. -CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg) -{ - mc->cr->AddCall("getUnboxedEntry"); - bool localRequiresInstMethodTableArg = false; - CORINFO_METHOD_HANDLE result = original_ICorJitInfo->getUnboxedEntry(ftn, &localRequiresInstMethodTableArg); - mc->recGetUnboxedEntry(ftn, &localRequiresInstMethodTableArg, result); - if (requiresInstMethodTableArg != nullptr) - { - *requiresInstMethodTableArg = localRequiresInstMethodTableArg; - } - return result; -} - -CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodHandle, CORINFO_CLASS_HANDLE* classHandle) -{ - mc->cr->AddCall("getInstantaitedEntry"); - CORINFO_METHOD_HANDLE result = original_ICorJitInfo->getInstantiatedEntry(ftn, methodHandle, classHandle); - mc->recGetInstantiatedEntry(ftn, *methodHandle, *classHandle, result); - return result; -} - CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) { mc->cr->AddCall("getAsyncOtherVariant"); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 874083bd15b940..3b3ad4933853be 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -154,23 +154,6 @@ bool interceptor_ICJI::resolveVirtualMethod( return original_ICorJitInfo->resolveVirtualMethod(info); } -CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) -{ - mcs->AddCall("getUnboxedEntry"); - return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); -} - -CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) -{ - mcs->AddCall("getInstantiatedEntry"); - return original_ICorJitInfo->getInstantiatedEntry(ftn, methodArg, classArg); -} - CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index b870f0f947f2f6..5e9dbe609bde3b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -137,21 +137,6 @@ bool interceptor_ICJI::resolveVirtualMethod( return original_ICorJitInfo->resolveVirtualMethod(info); } -CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) -{ - return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); -} - -CORINFO_METHOD_HANDLE interceptor_ICJI::getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) -{ - return original_ICorJitInfo->getInstantiatedEntry(ftn, methodArg, classArg); -} - CORINFO_METHOD_HANDLE interceptor_ICJI::getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 93262fd2279b01..c2743ff9524bb4 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -203,21 +203,6 @@ bool MyICJI::resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info) return result; } -// Get the unboxed entry point for a method, if possible. -CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg) -{ - jitInstance->mc->cr->AddCall("getUnboxedEntry"); - CORINFO_METHOD_HANDLE result = jitInstance->mc->repGetUnboxedEntry(ftn, requiresInstMethodTableArg); - return result; -} - -CORINFO_METHOD_HANDLE MyICJI::getInstantiatedEntry(CORINFO_METHOD_HANDLE ftn, CORINFO_METHOD_HANDLE* methodHandle, CORINFO_CLASS_HANDLE* classHandle) -{ - jitInstance->mc->cr->AddCall("getInstantiatedEntry"); - CORINFO_METHOD_HANDLE result = jitInstance->mc->repGetInstantiatedEntry(ftn, methodHandle, classHandle); - return result; -} - CORINFO_METHOD_HANDLE MyICJI::getAsyncOtherVariant(CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) { jitInstance->mc->cr->AddCall("getAsyncOtherVariant"); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index c0ec22f3e75a8f..b6bce399a68a56 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8880,6 +8880,15 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) info->tokenLookupContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT); } + // If we devirtualized into an unboxing stub, also hand back the unboxed entry + // so the jit can perform the unboxing transformation. + // + if (pDevirtMD->IsUnboxingStub()) + { + MethodDesc* pUnboxedMD = pDevirtMD->GetMethodTable()->GetUnboxedEntryPointMD(pDevirtMD); + info->resolvedTokenDevirtualizedUnboxedMethod.hMethod = (CORINFO_METHOD_HANDLE) pUnboxedMD; + } + // Success! Pass back the results. // info->devirtualizedMethod = (CORINFO_METHOD_HANDLE) pDevirtMD; @@ -8907,81 +8916,6 @@ bool CEEInfo::resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO * info) return result; } -/*********************************************************************/ -CORINFO_METHOD_HANDLE CEEInfo::getUnboxedEntry( - CORINFO_METHOD_HANDLE ftn, - bool* requiresInstMethodTableArg) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } CONTRACTL_END; - - CORINFO_METHOD_HANDLE result = NULL; - - JIT_TO_EE_TRANSITION(); - - MethodDesc* pMD = GetMethod(ftn); - bool requiresInstMTArg = false; - - if (pMD->IsUnboxingStub()) - { - MethodTable* pMT = pMD->GetMethodTable(); - MethodDesc* pUnboxedMD = pMT->GetUnboxedEntryPointMD(pMD); - - result = (CORINFO_METHOD_HANDLE)pUnboxedMD; - requiresInstMTArg = !!pUnboxedMD->RequiresInstMethodTableArg(); - } - - *requiresInstMethodTableArg = requiresInstMTArg; - - EE_TO_JIT_TRANSITION(); - - return result; -} - -/*********************************************************************/ -CORINFO_METHOD_HANDLE CEEInfo::getInstantiatedEntry( - CORINFO_METHOD_HANDLE ftn, - CORINFO_METHOD_HANDLE* methodArg, - CORINFO_CLASS_HANDLE* classArg) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } CONTRACTL_END; - - CORINFO_METHOD_HANDLE result = NULL; - - JIT_TO_EE_TRANSITION(); - - *methodArg = NULL; - *classArg = NULL; - - MethodDesc* pMD = GetMethod(ftn); - bool requiresInstMTArg = false; - - if (pMD->IsInstantiatingStub()) - { - result = (CORINFO_METHOD_HANDLE) pMD->GetWrappedMethodDesc(); - - if (pMD->HasMethodInstantiation()) - { - *methodArg = ftn; - } - else - { - *classArg = (CORINFO_CLASS_HANDLE)pMD->GetMethodTable(); - } - } - - EE_TO_JIT_TRANSITION(); - - return result; -} - CORINFO_METHOD_HANDLE CEEInfo::getAsyncOtherVariant( CORINFO_METHOD_HANDLE ftn, bool* variantIsThunk) From 852a5724f42f0783e464e6bae9a6d3a1a4abde5e Mon Sep 17 00:00:00 2001 From: Steven He Date: Thu, 11 Jun 2026 21:50:42 +0900 Subject: [PATCH 30/33] Check the context directly --- src/coreclr/jit/importercalls.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 258067957e2091..7c9242370b7bd6 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -9406,10 +9406,8 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, if (unboxedEntrySig.hasTypeArg()) { - if (unboxedEntrySig.sigInst.methInstCount != 0) + if (((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_METHOD) { - assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == - CORINFO_CONTEXTFLAGS_METHOD); CORINFO_METHOD_HANDLE exactMethodHandle = (CORINFO_METHOD_HANDLE)((SIZE_T)dcInfo->tokenLookupContext & ~CORINFO_CONTEXTFLAGS_MASK); @@ -9418,6 +9416,9 @@ void Compiler::impTransformDevirtualizedCall(GenTreeCall* call, } else { + assert(((SIZE_T)dcInfo->tokenLookupContext & CORINFO_CONTEXTFLAGS_MASK) == + CORINFO_CONTEXTFLAGS_CLASS); + // Get the method table from the boxed object. // // TODO-CallArgs-REVIEW: Use thisObj here? Differs by gtEffectiveVal. From 176d6b9b9cd590823e91e8f484b02ae3ef8aec45 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sun, 21 Jun 2026 00:17:29 +0900 Subject: [PATCH 31/33] Rework unboxing stub and instantiating stub --- .../Compiler/DevirtualizationManager.cs | 4 ++ .../tools/Common/JitInterface/CorInfoImpl.cs | 18 +++++--- .../ReadyToRun/DevirtualizationManager.cs | 14 +++++- src/coreclr/vm/jitinterface.cpp | 43 +++++++++++++++---- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index a8a83cedec3bb5..9e23da8fb606b3 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -157,6 +157,10 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType else { impl = dimMethod; + if (originalDeclMethod.HasInstantiation) + { + impl = impl.GetMethodDefinition().MakeInstantiatedMethod(originalDeclMethod.Instantiation); + } } break; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 013ffcffa692ae..1212919f9a6533 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1476,8 +1476,11 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) bool isArray = decl.OwningType.IsInterface && objType.IsArray; bool isGenericVirtual = !isArray && decl.HasInstantiation; + MethodDesc instArgTarget = unboxingStub ? nonUnboxingImpl : impl; + bool requiresInstMethodDescArg = instArgTarget.RequiresInstMethodDescArg(); + bool requiresInstMethodTableArg = instArgTarget.RequiresInstMethodTableArg(); - if (!impl.AcquiresInstMethodTableFromThis()) + if (requiresInstMethodDescArg || (requiresInstMethodTableArg && !unboxingStub)) { if (originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) { @@ -1487,7 +1490,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) return false; } - if (originalImpl.IsSharedByGenericInstantiations) + if (originalImpl.IsRuntimeDeterminedExactMethod || originalImpl.IsSharedByGenericInstantiations) { // TODO: Support for runtime lookup info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON; @@ -1506,7 +1509,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) #endif } - if (impl.RequiresInstMethodDescArg()) + if (requiresInstMethodDescArg) { if (unboxingStub) { @@ -1522,14 +1525,17 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl)); #endif } - else if (impl.RequiresInstMethodTableArg()) + else if (requiresInstMethodTableArg) { + if (!unboxingStub) + { #if READYTORUN - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType)); #else - info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); + info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType)); #endif + } } #if READYTORUN diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs index e6e329e5b32663..d6ebb33f99db18 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs @@ -45,7 +45,8 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp // // Result method checking // 1. Ensure that the resolved result versions with the code, or is the decl method - // 2. When checking that the resolved result versions with the code, validate that all of the types + // 2. When devirtualizing to a default interface method, the resolved result method must version with the code. + // 3. When checking that the resolved result versions with the code, validate that all of the types // From implType to the owning type of resolved result method also version with the code. bool declMethodCheckFailed; @@ -161,6 +162,17 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp if (resolvedVirtualMethod != null) { + if (resolvedVirtualMethod.OwningType.IsInterface) + { + if (_compilationModuleGroup.VersionsWithMethodBody(resolvedVirtualMethod)) + { + return resolvedVirtualMethod; + } + + devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE; + return null; + } + // Validate that the inheritance chain for resolution is within version bubble // The rule is somewhat tricky here. // If the resolved method is the declMethod, then only types which derive from the diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index d047a0766f49dd..77535a69a7702b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8812,7 +8812,7 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) MethodTable* pExactMT = pApproxMT; bool isArray = false; bool isGenericVirtual = false; - + if (pBaseMT->IsInterface() && pObjMT->IsArray()) { isArray = true; @@ -8827,20 +8827,40 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) // This is generic virtual method devirtualization. if (!isArray && pBaseMD->HasMethodInstantiation()) { - MethodDesc* pPrimaryMD = pDevirtMD; + MethodDesc* pPrimaryMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true); - + pInstantiatedMD = MethodDesc::FindOrCreateAssociatedMethodDesc( pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false); isGenericVirtual = true; } - MethodDesc* pUnboxedDevirtMD = pDevirtMD->IsUnboxingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD; + MethodDesc* pInstArgMD = pDevirtMD; + bool isUnboxingStub = pDevirtMD->IsUnboxingStub(); + bool instArgIsInstantiatingStub = pDevirtMD->IsInstantiatingStub(); - if (!pUnboxedDevirtMD->AcquiresInstMethodTableFromThis()) + if (isUnboxingStub || instArgIsInstantiatingStub) + { + pInstArgMD = pDevirtMD->GetWrappedMethodDesc(); + if (pInstArgMD->IsInstantiatingStub()) + { + instArgIsInstantiatingStub = true; + } + } + + if (instArgIsInstantiatingStub && + (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetClassInstantiation()) || + TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetMethodInstantiation()))) + { + // TODO: Support for runtime lookup + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + + if (pInstArgMD->RequiresInstMethodDescArg()) { if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetClassInstantiation())) { @@ -8856,15 +8876,20 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info) info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; return false; } - } - if (pUnboxedDevirtMD->RequiresInstMethodDescArg()) - { info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pInstantiatedMD; info->instParamLookup.constLookup.accessType = IAT_VALUE; } - else if (pUnboxedDevirtMD->RequiresInstMethodTableArg()) + else if (pInstArgMD->RequiresInstMethodTableArg()) { + if (!isUnboxingStub && TypeHandle::IsCanonicalSubtypeInstantiation(pExactMT->GetInstantiation())) + { + // If we end up with a shared MethodTable that is not exact, + // we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup. + info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON; + return false; + } + info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pExactMT; info->instParamLookup.constLookup.accessType = IAT_VALUE; } From 5dec3737bfc73b76c9c8fd900fc90b91451a06b6 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sun, 21 Jun 2026 15:32:55 +0900 Subject: [PATCH 32/33] Address some verification issues --- .../Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs | 6 +++++- src/coreclr/vm/jitinterface.cpp | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs b/src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs index 6224974cd05155..e0b946b9341249 100644 --- a/src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs +++ b/src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs @@ -59,7 +59,11 @@ private static void ForEachEmbeddedGenericFormalWorker(TypeDesc type, Instantiat TypeDesc genericTypeDefinition = type.GetTypeDefinition(); Instantiation genericTypeParameters = genericTypeDefinition.Instantiation; Instantiation genericTypeArguments = type.Instantiation; - for (int i = 0; i < genericTypeArguments.Length; i++) + + // We have some negative IL tests that reference a generic type with more type arguments + // than the type definition has type parameters. + // Tolerate them as such a type can never load and therefore cannot form a cycle. + for (int i = 0; i < genericTypeArguments.Length && i < genericTypeParameters.Length; i++) { var genericTypeParameter = (EcmaGenericParameter)genericTypeParameters[i]; TypeDesc genericTypeArgument = genericTypeArguments[i]; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 77535a69a7702b..69924540109242 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14509,8 +14509,8 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } } - // Strip off method instantiation for comparison if the method is generic virtual. - if (pDeclMethod->HasMethodInstantiation()) + // Strip off method instantiation for comparison if the method is generic virtual or generic DIM. + if (pDeclMethod->HasMethodInstantiation() || pDeclMethod->IsInterface()) { if (pImplMethodRuntime != NULL) { From d0b8e364b80db67c893c33b3dc9d461652e80685 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sun, 21 Jun 2026 15:50:32 +0900 Subject: [PATCH 33/33] Add file header --- .../devirtualization/dim_devirtualization.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs index cb67af966d11cd..b71856d12b2df5 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/devirtualization/dim_devirtualization.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System; using System.Diagnostics; using System.Runtime.CompilerServices;