From 66653b97938134af4905251078cb1989b9bc6512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 7 Nov 2023 08:55:51 +0100 Subject: [PATCH 1/5] Disallow inlining Main When we compile managed code, `Main` is not the actual spot where execution of managed code starts. Instead it's the `StartupCodeMain` method that the compiler generates. This method is responsible for initializing the managed environment, calling `Main` and tearing down the environment. If `Main` is short enough, sometimes it gets inlined into `StartupCodeMain` this has bad impact on diagnostics (don't see `Main` in stack traces, can't set breakpoints). Pretend it was marked `NoInlining`. --- src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 01d2d15fc2997e..13eaf3ec557e1b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1129,6 +1129,15 @@ private uint getMethodAttribsInternal(MethodDesc method) { result |= CorInfoFlag.CORINFO_FLG_FORCEINLINE; } +#if !READYTORUN + else if (method.OwningType is EcmaType ecmaOwningType + && ecmaOwningType.EcmaModule.EntryPoint == method) + { + // Mark entrypoint as NoInlining for better debugging. We don't want it to inline + // into startup code because breakpoints won't hit and stack traces won't have Main. + result |= CorInfoFlag.CORINFO_FLG_DONT_INLINE; + } +#endif if (method.OwningType.IsDelegate && method.Name == "Invoke") { From 5c20d81f2c0da4ca892b4b5bc17476d67be6ae3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 9 Nov 2023 10:43:13 +0100 Subject: [PATCH 2/5] FB --- .../tools/Common/JitInterface/CorInfoImpl.cs | 9 -------- .../StartupCode/StartupCodeMainMethod.cs | 22 +++++++++++++++++++ .../JitInterface/CorInfoImpl.RyuJit.cs | 9 ++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 13eaf3ec557e1b..01d2d15fc2997e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1129,15 +1129,6 @@ private uint getMethodAttribsInternal(MethodDesc method) { result |= CorInfoFlag.CORINFO_FLG_FORCEINLINE; } -#if !READYTORUN - else if (method.OwningType is EcmaType ecmaOwningType - && ecmaOwningType.EcmaModule.EntryPoint == method) - { - // Mark entrypoint as NoInlining for better debugging. We don't want it to inline - // into startup code because breakpoints won't hit and stack traces won't have Main. - result |= CorInfoFlag.CORINFO_FLG_DONT_INLINE; - } -#endif if (method.OwningType.IsDelegate && method.Name == "Invoke") { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs index c86852973e527d..5d2db93ed1587f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -254,6 +254,25 @@ public override MethodSignature Signature } } + public override bool IsNoOptimization + { + get + { + // Mark as no optimization so that Main doesn't get inlined + // into this method. We want Main to be visible in stack traces. + return true; + } + } + + public override bool IsNoInlining + { + get + { + // Mark NoInlining so that IsNoOptimization is guaranteed to kick in. + return true; + } + } + public override MethodIL EmitIL() { ILEmitter emit = new ILEmitter(); @@ -268,6 +287,9 @@ public override MethodIL EmitIL() if (Context.Target.IsWindows) codeStream.MarkDebuggerStepInPoint(); + // This would be tail call eligible but we don't do tail calls + // if the method is marked NoOptimization and we just did it above. + codeStream.Emit(ILOpcode.tail); codeStream.Emit(ILOpcode.call, emit.NewToken(WrappedMethod)); codeStream.Emit(ILOpcode.ret); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 460dbb744d934d..0b0fd4e4431a23 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -13,6 +13,7 @@ using ILCompiler; using ILCompiler.DependencyAnalysis; +using Internal.TypeSystem.Ecma; #if SUPPORT_JIT using MethodCodeNode = Internal.Runtime.JitSupport.JitMethodCodeNode; @@ -799,6 +800,14 @@ private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUC { MethodDesc caller = HandleToObject(callerHnd); + if (caller.OwningType is EcmaType ecmaOwningType + && ecmaOwningType.EcmaModule.EntryPoint == caller) + { + // We don't want to tailcall the entrypoint for an application; It results in a rather + // confusing debugging experience. + result = false; + } + if (caller.IsNoInlining) { // Do not tailcall from methods that are marked as noinline (people often use no-inline From e6f8624113b7a1b4d53a8b17b2573977ba2f06ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 10 Nov 2023 08:38:11 +0100 Subject: [PATCH 3/5] Update jitinterface.cpp --- src/coreclr/vm/jitinterface.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 9471239e25a87a..1a844f7da4db07 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8226,9 +8226,8 @@ bool CEEInfo::canTailCall (CORINFO_METHOD_HANDLE hCaller, { mdMethodDef callerToken = pCaller->GetMemberDef(); - // We don't want to tailcall the entrypoint for an application; JIT64 will sometimes - // do this for simple entrypoints and it results in a rather confusing debugging - // experience. + // Do not tailcall from the application entrypoint. + // We want Main to be visible in stack traces. if (callerToken == pCaller->GetModule()->GetEntryPointToken()) { result = false; From b7713ff2968357fd3e7c954404f17672d88d0cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 10 Nov 2023 08:38:26 +0100 Subject: [PATCH 4/5] Update src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Co-authored-by: Jan Kotas --- .../aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 0b0fd4e4431a23..eb988b0b604f8c 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -803,8 +803,8 @@ private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUC if (caller.OwningType is EcmaType ecmaOwningType && ecmaOwningType.EcmaModule.EntryPoint == caller) { - // We don't want to tailcall the entrypoint for an application; It results in a rather - // confusing debugging experience. + // Do not tailcall from the application entrypoint. + // We want Main to be visible in stack traces. result = false; } From a156635ab8e5a4a8d20762e17a512a307d9580ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 10 Nov 2023 08:39:39 +0100 Subject: [PATCH 5/5] Update src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs --- .../IL/Stubs/StartupCode/StartupCodeMainMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs index 5d2db93ed1587f..3dcbb4f27cc09c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/StartupCode/StartupCodeMainMethod.cs @@ -288,7 +288,7 @@ public override MethodIL EmitIL() codeStream.MarkDebuggerStepInPoint(); // This would be tail call eligible but we don't do tail calls - // if the method is marked NoOptimization and we just did it above. + // if the method is marked NoInlining and we just did it above. codeStream.Emit(ILOpcode.tail); codeStream.Emit(ILOpcode.call, emit.NewToken(WrappedMethod));