Repository: tetratelabs/wazero (go)

Issue:
How do I wrap Go function and use it as import function in my WASM module?
I have WASM module that imports the `log_func` function. I'd like to wrap it similarly to the wastime-go API. How do we do that in wazero?

```go
func run() error {
	store := wasmtime.NewStore(wasmtime.NewEngine())
	module, err := wasmtime.NewModuleFromFile(store.Engine, "how_old_with_import.wasm")
	if err != nil {
		return err
	}
	item := wasmtime.WrapFunc(store, func(i int32) {
		fmt.Printf("%v\n", i)
	})
	instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{
		item,
	})
	if err != nil {
		return err
	}
	f := instance.GetFunc(store, "log_how_old")
	_, err = f.Call(store, 2022, 2000)
	if err != nil {
		return err
	}
	return nil
```
```wat
(module
  (func $log (import "imports" "log_func") (param i32))

  (func $how_old (param $year_now i32) (param $year_born i32) (result i32)
    local.get $year_now
    local.get $year_born
    i32.sub)

  (func $log_how_old (param $year_now i32) (param $year_born i32)
    local.get $year_now
    local.get $year_born
    call $how_old
    call $log)

  (export "how_old" (func $how_old))
  (export "log_how_old" (func $log_how_old))
)
```



Pull request description:
Adds import-go example
This shows how to define, export and import functions written in Go.

Fixes #464


Patch:
diff --git a/Makefile b/Makefile
index a9c96359..394e9973 100644
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,7 @@ build.spectest: # Note: wabt by default uses >1.0 features, so wast2json flags m
 .PHONY: test
 test:
 	@go test ./... -timeout 120s
+	@cd ./internal/integration_test && go test ./... -timeout 120s
 
 golangci_lint_path := $(shell go env GOPATH)/bin/golangci-lint
 
diff --git a/examples/README.md b/examples/README.md
index 83c12880..730031dd 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -2,7 +2,8 @@
 
 The following example projects can help you practice WebAssembly with wazero:
 
-* [basic](basic) - how to use WebAssembly and Go-defined functions.
+* [basic](basic) - how to use both WebAssembly and Go-defined functions.
+* [import-go](import-go) - how to define, import and call a Go-defined function from a WebAssembly-defined function.
 * [multiple-results](multiple-results) - how to return more than one result from WebAssembly or Go-defined functions.
 * [replace-import](replace-import) - how to override a module name hard-coded in a WebAssembly module.
 * [wasi](wasi) - how to use I/O in your WebAssembly modules using WASI (WebAssembly System Interface).
diff --git a/examples/basic/README.md b/examples/basic/README.md
index 3d95042b..174b26a2 100644
--- a/examples/basic/README.md
+++ b/examples/basic/README.md
@@ -1,3 +1,3 @@
 ## Basic example
 
