Skip to content

Commit cb08364

Browse files
authored
Crossgen2 support for static virtual method resolution (take 2) (#87438)
This change adds SVM resolution support to Crossgen2. We still resort to runtime JIT in case we cannot resolve the SVM call at compile time (typically for canonical generic methods); some of these cases are just due to current limitations of the JIT interface and can be fixed in the future. Thanks Tomas
1 parent 261e3a4 commit cb08364

File tree

11 files changed

+198
-34
lines changed

11 files changed

+198
-34
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,9 @@ public enum CorInfoTokenKind
13651365

13661366
// token comes from devirtualizing a method
13671367
CORINFO_TOKENKIND_DevirtualizedMethod = 0x800 | CORINFO_TOKENKIND_Method,
1368+
1369+
// token comes from resolved static virtual method
1370+
CORINFO_TOKENKIND_ResolvedStaticVirtualMethod = 0x1000 | CORINFO_TOKENKIND_Method,
13681371
};
13691372

13701373
// These are error codes returned by CompileMethod

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,20 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
4242
{
4343
SignatureContext innerContext = builder.EmitFixup(factory, ReadyToRunFixupKind.DelegateCtor, _methodToken.Token.Module, factory.SignatureContext);
4444

45+
bool needsInstantiatingStub = _targetMethod.Method.HasInstantiation;
46+
if (_targetMethod.Method.IsVirtual && _targetMethod.Method.Signature.IsStatic)
47+
{
48+
// For static virtual methods, we always require an instantiating stub as the method may resolve to a canonical representation
49+
// at runtime without us being able to detect that at compile time.
50+
needsInstantiatingStub |= (_targetMethod.Method.OwningType.HasInstantiation || _methodToken.ConstrainedType != null);
51+
}
52+
4553
builder.EmitMethodSignature(
4654
_methodToken,
4755
enforceDefEncoding: false,
4856
enforceOwningType: false,
4957
innerContext,
50-
isInstantiatingStub: _targetMethod.Method.HasInstantiation);
58+
isInstantiatingStub: needsInstantiatingStub);
5159

