Compiler feedback for 'target=wasm'+'gc=none'
TinyGo v0.21.0
When I compile like this:
tinygo build -o tinygo.wasm -target=wasm -gc=none -scheduler=none -no-debug ./findJD.go
And then convert the WASM to WAT to look at the output (using another tool), I can see that the following is created (among other things):
(import "env" "runtime.alloc" (func $fimport$0 (param i32 i32 i32 i32) (result i32)))
(import "env" "main.main" (func $fimport$1 (param i32 i32)))
If I then instantiate this file using WebAssembly.instantiateStreaming (and RXJS):
switchMap(() => fetch('assets/tinygo.wasm')),
switchMap(wasmBytes => WebAssembly.instantiateStreaming(wasmBytes, (new Go()).importObject)),
Then I get this error:
Uncaught LinkError: WebAssembly.instantiate(): Import #0 module="env" function="runtime.alloc" error: function import requires a callable
The root cause appears to be that wasm_exec.js has no env["runtime.alloc"] function. This is true of the current version (0.21.0), as well as the dev branch. The documentation mentions that wasm_exec.js comes from $GOROOT/misc/wasm/wasm_exec.js. Checking this for my local installation of Go v1.17, env["runtime.alloc"] does not appear there either.
The same conditions apply for env["main.main"].
Troubleshooting a bit, I tried a few compiler options, and found that main.main seemed to be expected regardless of setting (although I didn't try every setting). runtime.alloc begins to appear with the compiler setting gc=none.
The issue has a workaround: stub the functions in Javascript to satisfy the Javascript-WebAssembly linker, and then otherwise ignore them.
In Javascript, this should work:
const tinygoImportObj = (new Go()).importObject;
tinygoImportObj.env['runtime.alloc'] = (a, b, c, d) => 0;
tinygoImportObj.env['main.main'] = (a, b) => void 0;
...
WebAssembly.instantiateStreaming(wasmBytes, tinygoImportObj)
Possibly related to this issue
(import "env" "runtime.alloc" (func $fimport$0 (param i32 i32 i32 i32) (result i32)))
This is because you have passed -gc=none. You should probably omit this flag, to leave it at the default (conservative).
(import "env" "main.main" (func $fimport$1 (param i32 i32)))
This is because you didn't define a main function. You should have one, even if it is empty.
Also, note that you must call _start before calling any other exported function. Otherwise many parts will not be initialized, such as the heap.
Thanks, that resolves the immediate problem.
I think that wasm_exec.js is intended to be a static file: that is, developers are not expected to need to add anything to what is supplied in wasm_exec.js. It would be possible for developers to create their own runtime.alloc and main.main functions, but it doesn't sound like that is a goal of TinyGo.
As such:
- Should the combination of
target=wasmandgc=noneresult in a compiler error? - Should the lack of a
func main()in the source file result in a compiler error?
I think that
wasm_exec.jsis intended to be a static file: that is, developers are not expected to need to add anything to what is supplied inwasm_exec.js.
Correct.
It would be possible for developers to create their own
runtime.allocandmain.mainfunctions, but it doesn't sound like that is a goal of TinyGo.
Why would you want to replace runtime.alloc? There is a heap/GC implementation already in TinyGo.
As such:
- Should the combination of
target=wasmandgc=noneresult in a compiler error?
Probably. It doesn't at the moment because we have the flag --allow-undefined.
- Should the lack of a
func main()in the source file result in a compiler error?
Yes. It did in the past, but apparently this got removed at some point. This would be fixed by removing --allow-undefined.
@jzabinski-dolios is it ok to close this issue? it sounds like you are working now, even if some of the defaults are a bit confusing.
Yes, things are working now. I think that there are a few issues described in this comment that should be spun off to separate issues. (target=wasm+gc=none probably should result in a compiler error. Inputting a TInygo program without a func main() should result in a compiler error.)
Thanks for the tips @jzabinski-dolios! Here's some spin-offs, preferring re-use of tickets as there are so many :p
target=wasm+gc=none probably should result in a compiler error
I think maybe you can rename this issue to "target=wasm+gc=none probably should result in a compiler error" and keep it as it the best description of the problem.
Inputting a TInygo program without a func main() should result in a compiler error.
re-using https://github.com/tinygo-org/tinygo/issues/2703
This is working as intended, especially now with #3133. The intention of -gc=none is to result in a compiler error if you use a GC at all (unlike -gc=leaking, which simply disables collecting memory).
To help make sure we can close out issues, I just tried this. It is true that it fails, just hard to decipher why. Do we have any other argument incompatibility validation anywhere?
Ex.
$ ~/oss/tinygo/build/tinygo build -gc=none -target=wasm -o main.wasm main.go
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main.o: undefined symbol: runtime.alloc
tinygo:wasm-ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
failed to run tool: wasm-ld
error: failed to link /var/folders/vd/1cf8zdb1721f4z5rjggy8bp40000gn/T/tinygo2023991394/main: exit status 1
Yes it fails in your case, but it doesn't necessarily have to fail. If you have a very small program with -scheduler=none, it may in fact pass (if you use #3142, without it it will always fail). Here is an example:
$ tinygo run -target=wasi -scheduler=none -gc=none ./testdata/alias.go
x
apple
true
false
(Sidenote: sadly wasm-ld doesn't seem to be able to find the source location of the link error, LLD is usually able to do that for ELF etc).
So:
- "Inputting a TInygo program without a func main() should result in a compiler error" -Abandoning the issue in this ticket in favor of #2703
- "target=wasm+gc=none probably should result in a compiler error" -scheduler=none needs to be taken into account. The issue also partially depends on the fate of #3142.
I'll change the title to reflect these details. Let me know if I missed something.