Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tailcall regression with compiled F# #40864

Closed
brettfo opened this issue Aug 14, 2020 · 31 comments · Fixed by #41206
Closed

tailcall regression with compiled F# #40864

brettfo opened this issue Aug 14, 2020 · 31 comments · Fixed by #41206
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Milestone

Comments

@brettfo
Copy link
Member

brettfo commented Aug 14, 2020

Machine:
Latest Windows 10 x64

Pre-verification:

  1. On a clean machine install latest VS with the following workloads:
    • .NET desktop development
      • Add optional component F# desktop language support
    • .NET Core cross-platform development
  2. Clone https://github.com/dotnet/fsharp and reset to commit 1c1b5ac7eacbbfd79e7277982e15178cecee20b4
  3. Build with .\Build.cmd -c Release
  4. Run test with dotnet test tests\fsharp\FSharpSuite.Tests.fsproj --no-restore --no-build --filter LargeRecordDoesNotStackOverflow -f netcoreapp3.1 -c Release -v n

At this point the test should pass.

Bug repro:

  1. Install latest .NET 5 SDK from here.
  2. Invalidate the .NET 3 SDK and runtime by doing the following:
    • Rename/delete C:\Program Files\dotnet\sdk\3.1.401
    • Rename/delete C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.7
    • Ensure they're not listed via dotnet --list-sdks and dotnet --list-runtimes
  3. Re-run the test from step (4) exactly as above.
    N.b., you may need to alter global.json with the following:
     {
    +  "sdk": {
    +    "version": "5.0.100-preview.7.20366.6"
    +   },
       "tools": {
    -    "dotnet": "3.1.302",
    +    "dotnet": "5.0.100-preview.7.20366.6",
     ...

Result:
Stack overflow

Possibly related to #40581

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Aug 14, 2020
@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@AndyAyersMS AndyAyersMS added this to the 5.0.0 milestone Aug 14, 2020
@AndyAyersMS AndyAyersMS added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Aug 14, 2020
@AndyAyersMS
Copy link
Member

Presume codegen for now, and marking as 5.0 pending investigation.

cc @dotnet/jit-contrib

@AndyAyersMS
Copy link
Member

Going to take a preliminary look while waiting for other intermittent bugs to repro.

@dotnet/jit-contrib feel free to jump in and take this one.

@AndyAyersMS
Copy link
Member

Managed stack bactrace at point of overflow...

00000AA618C5710 00007ffa1664ece3 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
000000AA618C5790 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA618C57E0 00007ffa166511ff ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>>>, Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>>>, Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>, Expr, TOp, Microsoft.FSharp.Collections.FSharpList`1, Microsoft.FSharp.Collections.FSharpList`1)
000000AA618C5830 00007ffa16651165 ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)
000000AA618C58C0 00007ffa156e4f06 ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)
000000AA618C5940 00007ffa1664f796 FSharp.Compiler.AutoBox.DecideExpr(cenv, Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>>>, Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>>>, Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1>, Expr) [C:\repos\fsharp\src\fsharp\autobox.fs @ 101]
000000AA618C5A40 00007ffa1664eec8 ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2>>>>, Microsoft.FSharp.Core.FSharpFunc`2, Microsoft.FSharp.Core.FSharpFunc`2, Internal.Utilities.Collections.Tagged.Set`2, Expr) [/_/src/libraries/System.Private.CoreLib/src/System/Object.cs @ 27]
000000AA618C5A80 00007ffa1664ee40 ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)
000000AA618C5AF0 00007ffa156e4f06 ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)
000000AA618C5B70 00007ffa1664ed17 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
000000AA618C5BF0 00007ffa16650380 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprNoInterceptF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6523]
000000AA618C5C70 00007ffa1664eec8 ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2>>>>, Microsoft.FSharp.Core.FSharpFunc`2, Microsoft.FSharp.Core.FSharpFunc`2, Internal.Utilities.Collections.Tagged.Set`2, Expr) [/_/src/libraries/System.Private.CoreLib/src/System/Object.cs @ 27]
000000AA618C5CB0 00007ffa1664ee40 ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)
000000AA618C5D20 00007ffa156e4f06 ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)
000000AA618C5DA0 00007ffa1664ed17 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
000000AA618C5E20 00007ffa1664eec8 ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2>>>>, Microsoft.FSharp.Core.FSharpFunc`2, Microsoft.FSharp.Core.FSharpFunc`2, Internal.Utilities.Collections.Tagged.Set`2, Expr) [/_/src/libraries/System.Private.CoreLib/src/System/Object.cs @ 27]
000000AA618C5E60 00007ffa1664ee40 ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)
000000AA618C5ED0 00007ffa156e4f06 ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)
000000AA618C5F50 00007ffa1664ed17 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
000000AA618C5FD0 00007ffa16650380 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprNoInterceptF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6523]
000000AA618C6050 00007ffa1664eec8 ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2>>>>, Microsoft.FSharp.Core.FSharpFunc`2, Microsoft.FSharp.Core.FSharpFunc`2, Internal.Utilities.Collections.Tagged.Set`2, Expr) [/_/src/libraries/System.Private.CoreLib/src/System/Object.cs @ 27]
000000AA618C6090 00007ffa1664ee40 ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)
000000AA618C6100 00007ffa156e4f06 ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)
000000AA618C6180 00007ffa1664ed17 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
... (frames omitted) ...
000000AA61A3DF70 00007ffa1664ed17 FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr) [C:\repos\fsharp\src\fsharp\TypedTreeOps.fs @ 6511]
000000AA61A3DFF0 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA61A3E040 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA61A3E090 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA61A3E0E0 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA61A3E130 00007ffa1586f706 Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.__Canon>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 224]
000000AA61A3E180 00007ffa1664afbd FSharp.Compiler.AutoBox.TransformImplFile(TcGlobals, ImportMap, TypedImplFile) [C:\repos\fsharp\src\fsharp\autobox.fs @ 169]
000000AA61A3E220 00007ffa165d9ce3 FSharp.Compiler.CompileOptions+ApplyAllOptimizations@1726-1.Invoke(TypedImplFile) [C:\repos\fsharp\src\fsharp\CompileOptions.fs @ 1735]
000000AA61A3E460 00007ffa1599f99c Microsoft.FSharp.Primitives.Basics.List.mapFold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.Tuple`2<System.__Canon,System.__Canon>>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\local.fs @ 389]
000000AA61A3E580 00007ffa15e860ab Microsoft.FSharp.Collections.ListModule.MapFold[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,System.Tuple`2<System.__Canon,System.__Canon>>>, System.__Canon, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\list.fs @ 85]
000000AA61A3E5C0 00007ffa165d8d2b FSharp.Compiler.CompileOptions.ApplyAllOptimizations(TcConfig, TcGlobals, Microsoft.FSharp.Core.FSharpFunc`2<ValRef,Microsoft.FSharp.Core.FSharpFunc`2<ValUseFlag,Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Collections.FSharpList`1,Microsoft.FSharp.Core.FSharpFunc`2<range,System.Tuple`2<Expr,TType>>>>>, System.String, ImportMap, Boolean, IncrementalOptimizationEnv, CcuThunk, Microsoft.FSharp.Collections.FSharpList`1) [C:\repos\fsharp\src\fsharp\CompileOptions.fs @ 1723]
000000AA61A3E6E0 00007ffa15fda3e8 FSharp.Compiler.Driver.main2a[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Args`1<System.Tuple`8<System.__Canon,TcConfig,TcImports,TcImports,System.__Canon,ErrorLogger,CcuThunk,System.Tuple`8<System.String,Microsoft.FSharp.Collections.FSharpList`1,System.__Canon,System.__Canon,System.__Canon,System.__Canon,System.__Canon,System.Tuple`1>>>) [C:\repos\fsharp\src\fsharp\fsc.fs @ 2057]
000000AA61A3E950 00007ffa156db3a4 FSharp.Compiler.Driver.typecheckAndCompile(CompilationThreadToken, System.String[], Resolver, Boolean, ReduceMemoryFlag, CopyFSharpCoreFlag, Exiter, ErrorLoggerProvider, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<TcImports,Microsoft.FSharp.Core.Unit>>, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<TcGlobals,System.String,ILModuleDef>,Microsoft.FSharp.Core.Unit>>) [C:\repos\fsharp\src\fsharp\fsc.fs @ 2209]
000000AA61A3EA20 00007ffa156d9159 FSharp.Compiler.Driver.mainCompile(CompilationThreadToken, System.String[], Resolver, Boolean, ReduceMemoryFlag, CopyFSharpCoreFlag, Exiter, ErrorLoggerProvider, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<TcImports,Microsoft.FSharp.Core.Unit>>, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<TcGlobals,System.String,ILModuleDef>,Microsoft.FSharp.Core.Unit>>) [C:\repos\fsharp\src\fsharp\fsc.fs @ 2227]
000000AA61A3EA80 00007ffa156d8e6b FSharp.Compiler.SourceCodeServices.CompileHelpers+result@168-10.Invoke(Exiter) [C:\repos\fsharp\src\fsharp\service\service.fs @ 169]
000000AA61A3EB10 00007ffa156d52d6 FSharp.Compiler.SourceCodeServices.CompileHelpers.tryCompile(ErrorLogger, Microsoft.FSharp.Core.FSharpFunc`2<Exiter,Microsoft.FSharp.Core.Unit>) [C:\repos\fsharp\src\fsharp\service\service.fs @ 157]
000000AA61A3EBE0 00007ffa156d49a8 FSharp.Compiler.SourceCodeServices.CompileHelpers.compileFromArgs(CompilationThreadToken, System.String[], Resolver, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<TcImports,Microsoft.FSharp.Core.Unit>>, Microsoft.FSharp.Core.FSharpOption`1<Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<TcGlobals,System.String,ILModuleDef>,Microsoft.FSharp.Core.Unit>>) [C:\repos\fsharp\src\fsharp\service\service.fs @ 167]
000000AA61A3EC70 00007ffa156d4394 <StartupCode$FSharp-Compiler-Private>.$Service+Compile@1053-1.Invoke(Microsoft.FSharp.Core.Unit) [C:\repos\fsharp\src\fsharp\service\service.fs @ 1053]
000000AA61A3ECB0 00007ffa156d4318 FSharp.Compiler.AbstractIL.Internal.Library+CancellableModule+delay@741[[System.__Canon, System.Private.CoreLib]].Invoke(System.Threading.CancellationToken) [C:\repos\fsharp\src\absil\illib.fs @ 741]
000000AA61A3ECE0 00007ffa156d3b78 <StartupCode$FSharp-Compiler-Private>.$Reactor+EnqueueAndAwaitOpAsync@185-2[[System.__Canon, System.Private.CoreLib]].Invoke(CompilationThreadToken) [C:\repos\fsharp\src\fsharp\service\Reactor.fs @ 188]
000000AA61A3EE10 00007ffa156d2e48 <StartupCode$FSharp-Compiler-Private>.$Reactor+loop@71-115.Invoke(Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.SourceCodeServices.ReactorCommands>) [C:\repos\fsharp\src\fsharp\service\Reactor.fs @ 82]
000000AA61A3EED0 00007ffa156d021f Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]](Microsoft.FSharp.Control.AsyncActivation`1<System.__Canon>, Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,Microsoft.FSharp.Control.FSharpAsync`1<System.__Canon>>, System.__Canon) [C:\repos\fsharp\src\fsharp\FSharp.Core\async.fs @ 411]
000000AA61A3EF20 00007ffa156d037d <StartupCode$FSharp-Compiler-Private>.$Reactor+loop@61-113.Invoke(Microsoft.FSharp.Control.AsyncActivation`1<Microsoft.FSharp.Core.FSharpOption`1<FSharp.Compiler.SourceCodeServices.ReactorCommands>>) [C:\repos\fsharp\src\fsharp\service\Reactor.fs @ 61]
000000AA61A3EF50 00007ffa156cf504 <StartupCode$FSharp-Core>.$Mailbox+processFirstArrival@303-8[[System.__Canon, System.Private.CoreLib]].Invoke(Microsoft.FSharp.Control.AsyncActivation`1<System.__Canon>) [C:\repos\fsharp\src\fsharp\FSharp.Core\mailbox.fs @ 303]
000000AA61A3EFA0 00007ffa1529a364 Microsoft.FSharp.Control.Trampoline.Execute(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.AsyncReturn>) [C:\repos\fsharp\src\fsharp\FSharp.Core\async.fs @ 104]
000000AA61A3F000 00007ffa15299c00 Microsoft.FSharp.Control.TrampolineHolder.ExecuteWithTrampoline(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.AsyncReturn>) [C:\repos\fsharp\src\fsharp\FSharp.Core\async.fs @ 169]
000000AA61A3F060 00007ffa15299636 <StartupCode$FSharp-Core>.$Async+-ctor@155-1.Invoke(System.Object) [C:\repos\fsharp\src\fsharp\FSharp.Core\async.fs @ 157]
000000AA61A3F0B0 00007ffa731b6de9 System.Threading.QueueUserWorkItemCallback+c.<.cctor>b__6_0(System.Threading.QueueUserWorkItemCallback) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @ 784]
000000AA61A3F0E0 00007ffa731ac88e System.Threading.ExecutionContext.RunForThreadPoolUnsafe[[System.__Canon, System.Private.CoreLib]](System.Threading.ExecutionContext, System.Action`1<System.__Canon>, System.__Canon ByRef) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 332]
000000AA61A3F120 00007ffa731b6d2f System.Threading.QueueUserWorkItemCallback.Execute() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @ 800]
000000AA61A3F160 00007ffa731b5d2c System.Threading.ThreadPoolWorkQueue.Dispatch() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs @ 641]
000000AA61A3F210 00007ffa731a49aa System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() [/_/src/coreclr/src/System.Private.CoreLib/src/System/Threading/ThreadPool.CoreCLR.cs @ 29]
000000AA61A3F600 00007ffa744ffed3 [DebuggerU2MCatchHandlerFrame: 000000aa61a3f600] 

@AndyAyersMS
Copy link
Member

Something goes wrong in the new tail call helpers and we end up not reusing stack frames.

cc @jakobbotsch @erozenfeld

@jakobbotsch
Copy link
Member

I cannot quite see what segment of the stack is looping in your backtrace. However, it looks like the instantiating stub generated and used by the JIT here also needs to do a tailcall. Since it is leaving a normal frame on the stack the tailcalling mechanism is probably not kicking in. We might be missing tests for this scenario, though it is unclear to me, there are many generic tests around here, and some of them should involve instantiating stubs:

[MethodImpl(MethodImplOptions.NoInlining)]
private static string GenInterfaceForwardG<T1, T2>(T1 a, T2 b, IGenInterface<T1, T2> igen)
{
IL.Push(igen);
IL.Push(new S32());
IL.Push(a);
IL.Push(b);
IL.Emit.Tail();
IL.Emit.Callvirt(new MethodRef(typeof(IGenInterface<T1, T2>), nameof(IGenInterface<T1, T2>.G)));
return IL.Return<string>();
}

I'm not sure how this is normally handled, to me it seems like tail-calling a pointer produced by loadvirtftn will not guarantee that the stack does not grow since that pointer could be an instantiating stub.

@jkotas
Copy link
Member

jkotas commented Aug 15, 2020

The instantiating stubs for trivial signatures (e.g. where all arguments fit into registers) use regular tail calls. The instantiating stubs for more complex signatures do not tailcall. It is what's causing the problem.

@AndyAyersMS
Copy link
Member

Here's the top of the native stack, frame 0a is one of those complex instantiating stubs

00 00007ffa`743e86b1     : 00000000`00000000 00007ffa`743e8461 00007ffa`15a566f0 00000000`00000000 : coreclr!SigPointer::GetTypeHandleThrowing+0x30 [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp @ 993] 
01 00007ffa`743e86b1     : 00000000`00000000 00007ffa`00000000 00000000`ffffffe7 00007ffa`15a566f0 : coreclr!SigPointer::GetTypeHandleThrowing+0xde1 [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp @ 1379] 
02 00007ffa`743e86b1     : 00000000`00000000 00007ffa`743e8461 00007ffa`15a566f0 00000000`02000076 : coreclr!SigPointer::GetTypeHandleThrowing+0xde1 [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp @ 1379] 
03 00007ffa`7444a72b     : 00000000`00000000 00000000`00000003 00007ffa`00000000 000000aa`618c5640 : coreclr!SigPointer::GetTypeHandleThrowing+0xde1 [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp @ 1379] 
04 00007ffa`7444998e     : 0000025e`86367300 00000000`fffffffd 00007ffa`168dd020 00007ffa`743e7019 : coreclr!Dictionary::PopulateEntry+0xafb [F:\workspace\_work\1\s\src\coreclr\src\vm\genericdict.cpp @ 1197] 
05 (Inline Function)     : --------`-------- --------`-------- --------`-------- --------`-------- : coreclr!JIT_GenericHandleWorker+0x8e [F:\workspace\_work\1\s\src\coreclr\src\vm\jithelpers.cpp @ 3287] 
06 00007ffa`744491a8     : 0000025e`8930ffb0 00007ffa`00000006 00000000`00000018 00007ffa`1664e530 : coreclr!JIT_GenericHandle_Framed+0x1a6 [F:\workspace\_work\1\s\src\coreclr\src\vm\jithelpers.cpp @ 3351] 
07 00007ffa`1664ece3     : 000000aa`618c5530 00000000`00000003 0000025e`856a33c8 000000aa`618c5820 : coreclr!JIT_GenericHandleClass+0x58 [F:\workspace\_work\1\s\src\coreclr\src\vm\jithelpers.cpp @ 3448] 
08 00007ffa`1586f706     : 0000025e`8930ffb0 0000025e`8930ffd8 000000aa`618c5820 000000aa`618c59e0 : FSharp_Compiler_Private!FSharp.Compiler.TypedTreeOps+ExprFolders`1[[System.__Canon, System.Private.CoreLib]].exprF(System.__Canon, Expr)+0x43
09 00007ffa`166511ff     : 000000aa`618c5760 00000000`00000003 00007ffa`00000001 00007ffa`00000001 : FSharp_Core!Microsoft.FSharp.Collections.ListModule.Fold[[System.__Canon, System.Private.CoreLib],[FSharp.Compiler.Range+range, FSharp.Compiler.Private]](Microsoft.FSharp.Core.FSharpFunc`2<range,Microsoft.FSharp.Core.FSharpFunc`2<System.__Canon,range>>, range, Microsoft.FSharp.Collections.FSharpList`1<System.__Canon>)+0xffffffff`ffee1aa6
0a 00007ffa`16651165     : 00000001`168cc2e8 00000001`00000000 00001653`092758be 00000001`168edc58 : FSharp_Compiler_Private!ILStubClass.IL_STUB_InstantiatingStub(Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1<Val>>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1<Val>>>>, Microsoft.FSharp.Core.FSharpFunc`2<Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1<Val>>,Microsoft.FSharp.Core.FSharpFunc`2<Expr,Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1<Val>>>>, Internal.Utilities.Collections.Tagged.Set`2<Val,System.Collections.Generic.IComparer`1<Val>>, Expr, TOp, Microsoft.FSharp.Collections.FSharpList`1<TType>, Microsoft.FSharp.Collections.FSharpList`1<Expr>)+0x4f
0b 00007ffa`156e4f06     : 0000025e`8930ffd8 ffffffff`00000001 0000025e`00000001 00007ffa`1664e530 : System_Diagnostics_Process!ILStubClass.IL_STUB_CallTailCallTarget(IntPtr, IntPtr, IntPtr)+0x75
0c 00007ffa`1664f796     : 000000aa`618c5760 00007ffa`168edcb8 00000000`00000000 00007ffa`168dd020 : System_Private_CoreLib!ILStubClass.IL_STUB_DispatchTailCalls(IntPtr, IntPtr, IntPtr)+0x76

