github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/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 "testing" 10 11 "github.com/tetratelabs/wazero" 12 "github.com/tetratelabs/wazero/api" 13 "github.com/tetratelabs/wazero/experimental" 14 "github.com/tetratelabs/wazero/experimental/logging" 15 "github.com/tetratelabs/wazero/internal/engine/wazevo/testcases" 16 "github.com/tetratelabs/wazero/internal/leb128" 17 "github.com/tetratelabs/wazero/internal/testing/binaryencoding" 18 "github.com/tetratelabs/wazero/internal/testing/dwarftestdata" 19 "github.com/tetratelabs/wazero/internal/testing/require" 20 "github.com/tetratelabs/wazero/internal/wasm" 21 ) 22 23 const ( 24 i32 = wasm.ValueTypeI32 25 i64 = wasm.ValueTypeI64 26 f32 = wasm.ValueTypeF32 27 f64 = wasm.ValueTypeF64 28 v128 = wasm.ValueTypeV128 29 ) 30 31 func TestE2E(t *testing.T) { 32 tmp := t.TempDir() 33 type callCase struct { 34 funcName string // defaults to testcases.ExportedFunctionName 35 params, expResults []uint64 36 expErr string 37 } 38 for _, tc := range []struct { 39 name string 40 imported, m *wasm.Module 41 calls []callCase 42 features api.CoreFeatures 43 setupMemory func(mem api.Memory) 44 }{ 45 { 46 name: "empty", m: testcases.Empty.Module, 47 calls: []callCase{{expResults: []uint64{}}}, 48 }, 49 { 50 name: "only_return", m: testcases.OnlyReturn.Module, 51 calls: []callCase{{expResults: []uint64{}}}, 52 }, 53 { 54 name: "selects", m: testcases.Selects.Module, 55 calls: []callCase{ 56 { 57 params: []uint64{ 58 0, 1, // i32, 59 200, 100, // i64, 60 uint64(math.Float32bits(3.0)), uint64(math.Float32bits(10.0)), 61 math.Float64bits(-123.4), math.Float64bits(-10000000000.0), 62 }, 63 expResults: []uint64{ 64 1, 65 200, 66 uint64(math.Float32bits(3.0)), 67 math.Float64bits(-123.4), 68 }, 69 }, 70 }, 71 }, 72 { 73 name: "swap", m: testcases.SwapParamAndReturn.Module, 74 calls: []callCase{ 75 {params: []uint64{math.MaxUint32, math.MaxInt32}, expResults: []uint64{math.MaxInt32, math.MaxUint32}}, 76 }, 77 }, 78 { 79 name: "consts", m: testcases.Constants.Module, 80 calls: []callCase{ 81 {expResults: []uint64{1, 2, uint64(math.Float32bits(32.0)), math.Float64bits(64.0)}}, 82 }, 83 }, 84 { 85 name: "unreachable", m: testcases.Unreachable.Module, 86 calls: []callCase{{expErr: "unreachable"}}, 87 }, 88 { 89 name: "add_sub_return", m: testcases.AddSubReturn.Module, 90 calls: []callCase{ 91 { 92 params: []uint64{}, 93 expResults: []uint64{3, 3, 3, 3}, 94 }, 95 }, 96 }, 97 { 98 name: "arithm_return", m: testcases.ArithmReturn.Module, 99 calls: []callCase{ 100 { 101 params: []uint64{ 102 21, 10, 0xf0, 103 21, 10, 0xf0, 104 }, 105 expResults: []uint64{ 106 21 * 10, 21 & 10, 21 | 10, 21 ^ 10, 107 21 << 10, 21 >> 10, 0xf0 >> 10, 108 88080384, 20971520, 109 110 21 * 10, 21 & 10, 21 | 10, 21 ^ 10, 111 21 << 10, 21 >> 10, 0xf0 >> 10, 112 378302368699121664, 20971520, 113 }, 114 }, 115 }, 116 }, 117 { 118 name: "divrem_unsigned_return", m: testcases.DivUReturn32.Module, 119 calls: []callCase{ 120 { 121 params: []uint64{21, 10, 21, 10}, 122 expResults: []uint64{21 / 10, 21 % 10}, 123 }, 124 { 125 params: []uint64{21, 0, 1, 1}, 126 expErr: "wasm error: integer divide by zero", 127 }, 128 { 129 params: []uint64{1, 1, 21, 0}, 130 expErr: "wasm error: integer divide by zero", 131 }, 132 { 133 params: []uint64{ 134 0x80000000, 0xffffffff, 1, 1, 135 }, 136 expResults: []uint64{0, 0}, 137 }, 138 { 139 params: []uint64{ 140 1, 1, 0x80000000, 0xffffffff, 141 }, 142 expResults: []uint64{1, 0x80000000}, 143 }, 144 }, 145 }, 146 { 147 name: "divrem_unsigned_return", m: testcases.DivUReturn64.Module, 148 calls: []callCase{ 149 { 150 params: []uint64{21, 10, 21, 10}, 151 expResults: []uint64{21 / 10, 21 % 10}, 152 }, 153 { 154 params: []uint64{21, 0, 1, 1}, 155 expErr: "wasm error: integer divide by zero", 156 }, 157 { 158 params: []uint64{1, 1, 21, 0}, 159 expErr: "wasm error: integer divide by zero", 160 }, 161 { 162 params: []uint64{0x80000000, 0xffffffff, 1, 1}, 163 expResults: []uint64{0, 0}, 164 }, 165 { 166 params: []uint64{1, 1, 0x80000000, 0xffffffff}, 167 expResults: []uint64{1, 0x80000000}, 168 }, 169 }, 170 }, 171 { 172 name: "divrem_signed_return32", m: testcases.DivSReturn32.Module, 173 calls: []callCase{ 174 { 175 params: []uint64{21, 10, 21, 10}, 176 expResults: []uint64{21 / 10, 21 % 10}, 177 }, 178 { 179 params: []uint64{21, 0, 1, 1}, 180 expErr: "wasm error: integer divide by zero", 181 }, 182 { 183 params: []uint64{1, 1, 21, 0}, 184 expErr: "wasm error: integer divide by zero", 185 }, 186 { 187 params: []uint64{0x80000000, 0xffffffff, 1, 1}, 188 expErr: "wasm error: integer overflow", 189 }, 190 { 191 params: []uint64{1, 1, 0x80000000, 0xffffffff}, 192 expResults: []uint64{1, 0}, 193 }, 194 }, 195 }, 196 197 { 198 name: "divrem_signed_return32 inverted rem div order", m: testcases.DivSReturn32_weird.Module, 199 calls: []callCase{ 200 { 201 params: []uint64{21, 0, 1, 1}, 202 expErr: "wasm error: integer divide by zero", 203 }, 204 { 205 params: []uint64{1, 1, 21, 0}, 206 expErr: "wasm error: integer divide by zero", 207 }, 208 { 209 params: []uint64{0x80000000, 0xffffffff, 1, 1}, 210 expErr: "wasm error: integer overflow", 211 }, 212 { 213 params: []uint64{1, 1, 0x80000000, 0xffffffff}, 214 expResults: []uint64{0, 1}, 215 }, 216 }, 217 }, 218 { 219 name: "divrem_signed_return64", m: testcases.DivSReturn64.Module, 220 calls: []callCase{ 221 { 222 params: []uint64{21, 10, 21, 10}, 223 expResults: []uint64{21 / 10, 21 % 10}, 224 }, 225 { 226 params: []uint64{21, 0, 1, 1}, 227 expErr: "wasm error: integer divide by zero", 228 }, 229 { 230 params: []uint64{1, 1, 21, 0}, 231 expErr: "wasm error: integer divide by zero", 232 }, 233 { 234 params: []uint64{0x8000000000000000, 0xffffffffffffffff, 1, 1}, 235 expErr: "wasm error: integer overflow", 236 }, 237 }, 238 }, 239 { 240 name: "integer bit counts", m: testcases.IntegerBitCounts.Module, 241 calls: []callCase{{ 242 params: []uint64{10, 100}, 243 expResults: []uint64{ 244 28, 1, 2, 57, 2, 3, 245 }, 246 }}, 247 }, 248 { 249 name: "many_params_many_results", 250 m: testcases.ManyParamsManyResults.Module, 251 calls: []callCase{ 252 { 253 params: []uint64{ 254 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 256 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 257 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 258 }, 259 expResults: []uint64{ 260 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 261 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 262 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 263 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 264 }, 265 }, 266 }, 267 }, 268 { 269 name: "float_arithm", m: testcases.FloatArithm.Module, 270 calls: []callCase{ 271 { 272 params: []uint64{ 273 math.Float64bits(25), math.Float64bits(5), math.Float64bits(1.4), 274 uint64(math.Float32bits(25)), uint64(math.Float32bits(5)), uint64(math.Float32bits(1.4)), 275 }, 276 expResults: []uint64{ 277 math.Float64bits(-25), 278 math.Float64bits(25), 279 280 math.Float64bits(5), 281 math.Float64bits(30), 282 math.Float64bits(20), 283 math.Float64bits(125), 284 math.Float64bits(5), 285 286 math.Float64bits(1), 287 math.Float64bits(1), 288 math.Float64bits(2), 289 math.Float64bits(1), 290 291 math.Float64bits(-25), 292 293 uint64(math.Float32bits(-25)), 294 uint64(math.Float32bits(25)), 295 296 uint64(math.Float32bits(5)), 297 uint64(math.Float32bits(30)), 298 uint64(math.Float32bits(20)), 299 uint64(math.Float32bits(125)), 300 uint64(math.Float32bits(5)), 301 302 uint64(math.Float32bits(1)), 303 uint64(math.Float32bits(1)), 304 uint64(math.Float32bits(2)), 305 uint64(math.Float32bits(1)), 306 307 uint64(math.Float32bits(-25)), 308 }, 309 }, 310 }, 311 }, 312 { 313 name: "min_max_float", m: testcases.MinMaxFloat.Module, 314 calls: []callCase{ 315 { 316 params: []uint64{ 317 math.Float64bits(25), math.Float64bits(5), 318 uint64(math.Float32bits(25)), uint64(math.Float32bits(5)), 319 }, 320 expResults: []uint64{ 321 math.Float64bits(5), 322 math.Float64bits(25), 323 uint64(math.Float32bits(5)), 324 uint64(math.Float32bits(25)), 325 }, 326 }, 327 { 328 // Left-hand side is NaN. 329 params: []uint64{ 330 0x7ff8000000000001, math.Float64bits(5), 331 uint64(0x7ff80001), uint64(math.Float32bits(5)), 332 }, 333 expResults: []uint64{ 334 0x7ff8000000000001, 335 0x7ff8000000000001, 336 337 uint64(0x7ff80001), 338 uint64(0x7ff80001), 339 }, 340 }, 341 { 342 // Both NaN. 343 params: []uint64{ 344 0x7ff8000000000001, 0x7ff8000000000001, 345 uint64(0x7ff80001), uint64(0x7ff80001), 346 }, 347 expResults: []uint64{ 348 0x7ff8000000000001, 349 0x7ff8000000000001, 350 351 uint64(0x7ff80001), 352 uint64(0x7ff80001), 353 }, 354 }, 355 { 356 // Negative zero and zero. 357 params: []uint64{ 358 0x8000000000000000, 0, 359 uint64(0), uint64(0x80000000), 360 }, 361 expResults: []uint64{ 362 0x8000000000000000, 363 0, 364 365 uint64(0x80000000), 366 uint64(0), 367 }, 368 }, 369 }, 370 }, 371 { 372 name: "fibonacci_recursive", m: testcases.FibonacciRecursive.Module, 373 calls: []callCase{ 374 {params: []uint64{0}, expResults: []uint64{0}}, 375 {params: []uint64{1}, expResults: []uint64{1}}, 376 {params: []uint64{2}, expResults: []uint64{1}}, 377 {params: []uint64{10}, expResults: []uint64{55}}, 378 {params: []uint64{20}, expResults: []uint64{6765}}, 379 {params: []uint64{30}, expResults: []uint64{0xcb228}}, 380 }, 381 }, 382 {name: "call_simple", m: testcases.CallSimple.Module, calls: []callCase{{expResults: []uint64{40}}}}, 383 {name: "call", m: testcases.Call.Module, calls: []callCase{{expResults: []uint64{45, 45}}}}, 384 { 385 name: "stack overflow", 386 m: &wasm.Module{ 387 TypeSection: []wasm.FunctionType{{}}, 388 FunctionSection: []wasm.Index{0}, 389 CodeSection: []wasm.Code{{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}}, 390 ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Index: 0, Type: wasm.ExternTypeFunc}}, 391 }, 392 calls: []callCase{ 393 {expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"}, {expErr: "stack overflow"}, 394 }, 395 }, 396 { 397 name: "imported_function_call", 398 imported: testcases.ImportedFunctionCall.Imported, 399 m: testcases.ImportedFunctionCall.Module, 400 calls: []callCase{ 401 {params: []uint64{0}, expResults: []uint64{0}}, 402 {params: []uint64{2}, expResults: []uint64{2 * 2}}, 403 {params: []uint64{45}, expResults: []uint64{45 * 45}}, 404 {params: []uint64{90}, expResults: []uint64{90 * 90}}, 405 {params: []uint64{100}, expResults: []uint64{100 * 100}}, 406 {params: []uint64{100, 200}, expResults: []uint64{100 * 200}, funcName: "imported_exported"}, 407 }, 408 }, 409 { 410 name: "memory_store_basic", 411 m: testcases.MemoryStoreBasic.Module, 412 calls: []callCase{ 413 {params: []uint64{0, 0xf}, expResults: []uint64{0xf}}, 414 {params: []uint64{256, 0xff}, expResults: []uint64{0xff}}, 415 {params: []uint64{100, 0xffffffff}, expResults: []uint64{0xffffffff}}, 416 // We load I32, so we can't load from the last 3 bytes. 417 {params: []uint64{uint64(wasm.MemoryPageSize) - 3, 0}, expErr: "out of bounds memory access"}, 418 }, 419 }, 420 { 421 name: "memory_load_basic", 422 m: testcases.MemoryLoadBasic.Module, 423 calls: []callCase{ 424 {params: []uint64{0}, expResults: []uint64{0x03_02_01_00}}, 425 {params: []uint64{256}, expResults: []uint64{0x03_02_01_00}}, 426 {params: []uint64{100}, expResults: []uint64{103<<24 | 102<<16 | 101<<8 | 100}}, 427 // Last 4 bytes. 428 {params: []uint64{uint64(wasm.MemoryPageSize) - 4}, expResults: []uint64{0xfffefdfc}}, 429 }, 430 }, 431 { 432 name: "memory out of bounds", 433 m: testcases.MemoryLoadBasic.Module, 434 calls: []callCase{ 435 {params: []uint64{uint64(wasm.MemoryPageSize)}, expErr: "out of bounds memory access"}, 436 // We load I32, so we can't load from the last 3 bytes. 437 {params: []uint64{uint64(wasm.MemoryPageSize) - 3}, expErr: "out of bounds memory access"}, 438 }, 439 }, 440 { 441 name: "memory_loads", 442 m: testcases.MemoryLoads.Module, 443 calls: []callCase{ 444 {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}}, 445 {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}}, 446 {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}}, 447 {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}}, 448 {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}}, 449 {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}}, 450 {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}}, 451 {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}}, 452 }, 453 }, 454 { 455 name: "globals_get", 456 m: testcases.GlobalsGet.Module, 457 calls: []callCase{ 458 {expResults: []uint64{0x80000000, 0x8000000000000000, 0x7f7fffff, 0x7fefffffffffffff, 1234, 5678}}, 459 }, 460 }, 461 { 462 name: "globals_set", 463 m: testcases.GlobalsSet.Module, 464 calls: []callCase{{expResults: []uint64{ 465 1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0), 10, 20, 466 }}}, 467 }, 468 { 469 name: "globals_mutable", 470 m: testcases.GlobalsMutable.Module, 471 calls: []callCase{{expResults: []uint64{ 472 100, 200, uint64(math.Float32bits(300.0)), math.Float64bits(400.0), 473 1, 2, uint64(math.Float32bits(3.0)), math.Float64bits(4.0), 474 }}}, 475 }, 476 { 477 name: "memory_size_grow", 478 m: testcases.MemorySizeGrow.Module, 479 calls: []callCase{{expResults: []uint64{1, 2, 0xffffffff}}}, 480 }, 481 { 482 name: "imported_memory_grow", 483 imported: testcases.ImportedMemoryGrow.Imported, 484 m: testcases.ImportedMemoryGrow.Module, 485 calls: []callCase{{expResults: []uint64{1, 1, 11, 11}}}, 486 }, 487 { 488 name: "call_indirect", 489 m: testcases.CallIndirect.Module, 490 // parameter == table offset. 491 calls: []callCase{ 492 {params: []uint64{0}, expErr: "indirect call type mismatch"}, 493 {params: []uint64{1}, expResults: []uint64{10}}, 494 {params: []uint64{2}, expErr: "indirect call type mismatch"}, 495 {params: []uint64{10}, expErr: "invalid table access"}, // Null pointer. 496 {params: []uint64{math.MaxUint32}, expErr: "invalid table access"}, // Out of bounds. 497 }, 498 }, 499 { 500 name: "br_table", 501 m: testcases.BrTable.Module, 502 calls: []callCase{ 503 {params: []uint64{0}, expResults: []uint64{11}}, 504 {params: []uint64{1}, expResults: []uint64{12}}, 505 {params: []uint64{2}, expResults: []uint64{13}}, 506 {params: []uint64{3}, expResults: []uint64{14}}, 507 {params: []uint64{4}, expResults: []uint64{15}}, 508 {params: []uint64{5}, expResults: []uint64{16}}, 509 // Out of range --> default. 510 {params: []uint64{6}, expResults: []uint64{11}}, 511 {params: []uint64{1000}, expResults: []uint64{11}}, 512 }, 513 }, 514 { 515 name: "br_table_with_args", 516 m: testcases.BrTableWithArg.Module, 517 calls: []callCase{ 518 {params: []uint64{0, 100}, expResults: []uint64{11 + 100}}, 519 {params: []uint64{1, 100}, expResults: []uint64{12 + 100}}, 520 {params: []uint64{2, 100}, expResults: []uint64{13 + 100}}, 521 {params: []uint64{3, 100}, expResults: []uint64{14 + 100}}, 522 {params: []uint64{4, 100}, expResults: []uint64{15 + 100}}, 523 {params: []uint64{5, 100}, expResults: []uint64{16 + 100}}, 524 // Out of range --> default. 525 {params: []uint64{6, 200}, expResults: []uint64{11 + 200}}, 526 {params: []uint64{1000, 300}, expResults: []uint64{11 + 300}}, 527 }, 528 }, 529 { 530 name: "multi_predecessor_local_ref", 531 m: testcases.MultiPredecessorLocalRef.Module, 532 calls: []callCase{ 533 {params: []uint64{0, 100}, expResults: []uint64{100}}, 534 {params: []uint64{1, 100}, expResults: []uint64{1}}, 535 {params: []uint64{1, 200}, expResults: []uint64{1}}, 536 }, 537 }, 538 { 539 name: "vector_bit_select", 540 m: testcases.VecBitSelect.Module, 541 calls: []callCase{ 542 {params: []uint64{1, 2, 3, 4, 5, 6}, expResults: []uint64{0x3, 0x2, 0x5, 0x6}}, 543 }, 544 }, 545 { 546 name: "vector_shuffle", 547 m: testcases.VecShuffle.Module, 548 calls: []callCase{ 549 {params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x01010101, 0x04040404}}, 550 {params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x03030303, 0x02020202}}, 551 {params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x00000000, 0x00000000}}, 552 {params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffff, 0xffffffff}}, 553 {params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x00000000, 0xffffffff}}, 554 }, 555 }, 556 { 557 name: "vector_shuffle (1st only)", 558 m: testcases.VecShuffleWithLane(1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0), 559 calls: []callCase{ 560 {params: []uint64{0x0000000000000b0a, 0x0c0000, 0xffffffffffffffff, 0xffffffffffffffff}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}}, 561 {params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x0101010101010101, 0x101010102020202}}, 562 {params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x0303030303030303, 0x303030304040404}}, 563 {params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0x0000000000000000, 0x0000000ffffffff}}, 564 {params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}}, 565 {params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0x0000000011111111}}, 566 }, 567 }, 568 { 569 name: "vector_shuffle (2nd only)", 570 m: testcases.VecShuffleWithLane(17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16), 571 calls: []callCase{ 572 {params: []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0x0000000000000b0a, 0x0c0000}, expResults: []uint64{0x0a0a0a0a0b0b0b0b, 0x0a0a0a0a0c0c0c0c}}, 573 {params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x303030303030303, 0x303030304040404}}, 574 {params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x101010101010101, 0x101010102020202}}, 575 {params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xffffffffffffffff, 0xffffffff00000000}}, 576 {params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0x0000000000000000, 0xffffffff}}, 577 {params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x1111111111111111, 0x11111111ffffffff}}, 578 }, 579 }, 580 { 581 name: "vector_shuffle (mixed)", 582 m: testcases.VecShuffleWithLane(0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31), 583 calls: []callCase{ 584 {params: []uint64{0xff08ff07ff06ff05, 0xff04ff03ff02ff01, 0x18ff17ff16ff15ff, 0x14ff13ff12ff11ff}, expResults: []uint64{0x1808170716061505, 0x1404130312021101}}, 585 {params: []uint64{0x01010101, 0x02020202, 0x03030303, 0x04040404}, expResults: []uint64{0x3010301, 0x4020402}}, 586 {params: []uint64{0x03030303, 0x04040404, 0x01010101, 0x02020202}, expResults: []uint64{0x1030103, 0x2040204}}, 587 {params: []uint64{0x00000000, 0xffffffff, 0xffffffff, 0x00000000}, expResults: []uint64{0xff00ff00, 0xff00ff}}, 588 {params: []uint64{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}, expResults: []uint64{0xff00ff, 0xff00ff00}}, 589 {params: []uint64{0x00000000, 0x11111111, 0x11111111, 0xffffffff}, expResults: []uint64{0x11001100, 0xff11ff11}}, 590 }, 591 }, 592 { 593 name: "memory_wait32", 594 m: testcases.MemoryWait32.Module, 595 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 596 calls: []callCase{ 597 {params: []uint64{0x0, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1 598 {params: []uint64{0x1, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 599 {params: []uint64{0x2, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 600 {params: []uint64{0x3, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 601 {params: []uint64{0x4, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1 602 603 {params: []uint64{0xffffffff, 0xbeef, 0xffffffff}, expErr: "out of bounds memory access"}, 604 }, 605 }, 606 { 607 name: "memory_wait64", 608 m: testcases.MemoryWait64.Module, 609 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 610 calls: []callCase{ 611 {params: []uint64{0x0, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1 612 {params: []uint64{0x1, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 613 {params: []uint64{0x2, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 614 {params: []uint64{0x3, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 615 {params: []uint64{0x4, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 616 {params: []uint64{0x5, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 617 {params: []uint64{0x6, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 618 {params: []uint64{0x7, 0xbeef, 0xffffffff}, expErr: "unaligned atomic"}, 619 {params: []uint64{0x8, 0xbeef, 0xffffffff}, expResults: []uint64{1}}, // exp not equal, returns 1 620 621 {params: []uint64{0xffffffff, 0xbeef, 0xffffffff}, expErr: "out of bounds memory access"}, 622 }, 623 }, 624 { 625 name: "memory_notify", 626 m: testcases.MemoryNotify.Module, 627 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 628 calls: []callCase{ 629 {params: []uint64{0x0, 0x1}, expResults: []uint64{0}}, // no waiters, returns 0 630 {params: []uint64{0x1, 0x1}, expErr: "unaligned atomic"}, 631 {params: []uint64{0x2, 0x1}, expErr: "unaligned atomic"}, 632 {params: []uint64{0x3, 0x1}, expErr: "unaligned atomic"}, 633 {params: []uint64{0x4, 0x1}, expResults: []uint64{0}}, // no waiters, returns 0 634 }, 635 }, 636 { 637 name: "atomic_rmw_add", 638 m: testcases.AtomicRmwAdd.Module, 639 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 640 calls: []callCase{ 641 {params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{0, 0, 0, 0, 0, 0, 0}}, 642 {params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{1, 2, 3, 4, 5, 6, 7}}, 643 {params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{2, 4, 6, 8, 10, 12, 14}}, 644 }, 645 }, 646 { 647 name: "atomic_rmw_sub", 648 m: testcases.AtomicRmwSub.Module, 649 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 650 calls: []callCase{ 651 {params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{0, 0, 0, 0, 0, 0, 0}}, 652 { 653 params: []uint64{1, 2, 3, 4, 5, 6, 7}, 654 expResults: []uint64{ 655 api.EncodeI32(-1) & 0xff, 656 api.EncodeI32(-2) & 0xffff, 657 api.EncodeI32(-3), 658 api.EncodeI64(-4) & 0xff, 659 api.EncodeI64(-5) & 0xffff, 660 api.EncodeI64(-6) & 0xffffffff, 661 api.EncodeI64(-7), 662 }, 663 }, 664 { 665 params: []uint64{1, 2, 3, 4, 5, 6, 7}, 666 expResults: []uint64{ 667 api.EncodeI32(-2) & 0xff, 668 api.EncodeI32(-4) & 0xffff, 669 api.EncodeI32(-6), 670 api.EncodeI64(-8) & 0xff, 671 api.EncodeI64(-10) & 0xffff, 672 api.EncodeI64(-12) & 0xffffffff, 673 api.EncodeI64(-14), 674 }, 675 }, 676 }, 677 }, 678 { 679 name: "atomic_rmw_and", 680 m: testcases.AtomicRmwAnd.Module, 681 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 682 setupMemory: func(mem api.Memory) { 683 mem.WriteUint32Le(0, 0xffffffff) 684 mem.WriteUint32Le(8, 0xffffffff) 685 mem.WriteUint32Le(16, 0xffffffff) 686 mem.WriteUint64Le(24, 0xffffffffffffffff) 687 mem.WriteUint64Le(32, 0xffffffffffffffff) 688 mem.WriteUint64Le(40, 0xffffffffffffffff) 689 mem.WriteUint64Le(48, 0xffffffffffffffff) 690 }, 691 calls: []callCase{ 692 { 693 params: []uint64{0xfffffffe, 0xfffffffe, 0xfffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe, 0xfffffffffffffffe}, 694 expResults: []uint64{0xff, 0xffff, 0xffffffff, 0xff, 0xffff, 0xffffffff, 0xffffffffffffffff}, 695 }, 696 { 697 params: []uint64{0xffffffee, 0xffffffee, 0xffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee}, 698 expResults: []uint64{0xfe, 0xfffe, 0xfffffffe, 0xfe, 0xfffe, 0xfffffffe, 0xfffffffffffffffe}, 699 }, 700 { 701 params: []uint64{0xffffffee, 0xffffffee, 0xffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee, 0xffffffffffffffee}, 702 expResults: []uint64{0xee, 0xffee, 0xffffffee, 0xee, 0xffee, 0xffffffee, 0xffffffffffffffee}, 703 }, 704 }, 705 }, 706 { 707 name: "atomic_rmw_or", 708 m: testcases.AtomicRmwOr.Module, 709 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 710 calls: []callCase{ 711 { 712 params: []uint64{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, 713 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 714 }, 715 { 716 params: []uint64{0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf}, 717 expResults: []uint64{0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}, 718 }, 719 { 720 params: []uint64{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 721 expResults: []uint64{0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf}, 722 }, 723 }, 724 }, 725 { 726 name: "atomic_rmw_xor", 727 m: testcases.AtomicRmwXor.Module, 728 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 729 calls: []callCase{ 730 { 731 params: []uint64{0, 0, 0, 0, 0, 0, 0}, 732 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 733 }, 734 { 735 params: []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, 736 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 737 }, 738 { 739 params: []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, 740 expResults: []uint64{0xff, 0xffff, 0xffffffff, 0xff, 0xffff, 0xffffffff, 0xffffffffffffffff}, 741 }, 742 { 743 params: []uint64{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}, 744 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 745 }, 746 }, 747 }, 748 { 749 name: "atomic_rmw_xchg", 750 m: testcases.AtomicRmwXchg.Module, 751 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 752 calls: []callCase{ 753 { 754 params: []uint64{1, 2, 3, 4, 5, 6, 7}, 755 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 756 }, 757 { 758 params: []uint64{2, 3, 4, 5, 6, 7, 8}, 759 expResults: []uint64{1, 2, 3, 4, 5, 6, 7}, 760 }, 761 { 762 params: []uint64{2, 3, 4, 5, 6, 7, 8}, 763 expResults: []uint64{2, 3, 4, 5, 6, 7, 8}, 764 }, 765 }, 766 }, 767 { 768 name: "atomic_cas", 769 m: testcases.AtomicCas.Module, 770 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 771 calls: []callCase{ 772 // no store 773 { 774 params: []uint64{1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2}, 775 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 776 }, 777 // store 778 { 779 params: []uint64{0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2}, 780 expResults: []uint64{0, 0, 0, 0, 0, 0, 0}, 781 }, 782 // store 783 { 784 params: []uint64{2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3}, 785 expResults: []uint64{2, 2, 2, 2, 2, 2, 2}, 786 }, 787 // no store 788 { 789 params: []uint64{2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4}, 790 expResults: []uint64{3, 3, 3, 3, 3, 3, 3}, 791 }, 792 }, 793 }, 794 { 795 // Checks if load works when comparison value is zero. It wouldn't if 796 // the zero register gets used. 797 name: "atomic_cas_const0", 798 m: testcases.AtomicCasConst0.Module, 799 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 800 setupMemory: func(mem api.Memory) { 801 mem.WriteUint32Le(0, 1) 802 mem.WriteUint32Le(8, 2) 803 mem.WriteUint32Le(16, 3) 804 mem.WriteUint64Le(24, 4) 805 mem.WriteUint64Le(32, 5) 806 mem.WriteUint64Le(40, 6) 807 mem.WriteUint64Le(48, 7) 808 }, 809 calls: []callCase{ 810 { 811 params: []uint64{8, 9, 10, 11, 12, 13, 14}, 812 expResults: []uint64{1, 2, 3, 4, 5, 6, 7}, 813 }, 814 }, 815 }, 816 { 817 name: "atomic_store_load", 818 m: testcases.AtomicStoreLoad.Module, 819 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 820 calls: []callCase{ 821 {params: []uint64{1, 2, 3, 4, 5, 6, 7}, expResults: []uint64{1, 2, 3, 4, 5, 6, 7}}, 822 {params: []uint64{10, 20, 30, 40, 50, 60, 70}, expResults: []uint64{10, 20, 30, 40, 50, 60, 70}}, 823 }, 824 }, 825 { 826 name: "atomic_fence", 827 m: testcases.AtomicFence.Module, 828 features: api.CoreFeaturesV2 | experimental.CoreFeaturesThreads, 829 calls: []callCase{ 830 {params: []uint64{}, expResults: []uint64{}}, 831 }, 832 }, 833 { 834 name: "float_le", 835 m: testcases.FloatLe.Module, 836 calls: []callCase{ 837 {params: []uint64{math.Float64bits(1.0)}, expResults: []uint64{1, 1}}, 838 {params: []uint64{math.Float64bits(0.0)}, expResults: []uint64{1, 1}}, 839 {params: []uint64{math.Float64bits(1.1)}, expResults: []uint64{0, 0}}, 840 {params: []uint64{math.Float64bits(math.NaN())}, expResults: []uint64{0, 0}}, 841 }, 842 }, 843 } { 844 tc := tc 845 t.Run(tc.name, func(t *testing.T) { 846 for i := 0; i < 2; i++ { 847 var name string 848 if i == 0 { 849 name = "no cache" 850 } else { 851 name = "with cache" 852 } 853 t.Run(name, func(t *testing.T) { 854 cache, err := wazero.NewCompilationCacheWithDir(tmp) 855 require.NoError(t, err) 856 config := wazero.NewRuntimeConfigCompiler().WithCompilationCache(cache) 857 if tc.features != 0 { 858 config = config.WithCoreFeatures(tc.features) 859 } 860 861 ctx := context.Background() 862 r := wazero.NewRuntimeWithConfig(ctx, config) 863 defer func() { 864 require.NoError(t, r.Close(ctx)) 865 }() 866 867 if tc.imported != nil { 868 imported, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.imported)) 869 require.NoError(t, err) 870 871 _, err = r.InstantiateModule(ctx, imported, wazero.NewModuleConfig()) 872 require.NoError(t, err) 873 } 874 875 compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(tc.m)) 876 require.NoError(t, err) 877 878 inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 879 require.NoError(t, err) 880 881 if tc.setupMemory != nil { 882 tc.setupMemory(inst.Memory()) 883 } 884 885 for _, cc := range tc.calls { 886 name := cc.funcName 887 if name == "" { 888 name = testcases.ExportedFunctionName 889 } 890 t.Run(fmt.Sprintf("call_%s%v", name, cc.params), func(t *testing.T) { 891 f := inst.ExportedFunction(name) 892 require.NotNil(t, f) 893 result, err := f.Call(ctx, cc.params...) 894 if cc.expErr != "" { 895 require.Contains(t, err.Error(), cc.expErr) 896 } else { 897 require.NoError(t, err) 898 require.Equal(t, len(cc.expResults), len(result)) 899 for i := range cc.expResults { 900 if cc.expResults[i] != result[i] { 901 t.Errorf("result[%d]: exp %x, got %x", i, cc.expResults[i], result[i]) 902 } 903 } 904 } 905 }) 906 } 907 }) 908 } 909 }) 910 } 911 } 912 913 func TestE2E_host_functions(t *testing.T) { 914 var buf bytes.Buffer 915 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 916 917 for _, tc := range []struct { 918 name string 919 ctx context.Context 920 }{ 921 {name: "listener", ctx: ctx}, 922 {name: "no listener", ctx: context.Background()}, 923 } { 924 tc := tc 925 t.Run(tc.name, func(t *testing.T) { 926 ctx := tc.ctx 927 928 config := wazero.NewRuntimeConfigCompiler() 929 930 r := wazero.NewRuntimeWithConfig(ctx, config) 931 defer func() { 932 require.NoError(t, r.Close(ctx)) 933 }() 934 935 var expectedMod api.Module 936 937 b := r.NewHostModuleBuilder("env") 938 b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, d float64) float64 { 939 require.Equal(t, ctx, ctx2) 940 require.Equal(t, 35.0, d) 941 return math.Sqrt(d) 942 }).Export("root") 943 b.NewFunctionBuilder().WithFunc(func(ctx2 context.Context, mod api.Module, a uint32, b uint64, c float32, d float64) (uint32, uint64, float32, float64) { 944 require.Equal(t, expectedMod, mod) 945 require.Equal(t, ctx, ctx2) 946 require.Equal(t, uint32(2), a) 947 require.Equal(t, uint64(100), b) 948 require.Equal(t, float32(15.0), c) 949 require.Equal(t, 35.0, d) 950 return a * a, b * b, c * c, d * d 951 }).Export("square") 952 953 _, err := b.Instantiate(ctx) 954 require.NoError(t, err) 955 956 m := &wasm.Module{ 957 ImportFunctionCount: 2, 958 ImportSection: []wasm.Import{ 959 {Module: "env", Name: "root", Type: wasm.ExternTypeFunc, DescFunc: 0}, 960 {Module: "env", Name: "square", Type: wasm.ExternTypeFunc, DescFunc: 1}, 961 }, 962 TypeSection: []wasm.FunctionType{ 963 {Results: []wasm.ValueType{f64}, Params: []wasm.ValueType{f64}}, 964 {Results: []wasm.ValueType{i32, i64, f32, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}}, 965 {Results: []wasm.ValueType{i32, i64, f32, f64, f64}, Params: []wasm.ValueType{i32, i64, f32, f64}}, 966 }, 967 FunctionSection: []wasm.Index{2}, 968 CodeSection: []wasm.Code{{ 969 Body: []byte{ 970 wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeLocalGet, 2, wasm.OpcodeLocalGet, 3, 971 wasm.OpcodeCall, 1, 972 wasm.OpcodeLocalGet, 3, 973 wasm.OpcodeCall, 0, 974 wasm.OpcodeEnd, 975 }, 976 }}, 977 ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 2}}, 978 } 979 980 compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(m)) 981 require.NoError(t, err) 982 983 inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 984 require.NoError(t, err) 985 986 expectedMod = inst 987 988 f := inst.ExportedFunction(testcases.ExportedFunctionName) 989 990 res, err := f.Call(ctx, []uint64{2, 100, uint64(math.Float32bits(15.0)), math.Float64bits(35.0)}...) 991 require.NoError(t, err) 992 require.Equal(t, []uint64{ 993 2 * 2, 100 * 100, uint64(math.Float32bits(15.0 * 15.0)), math.Float64bits(35.0 * 35.0), 994 math.Float64bits(math.Sqrt(35.0)), 995 }, res) 996 }) 997 } 998 999 require.Equal(t, ` 1000 --> .$2(2,100,15,35) 1001 ==> env.square(2,100,15,35) 1002 <== (4,10000,225,1225) 1003 ==> env.root(35) 1004 <== 5.916079783099616 1005 <-- (4,10000,225,1225,5.916079783099616) 1006 `, "\n"+buf.String()) 1007 } 1008 1009 func TestE2E_stores(t *testing.T) { 1010 config := wazero.NewRuntimeConfigCompiler() 1011 1012 ctx := context.Background() 1013 r := wazero.NewRuntimeWithConfig(ctx, config) 1014 defer func() { 1015 require.NoError(t, r.Close(ctx)) 1016 }() 1017 1018 compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.MemoryStores.Module)) 1019 require.NoError(t, err) 1020 1021 inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 1022 require.NoError(t, err) 1023 1024 f := inst.ExportedFunction(testcases.ExportedFunctionName) 1025 1026 mem, ok := inst.Memory().Read(0, wasm.MemoryPageSize) 1027 require.True(t, ok) 1028 for _, tc := range []struct { 1029 i32 uint32 1030 i64 uint64 1031 f32 float32 1032 f64 float64 1033 }{ 1034 {0, 0, 0, 0}, 1035 {1, 2, 3.0, 4.0}, 1036 {math.MaxUint32, math.MaxUint64, float32(math.NaN()), math.NaN()}, 1037 {1 << 31, 1 << 63, 3.0, 4.0}, 1038 } { 1039 t.Run(fmt.Sprintf("i32=%#x,i64=%#x,f32=%#x,f64=%#x", tc.i32, tc.i64, tc.f32, tc.f64), func(t *testing.T) { 1040 _, err = f.Call(ctx, []uint64{uint64(tc.i32), tc.i64, uint64(math.Float32bits(tc.f32)), math.Float64bits(tc.f64)}...) 1041 require.NoError(t, err) 1042 1043 offset := 0 1044 require.Equal(t, binary.LittleEndian.Uint32(mem[offset:]), tc.i32) 1045 offset += 8 1046 require.Equal(t, binary.LittleEndian.Uint64(mem[offset:]), tc.i64) 1047 offset += 8 1048 require.Equal(t, math.Float32bits(tc.f32), binary.LittleEndian.Uint32(mem[offset:])) 1049 offset += 8 1050 require.Equal(t, math.Float64bits(tc.f64), binary.LittleEndian.Uint64(mem[offset:])) 1051 offset += 8 1052 1053 // i32.store_8 1054 view := binary.LittleEndian.Uint64(mem[offset:]) 1055 require.Equal(t, uint64(tc.i32)&0xff, view) 1056 offset += 8 1057 // i32.store_16 1058 view = binary.LittleEndian.Uint64(mem[offset:]) 1059 require.Equal(t, uint64(tc.i32)&0xffff, view) 1060 offset += 8 1061 // i64.store_8 1062 view = binary.LittleEndian.Uint64(mem[offset:]) 1063 require.Equal(t, tc.i64&0xff, view) 1064 offset += 8 1065 // i64.store_16 1066 view = binary.LittleEndian.Uint64(mem[offset:]) 1067 require.Equal(t, tc.i64&0xffff, view) 1068 offset += 8 1069 // i64.store_32 1070 view = binary.LittleEndian.Uint64(mem[offset:]) 1071 require.Equal(t, tc.i64&0xffffffff, view) 1072 }) 1073 } 1074 } 1075 1076 func TestE2E_reexported_memory(t *testing.T) { 1077 m1 := &wasm.Module{ 1078 ExportSection: []wasm.Export{{Name: "mem", Type: wasm.ExternTypeMemory, Index: 0}}, 1079 MemorySection: &wasm.Memory{Min: 1}, 1080 NameSection: &wasm.NameSection{ModuleName: "m1"}, 1081 } 1082 m2 := &wasm.Module{ 1083 ImportMemoryCount: 1, 1084 ExportSection: []wasm.Export{{Name: "mem2", Type: wasm.ExternTypeMemory, Index: 0}}, 1085 ImportSection: []wasm.Import{{Module: "m1", Name: "mem", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}}, 1086 NameSection: &wasm.NameSection{ModuleName: "m2"}, 1087 } 1088 m3 := &wasm.Module{ 1089 ImportMemoryCount: 1, 1090 ImportSection: []wasm.Import{{Module: "m2", Name: "mem2", Type: wasm.ExternTypeMemory, DescMem: &wasm.Memory{Min: 1}}}, 1091 TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}}}, 1092 ExportSection: []wasm.Export{{Name: testcases.ExportedFunctionName, Type: wasm.ExternTypeFunc, Index: 0}}, 1093 FunctionSection: []wasm.Index{0}, 1094 CodeSection: []wasm.Code{{Body: []byte{wasm.OpcodeI32Const, 10, wasm.OpcodeMemoryGrow, 0, wasm.OpcodeEnd}}}, 1095 } 1096 1097 config := wazero.NewRuntimeConfigCompiler() 1098 1099 ctx := context.Background() 1100 r := wazero.NewRuntimeWithConfig(ctx, config) 1101 defer func() { 1102 require.NoError(t, r.Close(ctx)) 1103 }() 1104 1105 m1Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m1)) 1106 require.NoError(t, err) 1107 1108 m2Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m2)) 1109 require.NoError(t, err) 1110 1111 m3Inst, err := r.Instantiate(ctx, binaryencoding.EncodeModule(m3)) 1112 require.NoError(t, err) 1113 1114 f := m3Inst.ExportedFunction(testcases.ExportedFunctionName) 1115 result, err := f.Call(ctx) 1116 require.NoError(t, err) 1117 require.Equal(t, uint64(1), result[0]) 1118 mem := m1Inst.Memory() 1119 require.Equal(t, mem, m3Inst.Memory()) 1120 require.Equal(t, mem, m2Inst.Memory()) 1121 require.Equal(t, uint32(11), mem.Size()/65536) 1122 } 1123 1124 func TestStackUnwind_panic_in_host(t *testing.T) { 1125 unreachable := &wasm.Module{ 1126 ImportFunctionCount: 1, 1127 ImportSection: []wasm.Import{{Module: "host", Name: "cause_unreachable", Type: wasm.ExternTypeFunc, DescFunc: 0}}, 1128 TypeSection: []wasm.FunctionType{{}}, 1129 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}}, 1130 FunctionSection: []wasm.Index{0, 0, 0}, 1131 CodeSection: []wasm.Code{ 1132 {Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}}, 1133 {Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}}, 1134 {Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // call host.cause_unreachable. 1135 }, 1136 NameSection: &wasm.NameSection{ 1137 FunctionNames: wasm.NameMap{ 1138 wasm.NameAssoc{Index: 0, Name: "host.unreachable"}, 1139 wasm.NameAssoc{Index: 1, Name: "main"}, 1140 wasm.NameAssoc{Index: 2, Name: "one"}, 1141 wasm.NameAssoc{Index: 3, Name: "two"}, 1142 }, 1143 }, 1144 } 1145 1146 config := wazero.NewRuntimeConfigCompiler() 1147 1148 ctx := context.Background() 1149 r := wazero.NewRuntimeWithConfig(ctx, config) 1150 defer func() { 1151 require.NoError(t, r.Close(ctx)) 1152 }() 1153 1154 callUnreachable := func() { 1155 panic("panic in host function") 1156 } 1157 1158 _, err := r.NewHostModuleBuilder("host"). 1159 NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable"). 1160 Instantiate(ctx) 1161 require.NoError(t, err) 1162 1163 module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable)) 1164 require.NoError(t, err) 1165 defer module.Close(ctx) 1166 1167 _, err = module.ExportedFunction("main").Call(ctx) 1168 exp := `panic in host function (recovered by wazero) 1169 wasm stack trace: 1170 host.cause_unreachable() 1171 .two() 1172 .one() 1173 .main()` 1174 require.Equal(t, exp, err.Error()) 1175 } 1176 1177 func TestStackUnwind_unreachable(t *testing.T) { 1178 unreachable := &wasm.Module{ 1179 TypeSection: []wasm.FunctionType{{}}, 1180 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}}, 1181 FunctionSection: []wasm.Index{0, 0, 0}, 1182 CodeSection: []wasm.Code{ 1183 {Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}}, 1184 {Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}}, 1185 {Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}}, 1186 }, 1187 NameSection: &wasm.NameSection{ 1188 FunctionNames: wasm.NameMap{ 1189 wasm.NameAssoc{Index: 0, Name: "main"}, 1190 wasm.NameAssoc{Index: 1, Name: "one"}, 1191 wasm.NameAssoc{Index: 2, Name: "two"}, 1192 }, 1193 }, 1194 } 1195 1196 config := wazero.NewRuntimeConfigCompiler() 1197 ctx := context.Background() 1198 r := wazero.NewRuntimeWithConfig(ctx, config) 1199 defer func() { 1200 require.NoError(t, r.Close(ctx)) 1201 }() 1202 1203 module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable)) 1204 require.NoError(t, err) 1205 defer module.Close(ctx) 1206 1207 _, err = module.ExportedFunction("main").Call(ctx) 1208 exp := `wasm error: unreachable 1209 wasm stack trace: 1210 .two() 1211 .one() 1212 .main()` 1213 require.Equal(t, exp, err.Error()) 1214 } 1215 1216 func TestListener_local(t *testing.T) { 1217 var buf bytes.Buffer 1218 config := wazero.NewRuntimeConfigCompiler() 1219 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1220 1221 r := wazero.NewRuntimeWithConfig(ctx, config) 1222 defer func() { 1223 require.NoError(t, r.Close(ctx)) 1224 }() 1225 1226 compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.CallIndirect.Module)) 1227 require.NoError(t, err) 1228 1229 inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 1230 require.NoError(t, err) 1231 1232 res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 1) 1233 require.NoError(t, err) 1234 require.Equal(t, []uint64{10}, res) 1235 1236 require.Equal(t, ` 1237 --> .$0(1) 1238 --> .$2() 1239 <-- 10 1240 <-- 10 1241 `, "\n"+buf.String()) 1242 } 1243 1244 func TestListener_imported(t *testing.T) { 1245 var buf bytes.Buffer 1246 config := wazero.NewRuntimeConfigCompiler() 1247 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1248 1249 r := wazero.NewRuntimeWithConfig(ctx, config) 1250 defer func() { 1251 require.NoError(t, r.Close(ctx)) 1252 }() 1253 1254 _, err := r.Instantiate(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Imported)) 1255 require.NoError(t, err) 1256 1257 compiled, err := r.CompileModule(ctx, binaryencoding.EncodeModule(testcases.ImportedFunctionCall.Module)) 1258 require.NoError(t, err) 1259 1260 inst, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) 1261 require.NoError(t, err) 1262 1263 res, err := inst.ExportedFunction(testcases.ExportedFunctionName).Call(ctx, 100) 1264 require.NoError(t, err) 1265 require.Equal(t, []uint64{10000}, res) 1266 1267 require.Equal(t, ` 1268 --> .$1(100) 1269 --> env.$0(100,100) 1270 <-- 10000 1271 <-- 10000 1272 `, "\n"+buf.String()) 1273 } 1274 1275 func TestListener_long(t *testing.T) { 1276 pickOneParam := binaryencoding.EncodeModule(&wasm.Module{ 1277 TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}, Params: []wasm.ValueType{ 1278 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1279 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1280 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1281 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1282 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1283 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1284 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1285 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1286 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1287 i32, i32, f32, f64, i64, i32, i32, v128, f32, 1288 }}}, 1289 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}}, 1290 FunctionSection: []wasm.Index{0}, 1291 CodeSection: []wasm.Code{ 1292 {Body: []byte{wasm.OpcodeLocalGet, 10, wasm.OpcodeEnd}}, 1293 }, 1294 }) 1295 1296 var buf bytes.Buffer 1297 config := wazero.NewRuntimeConfigCompiler() 1298 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1299 1300 r := wazero.NewRuntimeWithConfig(ctx, config) 1301 defer func() { 1302 require.NoError(t, r.Close(ctx)) 1303 }() 1304 1305 inst, err := r.Instantiate(ctx, pickOneParam) 1306 require.NoError(t, err) 1307 1308 f := inst.ExportedFunction("main") 1309 require.NotNil(t, f) 1310 param := make([]uint64, 100) 1311 for i := range param { 1312 param[i] = uint64(i) 1313 } 1314 res, err := f.Call(ctx, param...) 1315 require.NoError(t, err) 1316 require.Equal(t, []uint64{0xb}, res) 1317 1318 require.Equal(t, ` 1319 --> .$0(0,1,3e-45,1.5e-323,4,5,6,00000000000000070000000000000008,1.3e-44,10,11,1.7e-44,6.4e-323,14,15,16,00000000000000110000000000000012,2.7e-44,20,21,3.1e-44,1.14e-322,24,25,26,000000000000001b000000000000001c,4e-44,30,31,4.5e-44,1.63e-322,34,35,36,00000000000000250000000000000026,5.5e-44,40,41,5.9e-44,2.1e-322,44,45,46,000000000000002f0000000000000030,6.9e-44,50,51,7.3e-44,2.6e-322,54,55,56,0000000000000039000000000000003a,8.3e-44,60,61,8.7e-44,3.1e-322,64,65,66,00000000000000430000000000000044,9.7e-44,70,71,1.01e-43,3.6e-322,74,75,76,000000000000004d000000000000004e,1.11e-43,80,81,1.15e-43,4.1e-322,84,85,86,00000000000000570000000000000058,1.25e-43,90,91,1.29e-43,4.6e-322,94,95,96,00000000000000610000000000000062,1.39e-43) 1320 <-- 11 1321 `, "\n"+buf.String()) 1322 } 1323 1324 func TestListener_long_as_is(t *testing.T) { 1325 params := []wasm.ValueType{ 1326 i32, i64, i32, i64, i32, i64, i32, i64, i32, i64, 1327 i32, i64, i32, i64, i32, i64, i32, i64, i32, i64, 1328 } 1329 1330 const paramNum = 20 1331 1332 var body []byte 1333 for i := 0; i < paramNum; i++ { 1334 body = append(body, wasm.OpcodeLocalGet) 1335 body = append(body, leb128.EncodeUint32(uint32(i))...) 1336 } 1337 body = append(body, wasm.OpcodeEnd) 1338 1339 bin := binaryencoding.EncodeModule(&wasm.Module{ 1340 TypeSection: []wasm.FunctionType{{Results: params, Params: params}}, 1341 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}}, 1342 FunctionSection: []wasm.Index{0}, 1343 CodeSection: []wasm.Code{{Body: body}}, 1344 }) 1345 1346 var buf bytes.Buffer 1347 config := wazero.NewRuntimeConfigCompiler() 1348 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1349 1350 r := wazero.NewRuntimeWithConfig(ctx, config) 1351 defer func() { 1352 require.NoError(t, r.Close(ctx)) 1353 }() 1354 1355 inst, err := r.Instantiate(ctx, bin) 1356 require.NoError(t, err) 1357 1358 f := inst.ExportedFunction("main") 1359 require.NotNil(t, f) 1360 param := make([]uint64, paramNum) 1361 for i := range param { 1362 param[i] = uint64(i) 1363 } 1364 res, err := f.Call(ctx, param...) 1365 require.NoError(t, err) 1366 require.Equal(t, param, res) 1367 1368 require.Equal(t, ` 1369 --> .$0(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19) 1370 <-- (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19) 1371 `, "\n"+buf.String()) 1372 } 1373 1374 func TestListener_long_many_consts(t *testing.T) { 1375 const paramNum = 61 1376 1377 var exp []uint64 1378 var body []byte 1379 var resultTypes []wasm.ValueType 1380 for i := 0; i < paramNum; i++ { 1381 exp = append(exp, uint64(i)) 1382 resultTypes = append(resultTypes, i32) 1383 body = append(body, wasm.OpcodeI32Const) 1384 body = append(body, leb128.EncodeInt32(int32(i))...) 1385 } 1386 body = append(body, wasm.OpcodeEnd) 1387 1388 bin := binaryencoding.EncodeModule(&wasm.Module{ 1389 TypeSection: []wasm.FunctionType{{Results: resultTypes}}, 1390 ExportSection: []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}}, 1391 FunctionSection: []wasm.Index{0}, 1392 CodeSection: []wasm.Code{{Body: body}}, 1393 }) 1394 1395 var buf bytes.Buffer 1396 config := wazero.NewRuntimeConfigCompiler() 1397 ctx := experimental.WithFunctionListenerFactory(context.Background(), logging.NewLoggingListenerFactory(&buf)) 1398 1399 r := wazero.NewRuntimeWithConfig(ctx, config) 1400 defer func() { 1401 require.NoError(t, r.Close(ctx)) 1402 }() 1403 1404 inst, err := r.Instantiate(ctx, bin) 1405 require.NoError(t, err) 1406 1407 f := inst.ExportedFunction("main") 1408 require.NotNil(t, f) 1409 res, err := f.Call(ctx) 1410 require.NoError(t, err) 1411 require.Equal(t, exp, res) 1412 1413 require.Equal(t, ` 1414 --> .$0() 1415 <-- (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) 1416 `, "\n"+buf.String()) 1417 } 1418 1419 // TestDWARF verifies that the DWARF based stack traces work as expected before/after compilation cache. 1420 func TestDWARF(t *testing.T) { 1421 config := wazero.NewRuntimeConfigCompiler() 1422 ctx := context.Background() 1423 1424 bin := dwarftestdata.ZigWasm 1425 1426 dir := t.TempDir() 1427 1428 var expErr error 1429 { 1430 cc, err := wazero.NewCompilationCacheWithDir(dir) 1431 require.NoError(t, err) 1432 rc := config.WithCompilationCache(cc) 1433 1434 r := wazero.NewRuntimeWithConfig(ctx, rc) 1435 _, expErr = r.Instantiate(ctx, bin) 1436 require.Error(t, expErr) 1437 1438 err = r.Close(ctx) 1439 require.NoError(t, err) 1440 } 1441 1442 cc, err := wazero.NewCompilationCacheWithDir(dir) 1443 require.NoError(t, err) 1444 rc := config.WithCompilationCache(cc) 1445 r := wazero.NewRuntimeWithConfig(ctx, rc) 1446 _, err = r.Instantiate(ctx, bin) 1447 require.Error(t, err) 1448 require.Equal(t, expErr.Error(), err.Error()) 1449 1450 err = r.Close(ctx) 1451 require.NoError(t, err) 1452 }