You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
interp: building the text of every error is quadratic in program size #5833
errorAt in interp/errors.go calls llvm.Value.String() on the failing instruction for every error it creates, once for Inst and once for the traceback. Printing a value makes LLVM number the whole module first, so each call costs time in proportion to the module size.
Most of these errors are never shown. They are the normal "leave this instruction for runtime" path, and the interpreter catches them and moves on. A large program makes thousands of them, so the cost grows with the square of the program size.
Building skycoin (github.com/skycoin/skycoin, 356 packages, -opt=z -no-debug -tags "safe netgo noasm") on linux/amd64 with LLVM 23 takes 341 s on dev with #5782 merged (for LLVM 23) and 303 s with the change below, with no change in peak memory. On a larger program (426 packages) the printing was 93 of 191 CPU seconds in a CPU profile.
The fix I have keeps the llvm.Value in the error and only builds the strings in a Render method that the diagnostics printer calls before it shows an error: 0magnet@ef96070
I tried the lazy version and it has a lifetime problem. The text comes from the LLVM module, and by the time anything calls Error() or prints diagnostics the module can already be disposed. The first version (the commit linked above, rendering from the diagnostics printer) was a use-after-free for that reason. Building it in Error() and caching it would have the same problem. diagnostics also reads Inst and Traceback directly, without calling Error().
The branch now has no exported method. Errors keep the llvm.Value, and Run and RunFunc fill in the text in a deferred call just before they return an error, while the module is still alive. The thousands of errors the interpreter catches internally are never printed. 0magnet@b0b40a12
Activity
dgryski commentedon Oct 6, 2026
I feel the
Render()logic should be part ofError()instead. Maybe cache the resulting string if you're concerned about multiple calls.interp: render an error's instruction text only when it is shown
interp: render an error's instruction text only when it is shown
0pcom commentedon Oct 14, 2026
I tried the lazy version and it has a lifetime problem. The text comes from the LLVM module, and by the time anything calls
Error()or prints diagnostics the module can already be disposed. The first version (the commit linked above, rendering from the diagnostics printer) was a use-after-free for that reason. Building it inError()and caching it would have the same problem. diagnostics also readsInstandTracebackdirectly, without callingError().The branch now has no exported method. Errors keep the
llvm.Value, andRunandRunFuncfill in the text in a deferred call just before they return an error, while the module is still alive. The thousands of errors the interpreter catches internally are never printed. 0magnet@b0b40a12Would that work for you?
0pcom commentedon Oct 21, 2026
Fixed by #5935. Thanks @joeshaw.