wasm-ld: error: couldn't allocate output register for constraint '{r0}'
Getting this error intermittently when compiling to wasip2
tinygo build -o test-tinygo-wasi2.wasm --target wasip2 --no-debug
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
I am running this on Windows.
tinygo version tinygo version 0.38.0 windows/amd64 (using go version go1.23.2 and LLVM version 19.1.2)
Hello @anshuman-mor could you please provide some code that reproduces this error? Thank you.
Hello, I have the same issue. I am compiling this library, which has some crypto operations:
https://github.com/gagliardetto/solana-go
I am using the same version as @anshuman-mor
package main
import sgo "github.com/gagliardetto/solana-go"
func main() {
_, err := sgo.NewRandomPrivateKey()
if err != nil {
panic(err)
}
time.Sleep(50 * time.Second)
}
I am trying to generate a ed25519 key pair.
I see that for wasip2, tinygo pulls in ARM code:
https://github.com/tinygo-org/tinygo/blob/release/targets/wasip2.json#L8
Oh, huh. Somehow this program is calling syscalls, which of course won't work on wasip2. I wonder how that happened.
I traced the calls a little bit:
github.com/streamingfast/logging.maybeNewLoggergolang.org/x/crypto/ssh/terminal.IsTerminalgolang.org/x/term.IsTerminalgolang.org/x/term.isTerminalgolang.org/x/sys/unix.IoctlGetTermiosgolang.org/x/sys/unix.ioctl
So yeah this all makes sense, considering we emulate linux/arm for wasip2 (since upstream doesn't support wasip2 in the stdlib). But the compiler should not have emitted these syscalls at least.
Ok, here you go: https://github.com/tinygo-org/tinygo/pull/5054 Not a fix, but a better error message.
I don't know what the correct fix for this would be. A workaround would be to add the right build tags in a place like this but I doubt that will be accepted upstream. A proper fix would be to add support for wasip2 in Go so that we don't have to pretend to be linux/arm just to get the standard library working.
I ran into the same issue as well, but a lot weirder. On 0.39.0 the first time I compile the code, it would give the errors described in the original description. However, if I just try it again it works on all subsequent attempts:
$ tinygo build -target wasip2 -no-debug -o main.wasm
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
wasm-ld: error: couldn't allocate output register for constraint '{r0}'
$ tinygo build -target wasip2 -no-debug -o main.wasm
$ tinygo build -target wasip2 -no-debug -o main.wasm
If I change any line of code, then it doesn't work for the first time again, but if I compile again it works.
On dev, after #5054, it now throws the error added and never compiles (of course now no longer after multiple attempts either). This let me know that the issue was in golang.org/x/sys for me as well.
As a workaround to fix it (on dev, and so it compiles successfully on the first time on 0.39.0), I forked golang.org/x/sys and did a replacement in my go.mod:
replace golang.org/x/sys => github.com/terminal-games/sys v0.0.1
I wrote a codemod script to add panics before every syscall in unix.
func fcntl(fd int, cmd, arg int) (int, error) {
+ panic("syscall not supported in wasm: Syscall(fcntl64Syscall, uintptr(fd), uintptr(cmd), uintptr(arg))")
valptr, _, errno := Syscall(fcntl64Syscall, uintptr(fd), uintptr(cmd), uintptr(arg))
var err error
if errno != 0 {
err = errno
}
return int(valptr), err
}
This way, the rest of the function body is optimized out, and the call to the syscall is never emitted. Also, it happens to reduce the bundle slightly from 897K (on a second-time run on 0.39.0) to 889K (with my fork, also on 0.39.0).
Lots of go projects have a transitive dependency on golang.org/x/sys, but even if the functions are never called, the compiler tries to emit instructions for the syscalls within them since tinygo does linux/arm "emulation" for WASM. Lowering it to a panic instead of just not being compilable allows for greater package compatibility.
It would be nice if instead of stopping the compilation like in #5054, tinygo would actually just insert a similar runtime panic like my fork of golang.org/x/sys does. I think it will increase compatibility while tinygo still does linux/arm "emulation" for WASM. And that way it will also cover the syscall package in the standard library.
Another scarier idea could be to just make syscalls no-ops (return immediately) and see what happens. People will then compile to WASM with the understanding that obviously syscalls won't work. This might make sense because sometimes packages really do define a _other.go version which does a no-op, but tinygo wasip2 tries to compile the linux/arm version and fails. Though this idea obviously has its own problems.
Side note: is there a list somewhere of what standard library packages we gain by "emulating" linux/arm, over just setting GOOS/GOARCH correctly?