tinygo icon indicating copy to clipboard operation
tinygo copied to clipboard

Compiler feedback for 'target=wasm'+'gc=none'

Open jzabinski-dolios opened this issue 4 years ago • 10 comments

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

jzabinski-dolios avatar Jan 07 '22 17:01 jzabinski-dolios

(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.

aykevl avatar Jan 19 '22 14:01 aykevl

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:

  1. Should the combination of target=wasm and gc=none result in a compiler error?
  2. Should the lack of a func main() in the source file result in a compiler error?

jzabinski-dolios avatar Jan 19 '22 15:01 jzabinski-dolios

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.

Correct.

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.

Why would you want to replace runtime.alloc? There is a heap/GC implementation already in TinyGo.

As such:

  1. Should the combination of target=wasm and gc=none result in a compiler error?

Probably. It doesn't at the moment because we have the flag --allow-undefined.

  1. 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.

aykevl avatar Jan 21 '22 14:01 aykevl

@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.

codefromthecrypt avatar Sep 07 '22 08:09 codefromthecrypt

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.)

jzabinski-dolios avatar Sep 07 '22 14:09 jzabinski-dolios

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

codefromthecrypt avatar Sep 08 '22 00:09 codefromthecrypt

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).

aykevl avatar Sep 08 '22 11:09 aykevl

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

codefromthecrypt avatar Sep 08 '22 11:09 codefromthecrypt

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).

aykevl avatar Sep 08 '22 12:09 aykevl

So:

  1. "Inputting a TInygo program without a func main() should result in a compiler error" -Abandoning the issue in this ticket in favor of #2703
  2. "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.

jzabinski-dolios avatar Sep 08 '22 13:09 jzabinski-dolios