Skip to content

Commit

Permalink
Disallow inlining Main (dotnet#94449)
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
MichalStrehovsky authored Nov 10, 2023
1 parent 5501924 commit 9137cb4
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 NoInlining and we just did it above.
codeStream.Emit(ILOpcode.tail);
codeStream.Emit(ILOpcode.call, emit.NewToken(WrappedMethod));

codeStream.Emit(ILOpcode.ret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

using ILCompiler;
using ILCompiler.DependencyAnalysis;
using Internal.TypeSystem.Ecma;

#if SUPPORT_JIT
using MethodCodeNode = Internal.Runtime.JitSupport.JitMethodCodeNode;
Expand Down Expand Up @@ -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)
{
// Do not tailcall from the application entrypoint.
// We want Main to be visible in stack traces.
result = false;
}

if (caller.IsNoInlining)
{
// Do not tailcall from methods that are marked as noinline (people often use no-inline
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 9137cb4

Please sign in to comment.