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