github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/wazevo/e2e_test.go (about)

     1  package wazevo_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"math"
     9  	"runtime"
    10  	"testing"
    11  
    12  	"github.com/bananabytelabs/wazero"
    13  	"github.com/bananabytelabs/wazero/api"
    14  	"github.com/bananabytelabs/wazero/experimental"
    15  	"github.com/bananabytelabs/wazero/experimental/logging"
    16  	"github.com/bananabytelabs/wazero/experimental/opt"
    17  	"github.com/bananabytelabs/wazero/internal/engine/wazevo/testcases"
    18  	"github.com/bananabytelabs/wazero/internal/leb128"
    19  	"github.com/bananabytelabs/wazero/internal/testing/binaryencoding"
    20  	"github.com/bananabytelabs/wazero/internal/testing/dwarftestdata"
    21  	"github.com/bananabytelabs/wazero/internal/testing/require"
    22  	"github.com/bananabytelabs/wazero/internal/wasm"
    23  )
    24  
    25  const (
    26  	i32  = wasm.ValueTypeI32
    27  	i64  = wasm.ValueTypeI64
    28  	f32  = wasm.ValueTypeF32
    29  	f64  = wasm.ValueTypeF64
    30  	v128 = wasm.ValueTypeV128
    31  )
    32  
    33  // TODO: delete once we complete the implementation for amd64.
    34  func skipOnAmd64(t *testing.T) {
    35  	if runtime.GOARCH == "amd64" {
    36  		t.Skip("skip on amd64")
    37  	}
    38  }
    39  
    40  func TestE2E(t *testing.T) {
    41  	tmp := t.TempDir()
    42  	type callCase struct {
    43  		funcName           string // defaults to testcases.ExportedFunctionName
    44  		params, expResults []uint64
    45  		expErr             string
    46  	}
    47  	for _, tc := range []struct {
    48  		name        string
    49  		imported, m *wasm.Module
    50  		calls       []callCase
    51  		skipAMD64   bool
    52  	}{
    53  		{
    54  			name: "selects", m: testcases.Selects.Module,
    55  			skipAMD64: true,
    56  			calls: []callCase{
    57  				{
    58  					params: []uint64{
    59  						0, 1, // i32,
    60  						200, 100, // i64,
    61  						uint64(math.Float32bits(3.0)), uint64(math.Float32bits(10.0)),
    62  						math.Float64bits(-123.4), math.Float64bits(-10000000000.0),
    63  					},
    64  					expResults: []uint64{
    65  						1,
    66  						200,
    67  						uint64(math.Float32bits(3.0)),
    68  						math.Float64bits(-123.4),
    69  					},
    70  				},
    71  			},
    72  		},
    73  		{
    74  			name: "swap", m: testcases.SwapParamAndReturn.Module,
    75  			skipAMD64: true,
    76  			calls: []callCase{
    77  				{params: []uint64{math.MaxUint32, math.MaxInt32}, expResults: []uint64{math.MaxInt32, math.MaxUint32}},
    78  			},
    79  		},
    80  		{
    81  			name: "consts", m: testcases.Constants.Module,
    82  			skipAMD64: true,
    83  			calls: []callCase{
    84  				{expResults: []uint64{1, 2, uint64(math.Float32bits(32.0)), math.Float64bits(64.0)}},
    85  			},
    86  		},
    87  		{
    88  			name: "unreachable", m: testcases.Unreachable.Module,
    89  			skipAMD64: true,
    90  			calls:     []callCase{{expErr: "unreachable"}},
    91  		},
    92  		{
    93  			name: "fibonacci_recursive", m: testcases.FibonacciRecursive.Module,
    94  			skipAMD64: true,
    95  			calls: []callCase{
    96  				{params: []uint64{0}, expResults: []uint64{0}},
    97  				{params: []uint64{1}, expResults: []uint64{1}},
    98  				{params: []uint64{2}, expResults: []uint64{1}},
    99  				{params: []uint64{10}, expResults: []uint64{55}},
   100  				{params: []uint64{20}, expResults: []uint64{6765}},
   101  				{params: []uint64{30}, expResults: []uint64{0xcb228}},
   102  			},
   103  		},
   104  		{name: "call", m: testcases.Call.Module, calls: []callCase{{expResults: []uint64{45, 45}}}},
   105  		{
   106  			name: "stack overflow",
   107  			m: &wasm.Module{
   108  				TypeSection:     []wasm.FunctionType{{}},
   109  				FunctionSection: []wasm.Index{0},
   110  				CodeSection:     []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}},
   111  				ExportSection:   []wasm.Export{{Name: testcases.ExportedFunctionName, Index: 0, Type: wasm.ExternTypeFunc}},
   112  			},
   113  			skipAMD64: true,
   114  			calls: []callCase{
   115  				{expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"},
   116  			},
   117  		},
   118  		{
   119  			name:      "imported_function_call",
   120  			imported:  testcases.ImportedFunctionCall.Imported,
   121  			m:         testcases.ImportedFunctionCall.Module,
   122  			skipAMD64: true,
   123  			calls: []callCase{
   124  				{params: []uint64{0}, expResults: []uint64{0}},
   125  				{params: []uint64{2}, expResults: []uint64{2 * 2}},
   126  				{params: []uint64{45}, expResults: []uint64{45 * 45}},
   127  				{params: []uint64{90}, expResults: []uint64{90 * 90}},
   128  				{params: []uint64{100}, expResults: []uint64{100 * 100}},
   129  				{params: []uint64{100, 200}, expResults: []uint64{100 * 200}, funcName: "imported_exported"},
   130  			},
   131  		},
   132  		{
   133  			name:      "memory_store_basic",
   134  			m:         testcases.MemoryStoreBasic.Module,
   135  			skipAMD64: true,
   136  			calls: []callCase{
   137  				{params: []uint64{0, 0xf}, expResults: []uint64{0xf}},
   138  				{params: []uint64{256, 0xff}, expResults: []uint64{0xff}},
   139  				{params: []uint64{100, 0xffffffff}, expResults: []uint64{0xffffffff}},
   140  				// We load I32, so we can't load from the last 3 bytes.
   141  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3, 0}, expErr: "out of bounds memory access"},
   142  			},
   143  		},
   144  		{
   145  			name:      "memory_load_basic",
   146  			m:         testcases.MemoryLoadBasic.Module,
   147  			skipAMD64: true,
   148  			calls: []callCase{
   149  				{params: []uint64{0}, expResults: []uint64{0x03_02_01_00}},
   150  				{params: []uint64{256}, expResults: []uint64{0x03_02_01_00}},
   151  				{params: []uint64{100}, expResults: []uint64{103<<24 | 102<<16 | 101<<8 | 100}},
   152  				// Last 4 bytes.
   153  				{params: []uint64{uint64(wasm.MemoryPageSize) - 4}, expResults: []uint64{0xfffefdfc}},
   154  			},
   155  		},
   156  		{
   157  			name:      "memory out of bounds",
   158  			m:         testcases.MemoryLoadBasic.Module,
   159  			skipAMD64: true,
   160  			calls: []callCase{
   161  				{params: []uint64{uint64(wasm.MemoryPageSize)}, expErr: "out of bounds memory access"},
   162  				// We load I32, so we can't load from the last 3 bytes.
   163  				{params: []uint64{uint64(wasm.MemoryPageSize) - 3}, expErr: "out of bounds memory access"},
   164  			},
   165  		},
   166  		{
   167  			name:      "memory_loads",
   168  			m:         testcases.MemoryLoads.Module,
   169  			skipAMD64: true,
   170  			calls: []callCase{
   171  				// These expected results are derived by commenting out `configureWazevo(config)` below to run the old compiler, assuming that it is correct.
   172  				{params: []uint64{0}, expResults: []uint64{0x3020100, 0x706050403020100, 0x3020100, 0x706050403020100, 0x1211100f, 0x161514131211100f, 0x1211100f, 0x161514131211100f, 0x0, 0xf, 0x0, 0xf, 0x100, 0x100f, 0x100, 0x100f, 0x0, 0xf, 0x0, 0xf, 0x100, 0x100f, 0x100, 0x100f, 0x3020100, 0x1211100f, 0x3020100, 0x1211100f}},
   173  				{params: []uint64{1}, expResults: []uint64{0x4030201, 0x807060504030201, 0x4030201, 0x807060504030201, 0x13121110, 0x1716151413121110, 0x13121110, 0x1716151413121110, 0x1, 0x10, 0x1, 0x10, 0x201, 0x1110, 0x201, 0x1110, 0x1, 0x10, 0x1, 0x10, 0x201, 0x1110, 0x201, 0x1110, 0x4030201, 0x13121110, 0x4030201, 0x13121110}},
   174  				{params: []uint64{8}, expResults: []uint64{0xb0a0908, 0xf0e0d0c0b0a0908, 0xb0a0908, 0xf0e0d0c0b0a0908, 0x1a191817, 0x1e1d1c1b1a191817, 0x1a191817, 0x1e1d1c1b1a191817, 0x8, 0x17, 0x8, 0x17, 0x908, 0x1817, 0x908, 0x1817, 0x8, 0x17, 0x8, 0x17, 0x908, 0x1817, 0x908, 0x1817, 0xb0a0908, 0x1a191817, 0xb0a0908, 0x1a191817}},
   175  				{params: []uint64{0xb}, expResults: []uint64{0xe0d0c0b, 0x1211100f0e0d0c0b, 0xe0d0c0b, 0x1211100f0e0d0c0b, 0x1d1c1b1a, 0x21201f1e1d1c1b1a, 0x1d1c1b1a, 0x21201f1e1d1c1b1a, 0xb, 0x1a, 0xb, 0x1a, 0xc0b, 0x1b1a, 0xc0b, 0x1b1a, 0xb, 0x1a, 0xb, 0x1a, 0xc0b, 0x1b1a, 0xc0b, 0x1b1a, 0xe0d0c0b, 0x1d1c1b1a, 0xe0d0c0b, 0x1d1c1b1a}},
   176  				{params: []uint64{0xc}, expResults: []uint64{0xf0e0d0c, 0x131211100f0e0d0c, 0xf0e0d0c, 0x131211100f0e0d0c, 0x1e1d1c1b, 0x2221201f1e1d1c1b, 0x1e1d1c1b, 0x2221201f1e1d1c1b, 0xc, 0x1b, 0xc, 0x1b, 0xd0c, 0x1c1b, 0xd0c, 0x1c1b, 0xc, 0x1b, 0xc, 0x1b, 0xd0c, 0x1c1b, 0xd0c, 0x1c1b, 0xf0e0d0c, 0x1e1d1c1b, 0xf0e0d0c, 0x1e1d1c1b}},
   177  				{params: []uint64{0xd}, expResults: []uint64{0x100f0e0d, 0x14131211100f0e0d, 0x100f0e0d, 0x14131211100f0e0d, 0x1f1e1d1c, 0x232221201f1e1d1c, 0x1f1e1d1c, 0x232221201f1e1d1c, 0xd, 0x1c, 0xd, 0x1c, 0xe0d, 0x1d1c, 0xe0d, 0x1d1c, 0xd, 0x1c, 0xd, 0x1c, 0xe0d, 0x1d1c, 0xe0d, 0x1d1c, 0x100f0e0d, 0x1f1e1d1c, 0x100f0e0d, 0x1f1e1d1c}},
   178  				{params: []uint64{0xe}, expResults: []uint64{0x11100f0e, 0x1514131211100f0e, 0x11100f0e, 0x1514131211100f0e, 0x201f1e1d, 0x24232221201f1e1d, 0x201f1e1d, 0x24232221201f1e1d, 0xe, 0x1d, 0xe, 0x1d, 0xf0e, 0x1e1d, 0xf0e, 0x1e1d, 0xe, 0x1d, 0xe, 0x1d, 0xf0e, 0x1e1d, 0xf0e, 0x1e1d, 0x11100f0e, 0x201f1e1d, 0x11100f0e, 0x201f1e1d}},
   179  				{params: []uint64{0xf}, expResults: []uint64{0x1211100f, 0x161514131211100f, 0x1211100f, 0x161514131211100f, 0x21201f1e, 0x2524232221201f1e, 0x21201f1e, 0x2524232221201f1e, 0xf, 0x1e, 0xf, 0x1e, 0x100f, 0x1f1e, 0x100f, 0x1f1e, 0xf, 0x1e, 0xf, 0x1e, 0x100f, 0x1f1e, 0x100f, 0x1f1e, 0x1211100f, 0x21201f1e, 0x1211100f, 0x21201f1e}},
   180  			},
   181  		},
   182  		{
   183  			name:      "globals_get",
   184  			m:         testcases.GlobalsGet.Module,
   185  			skipAMD64: true,
   186  			calls: []callCase{
   187  				{expResults: []uint64{0x80000000, 0x8000000000000000, 0x7f7fffff, 0x7fefffffffffffff}},
   188  			},
   189  		},
   190  		{
   191  			name:      "globals_set",
   192  			m:         testcases.GlobalsSet.Module,
   193  			skipAMD64: true,
   194  			calls:     []callCase{{expResults: []uint64{1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0)}}},
   195  		},
   196  		{
   197  			name:      "globals_mutable",
   198  			m:         testcases.GlobalsMutable.Module,
   199  			skipAMD64: true,
   200  			calls: []callCase{{expResults: []uint64{
   201  				100, 200, uint64(math.Float32bits(300.0)), math.Float64bits(400.0),
   202  				1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0),
   203  			}}},
   204  		},
   205  		{
   206  			name:      "memory_size_grow",
   207  			m:         testcases.MemorySizeGrow.Module,
   208  			skipAMD64: true,
   209  			calls:     []callCase{{expResults: []uint64{1, 2, 0xffffffff}}},
   210  		},
   211  		{
   212  			name:      "imported_memory_grow",
   213  			imported:  testcases.ImportedMemoryGrow.Imported,
   214  			m:         testcases.ImportedMemoryGrow.Module,
   215  			skipAMD64: true,
   216  			calls:     []callCase{{expResults: []uint64{1, 1, 11, 11}}},
   217  		},
   218  		{
   219  			name: "call_indirect",
   220  			m:    testcases.CallIndirect.Module,
   221  			// parameter == table offset.
   222  			skipAMD64: true,
   223  			calls: []callCase{
   224  				{params: []uint64{0}, expErr: "indirect call type mismatch"},
   225  				{params: []uint64{1}, expResults: []uint64{10}},
   226  				{params: []uint64{2}, expErr: "indirect call type mismatch"},
   227  				{params: []uint64{10}, expErr: "invalid table access"},             // Null pointer.
   228  				{params: []uint64{math.MaxUint32}, expErr: "invalid table access"}, // Out of bounds.
   229  			},
   230  		},
   231  		{
   232  			name:      "br_table",
   233  			m:         testcases.BrTable.Module,
   234  			skipAMD64: true,
   235  			calls: []callCase{
   236  				{params: []uint64{0}, expResults: []uint64{11}},
   237  				{params: []uint64{1}, expResults: []uint64{12}},
   238  				{params: []uint64{2}, expResults: []uint64{13}},
   239  				{params: []uint64{3}, expResults: []uint64{14}},
   240  				{params: []uint64{4}, expResults: []uint64{15}},
   241  				{params: []uint64{5}, expResults: []uint64{16}},
   242  				// Out of range --> default.
   243  				{params: []uint64{6}, expResults: []uint64{11}},
   244  				{params: []uint64{1000}, expResults: []uint64{11}},
   245  			},
   246  		},
   247  		{
   248  			name:      "br_table_with_args",
   249  			m:         testcases.BrTableWithArg.Module,
   250  			skipAMD64: true,
   251  			calls: []callCase{
   252  				{params: []uint64{0, 100}, expResults: []uint64{11 + 100}},
   253  				{params: []uint64{1, 100}, expResults: []uint64{12 + 100}},
   254  				{params: []uint64{2, 100}, expResults: []uint64{13 + 100}},
   255  				{params: []uint64{3, 100}, expResults: []uint64{14 + 100}},
   256  				{params: []uint64{4, 100}, expResults: []uint64{15 + 100}},
   257  				{params: []uint64{5, 100}, expResults: []uint64{16 + 100}},
   258  				// Out of range --> default.
   259  				{params: []uint64{6, 200}, expResults: []uint64{11 + 200}},
   260  				{params: []uint64{1000, 300}, expResults: []uint64{11 + 300}},
   261  			},
   262  		},
   263  		{
   264  			name:      "multi_predecessor_local_ref",
   265  			m:         testcases.MultiPredecessorLocalRef.Module,
   266  			skipAMD64: true,
   267  			calls: []callCase{
   268  				{params: []uint64{0, 100}, expResults: []uint64{100}},
   269  				{params: []uint64{1, 100}, expResults: []uint64{1}},
   270  				{params: []uint64{1, 200}, expResults: []uint64{1}},
   271  			},
   272  		},
   273  		{
   274  			name:      "vector_bit_select",
   275  			m:         testcases.VecBitSelect.Module,
   276  			skipAMD64: true,
   277  			calls: []callCase{
   278  				{params: []uint64{1, 2, 3, 4, 5, 6}, expResults: []uint64{0x3, 0x2, 0x5, 0x6}},
   279  			},
   280  		},
   281  		{
   282  			name:      "vector_shuffle",
   283  			m:         testcases.VecShuffle.Module,
   284  			skipAMD64: true,
   285  			calls: []callCase{
   286  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x01010101, 0x04040404}},
   287  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x03030303, 0x02020202}},
   288  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x00000000, 0x00000000}},
   289  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffff, 0xffffffff}},
   290  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x00000000, 0xffffffff}},
   291  			},
   292  		},
   293  		{
   294  			name:      "vector_shuffle (1st only)",
   295  			m:         testcases.VecShuffleWithLane(1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0),
   296  			skipAMD64: true,
   297  			calls: []callCase{
   298  				{params: []uint64{0x0000000000000b0a, 0x0c0000, 0xffffffffffffffff, 0xffffffffffffffff}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   299  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x0101010101010101, 0x101010102020202}},
   300  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x0303030303030303, 0x303030304040404}},
   301  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x0000000000000000, 0x0000000ffffffff}},
   302  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   303  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0x0000000011111111}},
   304  			},
   305  		},
   306  		{
   307  			name:      "vector_shuffle (2nd only)",
   308  			m:         testcases.VecShuffleWithLane(17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16),
   309  			skipAMD64: true,
   310  			calls: []callCase{
   311  				{params: []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000b0a, 0x0c0000}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}},
   312  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x303030303030303, 0x303030304040404}},
   313  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x101010101010101, 0x101010102020202}},
   314  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}},
   315  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0xffffffff}},
   316  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x1111111111111111, 0x11111111ffffffff}},
   317  			},
   318  		},
   319  		{
   320  			name:      "vector_shuffle (mixed)",
   321  			m:         testcases.VecShuffleWithLane(0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31),
   322  			skipAMD64: true,
   323  			calls: []callCase{
   324  				{params: []uint64{0xff08ff07ff06ff05, 0xff04ff03ff02ff01, 0x18ff17ff16ff15ff, 0x14ff13ff12ff11ff}, expResults: []uint64{0x1808170716061505, 0x1404130312021101}},
   325  				{params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x3010301, 0x4020402}},
   326  				{params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x1030103, 0x2040204}},
   327  				{params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xff00ff00, 0xff00ff}},
   328  				{params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xff00ff, 0xff00ff00}},
   329  				{params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x11001100, 0xff11ff11}},
   330  			},
   331  		},
   332  	} {
   333  		tc := tc
   334  		if tc.skipAMD64 {
   335  			skipOnAmd64(t)
   336  		}
   337  		t.Run(tc.name, func(t *testing.T) {
   338  			for i := 0; i < 2; i++ {
   339  				var name string
   340  				if i == 0 {
   341  					name = "no cache"
   342  				} else {
   343  					name = "with cache"
   344  				}
   345  				t.Run(name, func(t *testing.T) {
   346  					cache, err := wazero.NewCompilationCacheWithDir(tmp)
   347  					require.NoError(t, err)
   348  					config := opt.NewRuntimeConfigOptimizingCompiler().WithCompilationCache(cache)
   349  
   350  					ctx := context.Background()
   351  					r := wazero.NewRuntimeWithConfig(ctx, config)
   352  					defer func() {
   353  						require.NoError(t, r.Close(ctx))
   354  					}()
   355  
   356  					if tc.imported != nil {
   357  						imported, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.imported))
   358  						require.NoError(t, err)
   359  
   360  						_, err = r.InstantiateModule(ctx, imported, wazero.NewModuleConfig())
   361  						require.NoError(t, err)
   362  					}
   363  
   364  					compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.m))
   365  					require.NoError(t, err)
   366  
   367  					inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   368  					require.NoError(t, err)
   369  
   370  					for _, cc := range tc.calls {
   371  						name := cc.funcName
   372  						if name == "" {
   373  							name = testcases.ExportedFunctionName
   374  						}
   375  						t.Run(fmt.Sprintf("call_%s%v", name, cc.params), func(t *testing.T) {
   376  							f := inst.ExportedFunction(name)
   377  							require.NotNil(t, f)
   378  							result, err := f.Call(ctx, cc.params...)
   379  							if cc.expErr != "" {
   380  								require.Contains(t, err.Error(), cc.expErr)
   381  							} else {
   382  								require.NoError(t, err)
   383  								require.Equal(t, len(cc.expResults), len(result))
   384  								require.Equal(t, cc.expResults, result)
   385  								for i := range cc.expResults {
   386  									if cc.expResults[i] != result[i] {
   387  										t.Errorf("result[%d]: exp %d, got %d", i, cc.expResults[i], result[i])
   388  									}
   389  								}
   390  							}
   391  						})
   392  					}
   393  				})
   394  			}
   395  		})
   396  	}
   397  }
   398  
   399  func TestE2E_host_functions(t *testing.T) {
   400  	skipOnAmd64(t)
   401  	var buf bytes.Buffer
   402  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   403  
   404  	for _, tc := range []struct {
   405  		name string
   406  		ctx  context.Context
   407  	}{
   408  		{name: "listener", ctx: ctx},
   409  		{name: "no listener", ctx: context.Background()},
   410  	} {
   411  		tc := tc
   412  		t.Run(tc.name, func(t *testing.T) {
   413  			ctx := tc.ctx
   414  
   415  			config := opt.NewRuntimeConfigOptimizingCompiler()
   416  
   417  			r := wazero.NewRuntimeWithConfig(ctx, config)
   418  			defer func() {
   419  				require.NoError(t, r.Close(ctx))
   420  			}()
   421  
   422  			var expectedMod api.Module
   423  
   424  			b := r.NewHostModuleBuilder("env")
   425  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, d float64) float64 {
   426  				require.Equal(t, ctx, ctx2)
   427  				require.Equal(t, 35.0, d)
   428  				return math.Sqrt(d)
   429  			}).Export("root")
   430  			b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, mod api.Module, a uint32, b uint64, c float32, d float64) (uint32, uint64, float32, float64) {
   431  				require.Equal(t, expectedMod, mod)
   432  				require.Equal(t, ctx, ctx2)
   433  				require.Equal(t, uint32(2), a)
   434  				require.Equal(t, uint64(100), b)
   435  				require.Equal(t, float32(15.0), c)
   436  				require.Equal(t, 35.0, d)
   437  				return a * a, b * b, c * c, d * d
   438  			}).Export("square")
   439  
   440  			_, err := b.Instantiate(ctx)
   441  			require.NoError(t, err)
   442  
   443  			m := &wasm.Module{
   444  				ImportFunctionCount: 2,
   445  				ImportSection: []wasm.Import{
   446  					{Module: "env", Name: "root", Type: wasm.ExternTypeFunc, DescFunc: 0},
   447  					{Module: "env", Name: "square", Type: wasm.ExternTypeFunc, DescFunc: 1},
   448  				},
   449  				TypeSection: []wasm.FunctionType{
   450  					{Results: []wasm.ValueType{f64}, Params: []wasm.ValueType{f64}},
   451  					{Results: []wasm.ValueType{i32, i64, f32, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   452  					{Results: []wasm.ValueType{i32, i64, f32, f64, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}},
   453  				},
   454  				FunctionSection: []wasm.Index{2},
   455  				CodeSection: []wasm.Code{{
   456  					Body: []byte{
   457  						wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeLocalGet, 2, wasm.OpcodeLocalGet, 3,
   458  						wasm.OpcodeCall, 1,
   459  						wasm.OpcodeLocalGet, 3,
   460  						wasm.OpcodeCall, 0,
   461  						wasm.OpcodeEnd,
   462  					},
   463  				}},
   464  				ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 2}},
   465  			}
   466  
   467  			compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(m))
   468  			require.NoError(t, err)
   469  
   470  			inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   471  			require.NoError(t, err)
   472  
   473  			expectedMod = inst
   474  
   475  			f := inst.ExportedFunction(testcases.ExportedFunctionName)
   476  
   477  			res, err := f.Call(ctx, []uint64{2, 100, uint64(math.Float32bits(15.0)), math.Float64bits(35.0)}...)
   478  			require.NoError(t, err)
   479  			require.Equal(t, []uint64{
   480  				2 * 2, 100 * 100, uint64(math.Float32bits(15.0 * 15.0)), math.Float64bits(35.0 * 35.0),
   481  				math.Float64bits(math.Sqrt(35.0)),
   482  			}, res)
   483  		})
   484  	}
   485  
   486  	require.Equal(t, `
   487  --> .$2(2,100,15,35)
   488  	==> env.square(2,100,15,35)
   489  	<== (4,10000,225,1225)
   490  	==> env.root(35)
   491  	<== 5.916079783099616
   492  <-- (4,10000,225,1225,5.916079783099616)
   493  `, "\n"+buf.String())
   494  }
   495  
   496  func TestE2E_stores(t *testing.T) {
   497  	skipOnAmd64(t)
   498  	config := opt.NewRuntimeConfigOptimizingCompiler()
   499  
   500  	ctx := context.Background()
   501  	r := wazero.NewRuntimeWithConfig(ctx, config)
   502  	defer func() {
   503  		require.NoError(t, r.Close(ctx))
   504  	}()
   505  
   506  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.MemoryStores.Module))
   507  	require.NoError(t, err)
   508  
   509  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   510  	require.NoError(t, err)
   511  
   512  	f := inst.ExportedFunction(testcases.ExportedFunctionName)
   513  
   514  	mem, ok := inst.Memory().Read(0, wasm.MemoryPageSize)
   515  	require.True(t, ok)
   516  	for _, tc := range []struct {
   517  		i32 uint32
   518  		i64 uint64
   519  		f32 float32
   520  		f64 float64
   521  	}{
   522  		{0, 0, 0, 0},
   523  		{1, 2, 3.0, 4.0},
   524  		{math.MaxUint32, math.MaxUint64, float32(math.NaN()), math.NaN()},
   525  		{1 << 31, 1 << 63, 3.0, 4.0},
   526  	} {
   527  		t.Run(fmt.Sprintf("i32=%#x,i64=%#x,f32=%#x,f64=%#x", tc.i32, tc.i64, tc.f32, tc.f64), func(t *testing.T) {
   528  			_, err = f.Call(ctx, []uint64{uint64(tc.i32), tc.i64, uint64(math.Float32bits(tc.f32)), math.Float64bits(tc.f64)}...)
   529  			require.NoError(t, err)
   530  
   531  			offset := 0
   532  			require.Equal(t, binary.LittleEndian.Uint32(mem[offset:]), tc.i32)
   533  			offset += 8
   534  			require.Equal(t, binary.LittleEndian.Uint64(mem[offset:]), tc.i64)
   535  			offset += 8
   536  			require.Equal(t, math.Float32bits(tc.f32), binary.LittleEndian.Uint32(mem[offset:]))
   537  			offset += 8
   538  			require.Equal(t, math.Float64bits(tc.f64), binary.LittleEndian.Uint64(mem[offset:]))
   539  			offset += 8
   540  
   541  			// i32.store_8
   542  			view := binary.LittleEndian.Uint64(mem[offset:])
   543  			require.Equal(t, uint64(tc.i32)&0xff, view)
   544  			offset += 8
   545  			// i32.store_16
   546  			view = binary.LittleEndian.Uint64(mem[offset:])
   547  			require.Equal(t, uint64(tc.i32)&0xffff, view)
   548  			offset += 8
   549  			// i64.store_8
   550  			view = binary.LittleEndian.Uint64(mem[offset:])
   551  			require.Equal(t, tc.i64&0xff, view)
   552  			offset += 8
   553  			// i64.store_16
   554  			view = binary.LittleEndian.Uint64(mem[offset:])
   555  			require.Equal(t, tc.i64&0xffff, view)
   556  			offset += 8
   557  			// i64.store_32
   558  			view = binary.LittleEndian.Uint64(mem[offset:])
   559  			require.Equal(t, tc.i64&0xffffffff, view)
   560  		})
   561  	}
   562  }
   563  
   564  func TestE2E_reexported_memory(t *testing.T) {
   565  	skipOnAmd64(t)
   566  	m1 := &wasm.Module{
   567  		ExportSection: []wasm.Export{{Name: "mem", Type: wasm.ExternTypeMemory, Index: 0}},
   568  		MemorySection: &wasm.Memory{Min: 1},
   569  		NameSection:   &wasm.NameSection{ModuleName: "m1"},
   570  	}
   571  	m2 := &wasm.Module{
   572  		ImportMemoryCount: 1,
   573  		ExportSection:     []wasm.Export{{Name: "mem2", Type: wasm.ExternTypeMemory, Index: 0}},
   574  		ImportSection:     []wasm.Import{{Module: "m1", Name: "mem", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
   575  		NameSection:       &wasm.NameSection{ModuleName: "m2"},
   576  	}
   577  	m3 := &wasm.Module{
   578  		ImportMemoryCount: 1,
   579  		ImportSection:     []wasm.Import{{Module: "m2", Name: "mem2", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}},
   580  		TypeSection:       []wasm.FunctionType{{Results: []wasm.ValueType{i32}}},
   581  		ExportSection:     []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 0}},
   582  		FunctionSection:   []wasm.Index{0},
   583  		CodeSection:       []wasm.Code{{Body: []byte{wasm.OpcodeI32Const, 10, wasm.OpcodeMemoryGrow, 0, wasm.OpcodeEnd}}},
   584  	}
   585  
   586  	config := opt.NewRuntimeConfigOptimizingCompiler()
   587  
   588  	ctx := context.Background()
   589  	r := wazero.NewRuntimeWithConfig(ctx, config)
   590  	defer func() {
   591  		require.NoError(t, r.Close(ctx))
   592  	}()
   593  
   594  	m1Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m1))
   595  	require.NoError(t, err)
   596  
   597  	m2Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m2))
   598  	require.NoError(t, err)
   599  
   600  	m3Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m3))
   601  	require.NoError(t, err)
   602  
   603  	f := m3Inst.ExportedFunction(testcases.ExportedFunctionName)
   604  	result, err := f.Call(ctx)
   605  	require.NoError(t, err)
   606  	require.Equal(t, uint64(1), result[0])
   607  	mem := m1Inst.Memory()
   608  	require.Equal(t, mem, m3Inst.Memory())
   609  	require.Equal(t, mem, m2Inst.Memory())
   610  	require.Equal(t, uint32(11), mem.Size()/65536)
   611  }
   612  
   613  func TestStackUnwind_panic_in_host(t *testing.T) {
   614  	skipOnAmd64(t)
   615  	unreachable := &wasm.Module{
   616  		ImportFunctionCount: 1,
   617  		ImportSection:       []wasm.Import{{Module: "host", Name: "cause_unreachable", Type: wasm.ExternTypeFunc, DescFunc: 0}},
   618  		TypeSection:         []wasm.FunctionType{{}},
   619  		ExportSection:       []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}},
   620  		FunctionSection:     []wasm.Index{0, 0, 0},
   621  		CodeSection: []wasm.Code{
   622  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
   623  			{Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}},
   624  			{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // call host.cause_unreachable.
   625  		},
   626  		NameSection: &wasm.NameSection{
   627  			FunctionNames: wasm.NameMap{
   628  				wasm.NameAssoc{Index: 0, Name: "host.unreachable"},
   629  				wasm.NameAssoc{Index: 1, Name: "main"},
   630  				wasm.NameAssoc{Index: 2, Name: "one"},
   631  				wasm.NameAssoc{Index: 3, Name: "two"},
   632  			},
   633  		},
   634  	}
   635  
   636  	config := opt.NewRuntimeConfigOptimizingCompiler()
   637  
   638  	ctx := context.Background()
   639  	r := wazero.NewRuntimeWithConfig(ctx, config)
   640  	defer func() {
   641  		require.NoError(t, r.Close(ctx))
   642  	}()
   643  
   644  	callUnreachable := func() {
   645  		panic("panic in host function")
   646  	}
   647  
   648  	_, err := r.NewHostModuleBuilder("host").
   649  		NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable").
   650  		Instantiate(ctx)
   651  	require.NoError(t, err)
   652  
   653  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
   654  	require.NoError(t, err)
   655  	defer module.Close(ctx)
   656  
   657  	_, err = module.ExportedFunction("main").Call(ctx)
   658  	exp := `panic in host function (recovered by wazero)
   659  wasm stack trace:
   660  	host.cause_unreachable()
   661  	.two()
   662  	.one()
   663  	.main()`
   664  	require.Equal(t, exp, err.Error())
   665  }
   666  
   667  func TestStackUnwind_unreachable(t *testing.T) {
   668  	skipOnAmd64(t)
   669  	unreachable := &wasm.Module{
   670  		TypeSection:     []wasm.FunctionType{{}},
   671  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   672  		FunctionSection: []wasm.Index{0, 0, 0},
   673  		CodeSection: []wasm.Code{
   674  			{Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}},
   675  			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
   676  			{Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}},
   677  		},
   678  		NameSection: &wasm.NameSection{
   679  			FunctionNames: wasm.NameMap{
   680  				wasm.NameAssoc{Index: 0, Name: "main"},
   681  				wasm.NameAssoc{Index: 1, Name: "one"},
   682  				wasm.NameAssoc{Index: 2, Name: "two"},
   683  			},
   684  		},
   685  	}
   686  
   687  	config := opt.NewRuntimeConfigOptimizingCompiler()
   688  	ctx := context.Background()
   689  	r := wazero.NewRuntimeWithConfig(ctx, config)
   690  	defer func() {
   691  		require.NoError(t, r.Close(ctx))
   692  	}()
   693  
   694  	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
   695  	require.NoError(t, err)
   696  	defer module.Close(ctx)
   697  
   698  	_, err = module.ExportedFunction("main").Call(ctx)
   699  	exp := `wasm error: unreachable
   700  wasm stack trace:
   701  	.two()
   702  	.one()
   703  	.main()`
   704  	require.Equal(t, exp, err.Error())
   705  }
   706  
   707  func TestListener_local(t *testing.T) {
   708  	skipOnAmd64(t)
   709  	var buf bytes.Buffer
   710  	config := opt.NewRuntimeConfigOptimizingCompiler()
   711  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   712  
   713  	r := wazero.NewRuntimeWithConfig(ctx, config)
   714  	defer func() {
   715  		require.NoError(t, r.Close(ctx))
   716  	}()
   717  
   718  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.CallIndirect.Module))
   719  	require.NoError(t, err)
   720  
   721  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   722  	require.NoError(t, err)
   723  
   724  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 1)
   725  	require.NoError(t, err)
   726  	require.Equal(t, []uint64{10}, res)
   727  
   728  	require.Equal(t, `
   729  --> .$0(1)
   730  	--> .$2()
   731  	<-- 10
   732  <-- 10
   733  `, "\n"+buf.String())
   734  }
   735  
   736  func TestListener_imported(t *testing.T) {
   737  	skipOnAmd64(t)
   738  	var buf bytes.Buffer
   739  	config := opt.NewRuntimeConfigOptimizingCompiler()
   740  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   741  
   742  	r := wazero.NewRuntimeWithConfig(ctx, config)
   743  	defer func() {
   744  		require.NoError(t, r.Close(ctx))
   745  	}()
   746  
   747  	_, err := r.Instantiate(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Imported))
   748  	require.NoError(t, err)
   749  
   750  	compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Module))
   751  	require.NoError(t, err)
   752  
   753  	inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
   754  	require.NoError(t, err)
   755  
   756  	res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 100)
   757  	require.NoError(t, err)
   758  	require.Equal(t, []uint64{10000}, res)
   759  
   760  	require.Equal(t, `
   761  --> .$1(100)
   762  	--> env.$0(100,100)
   763  	<-- 10000
   764  <-- 10000
   765  `, "\n"+buf.String())
   766  }
   767  
   768  func TestListener_long(t *testing.T) {
   769  	skipOnAmd64(t)
   770  	pickOneParam := binaryencoding.EncodeModule(&wasm.Module{
   771  		TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}, Params: []wasm.ValueType{
   772  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   773  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   774  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   775  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   776  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   777  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   778  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   779  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   780  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   781  			i32, i32, f32, f64, i64, i32, i32, v128, f32,
   782  		}}},
   783  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   784  		FunctionSection: []wasm.Index{0},
   785  		CodeSection: []wasm.Code{
   786  			{Body: []byte{wasm.OpcodeLocalGet, 10, wasm.OpcodeEnd}},
   787  		},
   788  	})
   789  
   790  	var buf bytes.Buffer
   791  	config := opt.NewRuntimeConfigOptimizingCompiler()
   792  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   793  
   794  	r := wazero.NewRuntimeWithConfig(ctx, config)
   795  	defer func() {
   796  		require.NoError(t, r.Close(ctx))
   797  	}()
   798  
   799  	inst, err := r.Instantiate(ctx, pickOneParam)
   800  	require.NoError(t, err)
   801  
   802  	f := inst.ExportedFunction("main")
   803  	require.NotNil(t, f)
   804  	param := make([]uint64, 100)
   805  	for i := range param {
   806  		param[i] = uint64(i)
   807  	}
   808  	res, err := f.Call(ctx, param...)
   809  	require.NoError(t, err)
   810  	require.Equal(t, []uint64{0xb}, res)
   811  
   812  	require.Equal(t, `
   813  --> .$0(0,1,3e-45,1.5e-323,4,5,6,00000000000000070000000000000008,1.1e-44,9,10,1.5e-44,6e-323,13,14,15,00000000000000100000000000000011,2.4e-44,18,19,2.8e-44,1.04e-322,22,23,24,0000000000000019000000000000001a,3.6e-44,27,28,4e-44,1.5e-322,31,32,33,00000000000000220000000000000023,4.9e-44,36,37,5.3e-44,1.93e-322,40,41,42,000000000000002b000000000000002c,6.2e-44,45,46,6.6e-44,2.37e-322,49,50,51,00000000000000340000000000000035,7.4e-44,54,55,7.8e-44,2.8e-322,58,59,60,000000000000003d000000000000003e,8.7e-44,63,64,9.1e-44,3.26e-322,67,68,69,00000000000000460000000000000047,1e-43,72,73,1.04e-43,3.7e-322,76,77,78,000000000000004f0000000000000050,1.12e-43,81,82,1.16e-43,4.15e-322,85,86,87,00000000000000580000000000000059,1.25e-43)
   814  <-- 11
   815  `, "\n"+buf.String())
   816  }
   817  
   818  func TestListener_long_as_is(t *testing.T) {
   819  	skipOnAmd64(t)
   820  	params := []wasm.ValueType{
   821  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
   822  		i32, i64, i32, i64, i32, i64, i32, i64, i32, i64,
   823  	}
   824  
   825  	const paramNum = 20
   826  
   827  	var body []byte
   828  	for i := 0; i < paramNum; i++ {
   829  		body = append(body, wasm.OpcodeLocalGet)
   830  		body = append(body, leb128.EncodeUint32(uint32(i))...)
   831  	}
   832  	body = append(body, wasm.OpcodeEnd)
   833  
   834  	bin := binaryencoding.EncodeModule(&wasm.Module{
   835  		TypeSection:     []wasm.FunctionType{{Results: params, Params: params}},
   836  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   837  		FunctionSection: []wasm.Index{0},
   838  		CodeSection:     []wasm.Code{{Body: body}},
   839  	})
   840  
   841  	var buf bytes.Buffer
   842  	config := opt.NewRuntimeConfigOptimizingCompiler()
   843  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   844  
   845  	r := wazero.NewRuntimeWithConfig(ctx, config)
   846  	defer func() {
   847  		require.NoError(t, r.Close(ctx))
   848  	}()
   849  
   850  	inst, err := r.Instantiate(ctx, bin)
   851  	require.NoError(t, err)
   852  
   853  	f := inst.ExportedFunction("main")
   854  	require.NotNil(t, f)
   855  	param := make([]uint64, paramNum)
   856  	for i := range param {
   857  		param[i] = uint64(i)
   858  	}
   859  	res, err := f.Call(ctx, param...)
   860  	require.NoError(t, err)
   861  	require.Equal(t, param, res)
   862  
   863  	require.Equal(t, `
   864  --> .$0(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
   865  <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
   866  `, "\n"+buf.String())
   867  }
   868  
   869  func TestListener_long_many_consts(t *testing.T) {
   870  	skipOnAmd64(t)
   871  	const paramNum = 61
   872  
   873  	var exp []uint64
   874  	var body []byte
   875  	var resultTypes []wasm.ValueType
   876  	for i := 0; i < paramNum; i++ {
   877  		exp = append(exp, uint64(i))
   878  		resultTypes = append(resultTypes, i32)
   879  		body = append(body, wasm.OpcodeI32Const)
   880  		body = append(body, leb128.EncodeInt32(int32(i))...)
   881  	}
   882  	body = append(body, wasm.OpcodeEnd)
   883  
   884  	bin := binaryencoding.EncodeModule(&wasm.Module{
   885  		TypeSection:     []wasm.FunctionType{{Results: resultTypes}},
   886  		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
   887  		FunctionSection: []wasm.Index{0},
   888  		CodeSection:     []wasm.Code{{Body: body}},
   889  	})
   890  
   891  	var buf bytes.Buffer
   892  	config := opt.NewRuntimeConfigOptimizingCompiler()
   893  	ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&buf))
   894  
   895  	r := wazero.NewRuntimeWithConfig(ctx, config)
   896  	defer func() {
   897  		require.NoError(t, r.Close(ctx))
   898  	}()
   899  
   900  	inst, err := r.Instantiate(ctx, bin)
   901  	require.NoError(t, err)
   902  
   903  	f := inst.ExportedFunction("main")
   904  	require.NotNil(t, f)
   905  	res, err := f.Call(ctx)
   906  	require.NoError(t, err)
   907  	require.Equal(t, exp, res)
   908  
   909  	require.Equal(t, `
   910  --> .$0()
   911  <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60)
   912  `, "\n"+buf.String())
   913  }
   914  
   915  // TestDWARF verifies that the DWARF based stack traces work as expected before/after compilation cache.
   916  func TestDWARF(t *testing.T) {
   917  	skipOnAmd64(t)
   918  	config := opt.NewRuntimeConfigOptimizingCompiler()
   919  	ctx := context.Background()
   920  
   921  	bin := dwarftestdata.ZigWasm
   922  
   923  	dir := t.TempDir()
   924  
   925  	var expErr error
   926  	{
   927  		cc, err := wazero.NewCompilationCacheWithDir(dir)
   928  		require.NoError(t, err)
   929  		rc := config.WithCompilationCache(cc)
   930  
   931  		r := wazero.NewRuntimeWithConfig(ctx, rc)
   932  		_, expErr = r.Instantiate(ctx, bin)
   933  		require.Error(t, expErr)
   934  
   935  		err = r.Close(ctx)
   936  		require.NoError(t, err)
   937  	}
   938  
   939  	cc, err := wazero.NewCompilationCacheWithDir(dir)
   940  	require.NoError(t, err)
   941  	rc := config.WithCompilationCache(cc)
   942  	r := wazero.NewRuntimeWithConfig(ctx, rc)
   943  	_, err = r.Instantiate(ctx, bin)
   944  	require.Error(t, err)
   945  	require.Equal(t, expErr.Error(), err.Error())
   946  
   947  	err = r.Close(ctx)
   948  	require.NoError(t, err)
   949  }