From d627208c48501e649a0c2a477f77556af1e35a99 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 7 Jul 2019 19:43:46 +0200 Subject: [PATCH] all: make WebAssembly initial linear memory size configurable When the target supports it, allow the (initial) heap size to be configured. Currently only supported in WebAssembly. This also changes the default heap size of WebAssembly from 64kB to 1MB. --- main.go | 41 +++++++++++++++++++++++++++++++++++++++- src/runtime/arch_wasm.go | 5 ++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index ad5ede3e..bbe7a1e6 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,7 @@ type BuildConfig struct { ldFlags []string tags string wasmAbi string + heapSize int64 testConfig compiler.TestConfig } @@ -240,10 +241,16 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act // Prepare link command. executable := filepath.Join(dir, "main") tmppath := executable // final file - ldflags := append(ldflags, "-o", executable, objfile, "-L", root) + ldflags = append(ldflags, "-o", executable, objfile, "-L", root) if spec.RTLib == "compiler-rt" { ldflags = append(ldflags, librt) } + if spec.GOARCH == "wasm" { + // Round heap size to next multiple of 65536 (the WebAssembly page + // size). + heapSize := (config.heapSize + (65536 - 1)) &^ (65536 - 1) + ldflags = append(ldflags, "--initial-memory="+strconv.FormatInt(heapSize, 10)) + } // Compile extra files. for i, path := range spec.ExtraFiles { @@ -540,6 +547,30 @@ func Run(pkgName, target string, config *BuildConfig) error { }) } +// parseSize converts a human-readable size (with k/m/g suffix) into a plain +// number. +func parseSize(s string) (int64, error) { + s = strings.ToLower(strings.TrimSpace(s)) + if len(s) == 0 { + return 0, errors.New("no size provided") + } + multiply := int64(1) + switch s[len(s)-1] { + case 'k': + multiply = 1 << 10 + case 'm': + multiply = 1 << 20 + case 'g': + multiply = 1 << 30 + } + if multiply != 1 { + s = s[:len(s)-1] + } + n, err := strconv.ParseInt(s, 0, 64) + n *= multiply + return n, err +} + func usage() { fmt.Fprintln(os.Stderr, "TinyGo is a Go compiler for small places.") fmt.Fprintln(os.Stderr, "version:", version) @@ -598,6 +629,7 @@ func main() { 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") + heapSize := flag.String("heap-size", "1M", "default heap size in bytes (only supported by WebAssembly)") if len(os.Args) < 2 { fmt.Fprintln(os.Stderr, "No command-line arguments supplied.") @@ -633,6 +665,13 @@ func main() { os.Exit(1) } + var err error + if config.heapSize, err = parseSize(*heapSize); err != nil { + fmt.Fprintln(os.Stderr, "Could not read heap size:", *heapSize) + usage() + os.Exit(1) + } + os.Setenv("CC", "clang -target="+*target) switch command { diff --git a/src/runtime/arch_wasm.go b/src/runtime/arch_wasm.go index 1015fe11..71d6724b 100644 --- a/src/runtime/arch_wasm.go +++ b/src/runtime/arch_wasm.go @@ -14,9 +14,12 @@ const TargetBits = 32 //go:extern __heap_base var heapStartSymbol unsafe.Pointer +//go:export llvm.wasm.memory.size.i32 +func wasm_memory_size(index int32) int32 + var ( heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = (heapStart + wasmPageSize - 1) &^ (wasmPageSize - 1) // conservative guess: one page of heap memory + heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) ) const wasmPageSize = 64 * 1024