and the code for this stub:

00007ffa`166511b0 55                   push    rbp
00007ffa`166511b1 4883ec40             sub     rsp, 40h
00007ffa`166511b5 488d6c2440           lea     rbp, [rsp+40h]
00007ffa`166511ba 488bc2               mov     rax, rdx
00007ffa`166511bd 4d8bd0               mov     r10, r8
00007ffa`166511c0 4c894c2420           mov     qword ptr [rsp+20h], r9
00007ffa`166511c5 488b5530             mov     rdx, qword ptr [rbp+30h]
00007ffa`166511c9 4889542428           mov     qword ptr [rsp+28h], rdx
00007ffa`166511ce 488b5538             mov     rdx, qword ptr [rbp+38h]
00007ffa`166511d2 4889542430           mov     qword ptr [rsp+30h], rdx
00007ffa`166511d7 488b5540             mov     rdx, qword ptr [rbp+40h]
00007ffa`166511db 4889542438           mov     qword ptr [rsp+38h], rdx
00007ffa`166511e0 488bd1               mov     rdx, rcx
00007ffa`166511e3 4c8bc0               mov     r8, rax
00007ffa`166511e6 4d8bca               mov     r9, r10
00007ffa`166511e9 48b9c8578e16fa7f0000 mov     rcx, 7FFA168E57C8h
00007ffa`166511f3 48b850e56416fa7f0000 mov     rax, offset CLRStub[MethodDescPrestub]@7ffa1664e550 (00007ffa`1664e550)
00007ffa`166511fd ffd0                 call    rax
00007ffa`166511ff 90                   nop     
00007ffa`16651200 488d6500             lea     rsp, [rbp]
00007ffa`16651204 5d                   pop     rbp
00007ffa`16651205 c3                   ret  

@AndyAyersMS
Copy link
Member

So is the fix it as simple as emitting tail prefixes for the callis in CreateInstantiatingILStub and perhaps CreateUnboxingILStubForSharedGenericValueTypeMethods?

@jkotas
Copy link
Member

jkotas commented Aug 15, 2020

It would introduce very significant performance regression for scenarios that happen to go through these instantiating stubs and do not care about tailcalls.

I think that the best way to solve this is by manually "inlining" the instantiating stub into the tailcall helper: Load address of the actual target + instantiating stub arg in the first tail call helper and match it in the second tail call helper. IIRC, @jakobbotsch tried this, but there were some difficulties with doing that.

Another way to solve this would be to create a tailcalling instantiating stubs (ie slow instantiating stubs that have the tail prefix before the call), but that feels too wide-spread.

@jakobbotsch
Copy link
Member

jakobbotsch commented Aug 15, 2020

I think that the best way to solve this is by manually "inlining" the instantiating stub into the tailcall helper: Load address of the actual target + instantiating stub arg in the first tail call helper and match it in the second tail call helper. IIRC, @jakobbotsch tried this, but there were some difficulties with doing that.

Related comment: dotnet/coreclr#26418 (comment)
I think at that time I had been through a few edge cases with VSDs and default interface methods and I was on the path of duplicating all the logic of ldvirtftn. I'm sure someone with more knowledge of the runtime/type system would not have significant problems doing this, however.

However doing this will still not fix the underlying issue, the symptom being that you cannot robustly tailcall a pointer returned by ldftn/ldvirtftn. Even with the suggested fix the F# example still breaks if the target method is first assigned to a delegate before being tail called (right?). But maybe this problem is academic, I suppose the old mechanisms did not handle it either.

@JulieLeeMSFT JulieLeeMSFT removed the untriaged New issue has not been triaged by the area owner label Aug 15, 2020
@jkotas
Copy link
Member

jkotas commented Aug 15, 2020

Delegates do not make any guarantees about when or whether they internally perform tailcalls.

@jkotas
Copy link
Member

jkotas commented Aug 15, 2020

I was on the path of duplicating all the logic of ldvirtftn

Does the F# example use virtual methods? I think we just need to fix the non-virtual method case for this.

@jakobbotsch
Copy link
Member

Delegates do not make any guarantees about when or whether they internally perform tailcalls.

What about explicit ldftn + tail. calli? Also, it seems likely to me that F# users are relying on tailcalls via delegates, but maybe I am wrong.

Does the F# example use virtual methods? I think we just need to fix the non-virtual method case for this.

No, but the same problem could occur with virtual methods I presume.

Is it feasible to detect when emitting the instantiating stub whether the target method contains a tail prefix anywhere in its IL? I'm not sure whether this is feasible due to virtual methods, ReJIT, EnC, just brainstorming...

@AndyAyersMS
Copy link
Member

AndyAyersMS commented Aug 15, 2020

[edit: had posted the backtrace for the version without IL edits; so updated that part]

Need to dig through what is happening in F# but I think it is non-virtual.

Here's a simple C# repro that overflows... (modified so calls F->G and G->F have a tail prefix):

using System;

class X
{
    static int F<T>(int a, int r, T c, Span<int> d)
    {
        int result = r;
        for (int i = a; i < d.Length; i++)
        {
            result += d[i];
        }
        return G(c, a, r, d, result);
    }

    static int G<T>(T c, int a, int r, Span<int> d, int result)
    {
        if (a == d.Length) return result;
        else return F(a + 1, result, c, d);
    }

    static int Main()
    {
        int[] a = new int[1_000_000];
        a[99] = 1;
        var s = new Span<int>(a);
        int r = F<string>(0, 0, "string", a);
        Console.WriteLine($"r = {r}");
        return r;
    }
}

results in

Stack overflow.
Repeat 1732 times:
--------------------------------
   at X.G[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon, Int32, Int32, System.Span`1<Int32>, Int32)
   at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr, Void (IntPtr, IntPtr, IntPtr*), IntPtr)
   at X.F[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](Int32, Int32, System.__Canon, System.Span`1<Int32>)
   at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr, Void (IntPtr, IntPtr, IntPtr*), IntPtr)
--------------------------------
   at X.G[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon, Int32, Int32, System.Span`1<Int32>, Int32)
   at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr, Void (IntPtr, IntPtr, IntPtr*), IntPtr)
   at X.F[[System.__Canon, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](Int32, Int32, System.__Canon, System.Span`1<Int32>)
   at X.Main()

@erozenfeld
Copy link
Member

So is the fix it as simple as emitting tail prefixes for the callis in CreateInstantiatingILStub and perhaps CreateUnboxingILStubForSharedGenericValueTypeMethods?

It would introduce very significant performance regression for scenarios that happen to go through these instantiating stubs and do not care about tailcalls.

Is it possible to create special instantiating stubs with tail prefixes that are only used when the caller dispatches a tail-prefixed call?

@jkotas
Copy link
Member

jkotas commented Aug 15, 2020

We could, but I think it is better to deal with it locally by "inlining" the instantiating stub into the tailcall helper: #40864 (comment) . It should be local change, with less risk, and it will have better performance compared to introduring new type of instantiating stubs .

New type of instantiating stubs would be a new type system entity. Introducing new type system entities tends to be non-trivial because of they typically have to handled in many places.

@jakobbotsch
Copy link
Member

jakobbotsch commented Aug 16, 2020

Is it feasible to detect when emitting the instantiating stub whether the target method contains a tail prefix anywhere in its IL?

Here is this approach: jakobbotsch@b1b668a
It fixes Andy's case above and should be much more pay-for-play than doing it unconditionally. Also, it is pretty self-contained and should work with delegates and virtual calls for free. On the other hand I could not anything similar in the runtime and I don't know if this kind of inspection of the IL will fly.

jakobbotsch added a commit to jakobbotsch/runtime that referenced this issue Aug 16, 2020
If the target function has explicit tailcalls out of it we must also
ensure that we explicitly tailcall into it when generating instantiating
stubs so that the instantiating stub does not leave a frame on the
stack.

Fix dotnet#40864
@jkotas
Copy link
Member

jkotas commented Aug 16, 2020

I agree it will fix this problem. The potential problems with this change:

  • It introduce performance regression when the instantiating stub is used in situations where tailcalls do not matter (e.g. delegates mentioned above)
  • It may have unpredictable behavior for obfuscated IL that sometimes does not have fully well-formed IL stream

@jkotas
Copy link
Member

jkotas commented Aug 16, 2020

Also, it may have GC hole for collectible types (not 100% about it). Is the hidden argument going to be reported to GC correctly to keep the collectible type alive when we are inside the tailcall dispatcher?

@jakobbotsch
Copy link
Member

It introduce performance regression when the instantiating stub is used in situations where tailcalls do not matter (e.g. delegates mentioned above)

IMO, if the target method has explicit tail calls out of it, then the user has already declared that tailcalls do matter and the instantiating stubs should conservative about it. Otherwise it seems we won't be able to support higher order code relying on tailcalls. Again, not sure how academic this problem is.

It may have unpredictable behavior for obfuscated IL that sometimes does not have fully well-formed IL stream

Ok, I see. I don't know how to deal with this.

Also, it may have GC hole for collectible types (not 100% about it). Is the hidden argument going to be reported to GC correctly to keep the collectible type alive when we are inside the tailcall dispatcher?

Hmm, I'm not too familiar with how this works. But I think you are right -- if there is a previous dispatcher then the instantiating stub will return immediately, and since the hidden arg is just a native sized integer in the signature of the calli it will not be reported to GC. I don't know if we can record in the signature used for the calli that this is a hidden arg and if the changes are then becoming too infectious.

@AndyAyersMS
Copy link
Member

Would it be feasible for the complex instantiating stubs to detect at runtime if tail calls are pending (say looking at the TLS data) and then have two call sites, one that tail calls (presumably, slowly), the other that does a normal call.

Stubs would get bigger even if we never need the tail call part. Might also have some false positives where we tail call when not needed, but perhaps not too frequent?

Also seems like we could leverage tail call stress (likely with the change to always use helpers for tail calls) plus gc stress to get more gc coverage on these paths.

@jakobbotsch
Copy link
Member

My guess is that we cannot detect that in general. Consider an example where the target function can do a fast tailcall to an instantiating stub for itself (which has one fewer arguments), while the instantiating stub cannot do a fast tailcall to the target function. Then there won't be any indication in the TLS that we are currently doing tailcalls.

@AndyAyersMS
Copy link
Member

@erozenfeld I take it you may not get around to fixing this.

Should we reassign? cc @JulieLeeMSFT

@erozenfeld
Copy link
Member

Yes, at this point I think we should reassign this.

@jkotas
Copy link
Member

jkotas commented Aug 22, 2020

#41206 implements approach described in #40864 (comment)

@JulieLeeMSFT
Copy link
Member

Can I assign this to @jkotas?

@erozenfeld erozenfeld assigned jkotas and unassigned erozenfeld Aug 25, 2020
@erozenfeld
Copy link
Member

I reassigned this issue to @jkotas since he already has a fix.

jkotas added a commit to jkotas/runtime that referenced this issue Aug 25, 2020
This change skips instantiating stubs for direct tailcalls and instead passes the inst argument directly to the target method.

Fixes dotnet#40864
jkotas added a commit that referenced this issue Aug 25, 2020
* Fix tailcall regression with compiled F#

This change skips instantiating stubs for direct tailcalls and instead passes the inst argument directly to the target method.

Fixes #40864

* Add regression test

Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
github-actions bot pushed a commit that referenced this issue Aug 25, 2020
This change skips instantiating stubs for direct tailcalls and instead passes the inst argument directly to the target method.

Fixes #40864
jkotas added a commit that referenced this issue Aug 25, 2020
* Fix tailcall regression with compiled F#

This change skips instantiating stubs for direct tailcalls and instead passes the inst argument directly to the target method.

Fixes #40864

* Add regression test

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
@ghost ghost locked as resolved and limited conversation to collaborators Dec 7, 2020
@KevinRansom KevinRansom reopened this Jan 28, 2021
@KevinRansom
Copy link
Member

@jkotas , we are seeing this failure again,the repro is the same as before

Machine:
Latest Windows 10 x64

Pre-verification:

On a clean machine install latest VS with the following workloads:
.NET desktop development
Add optional component F# desktop language support
.NET Core cross-platform development
Clone https://github.com/dotnet/fsharp and reset to commit 1c1b5ac7eacbbfd79e7277982e15178cecee20b4

Build with .\Build.cmd -c Release
Run test with dotnet test tests\fsharp\FSharpSuite.Tests.fsproj --no-restore --no-build --filter LargeRecordDoesNotStackOverflow -f netcoreapp3.1 -c Release -v n

At this point the test should pass.

Bug repro:

  • Install latest .NET 5 SDK from here.
  • Invalidate the .NET 3 SDK and runtime by doing the following:
  • Rename/delete C:\Program Files\dotnet\sdk\3.1.401
  • Rename/delete C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.7
  • Ensure they're not listed via dotnet --list-sdks and dotnet --list-runtimes
  • Re-run the test from step (4) exactly as above.

N.b., you may need to alter global.json with the following:
{
"sdk": {
"version": "5.0.200-preview.21075.10"
},
"tools": {
"dotnet": "3.1.302",
"dotnet": "5.0.200-preview.21075.10",
...

The stack overflow happens when running against the net5.0 runtime from : 5.0.200-preview.21075.10

@jkotas
Copy link
Member

jkotas commented Jan 29, 2021

Could you please open a new issue on this and extract the repro into a small program if possible?

I am not able to build using the steps above. I am getting errors like C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets( 131,5): error : Invalid restore input. Invalid target framework 'unsupported'.

@jkotas jkotas closed this as completed Jan 29, 2021
@KevinRansom
Copy link
Member

Sigh!!! ... I will try

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants