dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.2k stars 4.72k forks source link

tailcall regression with compiled F# #40864

Closed brettfo closed 3 years ago

brettfo commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

cc @dotnet/jit-contrib

AndyAyersMS commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

cc @jakobbotsch @erozenfeld

jakobbotsch commented 4 years ago

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: https://github.com/dotnet/runtime/blob/f35d747e8d7fe44d7dab76b9683d7c642ec26888/src/tests/JIT/Directed/tailcall/more_tailcalls.cs#L616-L626

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

jkotas commented 4 years ago

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 commented 4 years ago

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: https://github.com/dotnet/coreclr/pull/26418#issuecomment-536127958 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.

jkotas commented 4 years ago

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

jkotas commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

[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 commented 4 years ago

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 commented 4 years ago

We could, but I think it is better to deal with it locally by "inlining" the instantiating stub into the tailcall helper: https://github.com/dotnet/runtime/issues/40864#issuecomment-674409519 . 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 commented 4 years ago

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: https://github.com/jakobbotsch/runtime/commit/b1b668aa9e080f67a38924dbca0a41cdf1aac012 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.

jkotas commented 4 years ago

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

jkotas commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

Should we reassign? cc @JulieLeeMSFT

erozenfeld commented 4 years ago

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

jkotas commented 4 years ago

41206 implements approach described in https://github.com/dotnet/runtime/issues/40864#issuecomment-674409519

JulieLeeMSFT commented 4 years ago

Can I assign this to @jkotas?

erozenfeld commented 4 years ago

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

KevinRansom commented 3 years ago

@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:

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 commented 3 years ago

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'.

KevinRansom commented 3 years ago

Sigh!!! ... I will try