Skip to content

Commit ef5c352

Browse files
Merge branch 'main' into port-getcallingasm
2 parents 5857ffc + e137b2c commit ef5c352

42 files changed

Lines changed: 1369 additions & 1101 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-failure-fix.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ Apply these fixer-specific bounds on top of the skill's guidance:
185185
| NativeAOT outer loop | In bounds only if it satisfies Step 5.2. |
186186
| Generic | In bounds only if it satisfies Step 5.2. |
187187

188+
#### Step 5.1.1 — Pipeline-category gate (mandatory, before any fix attempt)
189+
190+
Before Step 5.2, resolve the KBE's pipeline and short-circuit JIT/GC/PGO
191+
codegen-stress failures. No fix or workaround PR is in bounds for them.
192+
193+
1. Read the build definition name and id from the KBE's `Build:` link and the
194+
`Build error leg or test failing:` leg name.
195+
2. Treat as codegen-stress when the name or leg matches (case-insensitive)
196+
`jitstress`, `gcstress`, `pgo`, `superpmi`, `jit-cfg`, or `jit-experimental`.
197+
3. If matched, skip Steps 5.2–5.4 and go to Step 5.5 (Branch COMMENT), recording
198+
`-> routed to loop-in: codegen-stress pipeline (<name>)`. Otherwise continue.
199+
188200
#### Step 5.2 — Attempt a fix, then classify confidence
189201

190202
Always try to produce a real candidate change first. Read every file you would modify at `HEAD`, work out the minimal correct change (e.g. wrong expected value in a test, missing `using`, wrong cast, missing `#if`, off-by-one in test setup, a missing platform guard that *enables* correct behavior rather than disabling the test), and stage it. If the change reduces to "do what the source already does", there is nothing to fix -> record `-> skipped: candidate fix already present in source`.

src/coreclr/jit/scev.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1451,7 +1451,18 @@ GenTree* ScalarEvolutionContext::Materialize(Scev* scev)
14511451
{
14521452
ValueNumPair vnp;
14531453
GenTree* result;
1454-
return Materialize(scev, true, &result, &vnp) ? result : nullptr;
1454+
1455+
// Materializing IR may create nodes before failing partway through (e.g.
1456+
// when a subexpression cannot be materialized). Snapshot the gen tree ID so
1457+
// that we can roll it back and avoid leaking IDs for the orphaned nodes.
1458+
INDEBUG(unsigned prevGenTreeID = m_compiler->compGenTreeID);
1459+
if (Materialize(scev, true, &result, &vnp))
1460+
{
1461+
return result;
1462+
}
1463+
1464+
INDEBUG(m_compiler->compGenTreeID = prevGenTreeID);
1465+
return nullptr;
14551466
}
14561467

14571468
//------------------------------------------------------------------------

src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,19 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType
148148
return null;
149149

150150
case DefaultInterfaceMethodResolution.DefaultImplementation:
151-
if (dimMethod.OwningType.HasInstantiation || (declMethod != defaultInterfaceDispatchDeclMethod))
151+
if (declMethod != defaultInterfaceDispatchDeclMethod)
152152
{
153-
// If we devirtualized into a default interface method on a generic type, we should actually return an
154-
// instantiating stub but this is not happening.
155-
// Making this work is tracked by https://github.com/dotnet/runtime/issues/9588
156-
157-
// In addition, we fail here for variant default interface dispatch
153+
// Fail for variant default interface dispatch
158154
devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DIM;
159155
return null;
160156
}
161157
else
162158
{
163159
impl = dimMethod;
160+
if (originalDeclMethod.HasInstantiation)
161+
{
162+
impl = impl.GetMethodDefinition().MakeInstantiatedMethod(originalDeclMethod.Instantiation);
163+
}
164164
}
165165
break;
166166
}
@@ -219,13 +219,6 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType
219219
}
220220
}
221221

222-
if (impl != null && impl.HasInstantiation && impl.GetCanonMethodTarget(CanonicalFormKind.Specific).IsCanonicalMethod(CanonicalFormKind.Specific))
223-
{
224-
// We don't support devirtualization of shared generic virtual methods yet.
225-
devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
226-
impl = null;
227-
}
228-
229222
return impl;
230223
}
231224

src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.ForEach.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ private static void ForEachEmbeddedGenericFormalWorker(TypeDesc type, Instantiat
5959
TypeDesc genericTypeDefinition = type.GetTypeDefinition();
6060
Instantiation genericTypeParameters = genericTypeDefinition.Instantiation;
6161
Instantiation genericTypeArguments = type.Instantiation;
62+
6263
for (int i = 0; i < genericTypeArguments.Length; i++)
6364
{
6465
var genericTypeParameter = (EcmaGenericParameter)genericTypeParameters[i];

src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,74 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
15171517
info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN);
15181518
}
15191519

