From a445e5dc1292c34c2156ce41c386b89e43010a67 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2020 13:07:34 -0700 Subject: [PATCH 1/5] Add new test that behaves incorrectly with function pointers --- .../tests/src/readytorun/crossgen2/Program.cs | 27 +++++++++++++++++++ .../crossgen2/crossgen2smoke.csproj | 12 +++++++-- .../src/readytorun/crossgen2/helperdll.cs | 22 +++++++++++++++ .../src/readytorun/crossgen2/helperdll.csproj | 10 +++++++ .../src/readytorun/crossgen2/helperildll.il | 25 +++++++++++++++++ .../readytorun/crossgen2/helperildll.ilproj | 9 +++++++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/coreclr/tests/src/readytorun/crossgen2/helperdll.cs create mode 100644 src/coreclr/tests/src/readytorun/crossgen2/helperdll.csproj create mode 100644 src/coreclr/tests/src/readytorun/crossgen2/helperildll.il create mode 100644 src/coreclr/tests/src/readytorun/crossgen2/helperildll.ilproj diff --git a/src/coreclr/tests/src/readytorun/crossgen2/Program.cs b/src/coreclr/tests/src/readytorun/crossgen2/Program.cs index c9b8e6c21386fc..41742cd867ce58 100644 --- a/src/coreclr/tests/src/readytorun/crossgen2/Program.cs +++ b/src/coreclr/tests/src/readytorun/crossgen2/Program.cs @@ -1237,6 +1237,31 @@ private static string EmitTextFileForTesting() return file; } + private static bool DelegateFromAnotherModuleTest() + { + // This test tests referencing a method from another module while creating a delegate. + Action del = HelperClass.DelegateReferencedMethod; + string delegateMethodString = del.Method.ToString(); + Console.WriteLine(delegateMethodString); + if (!delegateMethodString.Contains("DelegateReferencedMethod")) + return false; + else + return true; + } + + private static bool FunctionPointerFromAnotherModuleTest() + { + // This test tests referencing a method from another module while creating a function pointer. + // Function pointers to managed functions should be stable, and result in calling the right function + IntPtr initialFunctionPointer = HelperILCode.GetFunctionPointerFromOtherModule(); + HelperILCode.CallFunctionPointer(initialFunctionPointer); + HelperILCode.CallFunctionPointer(initialFunctionPointer); + HelperILCode.CallFunctionPointer(initialFunctionPointer); + IntPtr finalFunctionPointer = HelperILCode.GetFunctionPointerFromOtherModule(); + + return finalFunctionPointer == initialFunctionPointer; + } + public static int Main(string[] args) { _passedTests = new List(); @@ -1296,6 +1321,8 @@ public static int Main(string[] args) RunTest("ObjectToStringOnGenericParamTestSByte", ObjectToStringOnGenericParamTestSByte()); RunTest("ObjectToStringOnGenericParamTestVersionBubbleLocalStruct", ObjectToStringOnGenericParamTestVersionBubbleLocalStruct()); RunTest("EnumValuesToStringTest", EnumValuesToStringTest()); + RunTest("DelegateFromAnotherModuleTest", DelegateFromAnotherModuleTest()); + RunTest("FunctionPointerFromAnotherModuleTest", FunctionPointerFromAnotherModuleTest()); File.Delete(TextFileName); diff --git a/src/coreclr/tests/src/readytorun/crossgen2/crossgen2smoke.csproj b/src/coreclr/tests/src/readytorun/crossgen2/crossgen2smoke.csproj index ef9870bea9418b..b38f56eba8c7e7 100644 --- a/src/coreclr/tests/src/readytorun/crossgen2/crossgen2smoke.csproj +++ b/src/coreclr/tests/src/readytorun/crossgen2/crossgen2smoke.csproj @@ -14,17 +14,25 @@ false + + + + diff --git a/src/coreclr/tests/src/readytorun/crossgen2/helperdll.cs b/src/coreclr/tests/src/readytorun/crossgen2/helperdll.cs new file mode 100644 index 00000000000000..806dd7d753560d --- /dev/null +++ b/src/coreclr/tests/src/readytorun/crossgen2/helperdll.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +public class HelperClass +{ + // This method is used to test whether or not a method from a separate module + // referenced via Delegate is handled correctly. Do not call this method directly. + public static void DelegateReferencedMethod() + { + Console.WriteLine("In helper method"); + } + + // This method is used to test whether or not a method from a separate module + // referenced as a function pointer is handled correctly. Do not call this method directly + public static void FunctionPointerReferencedMethod() + { + Console.WriteLine("In function pointer method"); + } +} \ No newline at end of file diff --git a/src/coreclr/tests/src/readytorun/crossgen2/helperdll.csproj b/src/coreclr/tests/src/readytorun/crossgen2/helperdll.csproj new file mode 100644 index 00000000000000..9ff17919f10b7f --- /dev/null +++ b/src/coreclr/tests/src/readytorun/crossgen2/helperdll.csproj @@ -0,0 +1,10 @@ + + + library + SharedLibrary + True + + + + + diff --git a/src/coreclr/tests/src/readytorun/crossgen2/helperildll.il b/src/coreclr/tests/src/readytorun/crossgen2/helperildll.il new file mode 100644 index 00000000000000..5308f5b63df4f1 --- /dev/null +++ b/src/coreclr/tests/src/readytorun/crossgen2/helperildll.il @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +.assembly extern mscorlib { } +.assembly extern helperdll { } + +.assembly helperildll {} +.module helperildll.dll + +.class auto ansi public beforefieldinit HelperILCode + extends [mscorlib]System.Object +{ + .method public hidebysig static native int GetFunctionPointerFromOtherModule() cil managed noinlining + { + ldftn void [helperdll]HelperClass::FunctionPointerReferencedMethod() + ret + } + .method public hidebysig static void CallFunctionPointer(native int) cil managed noinlining + { + ldarg.0 + calli void() + ret + } +} diff --git a/src/coreclr/tests/src/readytorun/crossgen2/helperildll.ilproj b/src/coreclr/tests/src/readytorun/crossgen2/helperildll.ilproj new file mode 100644 index 00000000000000..bd9274e7e8f16d --- /dev/null +++ b/src/coreclr/tests/src/readytorun/crossgen2/helperildll.ilproj @@ -0,0 +1,9 @@ + + + Library + SharedLibrary + + + + + From 1c0a1e82dd5e2982cf5d5275239bb8b5d0d15873 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2020 13:44:19 -0700 Subject: [PATCH 2/5] Fix ldftn to external methods - Use existing Precode/Local import split - Remove special casing of ExternalMethodImport --- .../ReadyToRun/ExternalMethodImport.cs | 45 ------------------- .../ReadyToRun/LocalMethodImport.cs | 12 +++-- .../ReadyToRun/PrecodeMethodImport.cs | 12 +++-- .../ReadyToRunCodegenNodeFactory.cs | 38 +++++++--------- .../ILCompiler.ReadyToRun.csproj | 1 - 5 files changed, 32 insertions(+), 76 deletions(-) delete mode 100644 src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExternalMethodImport.cs diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExternalMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExternalMethodImport.cs deleted file mode 100644 index 1e57fe0312dd0d..00000000000000 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ExternalMethodImport.cs +++ /dev/null @@ -1,45 +0,0 @@ - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Internal.JitInterface; -using Internal.TypeSystem; -using Internal.ReadyToRunConstants; - -namespace ILCompiler.DependencyAnalysis.ReadyToRun -{ - public class ExternalMethodImport : DelayLoadHelperImport, IMethodNode - { - private readonly MethodWithToken _method; - - public ExternalMethodImport( - NodeFactory factory, - ReadyToRunFixupKind fixupKind, - MethodWithToken method, - bool isUnboxingStub, - bool isInstantiatingStub) - : base( - factory, - factory.MethodImports, - ReadyToRunHelper.DelayLoad_MethodCall, - factory.MethodSignature( - fixupKind, - method, - isUnboxingStub, - isInstantiatingStub)) - { - _method = method; - } - - public MethodDesc Method => _method.Method; - - public override int ClassCode => 458823351; - - // This is just here in case of future extension (_method is already compared in the base CompareToImpl) - public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) - { - return base.CompareToImpl(other, comparer); - } - } -} diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs index 55f634292bd27b..e969cac84a01cf 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs @@ -48,14 +48,18 @@ public override IEnumerable GetStaticDependencies(NodeFacto { yield return entry; } - yield return new DependencyListEntry(_localMethod, "Local method import"); + if (_localMethod != null) + yield return new DependencyListEntry(_localMethod, "Local method import"); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - int result = comparer.Compare(_localMethod, ((LocalMethodImport)other)._localMethod); - if (result != 0) - return result; + if (_localMethod != null) + { + int result = comparer.Compare(_localMethod, ((LocalMethodImport)other)._localMethod); + if (result != 0) + return result; + } return base.CompareToImpl(other, comparer); } diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs index d426faefa4b752..e09823599b2c54 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs @@ -55,14 +55,18 @@ public override IEnumerable GetStaticDependencies(NodeFacto { yield return entry; } - yield return new DependencyListEntry(_localMethod, "Precode Method Import"); + if (_localMethod != null) + yield return new DependencyListEntry(_localMethod, "Precode Method Import"); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - int result = comparer.Compare(_localMethod, ((PrecodeMethodImport)other)._localMethod); - if (result != 0) - return result; + if (_localMethod != null) + { + int result = comparer.Compare(_localMethod, ((PrecodeMethodImport)other)._localMethod); + if (result != 0) + return result; + } return base.CompareToImpl(other, comparer); } diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index eeb590bce993bd..476eacbaedec1f 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -351,36 +351,30 @@ private IMethodNode CreateMethodEntrypoint(TypeAndMethod key) bool isInstantiatingStub = key.IsInstantiatingStub; bool isPrecodeImportRequired = key.IsPrecodeImportRequired; MethodDesc compilableMethod = method.Method.GetCanonMethodTarget(CanonicalFormKind.Specific); + MethodWithGCInfo methodWithGCInfo = null; + if (CompilationModuleGroup.ContainsMethodBody(compilableMethod, false)) { - if (isPrecodeImportRequired) - { - return new PrecodeMethodImport( - this, - ReadyToRunFixupKind.MethodEntry, - method, - CompiledMethodNode(compilableMethod), - isUnboxingStub, - isInstantiatingStub); - } - else - { - return new LocalMethodImport( - this, - ReadyToRunFixupKind.MethodEntry, - method, - CompiledMethodNode(compilableMethod), - isUnboxingStub, - isInstantiatingStub); - } + methodWithGCInfo = CompiledMethodNode(compilableMethod); + } + + if (isPrecodeImportRequired) + { + return new PrecodeMethodImport( + this, + ReadyToRunFixupKind.MethodEntry, + method, + methodWithGCInfo, + isUnboxingStub, + isInstantiatingStub); } else { - // First time we see a given external method - emit indirection cell and the import entry - return new ExternalMethodImport( + return new LocalMethodImport( this, ReadyToRunFixupKind.MethodEntry, method, + methodWithGCInfo, isUnboxingStub, isInstantiatingStub); } diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 6cb5da336d1daf..9a188e52d13957 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -126,7 +126,6 @@ - From 338dadb71aac539e55d058140a99b3dddd3242d7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2020 14:07:47 -0700 Subject: [PATCH 3/5] Rename LocalMethodImport to DelayLoadMethodImport and fix comparison bug --- .../{LocalMethodImport.cs => DelayLoadMethodImport.cs} | 8 ++++---- .../DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs | 2 +- .../DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs | 6 +++--- .../ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/{LocalMethodImport.cs => DelayLoadMethodImport.cs} (86%) diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs similarity index 86% rename from src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs rename to src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs index e969cac84a01cf..1428ce8d3190b6 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs @@ -11,12 +11,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { - public class LocalMethodImport : DelayLoadHelperImport, IMethodNode + public class DelayLoadMethodImport : DelayLoadHelperImport, IMethodNode { private readonly MethodWithGCInfo _localMethod; private readonly MethodWithToken _method; - public LocalMethodImport( + public DelayLoadMethodImport( NodeFactory factory, ReadyToRunFixupKind fixupKind, MethodWithToken method, @@ -54,9 +54,9 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - if (_localMethod != null) + if ((_localMethod != null) && (((DelayLoadMethodImport)other)._localMethod != null)) { - int result = comparer.Compare(_localMethod, ((LocalMethodImport)other)._localMethod); + int result = comparer.Compare(_localMethod, ((DelayLoadMethodImport)other)._localMethod); if (result != 0) return result; } diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs index e09823599b2c54..2b9a4e70569bb6 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs @@ -61,7 +61,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { - if (_localMethod != null) + if ((_localMethod != null) && (((PrecodeMethodImport)other)._localMethod != null)) { int result = comparer.Compare(_localMethod, ((PrecodeMethodImport)other)._localMethod); if (result != 0) diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 476eacbaedec1f..5e363345e0f018 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -370,7 +370,7 @@ private IMethodNode CreateMethodEntrypoint(TypeAndMethod key) } else { - return new LocalMethodImport( + return new DelayLoadMethodImport( this, ReadyToRunFixupKind.MethodEntry, method, @@ -403,9 +403,9 @@ public IEnumerable EnumerateCompiledMethods(EcmaModule moduleT IMethodNode methodNodeDebug = MethodEntrypoint(new MethodWithToken(method, moduleToken, constrainedType: null), false, false, false); MethodWithGCInfo methodCodeNodeDebug = methodNodeDebug as MethodWithGCInfo; - if (methodCodeNodeDebug == null && methodNodeDebug is LocalMethodImport localMethodImport) + if (methodCodeNodeDebug == null && methodNodeDebug is DelayLoadMethodImport DelayLoadMethodImport) { - methodCodeNodeDebug = localMethodImport.MethodCodeNode; + methodCodeNodeDebug = DelayLoadMethodImport.MethodCodeNode; } if (methodCodeNodeDebug == null && methodNodeDebug is PrecodeMethodImport precodeMethodImport) { diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 9a188e52d13957..099820c53e54f4 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -135,7 +135,7 @@ - + From 1304c01c37050318f9e91698e10f6112caaa0196 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 22 Jun 2020 17:59:17 -0700 Subject: [PATCH 4/5] Fix crossgen2 determinism test --- .../readytorun/determinism/crossgen2determinism.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tests/src/readytorun/determinism/crossgen2determinism.csproj b/src/coreclr/tests/src/readytorun/determinism/crossgen2determinism.csproj index e4256dd7e19e19..7f1b93d2e20343 100644 --- a/src/coreclr/tests/src/readytorun/determinism/crossgen2determinism.csproj +++ b/src/coreclr/tests/src/readytorun/determinism/crossgen2determinism.csproj @@ -18,16 +18,16 @@ From 9a1a29da03277b6f4b31b6ac5a7f513bbdaca858 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 23 Jun 2020 15:01:09 -0700 Subject: [PATCH 5/5] Fix comparisons to provide total order in sort --- .../DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs | 4 ++++ .../DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs index 1428ce8d3190b6..2a921c01155c4d 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs @@ -60,6 +60,10 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer if (result != 0) return result; } + else if (_localMethod != null) + return 1; + else if (((DelayLoadMethodImport)other)._localMethod != null) + return -1; return base.CompareToImpl(other, comparer); } diff --git a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs index 2b9a4e70569bb6..7a71cb8534a391 100644 --- a/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs +++ b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs @@ -67,6 +67,10 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer if (result != 0) return result; } + else if (_localMethod != null) + return 1; + else if (((PrecodeMethodImport)other)._localMethod != null) + return -1; return base.CompareToImpl(other, comparer); }