-This example shows how to use WebAssembly and Go-defined functions with wazero.
+This example shows how to use both WebAssembly and Go-defined functions with wazero.
diff --git a/examples/basic/add.go b/examples/basic/add.go
index d2fd4fd7..0a229418 100644
--- a/examples/basic/add.go
+++ b/examples/basic/add.go
@@ -1,4 +1,4 @@
-package main
+package add
 
 import (
 	_ "embed"
diff --git a/examples/import-go/.gitignore b/examples/import-go/.gitignore
new file mode 100644
index 00000000..8d7abcc7
--- /dev/null
+++ b/examples/import-go/.gitignore
@@ -0,0 +1,1 @@
+age-calculator
diff --git a/examples/import-go/README.md b/examples/import-go/README.md
new file mode 100644
index 00000000..b1ed9d56
--- /dev/null
+++ b/examples/import-go/README.md
@@ -0,0 +1,38 @@
+## Import go func example
+
+This example shows how to define, import and call a Go-defined function from a WebAssembly-defined function.
+
+If the current year is 2022, and we give the argument 2000, [age-calculator.go](age-calculator.go) should output 22.
+```bash
+$ go run age-calculator.go 2000
+println >> 21
+log_i32 >> 21
+```
+
+### Background
+
+WebAssembly has neither a mechanism to get the current year, nor one to print to the console, so we define these in Go.
+Similar to Go, WebAssembly functions are namespaced, into modules instead of packages. Just like Go, only exported
+functions can be imported into another module. What you'll learn in [age-calculator.go](age-calculator.go), is how to
+export functions using [ModuleBuilder](https://pkg.go.dev/github.com/tetratelabs/wazero#ModuleBuilder) and how a
+WebAssembly module defined in its [text format](https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#text-format%E2%91%A0)
+imports it. This only uses the text format for demonstration purposes, to show you what's going on. It is likely, you
+will use another language to compile a Wasm (WebAssembly Module) binary, such as TinyGo. Regardless of how wasm is
+produced, the export/import mechanics are the same!
+
+### Where next?
+
+The following examples continue the path of learning about importing and exporting functions with wazero:
+
+#### [WebAssembly System Interface (WASI)](../wasi)
+
+This uses an ad-hoc Go-defined function to print to the console. There is an emerging specification to standardize
+system calls (similar to Go's [x/sys](https://pkg.go.dev/golang.org/x/sys/unix)) called WebAssembly System Interface
+[(WASI)](https://github.com/WebAssembly/WASI). While this is not yet a W3C standard, wazero includes a
+[wasi package](https://pkg.go.dev/github.com/tetratelabs/wazero/wasi).
+
+#### [Replace Import](../replace-import)
+
+You may use WebAssembly modules that have imports that don't match your ideal packaging structure. wazero allows you to
+replace imports with different module names as needed, on a function granularity using
+[ModuleConfig.WithImport](https://pkg.go.dev/github.com/tetratelabs/wazero#ModuleConfig.WithImport).
diff --git a/examples/import-go/age-calculator.go b/examples/import-go/age-calculator.go
new file mode 100644
index 00000000..0d080cc1
--- /dev/null
+++ b/examples/import-go/age-calculator.go
@@ -0,0 +1,105 @@
+package age_calculator
+
+import (
+	_ "embed"
+	"fmt"
+	"log"
+	"os"
+	"strconv"
+	"time"
+
+	"github.com/tetratelabs/wazero"
+)
+
+// main shows how to define, import and call a Go-defined function from a
+// WebAssembly-defined function.
+//
+// See README.md for a full description.
+func main() {
+	r := wazero.NewRuntime()
+
+	// Instantiate a module named "env" that exports functions to get the
+	// current year and log to the console.
+	//
+	// Note: As noted on ExportFunction documentation, function signatures are
+	// constrained to a subset of numeric types.
+	// Note: "env" is a module name conventionally used for arbitrary
+	// host-defined functions, but any name would do.
+	env, err := r.NewModuleBuilder("env").
+		ExportFunction("log_i32", func(v uint32) {
+			fmt.Println("log_i32 >>", v)
+		}).
+		ExportFunction("current_year", func() uint32 {
+			if envYear, err := strconv.ParseUint(os.Getenv("CURRENT_YEAR"), 10, 64); err == nil {
+				return uint32(envYear) // Allow env-override to prevent annual test maintenance!
+			}
+			return uint32(time.Now().Year())
+		}).
+		Instantiate()
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer env.Close()
+
+	// Instantiate a module named "age-calculator" that imports functions
+	// defined in "env".
+	//
+	// Note: The import syntax in both Text and Binary format is the same
+	// regardless of if the function was defined in Go or WebAssembly.
+	ageCalculator, err := r.InstantiateModuleFromCode([]byte(`
+;; Define the optional module name. '$' prefixing is a part of the text format.
+(module $age-calculator
+
+  ;; In WebAssembly, you don't import an entire module, rather each function.
+  ;; This imports the functions and gives them names which are easier to read
+  ;; than the alternative (zero-based index).
+  ;;
+  ;; Note: Importing unused functions is not an error in WebAssembly.
+  (import "env" "log_i32" (func $log (param i32)))
+  (import "env" "current_year" (func $year (result i32)))
+
+  ;; get_age looks up the current year and subtracts the input from it.
+  ;; Note: The stack begins empty and anything left must match the result type.
+  (func $get_age (param $year_born i32) (result i32)
+                 ;; stack: []
+    call $year   ;; stack: [$year.result]
+    local.get 0  ;; stack: [$year.result, $year_born]
+    i32.sub      ;; stack: [$year.result-$year_born]
+  )
+  ;; export allows api.Module to return this via ExportedFunction("get_age")
+  (export "get_age" (func $get_age))
+
+  ;; log_age
+  (func $log_age (param $year_born i32)
+	              ;; stack: []
+    local.get 0   ;; stack: [$year_born]
+    call $get_age ;; stack: [$get_age.result]
+    call $log     ;; stack: []
+  )
+  (export "log_age" (func $log_age))
+)`))
+	// ^^ Note: wazero's text compiler is incomplete #59. We are using it anyway to keep this example dependency free.
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer ageCalculator.Close()
+
+	// Read the birthYear from the arguments to main
+	birthYear, err := strconv.ParseUint(os.Args[1], 10, 64)
+	if err != nil {
+		log.Fatalf("invalid arg %v: %v", os.Args[1], err)
+	}
+
+	// First, try calling the "get_age" function and printing to the console externally.
+	results, err := ageCalculator.ExportedFunction("get_age").Call(nil, birthYear)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("println >>", results[0])
+
+	// First, try calling the "log_age" function and printing to the console externally.
+	_, err = ageCalculator.ExportedFunction("log_age").Call(nil, birthYear)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/examples/multiple-results/multiple-results.go b/examples/multiple-results/multiple-results.go
index b2277545..e5f041dd 100644
--- a/examples/multiple-results/multiple-results.go
+++ b/examples/multiple-results/multiple-results.go
@@ -1,4 +1,4 @@
-package main
+package multiple_results
 
 import (
 	_ "embed"
diff --git a/examples/replace-import/replace-import.go b/examples/replace-import/replace-import.go
index 2c299b3f..e46554ab 100644
--- a/examples/replace-import/replace-import.go
+++ b/examples/replace-import/replace-import.go
@@ -1,4 +1,4 @@
-package main
+package replace_import
 
 import (
 	_ "embed"
diff --git a/examples/wasi/cat.go b/examples/wasi/cat.go
index f28a67e6..6c5a7691 100644
--- a/examples/wasi/cat.go
+++ b/examples/wasi/cat.go
@@ -1,4 +1,4 @@
-package main
+package wasi_example
 
 import (
 	"embed"
diff --git a/go.mod b/go.mod
index f1eb04cf..4ccd9deb 100644
--- a/go.mod
+++ b/go.mod
@@ -2,6 +2,3 @@ module github.com/tetratelabs/wazero
 
 // This should be the minimum supported Go version per https://go.dev/doc/devel/release (1 version behind latest)
 go 1.17
-
-// Test-only dependency.
-require github.com/twitchyliquid64/golang-asm v0.15.1
diff --git a/go.sum b/go.sum
index 832898f2..e69de29b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +0,0 @@
-github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
-github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
diff --git a/internal/asm/amd64_debug/debug_assembler.go b/internal/integration_test/asm/amd64_debug/debug_assembler.go
similarity index 99%
rename from internal/asm/amd64_debug/debug_assembler.go
rename to internal/integration_test/asm/amd64_debug/debug_assembler.go
index c90787ff..03771751 100644
--- a/internal/asm/amd64_debug/debug_assembler.go
+++ b/internal/integration_test/asm/amd64_debug/debug_assembler.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/asm"
 	asm_amd64 "github.com/tetratelabs/wazero/internal/asm/amd64"
-	"github.com/tetratelabs/wazero/internal/asm/golang_asm"
+	"github.com/tetratelabs/wazero/internal/integration_test/asm/golang_asm"
 )
 
 // NewDebugAssembler can be used for ensuring that our assembler produces exactly the same binary as Go.
diff --git a/internal/asm/amd64_debug/golang_asm.go b/internal/integration_test/asm/amd64_debug/golang_asm.go
similarity index 99%
rename from internal/asm/amd64_debug/golang_asm.go
rename to internal/integration_test/asm/amd64_debug/golang_asm.go
index 99e9461d..0d0efb45 100644
--- a/internal/asm/amd64_debug/golang_asm.go
+++ b/internal/integration_test/asm/amd64_debug/golang_asm.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/asm"
 	asm_amd64 "github.com/tetratelabs/wazero/internal/asm/amd64"
-	"github.com/tetratelabs/wazero/internal/asm/golang_asm"
+	"github.com/tetratelabs/wazero/internal/integration_test/asm/golang_asm"
 )
 
 // assemblerGoAsmImpl implements asm_amd64.Assembler for golang-asm library.
diff --git a/internal/asm/amd64_debug/impl_test.go b/internal/integration_test/asm/amd64_debug/impl_test.go
similarity index 100%
rename from internal/asm/amd64_debug/impl_test.go
rename to internal/integration_test/asm/amd64_debug/impl_test.go
diff --git a/internal/asm/arm64_debug/debug_assembler.go b/internal/integration_test/asm/arm64_debug/debug_assembler.go
similarity index 99%
rename from internal/asm/arm64_debug/debug_assembler.go
rename to internal/integration_test/asm/arm64_debug/debug_assembler.go
index ab2768c1..2ac8744b 100644
--- a/internal/asm/arm64_debug/debug_assembler.go
+++ b/internal/integration_test/asm/arm64_debug/debug_assembler.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/asm"
 	asm_arm64 "github.com/tetratelabs/wazero/internal/asm/arm64"
-	"github.com/tetratelabs/wazero/internal/asm/golang_asm"
+	"github.com/tetratelabs/wazero/internal/integration_test/asm/golang_asm"
 )
 
 // NewDebugAssembler can be used for ensuring that our assembler produces exactly the same binary as Go.
diff --git a/internal/asm/arm64_debug/golang_asm.go b/internal/integration_test/asm/arm64_debug/golang_asm.go
similarity index 99%
rename from internal/asm/arm64_debug/golang_asm.go
rename to internal/integration_test/asm/arm64_debug/golang_asm.go
index 187cbbe7..0d9c2c99 100644
--- a/internal/asm/arm64_debug/golang_asm.go
+++ b/internal/integration_test/asm/arm64_debug/golang_asm.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/asm"
 	asm_arm64 "github.com/tetratelabs/wazero/internal/asm/arm64"
-	"github.com/tetratelabs/wazero/internal/asm/golang_asm"
+	"github.com/tetratelabs/wazero/internal/integration_test/asm/golang_asm"
 )
 
 // TODO: this comment seems wrong
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index cbb02a7c..43a107eb 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -480,7 +480,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			funcType := types[functions[index]]
 			for i := 0; i < len(funcType.Params); i++ {
 				if err := valueTypeStack.popAndVerifyType(funcType.Params[len(funcType.Params)-1-i]); err != nil {
-					return fmt.Errorf("type mismatch on %s operation param type", OpcodeCallName)
+					return fmt.Errorf("type mismatch on %s operation param type: %v", OpcodeCallName, err)
 				}
 			}
 			for _, exp := range funcType.Results {
diff --git a/internal/wasm/text/func_parser.go b/internal/wasm/text/func_parser.go
index b7d8da4c..2062df62 100644
--- a/internal/wasm/text/func_parser.go
+++ b/internal/wasm/text/func_parser.go
@@ -152,6 +152,9 @@ func (p *funcParser) beginInstruction(tokenBytes []byte) (next tokenParser, err
 	case wasm.OpcodeI32AddName: // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-instr-numeric
 		opCode = wasm.OpcodeI32Add
 		next = p.beginFieldOrInstruction
+	case wasm.OpcodeI32SubName: // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-instr-numeric
+		opCode = wasm.OpcodeI32Sub
+		next = p.beginFieldOrInstruction
 	case wasm.OpcodeI32ConstName: // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-instr-numeric
 		opCode = wasm.OpcodeI32Const
 		next = p.parseI32
diff --git a/wasi/wasi.go b/wasi/wasi.go
index 24d9988c..2524d6c5 100644
--- a/wasi/wasi.go
+++ b/wasi/wasi.go
@@ -1,3 +1,7 @@
+// Package wasi contains Go-defined functions to access system calls, such as opening a file, similar to Go's x/sys
+// package. These are accessible from WebAssembly-defined functions via importing ModuleSnapshotPreview1.
+//
+// See https://github.com/WebAssembly/WASI
 package wasi
 
 import (
@@ -22,10 +26,6 @@ const (
 
 // InstantiateSnapshotPreview1 instantiates ModuleSnapshotPreview1, so that other modules can import them.
 //
-// Ex. After you configure like this, other modules can import functions like "wasi_snapshot_preview1" "fd_write".
-//	wm, _ := wasi.InstantiateSnapshotPreview1(r)
-//	defer wm.Close()
-//
 // Note: All WASI functions return a single Errno result, ErrnoSuccess on success.
 func InstantiateSnapshotPreview1(r wazero.Runtime) (api.Module, error) {
 	_, fns := snapshotPreview1Functions()


Test patch:
diff --git a/examples/basic/add_test.go b/examples/basic/add_test.go
index 4e56c1a6..00164866 100644
--- a/examples/basic/add_test.go
+++ b/examples/basic/add_test.go
@@ -1,4 +1,4 @@
-package main
+package add
 
 import (
 	"os"
@@ -6,8 +6,7 @@ import (
 
 // Example_main ensures the following will work:
 //
-//	go build add.go
-//	./add 7 9
+//	go run add.go 7 9
 func Example_main() {
 
 	// Save the old os.Args and replace with our example input.
diff --git a/examples/import-go/age-calculator_test.go b/examples/import-go/age-calculator_test.go
new file mode 100644
index 00000000..a7c04d3c
--- /dev/null
+++ b/examples/import-go/age-calculator_test.go
@@ -0,0 +1,24 @@
+package age_calculator
+
+import "os"
+
+// Example_main ensures the following will work:
+//
+//	go run age-calculator.go 2000
+func Example_main() {
+
+	// Save the old os.Args and replace with our example input.
+	oldArgs := os.Args
+	_ = os.Setenv("CURRENT_YEAR", "2021")
+	os.Args = []string{"age-calculator", "2000"}
+	defer func() {
+		os.Args = oldArgs
+		_ = os.Unsetenv("CURRENT_YEAR")
+	}()
+
+	main()
+
+	// Output:
+	// println >> 21
+	// log_i32 >> 21
+}
diff --git a/examples/multiple-results/multiple-results_test.go b/examples/multiple-results/multiple-results_test.go
index 10395c61..36fd2952 100644
--- a/examples/multiple-results/multiple-results_test.go
+++ b/examples/multiple-results/multiple-results_test.go
@@ -1,9 +1,8 @@
-package main
+package multiple_results
 
 // Example_main ensures the following will work:
 //
-//	go build multiple-results.go
-//	./multiple-results
+//	go run multiple-results.go
 func Example_main() {
 
 	main()
diff --git a/examples/replace-import/replace-import_test.go b/examples/replace-import/replace-import_test.go
index 847b13de..d365bc7a 100644
--- a/examples/replace-import/replace-import_test.go
+++ b/examples/replace-import/replace-import_test.go
@@ -1,9 +1,8 @@
-package main
+package replace_import
 
 // Example_main ensures the following will work:
 //
-//	go build replace-import.go
-//	./replace-import
+//	go run replace-import.go
 func Example_main() {
 
 	main()
diff --git a/examples/wasi/cat_test.go b/examples/wasi/cat_test.go
index 42b7a61a..d57a2dbe 100644
--- a/examples/wasi/cat_test.go
+++ b/examples/wasi/cat_test.go
@@ -1,11 +1,10 @@
-package main
+package wasi_example
 
 import "os"
 
 // Example_main ensures the following will work:
 //
-//	go build cat.go
-//	./cat ./test.txt
+//	go run cat.go ./test.txt
 func Example_main() {
 
 	// Save the old os.Args and replace with our example input.
diff --git a/internal/asm/arm64_debug/impl_test.go b/internal/integration_test/asm/arm64_debug/impl_test.go
similarity index 100%
rename from internal/asm/arm64_debug/impl_test.go
rename to internal/integration_test/asm/arm64_debug/impl_test.go
diff --git a/internal/asm/golang_asm/golang_asm.go b/internal/integration_test/asm/golang_asm/golang_asm.go
similarity index 100%
rename from internal/asm/golang_asm/golang_asm.go
rename to internal/integration_test/asm/golang_asm/golang_asm.go
diff --git a/internal/integration_test/go.mod b/internal/integration_test/go.mod
new file mode 100644
index 00000000..985f6a72
--- /dev/null
+++ b/internal/integration_test/go.mod
@@ -0,0 +1,10 @@
+module github.com/tetratelabs/wazero/internal/integration_test
+
+go 1.17
+
+require (
+	github.com/tetratelabs/wazero v0.0.0
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+)
+
+replace github.com/tetratelabs/wazero => ../..
diff --git a/internal/integration_test/go.sum b/internal/integration_test/go.sum
new file mode 100644
index 00000000..37155b8a
--- /dev/null
+++ b/internal/integration_test/go.sum
@@ -0,0 +1,4 @@
+github.com/tetratelabs/wazero v0.0.0-20220414083743-1e383881edc0 h1:MYB8s3+HS4qaxvnEDJm3Qsn0NQXvBhowgSJVyBZeW+k=
+github.com/tetratelabs/wazero v0.0.0-20220414083743-1e383881edc0/go.mod h1:5JB7ectLUx/rulnLfKI628r3/wUm1grVYnHfmQ2hpBY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
diff --git a/internal/wasm/jit/jit_impl_amd64_test.go b/internal/wasm/jit/jit_impl_amd64_test.go
index b95701b3..c05c109f 100644
--- a/internal/wasm/jit/jit_impl_amd64_test.go
+++ b/internal/wasm/jit/jit_impl_amd64_test.go
@@ -5,26 +5,10 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/asm"
 	amd64 "github.com/tetratelabs/wazero/internal/asm/amd64"
-	"github.com/tetratelabs/wazero/internal/asm/amd64_debug"
 	"github.com/tetratelabs/wazero/internal/testing/require"
-	"github.com/tetratelabs/wazero/internal/wasm"
 	"github.com/tetratelabs/wazero/internal/wazeroir"
 )
 
-// newDebugAmd64Compiler allows debugging in tests, without pinning a dependency in main code.
-func newDebugAmd64Compiler(f *wasm.FunctionInstance, ir *wazeroir.CompilationResult) (compiler, error) {
-	c, err := newAmd64Compiler(f, ir)
-	if err != nil {
-		return nil, err
-	}
-	a, err := amd64_debug.NewDebugAssembler()
-	if err != nil {
-		return nil, err
-	}
-	c.(*amd64Compiler).assembler = a
-	return c, nil
-}
-
 func TestAmd64Compiler_compile_Mul_Div_Rem(t *testing.T) {
 	for _, kind := range []wazeroir.OperationKind{
 		wazeroir.OperationKindMul,
@@ -87,7 +71,7 @@ func TestAmd64Compiler_compile_Mul_Div_Rem(t *testing.T) {
 						const x2Value uint32 = 51
 						const dxValue uint64 = 111111
 
-						compiler := env.requireNewCompiler(t, newDebugAmd64Compiler, nil).(*amd64Compiler)
+						compiler := env.requireNewCompiler(t, newAmd64Compiler, nil).(*amd64Compiler)
 						err := compiler.compilePreamble()
 						require.NoError(t, err)
 
@@ -209,7 +193,7 @@ func TestAmd64Compiler_compile_Mul_Div_Rem(t *testing.T) {
 						const dxValue uint64 = 111111
 
 						env := newJITEnvironment()
-						compiler := env.requireNewCompiler(t, newDebugAmd64Compiler, nil).(*amd64Compiler)
+						compiler := env.requireNewCompiler(t, newAmd64Compiler, nil).(*amd64Compiler)
 						err := compiler.compilePreamble()
 						require.NoError(t, err)
 
@@ -288,7 +272,7 @@ func TestAmd64Compiler_compile_Mul_Div_Rem(t *testing.T) {
 func TestAmd64Compiler_readInstructionAddress(t *testing.T) {
 	t.Run("invalid", func(t *testing.T) {
 		env := newJITEnvironment()
-		compiler := env.requireNewCompiler(t, newDebugAmd64Compiler, nil).(*amd64Compiler)
+		compiler := env.requireNewCompiler(t, newAmd64Compiler, nil).(*amd64Compiler)
 
 		err := compiler.compilePreamble()
 		require.NoError(t, err)
@@ -304,7 +288,7 @@ func TestAmd64Compiler_readInstructionAddress(t *testing.T) {
 
 	t.Run("ok", func(t *testing.T) {
 		env := newJITEnvironment()
-		compiler := env.requireNewCompiler(t, newDebugAmd64Compiler, nil).(*amd64Compiler)
+		compiler := env.requireNewCompiler(t, newAmd64Compiler, nil).(*amd64Compiler)
 
 		err := compiler.compilePreamble()
 		require.NoError(t, err)
diff --git a/internal/wasm/text/func_parser_test.go b/internal/wasm/text/func_parser_test.go
index fe4d7a23..b36ae3bd 100644
--- a/internal/wasm/text/func_parser_test.go
+++ b/internal/wasm/text/func_parser_test.go
@@ -48,6 +48,20 @@ func TestFuncParser(t *testing.T) {
 			source:   "(func i32.const 306)",
 			expected: &wasm.Code{Body: []byte{wasm.OpcodeI32Const, 0xb2, 0x02, wasm.OpcodeEnd}},
 		},
+		{
+			name:   "i32.add",
+			source: "(func i32.const 2 i32.const 1 i32.add)",
+			expected: &wasm.Code{Body: []byte{
+				wasm.OpcodeI32Const, 0x02, wasm.OpcodeI32Const, 0x01, wasm.OpcodeI32Add, wasm.OpcodeEnd,
+			}},
+		},
+		{
+			name:   "i32.sub",
+			source: "(func i32.const 2 i32.const 1 i32.sub)",
+			expected: &wasm.Code{Body: []byte{
+				wasm.OpcodeI32Const, 0x02, wasm.OpcodeI32Const, 0x01, wasm.OpcodeI32Sub, wasm.OpcodeEnd,
+			}},
+		},
 		{
 			name:     "i64.const",
 			source:   "(func i64.const 356)",
diff --git a/wasi/example_test.go b/wasi/example_test.go
new file mode 100644
index 00000000..43196bf4
--- /dev/null
+++ b/wasi/example_test.go
@@ -0,0 +1,14 @@
+package wasi
+
+import "github.com/tetratelabs/wazero"
+
+var r = wazero.NewRuntime()
+
+// Example_InstantiateSnapshotPreview1 shows how to instantiate ModuleSnapshotPreview1, which allows other modules to
+// import functions such as "wasi_snapshot_preview1" "fd_write".
+func Example_instantiateSnapshotPreview1() {
+	wm, _ := InstantiateSnapshotPreview1(r)
+	defer wm.Close()
+
+	// Output:
+}
diff --git a/wasi/usage_test.go b/wasi/usage_test.go
index e77f36cd..a70930c8 100644
--- a/wasi/usage_test.go
+++ b/wasi/usage_test.go
@@ -24,13 +24,13 @@ func TestInstantiateModuleWithConfig(t *testing.T) {
 	require.NoError(t, err)
 	defer wm.Close()
 
-	code, err := r.CompileModule(wasiArg)
+	compiled, err := r.CompileModule(wasiArg)
 	require.NoError(t, err)
-	defer code.Close()
+	defer compiled.Close()
 
 	// Re-use the same module many times.
 	for _, tc := range []string{"a", "b", "c"} {
-		mod, err := r.InstantiateModuleWithConfig(code, sys.WithArgs(tc).WithName(tc))
+		mod, err := r.InstantiateModuleWithConfig(compiled, sys.WithArgs(tc).WithName(tc))
 		require.NoError(t, err)
 
 		// Ensure the scoped configuration applied. As the args are null-terminated, we append zero (NUL).