1520+
#if READYTORUN
1521+
bool isArray = decl.OwningType.IsInterface && objType.IsArray;
1522+
bool contextIsMethod = isArray || decl.HasInstantiation;
1523+
#else
1524+
bool contextIsMethod = decl.HasInstantiation;
1525+
#endif
1526+
MethodDesc instArgTarget = unboxingStub ? nonUnboxingImpl : impl;
1527+
bool requiresInstMethodDescArg = instArgTarget.RequiresInstMethodDescArg();
1528+
bool requiresInstMethodTableArg = instArgTarget.RequiresInstMethodTableArg();
1529+
1530+
// For unboxing stubs whose unboxed entry needs a MethodTable inst arg, the boxed object supplies the exact MT.
1531+
// For MethodDesc cases we always need to supply the exact MD.
1532+
if (requiresInstMethodDescArg || (requiresInstMethodTableArg && !unboxingStub))
1533+
{
1534+
if (originalImpl.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
1535+
{
1536+
// If we end up with a shared MethodTable that is not exact,
1537+
// we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup.
1538+
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
1539+
return false;
1540+
}
1541+
1542+
if (originalImpl.IsRuntimeDeterminedExactMethod || originalImpl.IsSharedByGenericInstantiations)
1543+
{
1544+
// TODO: Support for runtime lookup
1545+
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
1546+
return false;
1547+
}
1548+
}
1549+
1550+
#if READYTORUN
1551+
if (isArray)
1552+
{
1553+
// Array interface devirt is not yet supported by R2R.
1554+
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
1555+
return false;
1556+
}
1557+
#endif
1558+
1559+
if (requiresInstMethodDescArg)
1560+
{
1561+
if (unboxingStub)
1562+
{
1563+
// Bail out for now. We need an unboxing stub that points to an instantiated method.
1564+
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
1565+
return false;
1566+
}
1567+
#if READYTORUN
1568+
MethodWithToken originalImplWithToken = new MethodWithToken(originalImpl, methodWithTokenImpl.Token, null, false, null, null);
1569+
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.MethodDictionary, originalImplWithToken));
1570+
1571+
#else
1572+
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodGenericDictionary(originalImpl));
1573+
#endif
1574+
}
1575+
else if (requiresInstMethodTableArg)
1576+
{
1577+
if (!unboxingStub)
1578+
{
1579+
#if READYTORUN
1580+
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeDictionary, originalImpl.OwningType));
1581+
1582+
#else
1583+
info->instParamLookup.constLookup = CreateConstLookupToSymbol(_compilation.NodeFactory.ConstructedTypeSymbol(originalImpl.OwningType));
1584+
#endif
1585+
}
1586+
}
1587+
15201588
#if READYTORUN
15211589
// Testing has not shown that concerns about virtual matching are significant
15221590
// Only generate verification for builds with the stress mode enabled
@@ -1531,7 +1599,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
15311599
#endif
15321600
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_SUCCESS;
15331601
info->devirtualizedMethod = ObjectToHandle(impl);
1534-
info->tokenLookupContext = contextFromType(owningType);
1602+
info->tokenLookupContext = contextIsMethod ? contextFromMethod(originalImpl) : contextFromType(owningType);
15351603

15361604
return true;
15371605

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp
4545
//
4646
// Result method checking
4747
// 1. Ensure that the resolved result versions with the code, or is the decl method
48-
// 2. Devirtualizing to a default interface method is not currently considered to be useful, and how to check for version
49-
// resilience has not yet been analyzed.
48+
// 2. When devirtualizing to a default interface method, the resolved result method must version with the code.
5049
// 3. When checking that the resolved result versions with the code, validate that all of the types
5150
// From implType to the owning type of resolved result method also version with the code.
5251

@@ -163,6 +162,17 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp
163162

164163
if (resolvedVirtualMethod != null)
165164
{
165+
if (resolvedVirtualMethod.OwningType.IsInterface)
166+
{
167+
if (_compilationModuleGroup.VersionsWithMethodBody(resolvedVirtualMethod))
168+
{
169+
return resolvedVirtualMethod;
170+
}
171+
172+
devirtualizationDetail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_BUBBLE;
173+
return null;
174+
}
175+
166176
// Validate that the inheritance chain for resolution is within version bubble
167177
// The rule is somewhat tricky here.
168178
// If the resolved method is the declMethod, then only types which derive from the

src/coreclr/vm/interpexec.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,19 @@ static InterpByteCodeStart* PrepareInterpreterCode(MethodDesc* targetMethod, Int
13421342
// small subset of frames high.
13431343
pFrame->ip = ip;
13441344
pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame);
1345+
1346+
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
1347+
// Resolve .override before compilation: if a MethodImpl has remapped
1348+
// targetMethod's vtable slot, switch to the overriding method so we
1349+
// compile the correct body. Cache the result on the original MethodDesc
1350+
// so callers that check IsInterpreterCodeInitialized don't re-resolve.
1351+
MethodDesc* pOriginalMethod = targetMethod;
1352+
if (targetMethod->IsVtableSlot())
1353+
{
1354+
targetMethod = MethodTable::MapMethodDeclToMethodImpl(targetMethod);
1355+
}
1356+
#endif // FEATURE_PORTABLE_ENTRYPOINTS
1357+
13451358
{
13461359
GCX_PREEMP();
13471360
if (targetMethod->ShouldCallPrestub())
@@ -1353,11 +1366,22 @@ static InterpByteCodeStart* PrepareInterpreterCode(MethodDesc* targetMethod, Int
13531366
}
13541367
}
13551368
InterpByteCodeStart* targetIp = targetMethod->GetInterpreterCode();
1369+
13561370
if (targetIp == NULL)
13571371
{
13581372
// The prestub wasn't able to setup an interpreter code, so it will never be able to.
13591373
targetMethod->PoisonInterpreterCode();
1374+
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
1375+
if (pOriginalMethod != targetMethod)
1376+
pOriginalMethod->PoisonInterpreterCode();
1377+
#endif
1378+
}
1379+
#ifdef FEATURE_PORTABLE_ENTRYPOINTS
1380+
else if (pOriginalMethod != targetMethod)
1381+
{
1382+
pOriginalMethod->SetInterpreterCode(targetIp);
13601383
}
1384+
#endif
13611385

13621386
return targetIp;
13631387
}

src/coreclr/vm/jitinterface.cpp

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8770,15 +8770,6 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info)
87708770
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_LOOKUP;
87718771
return false;
87728772
}
8773-
8774-
// If we devirtualized into a default interface method on a generic type, we should actually return an
8775-
// instantiating stub but this is not happening.
8776-
// Making this work is tracked by https://github.com/dotnet/runtime/issues/9588
8777-
if (pDevirtMD->GetMethodTable()->IsInterface() && pDevirtMD->HasClassInstantiation())
8778-
{
8779-
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_DIM;
8780-
return false;
8781-
}
87828773
}
87838774
else
87848775
{
@@ -8841,53 +8832,105 @@ bool CEEInfo::resolveVirtualMethodHelper(CORINFO_DEVIRTUALIZATION_INFO * info)
88418832
bool isArray = false;
88428833
bool isGenericVirtual = false;
88438834

8844-
if (pApproxMT->IsInterface())
8845-
{
8846-
// As noted above, we can't yet handle generic interfaces
8847-
// with default methods.
8848-
_ASSERTE(!pDevirtMD->HasClassInstantiation());
8849-
8850-
}
8851-
else if (pBaseMT->IsInterface() && pObjMT->IsArray())
8835+
if (pBaseMT->IsInterface() && pObjMT->IsArray())
88528836
{
88538837
isArray = true;
88548838
}
8855-
else
8839+
else if (!pApproxMT->IsInterface())
88568840
{
88578841
pExactMT = pDevirtMD->GetExactDeclaringType(pObjMT);
88588842
}
88598843

8844+
MethodDesc* pInstantiatedMD = pDevirtMD;
8845+
88608846
// This is generic virtual method devirtualization.
88618847
if (!isArray && pBaseMD->HasMethodInstantiation())
88628848
{
8863-
MethodDesc* pPrimaryMD = pDevirtMD;
8849+
MethodDesc* pPrimaryMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD;
8850+
88648851
pDevirtMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
88658852
pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), true);
8866-
if (pDevirtMD->IsSharedByGenericMethodInstantiations())
8853+
8854+
pInstantiatedMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
8855+
pPrimaryMD, pExactMT, pExactMT->IsValueType() && !pPrimaryMD->IsStatic(), pBaseMD->GetMethodInstantiation(), false);
8856+
8857+
isGenericVirtual = true;
8858+
}
8859+
8860+
MethodDesc* pInstArgMD = pDevirtMD;
8861+
bool isUnboxingStubOfInstantiatingStub = false;
8862+
8863+
if (pDevirtMD->IsUnboxingStub())
8864+
{
8865+
// RequiresInstMethodDescArg and RequiresInstMethodTableArg are only valid for canonical instantiatins,
8866+
// use pDevirtMD instead of pInstantiatedMD for pInstArgMD.
8867+
//
8868+
pInstArgMD = pDevirtMD->GetWrappedMethodDesc();
8869+
if (pInstArgMD->IsInstantiatingStub())
88678870
{
8871+
isUnboxingStubOfInstantiatingStub = true;
8872+
}
8873+
}
8874+
else if (pDevirtMD->IsInstantiatingStub())
8875+
{
8876+
pInstArgMD = pDevirtMD->GetWrappedMethodDesc();
8877+
}
8878+
8879+
if (pInstArgMD->RequiresInstMethodDescArg())
8880+
{
8881+
if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetClassInstantiation()))
8882+
{
8883+
// If we end up with a shared MethodTable that is not exact,
8884+
// we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup.
88688885
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
88698886
return false;
88708887
}
88718888

