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 71% 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 55f634292bd27b..2a921c01155c4d 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, @@ -48,14 +48,22 @@ 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) && (((DelayLoadMethodImport)other)._localMethod != null)) + { + int result = comparer.Compare(_localMethod, ((DelayLoadMethodImport)other)._localMethod); + 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/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/PrecodeMethodImport.cs b/src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs index d426faefa4b752..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 @@ -55,14 +55,22 @@ 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) && (((PrecodeMethodImport)other)._localMethod != null)) + { + int result = comparer.Compare(_localMethod, ((PrecodeMethodImport)other)._localMethod); + if (result != 0) + return result; + } + else if (_localMethod != null) + return 1; + else if (((PrecodeMethodImport)other)._localMethod != null) + return -1; 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..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 @@ -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 DelayLoadMethodImport( this, ReadyToRunFixupKind.MethodEntry, method, + methodWithGCInfo, isUnboxingStub, isInstantiatingStub); } @@ -409,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 6cb5da336d1daf..099820c53e54f4 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 @@ - @@ -136,7 +135,7 @@ - + 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 + + + + + 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 @@