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:

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.