Known issues
This page lists specific bugs, each with a workaround. For the limits that come with running Go on .NET, and the rough spots real code hits today, see Limitations.
Ubuntu’s packaged .NET 10 can end a converted program early
On Ubuntu’s own build of .NET 10 (the dotnet installed with apt, runtime identifier ubuntu.*-x64), a
converted program that raises and recovers Go panics inside a hot loop can stop in one of two ways under the
default tiered compilation:
- a fatal
Internal CLR error. (0x80131506); - more often, a SILENT exit with code 0 and truncated output.
The fault needs the JIT’s on-stack replacement. Microsoft’s build of the same .NET version does not show it, and neither does Windows. No converted Go program has reproduced it so far, only a synthetic reproduction.
To avoid it, run on Microsoft’s .NET build, or turn off quick JIT for loops, which removes on-stack replacement. A build property does it without touching the converted project:
dotnet build -p:TieredCompilationQuickJitForLoops=false
The built program’s runtimeconfig.json then carries
"System.Runtime.TieredCompilation.QuickJitForLoops": false. Or set it in the converted executable’s project
file, though the next conversion rewrites that file:
<TieredCompilationQuickJitForLoops>false</TieredCompilationQuickJitForLoops>
Converted executable projects carry this line, commented out. It costs about 90 ms (16%) more startup time, and steady-state loop speed stays within measurement noise. It becomes the default if a converted Go program ever reproduces the fault.
A Native AOT publish of a program that references the packages is slow and large
A program that references the go.* packages and publishes with Native AOT (PublishAot) runs. The check
that gates each release publishes a C# consumer of the packages and runs it on linux-x64, win-x64 and
osx-x64. A project that sets no trim mode of its own gets the packages’ default, partial, which is the
setting a converted project carries in its generated project file. On a Mac with Apple silicon the publish has
not been measured: it does not finish within the six hours a hosted CI job is allowed.
A full trim (TrimMode set to full) produces a program that runs as well. That is measured on win-x64, on
one development machine; the hosted check measures the default.
What it costs. With partial, Native AOT compiles every go.* assembly the program references in
full, not only the parts the program uses. For a small program that references about thirty of them, the
publish took a little over an hour on a CI runner for Linux and for Windows, several hours for an Intel Mac,
and more than two hours on a four-core machine, and the
executable was about 250 MB. A program that uses fmt and reflect references about twice as many: on a
six-core Windows machine (win-x64, 31 GB of memory) its publish took about five and a half hours, the
Native AOT compiler held about 9.5 GB of memory throughout, and the executable was about 370 MB. It ran
correctly. The performance suite’s programs, which compile the whole converted standard library, peak at
15 to 18 GB while they publish (Performance). A converted project pays the same cost, because it uses the same setting. A build, dotnet run
and a publish without Native AOT are not affected.