diff options
-rw-r--r-- | compiler/compiler.go | 7 | ||||
-rw-r--r-- | docs/internals.rst | 15 | ||||
-rw-r--r-- | main.go | 6 |
3 files changed, 20 insertions, 8 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 436544d82..bb2ddc61e 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -3114,9 +3114,11 @@ func (c *Compiler) NonConstGlobals() { } } -// Replace i64 in an external function with a stack-allocated i64*, to work +// When -wasm-abi flag set to "js" (default), +// replace i64 in an external function with a stack-allocated i64*, to work // around the lack of 64-bit integers in JavaScript (commonly used together with // WebAssembly). Once that's resolved, this pass may be avoided. +// See also the -wasm-abi= flag // https://github.com/WebAssembly/design/issues/1172 func (c *Compiler) ExternalInt64AsPtr() error { int64Type := c.ctx.Int64Type() @@ -3220,7 +3222,8 @@ func (c *Compiler) ExternalInt64AsPtr() error { c.builder.SetInsertPointAtEnd(entryBlock) var callParams []llvm.Value if fnType.ReturnType() == int64Type { - return errors.New("todo: i64 return value in exported function") + return errors.New("not yet implemented: exported function returns i64 with -wasm-abi=js; " + + "see https://tinygo.org/compiler-internals/calling-convention/") } for i, origParam := range fn.Params() { paramValue := externalFn.Param(i) diff --git a/docs/internals.rst b/docs/internals.rst index 5cf61d611..14091d9bb 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -130,8 +130,8 @@ somewhat compatible with the C calling convention but with a few quirks: pointers. This avoids some overhead in the C calling convention and makes the work of the LLVM optimizers easier. - * The WebAssembly target never exports or imports a ``i64`` (``int64``, - ``uint64``) parameter. Instead, it replaces them with ``i64*``, allocating + * The WebAssembly target by default doesn't export or import ``i64`` (``int64``, + ``uint64``) parameters. Instead, it replaces them with ``i64*``, allocating the value on the stack. In other words, imported functions are called with a 64-bit integer on the stack and exported functions must be called with a pointer to a 64-bit integer somewhere in linear memory. @@ -144,10 +144,15 @@ somewhat compatible with the C calling convention but with a few quirks: calling convention workaround may be removed. Also see `this wasm-bindgen issue <https://github.com/rustwasm/wasm-bindgen/issues/35>`_. + Currently there are also non-browser WebAssembly execution environments + that do not have this limitation. Use the `-wasm-abi=generic` flag to remove + the behavior described above and enable emitting functions with i64 + parameters directly. + * The WebAssembly target does not return variables directly that cannot be - handled by JavaScript (``struct``, ``i64``, multiple return values, etc). - Instead, they are stored into a pointer passed as the first parameter by the - caller. + handled by JavaScript (see above about ``i64``, also ``struct``, multiple + return values, etc). Instead, they are stored into a pointer passed as the + first parameter by the caller. This is the calling convention as implemented by LLVM, with the extension that ``i64`` return values are returned in the same way as aggregate types. @@ -36,6 +36,7 @@ type BuildConfig struct { initInterp bool cFlags []string ldFlags []string + wasmAbi string } // Helper function for Compiler object. @@ -97,7 +98,8 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act // cannot be represented exactly in JavaScript (JS only has doubles). To // keep functions interoperable, pass int64 types as pointers to // stack-allocated values. - if strings.HasPrefix(spec.Triple, "wasm") { + // Use -wasm-abi=generic to disable this behaviour. + if config.wasmAbi == "js" && strings.HasPrefix(spec.Triple, "wasm") { err := c.ExternalInt64AsPtr() if err != nil { return err @@ -507,6 +509,7 @@ func main() { port := flag.String("port", "/dev/ttyACM0", "flash port") cFlags := flag.String("cflags", "", "additional cflags for compiler") ldFlags := flag.String("ldflags", "", "additional ldflags for linker") + wasmAbi := flag.String("wasm-abi", "js", "WebAssembly ABI conventions: js (no i64 params) or generic") if len(os.Args) < 2 { fmt.Fprintln(os.Stderr, "No command-line arguments supplied.") @@ -524,6 +527,7 @@ func main() { debug: !*nodebug, printSizes: *printSize, initInterp: *initInterp, + wasmAbi: *wasmAbi, } if *cFlags != "" { |