5260
builder.EmitTypeSignature(_delegateType, innerContext);
5361
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,13 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
8888
}
8989
else
9090
{
91-
bool isUnboxingStub = false;
91+
bool isStub = false;
9292
if (methodNode is DelayLoadHelperImport methodImport)
9393
{
94-
isUnboxingStub = ((MethodFixupSignature)methodImport.ImportSignature.Target).IsUnboxingStub;
94+
MethodFixupSignature signature = (MethodFixupSignature)methodImport.ImportSignature.Target;
95+
isStub = signature.IsUnboxingStub || signature.IsInstantiatingStub;
9596
}
96-
builder.GetCallRefMap(methodNode.Method, isUnboxingStub);
97+
builder.GetCallRefMap(methodNode.Method, isStub);
9798
}
9899
if (methodIndex >= nextMethodIndex)
99100
{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class Import : EmbeddedObjectNode, ISymbolDefinitionNode, ISortableSymbol
1919

2020
internal readonly MethodDesc CallingMethod;
2121

22+
public Signature Signature => ImportSignature.Target;
23+
2224
public Import(ImportSectionNode tableNode, Signature importSignature, MethodDesc callingMethod = null)
2325
{
2426
Table = tableNode;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public MethodFixupSignature(
3535
compilerContext.EnsureLoadableMethod(method.Method);
3636
compilerContext.EnsureLoadableType(_method.OwningType);
3737

38-
if (method.ConstrainedType != null)
38+
if (method.ConstrainedType != null && !method.ConstrainedType.IsRuntimeDeterminedSubtype)
3939
compilerContext.EnsureLoadableType(method.ConstrainedType);
4040
}
4141

@@ -46,6 +46,10 @@ public MethodFixupSignature(
4646

4747
public bool IsUnboxingStub => _method.Unboxing;
4848

49+
public TypeDesc ConstrainedType => _method.ConstrainedType;
50+
51+
public bool NeedsInstantiationArg => _method.ConstrainedType?.IsCanonicalSubtype(CanonicalFormKind.Any) ?? false;
52+
4953
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
5054
{
5155
DependencyList list = base.ComputeNonRelocationBasedDependencies(factory);

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

Lines changed: 132 additions & 25 deletions
Large diffs are not rendered by default.

src/coreclr/vm/methodtable.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9242,7 +9242,7 @@ MethodTable::TryResolveConstraintMethodApprox(
92429242
{
92439243
_ASSERTE(!thInterfaceType.IsTypeDesc());
92449244
_ASSERTE(thInterfaceType.IsInterface());
9245-
BOOL uniqueResolution;
9245+
BOOL uniqueResolution = TRUE;
92469246

92479247
ResolveVirtualStaticMethodFlags flags = ResolveVirtualStaticMethodFlags::AllowVariantMatches
92489248
| ResolveVirtualStaticMethodFlags::InstantiateResultOverFinalMethodDesc;
@@ -9255,7 +9255,8 @@ MethodTable::TryResolveConstraintMethodApprox(
92559255
thInterfaceType.GetMethodTable(),
92569256
pInterfaceMD,
92579257
flags,
9258-
&uniqueResolution);
9258+
(pfForceUseRuntimeLookup != NULL ? &uniqueResolution : NULL));
9259+
92599260
if (result == NULL || !uniqueResolution)
92609261
{
92619262
_ASSERTE(pfForceUseRuntimeLookup != NULL);

src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodBodyOnUnrelatedType.ilproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<Project Sdk="Microsoft.NET.Sdk.IL">
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
4+
5+
<!-- Crossgen2 currently doesn't support this negative check - that should be fine as runtime behavior is undefined in the presence of invalid IL. -->
6+
<CrossGenTest>false</CrossGenTest>
47
</PropertyGroup>
58
<PropertyGroup>
69
<DebugType>Full</DebugType>

src/tests/Loader/classloader/StaticVirtualMethods/Regression/GitHub_70385.il

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
1616
.ver 7:0:0:0
1717
}
18-
.assembly RecursiveGeneric
18+
.assembly GitHub_70385
1919
{
2020
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
2121
.custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@@ -27,7 +27,7 @@
2727
.hash algorithm 0x00008004
2828
.ver 0:0:0:0
2929
}
30-
.module RecursiveGeneric.dll
30+
.module GitHub_70385.dll
3131
// MVID: {10541B0F-16D6-4F9A-B0EB-E793F524F163}
3232
.imagebase 0x00400000
3333
.file alignment 0x00000200

src/tests/readytorun/tests/main.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,19 @@ static void TestILBodyChange()
441441
Assert.AreEqual(ILInliningTest.TestDifferentIntValue(), actualMethodCallResult);
442442
}
443443

444+
private class CallDefaultVsExactStaticVirtual<T> where T : IDefaultVsExactStaticVirtual
445+
{
446+
public static string CallMethodOnGenericType() => T.Method();
447+
}
448+
449+
[MethodImplAttribute(MethodImplOptions.NoInlining)]
450+
static void TestDefaultVsExactStaticVirtualMethodImplementation()
451+
{
452+
Assert.AreEqual(CallDefaultVsExactStaticVirtual<DefaultVsExactStaticVirtualClass>.CallMethodOnGenericType(), "DefaultVsExactStaticVirtualMethod");
453+
// Naively one would expect that the following should do, however Roslyn fails to compile it claiming that the type DVESVC doesn't contain 'Method':
454+
// Assert.AreEqual(DefaultVsExactStaticVirtualClass.Method(), "DefaultVsExactStaticVirtualMethod");
455+
}
456+
444457
static void RunAllTests()
445458
{
446459
Console.WriteLine("TestVirtualMethodCalls");
@@ -527,6 +540,10 @@ static void RunAllTests()
527540

528541
Console.WriteLine("TestILBodyChange");
529542
TestILBodyChange();
543+
544+
Console.WriteLine("TestDefaultVsExactStaticVirtualMethodImplementation");
545+
TestDefaultVsExactStaticVirtualMethodImplementation();
546+
530547
ILInliningVersioningTest<LocallyDefinedStructure>.RunAllTests(typeof(Program).Assembly);
531548
}
532549

0 commit comments

Comments
 (0)