8872-
isGenericVirtual = true;
8873-
}
8889+
if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetMethodInstantiation()))
8890+
{
8891+
// TODO: Support for runtime lookup
8892+
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
8893+
return false;
8894+
}
88748895

8875-
if (isArray || isGenericVirtual)
8896+
info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pInstantiatedMD;
8897+
info->instParamLookup.constLookup.accessType = IAT_VALUE;
8898+
}
8899+
else if (pInstArgMD->RequiresInstMethodTableArg())
88768900
{
8877-
if (pDevirtMD->IsInstantiatingStub())
8901+
if (!pDevirtMD->IsUnboxingStub() && TypeHandle::IsCanonicalSubtypeInstantiation(pExactMT->GetInstantiation()))
88788902
{
8879-
info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE)pDevirtMD;
8880-
info->instParamLookup.constLookup.accessType = IAT_VALUE;
8903+
// If we end up with a shared MethodTable that is not exact,
8904+
// we can't devirtualize since it's not possible to compute the instantiation argument even as a runtime lookup.
8905+
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
8906+
return false;
88818907
}
88828908

8883-
info->tokenLookupContext = MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pDevirtMD);
8884-
pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD;
8909+
info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pExactMT;
8910+
info->instParamLookup.constLookup.accessType = IAT_VALUE;
88858911
}
8886-
else
8912+
else if (isUnboxingStubOfInstantiatingStub)
88878913
{
8888-
info->tokenLookupContext = MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT);
8914+
if (TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetClassInstantiation()) ||
8915+
TypeHandle::IsCanonicalSubtypeInstantiation(pInstantiatedMD->GetMethodInstantiation()))
8916+
{
8917+
// This is an unboxing stub that points to an instantiating stub that requires a runtime lookup.
8918+
// Bail out.
8919+
info->detail = CORINFO_DEVIRTUALIZATION_FAILED_CANON;
8920+
return false;
8921+
}
8922+
8923+
// pInstArgMD is the wrapped instantiating stub in the unboxing stub.
8924+
//
8925+
info->instParamLookup.constLookup.handle = (CORINFO_GENERIC_HANDLE) pInstArgMD;
8926+
info->instParamLookup.constLookup.accessType = IAT_VALUE;
88898927
}
88908928

8929+
pDevirtMD = pDevirtMD->IsInstantiatingStub() ? pDevirtMD->GetWrappedMethodDesc() : pDevirtMD;
8930+
info->tokenLookupContext = (isArray || isGenericVirtual)
8931+
? MAKE_METHODCONTEXT((CORINFO_METHOD_HANDLE) pInstantiatedMD)
8932+
: MAKE_CLASSCONTEXT((CORINFO_CLASS_HANDLE) pExactMT);
8933+
88918934
// If we devirtualized into an unboxing stub, also hand back the unboxed entry
88928935
// so the jit can perform the unboxing transformation.
88938936
//
@@ -14652,8 +14695,8 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
1465214695
}
1465314696
}
1465414697

14655-
// Strip off method instantiation for comparison if the method is generic virtual.
14656-
if (pDeclMethod->HasMethodInstantiation())
14698+
// Strip off method instantiation for comparison if the method is generic virtual or generic DIM.
14699+
if (pDeclMethod->HasMethodInstantiation() || pDeclMethod->IsInterface())
1465714700
{
1465814701
if (pImplMethodRuntime != NULL)
1465914702
{

0 commit comments

Comments
 (0)