From f3ef84c9b338bc3b23c056491585e79e4d088c75 Mon Sep 17 00:00:00 2001 From: Edoardo Vacchi Date: Sat, 1 Apr 2023 01:00:27 +0200 Subject: [PATCH] wazeroir: Load Ops, Store Ops, Set, Pick, Select, CallIndirect (#1329) Signed-off-by: Edoardo Vacchi Co-authored-by: Takeshi Yoneda --- internal/engine/compiler/compiler.go | 24 +- .../compiler_conditional_save_test.go | 4 +- .../compiler/compiler_controlflow_test.go | 19 +- .../engine/compiler/compiler_memory_test.go | 50 ++-- .../engine/compiler/compiler_stack_test.go | 10 +- internal/engine/compiler/compiler_vec_test.go | 2 +- internal/engine/compiler/engine.go | 48 ++-- internal/engine/compiler/impl_amd64.go | 92 +++--- internal/engine/compiler/impl_amd64_test.go | 2 +- internal/engine/compiler/impl_arm64.go | 94 ++++--- internal/engine/compiler/impl_arm64_test.go | 2 +- internal/engine/interpreter/interpreter.go | 57 +--- internal/wazeroir/compiler.go | 85 +++--- internal/wazeroir/compiler_test.go | 70 ++--- internal/wazeroir/operations.go | 266 ++++++------------ internal/wazeroir/operations_test.go | 9 + 16 files changed, 370 insertions(+), 464 deletions(-) diff --git a/internal/engine/compiler/compiler.go b/internal/engine/compiler/compiler.go index 9ed519fc..8fc9d31c 100644 --- a/internal/engine/compiler/compiler.go +++ b/internal/engine/compiler/compiler.go @@ -28,7 +28,7 @@ type compiler interface { // compileUnreachable adds instruction to perform wazeroir.OperationUnreachable. compileUnreachable() error // compileSet adds instruction to perform wazeroir.OperationSet. - compileSet(o wazeroir.OperationSet) error + compileSet(o wazeroir.UnionOperation) error // compileGlobalGet adds instructions to perform wazeroir.OperationGlobalGet. compileGlobalGet(o wazeroir.UnionOperation) error // compileGlobalSet adds instructions to perform wazeroir.OperationGlobalSet. @@ -42,13 +42,13 @@ type compiler interface { // compileCall adds instructions to perform wazeroir.OperationCall. compileCall(o wazeroir.UnionOperation) error // compileCallIndirect adds instructions to perform wazeroir.OperationCallIndirect. - compileCallIndirect(o wazeroir.OperationCallIndirect) error + compileCallIndirect(o wazeroir.UnionOperation) error // compileDrop adds instructions to perform wazeroir.OperationDrop. compileDrop(o wazeroir.OperationDrop) error // compileSelect adds instructions to perform wazeroir.OperationSelect. - compileSelect(o wazeroir.OperationSelect) error + compileSelect(o wazeroir.UnionOperation) error // compilePick adds instructions to perform wazeroir.OperationPick. - compilePick(o wazeroir.OperationPick) error + compilePick(o wazeroir.UnionOperation) error // compileAdd adds instructions to perform wazeroir.OperationAdd. compileAdd(o wazeroir.UnionOperation) error // compileSub adds instructions to perform wazeroir.OperationSub. @@ -134,21 +134,21 @@ type compiler interface { // compileLe adds instructions to perform wazeroir.OperationGe. compileGe(o wazeroir.UnionOperation) error // compileLoad adds instructions to perform wazeroir.OperationLoad. - compileLoad(o wazeroir.OperationLoad) error + compileLoad(o wazeroir.UnionOperation) error // compileLoad8 adds instructions to perform wazeroir.OperationLoad8. - compileLoad8(o wazeroir.OperationLoad8) error + compileLoad8(o wazeroir.UnionOperation) error // compileLoad16 adds instructions to perform wazeroir.OperationLoad16. - compileLoad16(o wazeroir.OperationLoad16) error + compileLoad16(o wazeroir.UnionOperation) error // compileLoad32 adds instructions to perform wazeroir.OperationLoad32. - compileLoad32(o wazeroir.OperationLoad32) error + compileLoad32(o wazeroir.UnionOperation) error // compileStore adds instructions to perform wazeroir.OperationStore. - compileStore(o wazeroir.OperationStore) error + compileStore(o wazeroir.UnionOperation) error // compileStore8 adds instructions to perform wazeroir.OperationStore8. - compileStore8(o wazeroir.OperationStore8) error + compileStore8(o wazeroir.UnionOperation) error // compileStore16 adds instructions to perform wazeroir.OperationStore16. - compileStore16(o wazeroir.OperationStore16) error + compileStore16(o wazeroir.UnionOperation) error // compileStore32 adds instructions to perform wazeroir.OperationStore32. - compileStore32(o wazeroir.OperationStore32) error + compileStore32(o wazeroir.UnionOperation) error // compileMemorySize adds instruction to perform wazeroir.OperationMemoryGrow. compileMemoryGrow() error // compileMemorySize adds instruction to perform wazeroir.OperationMemorySize. diff --git a/internal/engine/compiler/compiler_conditional_save_test.go b/internal/engine/compiler/compiler_conditional_save_test.go index 803ecd97..caad721a 100644 --- a/internal/engine/compiler/compiler_conditional_save_test.go +++ b/internal/engine/compiler/compiler_conditional_save_test.go @@ -27,10 +27,10 @@ func TestCompiler_conditional_value_saving(t *testing.T) { // Pick the f32 floating point local (1.0) twice. // Note that the f32 (function local variable in general) is placed above the call frame. - err = compiler.compilePick(wazeroir.OperationPick{Depth: int(compiler.runtimeValueLocationStack().sp - 1 - callFrameDataSizeInUint64)}) + err = compiler.compilePick(wazeroir.NewOperationPick(int(compiler.runtimeValueLocationStack().sp-1-callFrameDataSizeInUint64), false)) require.NoError(t, err) - err = compiler.compilePick(wazeroir.OperationPick{Depth: int(compiler.runtimeValueLocationStack().sp - 1 - callFrameDataSizeInUint64)}) + err = compiler.compilePick(wazeroir.NewOperationPick(int(compiler.runtimeValueLocationStack().sp-1-callFrameDataSizeInUint64), false)) require.NoError(t, err) // Generate conditional flag via floating point comparisons. diff --git a/internal/engine/compiler/compiler_controlflow_test.go b/internal/engine/compiler/compiler_controlflow_test.go index 682422d0..1dbb2808 100644 --- a/internal/engine/compiler/compiler_controlflow_test.go +++ b/internal/engine/compiler/compiler_controlflow_test.go @@ -547,7 +547,7 @@ func TestCompiler_compileCallIndirect(t *testing.T) { err := compiler.compilePreamble() require.NoError(t, err) - targetOperation := wazeroir.OperationCallIndirect{} + targetOperation := wazeroir.NewOperationCallIndirect(0, 0) // Place the offset value. err = compiler.compileConstI32(wazeroir.NewOperationConstI32(10)) @@ -577,7 +577,7 @@ func TestCompiler_compileCallIndirect(t *testing.T) { err := compiler.compilePreamble() require.NoError(t, err) - targetOperation := wazeroir.OperationCallIndirect{} + targetOperation := wazeroir.NewOperationCallIndirect(0, 0) targetOffset := wazeroir.NewOperationConstI32(uint32(0)) // and the typeID doesn't match the table[targetOffset]'s type ID. @@ -613,7 +613,7 @@ func TestCompiler_compileCallIndirect(t *testing.T) { err := compiler.compilePreamble() require.NoError(t, err) - targetOperation := wazeroir.OperationCallIndirect{} + targetOperation := wazeroir.NewOperationCallIndirect(0, 0) targetOffset := wazeroir.NewOperationConstI32(uint32(0)) env.module().TypeIDs = []wasm.FunctionTypeID{1000} // Ensure that the module instance has the type information for targetOperation.TypeIndex, @@ -648,8 +648,9 @@ func TestCompiler_compileCallIndirect(t *testing.T) { Results: []wasm.ValueType{wasm.ValueTypeI32}, ResultNumInUint64: 1, } + const typeIndex = 0 targetTypeID := wasm.FunctionTypeID(10) - operation := wazeroir.OperationCallIndirect{TypeIndex: 0} + operation := wazeroir.NewOperationCallIndirect(typeIndex, 0) table := make([]wasm.Reference, 10) env := newCompilerEnvironment() @@ -658,7 +659,7 @@ func TestCompiler_compileCallIndirect(t *testing.T) { // Ensure that the module instance has the type information for targetOperation.TypeIndex, // and the typeID matches the table[targetOffset]'s type ID. env.module().TypeIDs = make([]wasm.FunctionTypeID, 100) - env.module().TypeIDs[operation.TypeIndex] = targetTypeID + env.module().TypeIDs[typeIndex] = targetTypeID env.module().Engine = &moduleEngine{functions: []function{}} me := env.moduleEngine() @@ -678,7 +679,7 @@ func TestCompiler_compileCallIndirect(t *testing.T) { requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) // The function result value must be set at the bottom of the stack. - err = compiler.compileSet(wazeroir.OperationSet{Depth: int(compiler.runtimeValueLocationStack().sp - 1)}) + err = compiler.compileSet(wazeroir.NewOperationSet(int(compiler.runtimeValueLocationStack().sp-1), false)) require.NoError(t, err) err = compiler.compileReturnFunction() require.NoError(t, err) @@ -749,7 +750,7 @@ func TestCompiler_callIndirect_largeTypeIndex(t *testing.T) { // Ensure that the module instance has the type information for targetOperation.TypeIndex, // and the typeID matches the table[targetOffset]'s type ID. const typeIndex, typeID = 12345, 0 - operation := wazeroir.OperationCallIndirect{TypeIndex: typeIndex} + operation := wazeroir.NewOperationCallIndirect(typeIndex, 0) env.module().TypeIDs = make([]wasm.FunctionTypeID, typeIndex+1) env.module().TypeIDs[typeIndex] = typeID env.module().Engine = &moduleEngine{functions: []function{}} @@ -825,13 +826,13 @@ func TestCompiler_compileCall(t *testing.T) { err = compiler.compileConstI32(wazeroir.NewOperationConstI32(addTargetValue)) require.NoError(t, err) // Picks the function argument placed at the bottom of the stack. - err = compiler.compilePick(wazeroir.OperationPick{Depth: int(compiler.runtimeValueLocationStack().sp - 1)}) + err = compiler.compilePick(wazeroir.NewOperationPick(int(compiler.runtimeValueLocationStack().sp-1), false)) require.NoError(t, err) // Adds the const to the picked value. err = compiler.compileAdd(wazeroir.NewOperationAdd(wazeroir.UnsignedTypeI32)) require.NoError(t, err) // Then store the added result into the bottom of the stack (which is treated as the result of the function). - err = compiler.compileSet(wazeroir.OperationSet{Depth: int(compiler.runtimeValueLocationStack().sp - 1)}) + err = compiler.compileSet(wazeroir.NewOperationSet(int(compiler.runtimeValueLocationStack().sp-1), false)) require.NoError(t, err) err = compiler.compileReturnFunction() diff --git a/internal/engine/compiler/compiler_memory_test.go b/internal/engine/compiler/compiler_memory_test.go index 4cd425c2..55ca5d7b 100644 --- a/internal/engine/compiler/compiler_memory_test.go +++ b/internal/engine/compiler/compiler_memory_test.go @@ -91,7 +91,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i32.load", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad(wazeroir.OperationLoad{Arg: arg, Type: wazeroir.UnsignedTypeI32}) + err := compiler.compileLoad(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeI32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -101,7 +101,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad(wazeroir.OperationLoad{Arg: arg, Type: wazeroir.UnsignedTypeI64}) + err := compiler.compileLoad(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeI64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -111,7 +111,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "f32.load", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad(wazeroir.OperationLoad{Arg: arg, Type: wazeroir.UnsignedTypeF32}) + err := compiler.compileLoad(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -122,7 +122,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "f64.load", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad(wazeroir.OperationLoad{Arg: arg, Type: wazeroir.UnsignedTypeF64}) + err := compiler.compileLoad(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -133,7 +133,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i32.load8s", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad8(wazeroir.OperationLoad8{Arg: arg, Type: wazeroir.SignedInt32}) + err := compiler.compileLoad8(wazeroir.NewOperationLoad8(wazeroir.SignedInt32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -143,7 +143,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i32.load8u", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad8(wazeroir.OperationLoad8{Arg: arg, Type: wazeroir.SignedUint32}) + err := compiler.compileLoad8(wazeroir.NewOperationLoad8(wazeroir.SignedUint32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -153,7 +153,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load8s", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad8(wazeroir.OperationLoad8{Arg: arg, Type: wazeroir.SignedInt64}) + err := compiler.compileLoad8(wazeroir.NewOperationLoad8(wazeroir.SignedInt64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -163,7 +163,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load8u", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad8(wazeroir.OperationLoad8{Arg: arg, Type: wazeroir.SignedUint64}) + err := compiler.compileLoad8(wazeroir.NewOperationLoad8(wazeroir.SignedUint64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -173,7 +173,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i32.load16s", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad16(wazeroir.OperationLoad16{Arg: arg, Type: wazeroir.SignedInt32}) + err := compiler.compileLoad16(wazeroir.NewOperationLoad16(wazeroir.SignedInt32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -183,7 +183,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i32.load16u", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad16(wazeroir.OperationLoad16{Arg: arg, Type: wazeroir.SignedUint32}) + err := compiler.compileLoad16(wazeroir.NewOperationLoad16(wazeroir.SignedUint32, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -193,7 +193,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load16s", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad16(wazeroir.OperationLoad16{Arg: arg, Type: wazeroir.SignedInt64}) + err := compiler.compileLoad16(wazeroir.NewOperationLoad16(wazeroir.SignedInt64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -203,7 +203,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load16u", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad16(wazeroir.OperationLoad16{Arg: arg, Type: wazeroir.SignedUint64}) + err := compiler.compileLoad16(wazeroir.NewOperationLoad16(wazeroir.SignedUint64, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -213,7 +213,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load32s", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad32(wazeroir.OperationLoad32{Arg: arg, Signed: true}) + err := compiler.compileLoad32(wazeroir.NewOperationLoad32(true, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -223,7 +223,7 @@ func TestCompiler_compileLoad(t *testing.T) { { name: "i64.load32u", operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileLoad32(wazeroir.OperationLoad32{Arg: arg, Signed: false}) + err := compiler.compileLoad32(wazeroir.NewOperationLoad32(false, arg)) require.NoError(t, err) }, loadedValueVerifyFn: func(t *testing.T, loadedValueAsUint64 uint64) { @@ -292,7 +292,7 @@ func TestCompiler_compileStore(t *testing.T) { name: "i32.store", targetSizeInBytes: 32 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore(wazeroir.OperationStore{Arg: arg, Type: wazeroir.UnsignedTypeI32}) + err := compiler.compileStore(wazeroir.NewOperationStore(wazeroir.UnsignedTypeI32, arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -304,7 +304,7 @@ func TestCompiler_compileStore(t *testing.T) { isFloatTarget: true, targetSizeInBytes: 32 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore(wazeroir.OperationStore{Arg: arg, Type: wazeroir.UnsignedTypeF32}) + err := compiler.compileStore(wazeroir.NewOperationStore(wazeroir.UnsignedTypeF32, arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -315,7 +315,7 @@ func TestCompiler_compileStore(t *testing.T) { name: "i64.store", targetSizeInBytes: 64 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore(wazeroir.OperationStore{Arg: arg, Type: wazeroir.UnsignedTypeI64}) + err := compiler.compileStore(wazeroir.NewOperationStore(wazeroir.UnsignedTypeI64, arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -327,7 +327,7 @@ func TestCompiler_compileStore(t *testing.T) { isFloatTarget: true, targetSizeInBytes: 64 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore(wazeroir.OperationStore{Arg: arg, Type: wazeroir.UnsignedTypeF64}) + err := compiler.compileStore(wazeroir.NewOperationStore(wazeroir.UnsignedTypeF64, arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -338,7 +338,7 @@ func TestCompiler_compileStore(t *testing.T) { name: "store8", targetSizeInBytes: 1, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore8(wazeroir.OperationStore8{Arg: arg}) + err := compiler.compileStore8(wazeroir.NewOperationStore8(arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -349,7 +349,7 @@ func TestCompiler_compileStore(t *testing.T) { name: "store16", targetSizeInBytes: 16 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore16(wazeroir.OperationStore16{Arg: arg}) + err := compiler.compileStore16(wazeroir.NewOperationStore16(arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -360,7 +360,7 @@ func TestCompiler_compileStore(t *testing.T) { name: "store32", targetSizeInBytes: 32 / 8, operationSetupFn: func(t *testing.T, compiler compilerImpl) { - err := compiler.compileStore32(wazeroir.OperationStore32{Arg: arg}) + err := compiler.compileStore32(wazeroir.NewOperationStore32(arg)) require.NoError(t, err) }, storedValueVerifyFn: func(t *testing.T, mem []byte) { @@ -450,13 +450,13 @@ func TestCompiler_MemoryOutOfBounds(t *testing.T) { switch targetSizeInByte { case 1: - err = compiler.compileLoad8(wazeroir.OperationLoad8{Type: wazeroir.SignedInt32, Arg: arg}) + err = compiler.compileLoad8(wazeroir.NewOperationLoad8(wazeroir.SignedInt32, arg)) case 2: - err = compiler.compileLoad16(wazeroir.OperationLoad16{Type: wazeroir.SignedInt32, Arg: arg}) + err = compiler.compileLoad16(wazeroir.NewOperationLoad16(wazeroir.SignedInt32, arg)) case 4: - err = compiler.compileLoad32(wazeroir.OperationLoad32{Signed: false, Arg: arg}) + err = compiler.compileLoad32(wazeroir.NewOperationLoad32(false, arg)) case 8: - err = compiler.compileLoad(wazeroir.OperationLoad{Type: wazeroir.UnsignedTypeF64, Arg: arg}) + err = compiler.compileLoad(wazeroir.NewOperationLoad(wazeroir.UnsignedTypeF64, arg)) default: t.Fail() } diff --git a/internal/engine/compiler/compiler_stack_test.go b/internal/engine/compiler/compiler_stack_test.go index 56b0b5d9..2db31654 100644 --- a/internal/engine/compiler/compiler_stack_test.go +++ b/internal/engine/compiler/compiler_stack_test.go @@ -160,7 +160,7 @@ func TestCompiler_compileLoadValueOnStackToRegister(t *testing.T) { func TestCompiler_compilePick_v128(t *testing.T) { const pickTargetLo, pickTargetHi uint64 = 12345, 6789 - op := wazeroir.OperationPick{Depth: 2, IsTargetVector: true} + op := wazeroir.NewOperationPick(2, true) tests := []struct { name string isPickTargetOnRegister bool @@ -231,7 +231,7 @@ func TestCompiler_compilePick_v128(t *testing.T) { func TestCompiler_compilePick(t *testing.T) { const pickTargetValue uint64 = 12345 - op := wazeroir.OperationPick{Depth: 1} + op := wazeroir.NewOperationPick(1, false) tests := []struct { name string pickTargetSetupFunc func(compiler compilerImpl, ce *callEngine) error @@ -580,7 +580,7 @@ func TestCompiler_compileSelect(t *testing.T) { } // Now emit code for select. - err = compiler.compileSelect(wazeroir.OperationSelect{}) + err = compiler.compileSelect(wazeroir.NewOperationSelect(false)) require.NoError(t, err) // x1 should be top of the stack. @@ -655,7 +655,7 @@ func TestCompiler_compileSwap_v128(t *testing.T) { } // Swap x1 and x2. - err = compiler.compileSet(wazeroir.OperationSet{Depth: 4, IsTargetVector: true}) + err = compiler.compileSet(wazeroir.NewOperationSet(4, true)) require.NoError(t, err) require.NoError(t, compiler.compileReturnFunction()) @@ -730,7 +730,7 @@ func TestCompiler_compileSet(t *testing.T) { } // Set x2 into the x1. - err = compiler.compileSet(wazeroir.OperationSet{Depth: 2}) + err = compiler.compileSet(wazeroir.NewOperationSet(2, false)) require.NoError(t, err) require.NoError(t, compiler.compileReturnFunction()) diff --git a/internal/engine/compiler/compiler_vec_test.go b/internal/engine/compiler/compiler_vec_test.go index 5096abc3..418b601c 100644 --- a/internal/engine/compiler/compiler_vec_test.go +++ b/internal/engine/compiler/compiler_vec_test.go @@ -7394,7 +7394,7 @@ func TestCompiler_compileSelect_v128(t *testing.T) { err = compiler.compileConstI32(wazeroir.NewOperationConstI32(selector)) require.NoError(t, err) - err = compiler.compileSelect(wazeroir.OperationSelect{IsTargetVector: true}) + err = compiler.compileSelect(wazeroir.NewOperationSelect(true)) require.NoError(t, err) requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go index ac9b7fea..27245221 100644 --- a/internal/engine/compiler/engine.go +++ b/internal/engine/compiler/engine.go @@ -1100,32 +1100,8 @@ func compileWasmFunction(cmp compiler, ir *wazeroir.CompilationResult) (*code, e err = cmp.compileBrIf(o) case wazeroir.OperationBrTable: err = cmp.compileBrTable(o) - case wazeroir.OperationCallIndirect: - err = cmp.compileCallIndirect(o) case wazeroir.OperationDrop: err = cmp.compileDrop(o) - case wazeroir.OperationSelect: - err = cmp.compileSelect(o) - case wazeroir.OperationPick: - err = cmp.compilePick(o) - case wazeroir.OperationSet: - err = cmp.compileSet(o) - case wazeroir.OperationLoad: - err = cmp.compileLoad(o) - case wazeroir.OperationLoad8: - err = cmp.compileLoad8(o) - case wazeroir.OperationLoad16: - err = cmp.compileLoad16(o) - case wazeroir.OperationLoad32: - err = cmp.compileLoad32(o) - case wazeroir.OperationStore: - err = cmp.compileStore(o) - case wazeroir.OperationStore8: - err = cmp.compileStore8(o) - case wazeroir.OperationStore16: - err = cmp.compileStore16(o) - case wazeroir.OperationStore32: - err = cmp.compileStore32(o) case wazeroir.OperationITruncFromF: err = cmp.compileITruncFromF(o) case wazeroir.OperationFConvertFromI: @@ -1262,11 +1238,35 @@ func compileWasmFunction(cmp compiler, ir *wazeroir.CompilationResult) (*code, e err = cmp.compileUnreachable() case wazeroir.OperationKindCall: err = cmp.compileCall(o) + case wazeroir.OperationKindCallIndirect: + err = cmp.compileCallIndirect(o) + case wazeroir.OperationKindSelect: + err = cmp.compileSelect(o) + case wazeroir.OperationKindPick: + err = cmp.compilePick(o) + case wazeroir.OperationKindSet: + err = cmp.compileSet(o) case wazeroir.OperationKindGlobalGet: err = cmp.compileGlobalGet(o) case wazeroir.OperationKindGlobalSet: err = cmp.compileGlobalSet(o) + case wazeroir.OperationKindLoad: + err = cmp.compileLoad(o) + case wazeroir.OperationKindLoad8: + err = cmp.compileLoad8(o) + case wazeroir.OperationKindLoad16: + err = cmp.compileLoad16(o) + case wazeroir.OperationKindLoad32: + err = cmp.compileLoad32(o) + case wazeroir.OperationKindStore: + err = cmp.compileStore(o) + case wazeroir.OperationKindStore8: + err = cmp.compileStore8(o) + case wazeroir.OperationKindStore16: + err = cmp.compileStore16(o) + case wazeroir.OperationKindStore32: + err = cmp.compileStore32(o) case wazeroir.OperationKindMemorySize: err = cmp.compileMemorySize() diff --git a/internal/engine/compiler/impl_amd64.go b/internal/engine/compiler/impl_amd64.go index eb3e8dfb..8a90e083 100644 --- a/internal/engine/compiler/impl_amd64.go +++ b/internal/engine/compiler/impl_amd64.go @@ -267,10 +267,13 @@ func (c *amd64Compiler) compileUnreachable() error { } // compileSet implements compiler.compileSet for the amd64 architecture. -func (c *amd64Compiler) compileSet(o wazeroir.OperationSet) error { - setTargetIndex := int(c.locationStack.sp) - 1 - o.Depth +func (c *amd64Compiler) compileSet(o wazeroir.UnionOperation) error { + depth := int(o.U1) + isTargetVector := o.B3 - if o.IsTargetVector { + setTargetIndex := int(c.locationStack.sp) - 1 - depth + + if isTargetVector { _ = c.locationStack.pop() // ignore the higher 64-bits. } v := c.locationStack.pop() @@ -287,7 +290,7 @@ func (c *amd64Compiler) compileSet(o wazeroir.OperationSet) error { reg := v.register targetLocation.setRegister(reg) targetLocation.valueType = v.valueType - if o.IsTargetVector { + if isTargetVector { hi := &c.locationStack.stack[setTargetIndex+1] hi.setRegister(reg) } @@ -756,11 +759,13 @@ func (c *amd64Compiler) compileCall(o wazeroir.UnionOperation) error { } // compileCallIndirect implements compiler.compileCallIndirect for the amd64 architecture. -func (c *amd64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) error { +func (c *amd64Compiler) compileCallIndirect(o wazeroir.UnionOperation) error { offset := c.locationStack.pop() if err := c.compileEnsureOnRegister(offset); err != nil { return nil } + typeIndex := o.U1 + tableIndex := o.U2 tmp, err := c.allocateRegister(registerTypeGeneralPurpose) if err != nil { @@ -777,7 +782,7 @@ func (c *amd64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) er // Load the address of the target table: tmp = &module.Tables[0] c.assembler.CompileMemoryToRegister(amd64.MOVQ, amd64ReservedRegisterForCallEngine, callEngineModuleContextTablesElement0AddressOffset, tmp) // tmp = &module.Tables[0] + Index*8 = &module.Tables[0] + sizeOf(*TableInstance)*index = module.Tables[o.TableIndex]. - c.assembler.CompileMemoryToRegister(amd64.MOVQ, tmp, int64(o.TableIndex*8), tmp) + c.assembler.CompileMemoryToRegister(amd64.MOVQ, tmp, int64(tableIndex*8), tmp) // Then, we need to check if the offset doesn't exceed the length of table. c.assembler.CompileMemoryToRegister(amd64.CMPQ, tmp, tableInstanceTableLenOffset, offset.register) @@ -819,7 +824,7 @@ func (c *amd64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) er c.assembler.CompileMemoryToRegister(amd64.MOVQ, amd64ReservedRegisterForCallEngine, callEngineModuleContextTypeIDsElement0AddressOffset, tmp2) - c.assembler.CompileMemoryToRegister(amd64.MOVL, tmp2, int64(o.TypeIndex)*4, tmp2) + c.assembler.CompileMemoryToRegister(amd64.MOVL, tmp2, int64(typeIndex)*4, tmp2) // Jump if the type matches. c.assembler.CompileMemoryToRegister(amd64.CMPL, offset.register, functionTypeIDOffset, tmp2) @@ -829,7 +834,7 @@ func (c *amd64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) er c.compileExitFromNativeCode(nativeCallStatusCodeTypeMismatchOnIndirectCall) c.assembler.SetJumpTargetOnNext(jumpIfTypeMatch) - targetFunctionType := &c.ir.Types[o.TypeIndex] + targetFunctionType := &c.ir.Types[typeIndex] if err = c.compileCallFunctionImpl(offset.register, targetFunctionType); err != nil { return nil } @@ -881,13 +886,14 @@ func (c *amd64Compiler) compileSelectV128Impl(selectorReg asm.Register) error { // // The emitted native code depends on whether the values are on // the physical registers or memory stack, or maybe conditional register. -func (c *amd64Compiler) compileSelect(o wazeroir.OperationSelect) error { +func (c *amd64Compiler) compileSelect(o wazeroir.UnionOperation) error { cv := c.locationStack.pop() if err := c.compileEnsureOnRegister(cv); err != nil { return err } - if o.IsTargetVector { + isTargetVector := o.B3 + if isTargetVector { return c.compileSelectV128Impl(cv.register) } @@ -936,15 +942,17 @@ func (c *amd64Compiler) compileSelect(o wazeroir.OperationSelect) error { } // compilePick implements compiler.compilePick for the amd64 architecture. -func (c *amd64Compiler) compilePick(o wazeroir.OperationPick) error { +func (c *amd64Compiler) compilePick(o wazeroir.UnionOperation) error { if err := c.maybeCompileMoveTopConditionalToGeneralPurposeRegister(); err != nil { return err } + depth := o.U1 + isTargetVector := o.B3 // TODO: if we track the type of values on the stack, // we could optimize the instruction according to the bit size of the value. // For now, we just move the entire register i.e. as a quad word (8 bytes). - pickTarget := &c.locationStack.stack[c.locationStack.sp-1-uint64(o.Depth)] + pickTarget := &c.locationStack.stack[c.locationStack.sp-1-uint64(depth)] reg, err := c.allocateRegister(pickTarget.getRegisterType()) if err != nil { return err @@ -952,7 +960,7 @@ func (c *amd64Compiler) compilePick(o wazeroir.OperationPick) error { if pickTarget.onRegister() { var inst asm.Instruction - if o.IsTargetVector { + if isTargetVector { inst = amd64.MOVDQU } else if pickTarget.valueType == runtimeValueTypeI32 { // amd64 cannot copy single-precisions between registers. inst = amd64.MOVL @@ -963,7 +971,7 @@ func (c *amd64Compiler) compilePick(o wazeroir.OperationPick) error { } else if pickTarget.onStack() { // Copy the value from the stack. var inst asm.Instruction - if o.IsTargetVector { + if isTargetVector { inst = amd64.MOVDQU } else if pickTarget.valueType == runtimeValueTypeI32 || pickTarget.valueType == runtimeValueTypeF32 { inst = amd64.MOVL @@ -976,7 +984,7 @@ func (c *amd64Compiler) compilePick(o wazeroir.OperationPick) error { } // Now we already placed the picked value on the register, // so push the location onto the stack. - if o.IsTargetVector { + if isTargetVector { c.pushVectorRuntimeValueLocationOnRegister(reg) } else { c.pushRuntimeValueLocationOnRegister(reg, pickTarget.valueType) @@ -3229,14 +3237,18 @@ func (c *amd64Compiler) compileGe(o wazeroir.UnionOperation) error { } // compileLoad implements compiler.compileLoad for the amd64 architecture. -func (c *amd64Compiler) compileLoad(o wazeroir.OperationLoad) error { +func (c *amd64Compiler) compileLoad(o wazeroir.UnionOperation) error { var ( isIntType bool movInst asm.Instruction targetSizeInBytes int64 vt runtimeValueType ) - switch o.Type { + + unsignedType := wazeroir.UnsignedType(o.B1) + offset := uint32(o.U2) + + switch unsignedType { case wazeroir.UnsignedTypeI32: isIntType = true movInst = amd64.MOVL @@ -3259,7 +3271,7 @@ func (c *amd64Compiler) compileLoad(o wazeroir.OperationLoad) error { vt = runtimeValueTypeF64 } - reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + reg, err := c.compileMemoryAccessCeilSetup(offset, targetSizeInBytes) if err != nil { return err } @@ -3290,9 +3302,10 @@ func (c *amd64Compiler) compileLoad(o wazeroir.OperationLoad) error { } // compileLoad8 implements compiler.compileLoad8 for the amd64 architecture. -func (c *amd64Compiler) compileLoad8(o wazeroir.OperationLoad8) error { +func (c *amd64Compiler) compileLoad8(o wazeroir.UnionOperation) error { const targetSizeInBytes = 1 - reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + offset := uint32(o.U2) + reg, err := c.compileMemoryAccessCeilSetup(offset, targetSizeInBytes) if err != nil { return err } @@ -3301,7 +3314,8 @@ func (c *amd64Compiler) compileLoad8(o wazeroir.OperationLoad8) error { // Note that Load8 is only for integer types. var inst asm.Instruction var vt runtimeValueType - switch o.Type { + signedInt := wazeroir.SignedInt(o.B1) + switch signedInt { case wazeroir.SignedInt32: inst = amd64.MOVBLSX vt = runtimeValueTypeI32 @@ -3326,9 +3340,10 @@ func (c *amd64Compiler) compileLoad8(o wazeroir.OperationLoad8) error { } // compileLoad16 implements compiler.compileLoad16 for the amd64 architecture. -func (c *amd64Compiler) compileLoad16(o wazeroir.OperationLoad16) error { +func (c *amd64Compiler) compileLoad16(o wazeroir.UnionOperation) error { const targetSizeInBytes = 16 / 8 - reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + offset := uint32(o.U2) + reg, err := c.compileMemoryAccessCeilSetup(offset, targetSizeInBytes) if err != nil { return err } @@ -3337,7 +3352,8 @@ func (c *amd64Compiler) compileLoad16(o wazeroir.OperationLoad16) error { // Note that Load16 is only for integer types. var inst asm.Instruction var vt runtimeValueType - switch o.Type { + signedInt := wazeroir.SignedInt(o.B1) + switch signedInt { case wazeroir.SignedInt32: inst = amd64.MOVWLSX vt = runtimeValueTypeI32 @@ -3362,16 +3378,18 @@ func (c *amd64Compiler) compileLoad16(o wazeroir.OperationLoad16) error { } // compileLoad32 implements compiler.compileLoad32 for the amd64 architecture. -func (c *amd64Compiler) compileLoad32(o wazeroir.OperationLoad32) error { +func (c *amd64Compiler) compileLoad32(o wazeroir.UnionOperation) error { const targetSizeInBytes = 32 / 8 - reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + offset := uint32(o.U2) + reg, err := c.compileMemoryAccessCeilSetup(offset, targetSizeInBytes) if err != nil { return err } // Then move 4 bytes at the offset to the register. var inst asm.Instruction - if o.Signed { + signed := o.B1 == 1 + if signed { inst = amd64.MOVLQSX } else { inst = amd64.MOVLQZX @@ -3435,10 +3453,12 @@ func (c *amd64Compiler) compileMemoryAccessCeilSetup(offsetArg uint32, targetSiz } // compileStore implements compiler.compileStore for the amd64 architecture. -func (c *amd64Compiler) compileStore(o wazeroir.OperationStore) error { +func (c *amd64Compiler) compileStore(o wazeroir.UnionOperation) error { var movInst asm.Instruction var targetSizeInByte int64 - switch o.Type { + unsignedType := wazeroir.UnsignedType(o.B1) + offset := uint32(o.U2) + switch unsignedType { case wazeroir.UnsignedTypeI32, wazeroir.UnsignedTypeF32: movInst = amd64.MOVL targetSizeInByte = 32 / 8 @@ -3446,22 +3466,22 @@ func (c *amd64Compiler) compileStore(o wazeroir.OperationStore) error { movInst = amd64.MOVQ targetSizeInByte = 64 / 8 } - return c.compileStoreImpl(o.Arg.Offset, movInst, targetSizeInByte) + return c.compileStoreImpl(offset, movInst, targetSizeInByte) } // compileStore8 implements compiler.compileStore8 for the amd64 architecture. -func (c *amd64Compiler) compileStore8(o wazeroir.OperationStore8) error { - return c.compileStoreImpl(o.Arg.Offset, amd64.MOVB, 1) +func (c *amd64Compiler) compileStore8(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), amd64.MOVB, 1) } // compileStore32 implements compiler.compileStore32 for the amd64 architecture. -func (c *amd64Compiler) compileStore16(o wazeroir.OperationStore16) error { - return c.compileStoreImpl(o.Arg.Offset, amd64.MOVW, 16/8) +func (c *amd64Compiler) compileStore16(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), amd64.MOVW, 16/8) } // compileStore32 implements compiler.compileStore32 for the amd64 architecture. -func (c *amd64Compiler) compileStore32(o wazeroir.OperationStore32) error { - return c.compileStoreImpl(o.Arg.Offset, amd64.MOVL, 32/8) +func (c *amd64Compiler) compileStore32(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), amd64.MOVL, 32/8) } func (c *amd64Compiler) compileStoreImpl(offsetConst uint32, inst asm.Instruction, targetSizeInBytes int64) error { diff --git a/internal/engine/compiler/impl_amd64_test.go b/internal/engine/compiler/impl_amd64_test.go index 437d5b57..fe6e5e1b 100644 --- a/internal/engine/compiler/impl_amd64_test.go +++ b/internal/engine/compiler/impl_amd64_test.go @@ -22,7 +22,7 @@ func TestAmd64Compiler_indirectCallWithTargetOnCallingConvReg(t *testing.T) { env.addTable(&wasm.TableInstance{References: table}) // Ensure that the module instance has the type information for targetOperation.TypeIndex, // and the typeID matches the table[targetOffset]'s type ID. - operation := wazeroir.OperationCallIndirect{TypeIndex: 0} + operation := wazeroir.NewOperationCallIndirect(0, 0) env.module().TypeIDs = []wasm.FunctionTypeID{0} env.module().Engine = &moduleEngine{functions: []function{}} diff --git a/internal/engine/compiler/impl_arm64.go b/internal/engine/compiler/impl_arm64.go index 1329a93d..ce5f31cf 100644 --- a/internal/engine/compiler/impl_arm64.go +++ b/internal/engine/compiler/impl_arm64.go @@ -496,10 +496,13 @@ func (c *arm64Compiler) compileUnreachable() error { } // compileSet implements compiler.compileSet for the arm64 architecture. -func (c *arm64Compiler) compileSet(o wazeroir.OperationSet) error { - setTargetIndex := int(c.locationStack.sp) - 1 - o.Depth +func (c *arm64Compiler) compileSet(o wazeroir.UnionOperation) error { + depth := int(o.U1) + isTargetVector := o.B3 - if o.IsTargetVector { + setTargetIndex := int(c.locationStack.sp) - 1 - depth + + if isTargetVector { _ = c.locationStack.pop() } v := c.locationStack.pop() @@ -516,7 +519,7 @@ func (c *arm64Compiler) compileSet(o wazeroir.OperationSet) error { reg := v.register targetLocation.setRegister(reg) targetLocation.valueType = v.valueType - if o.IsTargetVector { + if isTargetVector { hi := &c.locationStack.stack[setTargetIndex+1] hi.setRegister(reg) } @@ -1080,11 +1083,13 @@ func (c *arm64Compiler) compileCallImpl(targetFunctionAddressRegister asm.Regist } // compileCallIndirect implements compiler.compileCallIndirect for the arm64 architecture. -func (c *arm64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) (err error) { +func (c *arm64Compiler) compileCallIndirect(o wazeroir.UnionOperation) (err error) { offset := c.locationStack.pop() if err = c.compileEnsureOnRegister(offset); err != nil { return err } + typeIndex := o.U1 + tableIndex := o.U2 offsetReg := offset.register if isZeroRegister(offsetReg) { @@ -1118,7 +1123,7 @@ func (c *arm64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) (e ) // tmp = [tmp + TableIndex*8] = [&Tables[0] + TableIndex*sizeOf(*tableInstance)] = Tables[tableIndex] c.assembler.CompileMemoryToRegister(arm64.LDRD, - tmp, int64(o.TableIndex)*8, + tmp, int64(tableIndex)*8, tmp, ) // tmp2 = [tmp + tableInstanceTableLenOffset] = len(Tables[tableIndex]) @@ -1171,7 +1176,7 @@ func (c *arm64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) (e c.assembler.CompileMemoryToRegister(arm64.LDRD, arm64ReservedRegisterForCallEngine, callEngineModuleContextTypeIDsElement0AddressOffset, tmp2) - c.assembler.CompileMemoryToRegister(arm64.LDRW, tmp2, int64(o.TypeIndex)*4, tmp2) + c.assembler.CompileMemoryToRegister(arm64.LDRW, tmp2, int64(typeIndex)*4, tmp2) // Compare these two values, and if they equal, we are ready to make function call. c.assembler.CompileTwoRegistersToNone(arm64.CMPW, tmp, tmp2) @@ -1180,7 +1185,7 @@ func (c *arm64Compiler) compileCallIndirect(o wazeroir.OperationCallIndirect) (e c.assembler.SetJumpTargetOnNext(brIfTypeMatched) - targetFunctionType := &c.ir.Types[o.TypeIndex] + targetFunctionType := &c.ir.Types[typeIndex] if err := c.compileCallImpl(offsetReg, targetFunctionType); err != nil { return err } @@ -1224,13 +1229,14 @@ func (c *arm64Compiler) compileSelectV128Impl(selectorRegister asm.Register) err } // compileSelect implements compiler.compileSelect for the arm64 architecture. -func (c *arm64Compiler) compileSelect(o wazeroir.OperationSelect) error { +func (c *arm64Compiler) compileSelect(o wazeroir.UnionOperation) error { cv, err := c.popValueOnRegister() if err != nil { return err } - if o.IsTargetVector { + isTargetVector := o.B3 + if isTargetVector { return c.compileSelectV128Impl(cv.register) } @@ -1300,12 +1306,14 @@ func (c *arm64Compiler) compileSelect(o wazeroir.OperationSelect) error { } // compilePick implements compiler.compilePick for the arm64 architecture. -func (c *arm64Compiler) compilePick(o wazeroir.OperationPick) error { +func (c *arm64Compiler) compilePick(o wazeroir.UnionOperation) error { if err := c.maybeCompileMoveTopConditionalToGeneralPurposeRegister(); err != nil { return err } + depth := o.U1 + isTargetVector := o.B3 - pickTarget := &c.locationStack.stack[c.locationStack.sp-1-uint64(o.Depth)] + pickTarget := &c.locationStack.stack[c.locationStack.sp-1-uint64(depth)] pickedRegister, err := c.allocateRegister(pickTarget.getRegisterType()) if err != nil { return err @@ -1334,7 +1342,7 @@ func (c *arm64Compiler) compilePick(o wazeroir.OperationPick) error { // After the load, we revert the register assignment to the pick target. pickTarget.setRegister(asm.NilRegister) - if o.IsTargetVector { + if isTargetVector { hi := &c.locationStack.stack[pickTarget.stackPointer+1] hi.setRegister(asm.NilRegister) } @@ -1343,7 +1351,7 @@ func (c *arm64Compiler) compilePick(o wazeroir.OperationPick) error { // Now we have the value of the target on the pickedRegister, // so push the location. c.pushRuntimeValueLocationOnRegister(pickedRegister, pickTarget.valueType) - if o.IsTargetVector { + if isTargetVector { c.pushRuntimeValueLocationOnRegister(pickedRegister, runtimeValueTypeV128Hi) } return nil @@ -2585,7 +2593,7 @@ func (c *arm64Compiler) compileGe(o wazeroir.UnionOperation) error { } // compileLoad implements compiler.compileLoad for the arm64 architecture. -func (c *arm64Compiler) compileLoad(o wazeroir.OperationLoad) error { +func (c *arm64Compiler) compileLoad(o wazeroir.UnionOperation) error { var ( isFloat bool loadInst asm.Instruction @@ -2593,7 +2601,10 @@ func (c *arm64Compiler) compileLoad(o wazeroir.OperationLoad) error { vt runtimeValueType ) - switch o.Type { + unsignedType := wazeroir.UnsignedType(o.B1) + offset := uint32(o.U2) + + switch unsignedType { case wazeroir.UnsignedTypeI32: loadInst = arm64.LDRW targetSizeInBytes = 32 / 8 @@ -2613,14 +2624,18 @@ func (c *arm64Compiler) compileLoad(o wazeroir.OperationLoad) error { targetSizeInBytes = 64 / 8 vt = runtimeValueTypeF64 } - return c.compileLoadImpl(o.Arg.Offset, loadInst, targetSizeInBytes, isFloat, vt) + return c.compileLoadImpl(offset, loadInst, targetSizeInBytes, isFloat, vt) } // compileLoad8 implements compiler.compileLoad8 for the arm64 architecture. -func (c *arm64Compiler) compileLoad8(o wazeroir.OperationLoad8) error { +func (c *arm64Compiler) compileLoad8(o wazeroir.UnionOperation) error { var loadInst asm.Instruction var vt runtimeValueType - switch o.Type { + + signedInt := wazeroir.SignedInt(o.B1) + offset := uint32(o.U2) + + switch signedInt { case wazeroir.SignedInt32: loadInst = arm64.LDRSBW vt = runtimeValueTypeI32 @@ -2634,14 +2649,18 @@ func (c *arm64Compiler) compileLoad8(o wazeroir.OperationLoad8) error { loadInst = arm64.LDRB vt = runtimeValueTypeI64 } - return c.compileLoadImpl(o.Arg.Offset, loadInst, 1, false, vt) + return c.compileLoadImpl(offset, loadInst, 1, false, vt) } // compileLoad16 implements compiler.compileLoad16 for the arm64 architecture. -func (c *arm64Compiler) compileLoad16(o wazeroir.OperationLoad16) error { +func (c *arm64Compiler) compileLoad16(o wazeroir.UnionOperation) error { var loadInst asm.Instruction var vt runtimeValueType - switch o.Type { + + signedInt := wazeroir.SignedInt(o.B1) + offset := uint32(o.U2) + + switch signedInt { case wazeroir.SignedInt32: loadInst = arm64.LDRSHW vt = runtimeValueTypeI32 @@ -2655,18 +2674,21 @@ func (c *arm64Compiler) compileLoad16(o wazeroir.OperationLoad16) error { loadInst = arm64.LDRH vt = runtimeValueTypeI64 } - return c.compileLoadImpl(o.Arg.Offset, loadInst, 16/8, false, vt) + return c.compileLoadImpl(offset, loadInst, 16/8, false, vt) } // compileLoad32 implements compiler.compileLoad32 for the arm64 architecture. -func (c *arm64Compiler) compileLoad32(o wazeroir.OperationLoad32) error { +func (c *arm64Compiler) compileLoad32(o wazeroir.UnionOperation) error { var loadInst asm.Instruction - if o.Signed { + signed := o.B1 == 1 + offset := uint32(o.U2) + + if signed { loadInst = arm64.LDRSW } else { loadInst = arm64.LDRW } - return c.compileLoadImpl(o.Arg.Offset, loadInst, 32/8, false, runtimeValueTypeI64) + return c.compileLoadImpl(offset, loadInst, 32/8, false, runtimeValueTypeI64) } // compileLoadImpl implements compileLoadImpl* variants for arm64 architecture. @@ -2699,10 +2721,12 @@ func (c *arm64Compiler) compileLoadImpl(offsetArg uint32, loadInst asm.Instructi } // compileStore implements compiler.compileStore for the arm64 architecture. -func (c *arm64Compiler) compileStore(o wazeroir.OperationStore) error { +func (c *arm64Compiler) compileStore(o wazeroir.UnionOperation) error { var movInst asm.Instruction var targetSizeInBytes int64 - switch o.Type { + unsignedType := wazeroir.UnsignedType(o.B1) + offset := uint32(o.U2) + switch unsignedType { case wazeroir.UnsignedTypeI32: movInst = arm64.STRW targetSizeInBytes = 32 / 8 @@ -2716,22 +2740,22 @@ func (c *arm64Compiler) compileStore(o wazeroir.OperationStore) error { movInst = arm64.FSTRD targetSizeInBytes = 64 / 8 } - return c.compileStoreImpl(o.Arg.Offset, movInst, targetSizeInBytes) + return c.compileStoreImpl(offset, movInst, targetSizeInBytes) } // compileStore8 implements compiler.compileStore8 for the arm64 architecture. -func (c *arm64Compiler) compileStore8(o wazeroir.OperationStore8) error { - return c.compileStoreImpl(o.Arg.Offset, arm64.STRB, 1) +func (c *arm64Compiler) compileStore8(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), arm64.STRB, 1) } // compileStore16 implements compiler.compileStore16 for the arm64 architecture. -func (c *arm64Compiler) compileStore16(o wazeroir.OperationStore16) error { - return c.compileStoreImpl(o.Arg.Offset, arm64.STRH, 16/8) +func (c *arm64Compiler) compileStore16(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), arm64.STRH, 16/8) } // compileStore32 implements compiler.compileStore32 for the arm64 architecture. -func (c *arm64Compiler) compileStore32(o wazeroir.OperationStore32) error { - return c.compileStoreImpl(o.Arg.Offset, arm64.STRW, 32/8) +func (c *arm64Compiler) compileStore32(o wazeroir.UnionOperation) error { + return c.compileStoreImpl(uint32(o.U2), arm64.STRW, 32/8) } // compileStoreImpl implements compleStore* variants for arm64 architecture. diff --git a/internal/engine/compiler/impl_arm64_test.go b/internal/engine/compiler/impl_arm64_test.go index 0fc7ac29..72a6fe2b 100644 --- a/internal/engine/compiler/impl_arm64_test.go +++ b/internal/engine/compiler/impl_arm64_test.go @@ -19,7 +19,7 @@ func TestArm64Compiler_indirectCallWithTargetOnCallingConvReg(t *testing.T) { env.addTable(&wasm.TableInstance{References: table}) // Ensure that the module instance has the type information for targetOperation.TypeIndex, // and the typeID matches the table[targetOffset]'s type ID. - operation := wazeroir.OperationCallIndirect{TypeIndex: 0} + operation := wazeroir.NewOperationCallIndirect(0, 0) env.module().TypeIDs = []wasm.FunctionTypeID{0} env.module().Engine = &moduleEngine{functions: []function{}} diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go index 664283a6..d95b8f79 100644 --- a/internal/engine/interpreter/interpreter.go +++ b/internal/engine/interpreter/interpreter.go @@ -291,8 +291,21 @@ func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) { // Nullary operations don't need any further processing. switch o.Kind() { case wazeroir.OperationKindCall: + case wazeroir.OperationKindCallIndirect: + + case wazeroir.OperationKindSelect: + case wazeroir.OperationKindPick: + case wazeroir.OperationKindSet: case wazeroir.OperationKindGlobalGet: case wazeroir.OperationKindGlobalSet: + case wazeroir.OperationKindLoad: + case wazeroir.OperationKindLoad8: + case wazeroir.OperationKindLoad16: + case wazeroir.OperationKindLoad32: + case wazeroir.OperationKindStore: + case wazeroir.OperationKindStore8: + case wazeroir.OperationKindStore16: + case wazeroir.OperationKindStore32: case wazeroir.OperationKindConstI32: case wazeroir.OperationKindConstI64: @@ -422,52 +435,10 @@ func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) { } } } - case wazeroir.OperationCallIndirect: - op.U1 = uint64(o.TypeIndex) - op.U2 = uint64(o.TableIndex) case wazeroir.OperationDrop: op.Rs = make([]*wazeroir.InclusiveRange, 1) op.Rs[0] = o.Depth - case wazeroir.OperationSelect: - op.B3 = o.IsTargetVector - case wazeroir.OperationPick: - op.U1 = uint64(o.Depth) - op.B3 = o.IsTargetVector - case wazeroir.OperationSet: - op.U1 = uint64(o.Depth) - op.B3 = o.IsTargetVector - case wazeroir.OperationLoad: - op.B1 = byte(o.Type) - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationLoad8: - op.B1 = byte(o.Type) - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationLoad16: - op.B1 = byte(o.Type) - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationLoad32: - if o.Signed { - op.B1 = 1 - } - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationStore: - op.B1 = byte(o.Type) - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationStore8: - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationStore16: - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - case wazeroir.OperationStore32: - op.U1 = uint64(o.Arg.Alignment) - op.U2 = uint64(o.Arg.Offset) - // const ops... + case wazeroir.OperationITruncFromF: op.B1 = byte(o.InputType) op.B2 = byte(o.OutputType) diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go index 4fea98b4..15ce1d4d 100644 --- a/internal/wazeroir/compiler.go +++ b/internal/wazeroir/compiler.go @@ -828,13 +828,14 @@ operatorSwitch: NewOperationCall(index), ) case wasm.OpcodeCallIndirect: + typeIndex := index tableIndex, n, err := leb128.LoadUint32(c.body[c.pc+1:]) if err != nil { return fmt.Errorf("read target for br_table: %w", err) } c.pc += n c.emit( - OperationCallIndirect{TypeIndex: index, TableIndex: tableIndex}, + NewOperationCallIndirect(typeIndex, tableIndex), ) case wasm.OpcodeDrop: r := &InclusiveRange{Start: 0, End: 0} @@ -851,8 +852,9 @@ operatorSwitch: if c.unreachableState.on { break operatorSwitch } + isTargetVector := c.stackPeek() == UnsignedTypeV128 c.emit( - OperationSelect{IsTargetVector: c.stackPeek() == UnsignedTypeV128}, + NewOperationSelect(isTargetVector), ) case wasm.OpcodeTypedSelect: // Skips two bytes: vector size fixed to 1, and the value type for select. @@ -862,8 +864,9 @@ operatorSwitch: break operatorSwitch } // Typed select is semantically equivalent to select at runtime. + isTargetVector := c.stackPeek() == UnsignedTypeV128 c.emit( - OperationSelect{IsTargetVector: c.stackPeek() == UnsignedTypeV128}, + NewOperationSelect(isTargetVector), ) case wasm.OpcodeLocalGet: depth := c.localDepth(index) @@ -871,13 +874,13 @@ operatorSwitch: c.emit( // -1 because we already manipulated the stack before // called localDepth ^^. - OperationPick{Depth: depth - 1, IsTargetVector: isVector}, + NewOperationPick(depth-1, isVector), ) } else { c.emit( // -2 because we already manipulated the stack before // called localDepth ^^. - OperationPick{Depth: depth - 2, IsTargetVector: isVector}, + NewOperationPick(depth-2, isVector), ) } case wasm.OpcodeLocalSet: @@ -888,13 +891,13 @@ operatorSwitch: c.emit( // +2 because we already popped the operands for this operation from the c.stack before // called localDepth ^^, - OperationSet{Depth: depth + 2, IsTargetVector: isVector}, + NewOperationSet(depth+2, isVector), ) } else { c.emit( // +1 because we already popped the operands for this operation from the c.stack before // called localDepth ^^, - OperationSet{Depth: depth + 1, IsTargetVector: isVector}, + NewOperationSet(depth+1, isVector), ) } case wasm.OpcodeLocalTee: @@ -902,13 +905,13 @@ operatorSwitch: isVector := c.localType(index) == wasm.ValueTypeV128 if isVector { c.emit( - OperationPick{Depth: 1, IsTargetVector: isVector}, - OperationSet{Depth: depth + 2, IsTargetVector: isVector}, + NewOperationPick(1, isVector), + NewOperationSet(depth+2, isVector), ) } else { c.emit( - OperationPick{Depth: 0, IsTargetVector: isVector}, - OperationSet{Depth: depth + 1, IsTargetVector: isVector}, + NewOperationPick(0, isVector), + NewOperationSet(depth+1, isVector), ) } case wasm.OpcodeGlobalGet: @@ -924,106 +927,92 @@ operatorSwitch: if err != nil { return err } - c.emit(OperationLoad{Type: UnsignedTypeI32, Arg: imm}) + c.emit(NewOperationLoad(UnsignedTypeI32, imm)) case wasm.OpcodeI64Load: imm, err := c.readMemoryArg(wasm.OpcodeI64LoadName) if err != nil { return err } - c.emit(OperationLoad{Type: UnsignedTypeI64, Arg: imm}) + c.emit(NewOperationLoad(UnsignedTypeI64, imm)) case wasm.OpcodeF32Load: imm, err := c.readMemoryArg(wasm.OpcodeF32LoadName) if err != nil { return err } - c.emit(OperationLoad{Type: UnsignedTypeF32, Arg: imm}) + c.emit(NewOperationLoad(UnsignedTypeF32, imm)) case wasm.OpcodeF64Load: imm, err := c.readMemoryArg(wasm.OpcodeF64LoadName) if err != nil { return err } - c.emit(OperationLoad{Type: UnsignedTypeF64, Arg: imm}) + c.emit(NewOperationLoad(UnsignedTypeF64, imm)) case wasm.OpcodeI32Load8S: imm, err := c.readMemoryArg(wasm.OpcodeI32Load8SName) if err != nil { return err } - c.emit(OperationLoad8{Type: SignedInt32, Arg: imm}) + c.emit(NewOperationLoad8(SignedInt32, imm)) case wasm.OpcodeI32Load8U: imm, err := c.readMemoryArg(wasm.OpcodeI32Load8UName) if err != nil { return err } - c.emit(OperationLoad8{Type: SignedUint32, Arg: imm}) + c.emit(NewOperationLoad8(SignedUint32, imm)) case wasm.OpcodeI32Load16S: imm, err := c.readMemoryArg(wasm.OpcodeI32Load16SName) if err != nil { return err } - c.emit(OperationLoad16{Type: SignedInt32, Arg: imm}) + c.emit(NewOperationLoad16(SignedInt32, imm)) case wasm.OpcodeI32Load16U: imm, err := c.readMemoryArg(wasm.OpcodeI32Load16UName) if err != nil { return err } - c.emit( - OperationLoad16{Type: SignedUint32, Arg: imm}, - ) + c.emit(NewOperationLoad16(SignedUint32, imm)) case wasm.OpcodeI64Load8S: imm, err := c.readMemoryArg(wasm.OpcodeI64Load8SName) if err != nil { return err } - c.emit( - OperationLoad8{Type: SignedInt64, Arg: imm}, - ) + c.emit(NewOperationLoad8(SignedInt64, imm)) case wasm.OpcodeI64Load8U: imm, err := c.readMemoryArg(wasm.OpcodeI64Load8UName) if err != nil { return err } - c.emit( - OperationLoad8{Type: SignedUint64, Arg: imm}, - ) + c.emit(NewOperationLoad8(SignedUint64, imm)) case wasm.OpcodeI64Load16S: imm, err := c.readMemoryArg(wasm.OpcodeI64Load16SName) if err != nil { return err } - c.emit( - OperationLoad16{Type: SignedInt64, Arg: imm}, - ) + c.emit(NewOperationLoad16(SignedInt64, imm)) case wasm.OpcodeI64Load16U: imm, err := c.readMemoryArg(wasm.OpcodeI64Load16UName) if err != nil { return err } - c.emit( - OperationLoad16{Type: SignedUint64, Arg: imm}, - ) + c.emit(NewOperationLoad16(SignedUint64, imm)) case wasm.OpcodeI64Load32S: imm, err := c.readMemoryArg(wasm.OpcodeI64Load32SName) if err != nil { return err } - c.emit( - OperationLoad32{Signed: true, Arg: imm}, - ) + c.emit(NewOperationLoad32(true, imm)) case wasm.OpcodeI64Load32U: imm, err := c.readMemoryArg(wasm.OpcodeI64Load32UName) if err != nil { return err } - c.emit( - OperationLoad32{Signed: false, Arg: imm}, - ) + c.emit(NewOperationLoad32(false, imm)) case wasm.OpcodeI32Store: imm, err := c.readMemoryArg(wasm.OpcodeI32StoreName) if err != nil { return err } c.emit( - OperationStore{Type: UnsignedTypeI32, Arg: imm}, + NewOperationStore(UnsignedTypeI32, imm), ) case wasm.OpcodeI64Store: imm, err := c.readMemoryArg(wasm.OpcodeI64StoreName) @@ -1031,7 +1020,7 @@ operatorSwitch: return err } c.emit( - OperationStore{Type: UnsignedTypeI64, Arg: imm}, + NewOperationStore(UnsignedTypeI64, imm), ) case wasm.OpcodeF32Store: imm, err := c.readMemoryArg(wasm.OpcodeF32StoreName) @@ -1039,7 +1028,7 @@ operatorSwitch: return err } c.emit( - OperationStore{Type: UnsignedTypeF32, Arg: imm}, + NewOperationStore(UnsignedTypeF32, imm), ) case wasm.OpcodeF64Store: imm, err := c.readMemoryArg(wasm.OpcodeF64StoreName) @@ -1047,7 +1036,7 @@ operatorSwitch: return err } c.emit( - OperationStore{Type: UnsignedTypeF64, Arg: imm}, + NewOperationStore(UnsignedTypeF64, imm), ) case wasm.OpcodeI32Store8: imm, err := c.readMemoryArg(wasm.OpcodeI32Store8Name) @@ -1055,7 +1044,7 @@ operatorSwitch: return err } c.emit( - OperationStore8{Arg: imm}, + NewOperationStore8(imm), ) case wasm.OpcodeI32Store16: imm, err := c.readMemoryArg(wasm.OpcodeI32Store16Name) @@ -1063,7 +1052,7 @@ operatorSwitch: return err } c.emit( - OperationStore16{Arg: imm}, + NewOperationStore16(imm), ) case wasm.OpcodeI64Store8: imm, err := c.readMemoryArg(wasm.OpcodeI64Store8Name) @@ -1071,7 +1060,7 @@ operatorSwitch: return err } c.emit( - OperationStore8{Arg: imm}, + NewOperationStore8(imm), ) case wasm.OpcodeI64Store16: imm, err := c.readMemoryArg(wasm.OpcodeI64Store16Name) @@ -1079,7 +1068,7 @@ operatorSwitch: return err } c.emit( - OperationStore16{Arg: imm}, + NewOperationStore16(imm), ) case wasm.OpcodeI64Store32: imm, err := c.readMemoryArg(wasm.OpcodeI64Store32Name) @@ -1087,7 +1076,7 @@ operatorSwitch: return err } c.emit( - OperationStore32{Arg: imm}, + NewOperationStore32(imm), ) case wasm.OpcodeMemorySize: c.result.UsesMemory = true diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go index 80b9bda6..ffd46cd3 100644 --- a/internal/wazeroir/compiler_test.go +++ b/internal/wazeroir/compiler_test.go @@ -103,7 +103,7 @@ func TestCompile(t *testing.T) { }, expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$x] - OperationPick{Depth: 0}, // [$x, $x] + NewOperationPick(0, false), // [$x, $x] OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$x] OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -139,9 +139,9 @@ func TestCompile(t *testing.T) { expected: &CompilationResult{ Operations: []Operation{ // begin with params: [] NewOperationConstI32(8), // [8] - OperationLoad{Type: UnsignedTypeI32, Arg: MemoryArg{Alignment: 2, Offset: 0}}, // [x] - OperationDrop{Depth: &InclusiveRange{}}, // [] - OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! + NewOperationLoad(UnsignedTypeI32, MemoryArg{Alignment: 2, Offset: 0}), // [x] + OperationDrop{Depth: &InclusiveRange{}}, // [] + OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, LabelCallers: map[LabelID]uint32{}, Types: []wasm.FunctionType{v_v}, @@ -166,9 +166,9 @@ func TestCompile(t *testing.T) { expected: &CompilationResult{ Operations: []Operation{ // begin with params: [] NewOperationConstI32(8), // [8] - OperationLoad{Type: UnsignedTypeI32, Arg: MemoryArg{Alignment: 2, Offset: 0}}, // [x] - OperationDrop{Depth: &InclusiveRange{}}, // [] - OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! + NewOperationLoad(UnsignedTypeI32, MemoryArg{Alignment: 2, Offset: 0}), // [x] + OperationDrop{Depth: &InclusiveRange{}}, // [] + OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, LabelCallers: map[LabelID]uint32{}, Types: []wasm.FunctionType{v_v}, @@ -189,7 +189,7 @@ func TestCompile(t *testing.T) { }, expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$delta] - OperationPick{Depth: 0}, // [$delta, $delta] + NewOperationPick(0, false), // [$delta, $delta] NewOperationMemoryGrow(), // [$delta, $old_size] OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$old_size] OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! @@ -397,8 +397,8 @@ func TestCompile_MultiValue(t *testing.T) { }, expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$x, $y] - OperationPick{Depth: 0}, // [$x, $y, $y] - OperationPick{Depth: 2}, // [$x, $y, $y, $x] + NewOperationPick(0, false), // [$x, $y, $y] + NewOperationPick(2, false), // [$x, $y, $y, $x] OperationDrop{Depth: &InclusiveRange{Start: 2, End: 3}}, // [$y, $x] OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -499,8 +499,8 @@ func TestCompile_MultiValue(t *testing.T) { // ) expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$0] - NewOperationConstI32(1), // [$0, 1] - OperationPick{Depth: 1}, // [$0, 1, $0] + NewOperationConstI32(1), // [$0, 1] + NewOperationPick(1, false), // [$0, 1, $0] OperationBrIf{ // [$0, 1] Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}}, Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}}, @@ -557,9 +557,9 @@ func TestCompile_MultiValue(t *testing.T) { // ) expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$0] - NewOperationConstI32(1), // [$0, 1] - NewOperationConstI32(2), // [$0, 1, 2] - OperationPick{Depth: 2}, // [$0, 1, 2, $0] + NewOperationConstI32(1), // [$0, 1] + NewOperationConstI32(2), // [$0, 1, 2] + NewOperationPick(2, false), // [$0, 1, 2, $0] OperationBrIf{ // [$0, 1, 2] Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}}, Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}}, @@ -614,9 +614,9 @@ func TestCompile_MultiValue(t *testing.T) { // ) expected: &CompilationResult{ Operations: []Operation{ // begin with params: [$0] - NewOperationConstI32(1), // [$0, 1] - NewOperationConstI32(2), // [$0, 1, 2] - OperationPick{Depth: 2}, // [$0, 1, 2, $0] + NewOperationConstI32(1), // [$0, 1] + NewOperationConstI32(2), // [$0, 1, 2] + NewOperationPick(2, false), // [$0, 1, 2, $0] OperationBrIf{ // [$0, 1, 2] Then: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindHeader}}, Else: BranchTargetDrop{Target: Label{FrameID: 2, Kind: LabelKindElse}}, @@ -675,7 +675,7 @@ func TestCompile_NonTrappingFloatToIntConversion(t *testing.T) { expected := &CompilationResult{ Operations: []Operation{ // begin with params: [$0] - OperationPick{Depth: 0}, // [$0, $0] + NewOperationPick(0, false), // [$0, $0] OperationITruncFromF{ // [$0, i32.trunc_sat_f32_s($0)] InputType: Float32, OutputType: SignedInt32, @@ -710,7 +710,7 @@ func TestCompile_SignExtensionOps(t *testing.T) { expected := &CompilationResult{ Operations: []Operation{ // begin with params: [$0] - OperationPick{Depth: 0}, // [$0, $0] + NewOperationPick(0, false), // [$0, $0] NewOperationSignExtend32From8(), // [$0, i32.extend8_s($0)] OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [i32.extend8_s($0)] OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! @@ -762,7 +762,7 @@ func TestCompile_CallIndirectNonZeroTableIndex(t *testing.T) { expected := &CompilationResult{ Operations: []Operation{ // begin with params: [] NewOperationConstI32(0), - OperationCallIndirect{TypeIndex: 2, TableIndex: 5}, + NewOperationCallIndirect(2, 5), OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, HasTable: true, @@ -1028,7 +1028,7 @@ func TestCompile_Locals(t *testing.T) { }}}, }, expected: []Operation{ - OperationPick{Depth: 1, IsTargetVector: true}, // [param[0].low, param[0].high] -> [param[0].low, param[0].high, param[0].low, param[0].high] + NewOperationPick(1, true), // [param[0].low, param[0].high] -> [param[0].low, param[0].high, param[0].low, param[0].high] OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1044,7 +1044,7 @@ func TestCompile_Locals(t *testing.T) { }}}, }, expected: []Operation{ - OperationPick{Depth: 0, IsTargetVector: false}, // [param[0]] -> [param[0], param[0]] + NewOperationPick(0, false), // [param[0]] -> [param[0], param[0]] OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1064,7 +1064,7 @@ func TestCompile_Locals(t *testing.T) { }, expected: []Operation{ OperationV128Const{Lo: 0, Hi: 0}, - OperationPick{Depth: 1, IsTargetVector: true}, // [p[0].low, p[0].high] -> [p[0].low, p[0].high, p[0].low, p[0].high] + NewOperationPick(1, true), // [p[0].low, p[0].high] -> [p[0].low, p[0].high, p[0].low, p[0].high] OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1086,7 +1086,7 @@ func TestCompile_Locals(t *testing.T) { // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02] - OperationSet{Depth: 3, IsTargetVector: true}, + NewOperationSet(3, true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1104,7 +1104,7 @@ func TestCompile_Locals(t *testing.T) { }, expected: []Operation{ NewOperationConstI32(0x1), - OperationSet{Depth: 1, IsTargetVector: false}, + NewOperationSet(1, false), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1130,7 +1130,7 @@ func TestCompile_Locals(t *testing.T) { // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02] - OperationSet{Depth: 3, IsTargetVector: true}, + NewOperationSet(3, true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1152,9 +1152,9 @@ func TestCompile_Locals(t *testing.T) { // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] - OperationPick{Depth: 1, IsTargetVector: true}, + NewOperationPick(1, true), // [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] -> [0x01, 0x02, 0x01, 0x02] - OperationSet{Depth: 5, IsTargetVector: true}, + NewOperationSet(5, true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1172,8 +1172,8 @@ func TestCompile_Locals(t *testing.T) { }, expected: []Operation{ NewOperationConstF32(math.Float32frombits(1)), - OperationPick{Depth: 0, IsTargetVector: false}, - OperationSet{Depth: 2, IsTargetVector: false}, + NewOperationPick(0, false), + NewOperationSet(2, false), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -1199,9 +1199,9 @@ func TestCompile_Locals(t *testing.T) { // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] - OperationPick{Depth: 1, IsTargetVector: true}, + NewOperationPick(1, true), // [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x2] -> [0x01, 0x02, 0x01, 0x02] - OperationSet{Depth: 5, IsTargetVector: true}, + NewOperationSet(5, true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, // return! }, @@ -2938,7 +2938,7 @@ func TestCompile_select_vectors(t *testing.T) { OperationV128Const{Lo: 0x1, Hi: 0x2}, OperationV128Const{Lo: 0x3, Hi: 0x4}, NewOperationConstI32(0), - OperationSelect{IsTargetVector: true}, + NewOperationSelect(true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, }, @@ -2964,7 +2964,7 @@ func TestCompile_select_vectors(t *testing.T) { OperationV128Const{Lo: 0x1, Hi: 0x2}, OperationV128Const{Lo: 0x3, Hi: 0x4}, NewOperationConstI32(0), - OperationSelect{IsTargetVector: true}, + NewOperationSelect(true), OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}}, OperationBr{Target: Label{Kind: LabelKindReturn}}, }, diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go index fe7c03d3..e568bbf2 100644 --- a/internal/wazeroir/operations.go +++ b/internal/wazeroir/operations.go @@ -721,19 +721,7 @@ var ( _ Operation = OperationBr{} _ Operation = OperationBrIf{} _ Operation = OperationBrTable{} - _ Operation = OperationCallIndirect{} _ Operation = OperationDrop{} - _ Operation = OperationSelect{} - _ Operation = OperationPick{} - _ Operation = OperationSet{} - _ Operation = OperationLoad{} - _ Operation = OperationLoad8{} - _ Operation = OperationLoad16{} - _ Operation = OperationLoad32{} - _ Operation = OperationStore{} - _ Operation = OperationStore8{} - _ Operation = OperationStore16{} - _ Operation = OperationStore32{} _ Operation = OperationITruncFromF{} _ Operation = OperationFConvertFromI{} _ Operation = OperationExtend{} @@ -919,6 +907,7 @@ type UnionOperation struct { func (o UnionOperation) String() string { switch o.OpKind { case OperationKindUnreachable, + OperationKindSelect, OperationKindMemorySize, OperationKindMemoryGrow, OperationKindI32WrapFromI64, @@ -943,6 +932,33 @@ func (o UnionOperation) String() string { OperationKindGlobalSet: return fmt.Sprintf("%s %d", o.Kind(), o.B1) + case OperationKindCallIndirect: + return fmt.Sprintf("%s: type=%d, table=%d", o.Kind(), o.U1, o.U2) + + case OperationKindPick, OperationKindSet: + return fmt.Sprintf("%s %d (is_vector=%v)", o.Kind(), o.U1, o.B3) + + case OperationKindLoad, OperationKindStore: + return fmt.Sprintf("%s.%s (align=%d, offset=%d)", UnsignedType(o.B1), o.Kind(), o.U1, o.U2) + + case OperationKindLoad8, + OperationKindLoad16: + return fmt.Sprintf("%s.%s (align=%d, offset=%d)", SignedType(o.B1), o.Kind(), o.U1, o.U2) + + case OperationKindStore8, + OperationKindStore16, + OperationKindStore32: + return fmt.Sprintf("%s (align=%d, offset=%d)", o.Kind(), o.U1, o.U2) + + case OperationKindLoad32: + var t string + if o.B1 == 1 { + t = "i64" + } else { + t = "u64" + } + return fmt.Sprintf("%s.%s (align=%d, offset=%d)", t, o.Kind(), o.U1, o.U2) + case OperationKindEq, OperationKindNe, OperationKindAdd, @@ -1097,7 +1113,7 @@ func NewOperationCall(functionIndex uint32) UnionOperation { return UnionOperation{OpKind: OperationKindCall, U1: uint64(functionIndex)} } -// OperationCallIndirect implements Operation. +// NewOperationCallIndirect implements Operation. // // This corresponds to wasm.OpcodeCallIndirectName, and engines are expected to // consume the one value from the top of stack (called "offset"), @@ -1109,18 +1125,8 @@ func NewOperationCall(functionIndex uint32) UnionOperation { // Therefore, two checks are performed at runtime before entering the target function: // 1) whether "offset" exceeds the length of table Tables[OperationCallIndirect.TableIndex]. // 2) whether the type of the function table[offset] matches the function type specified by OperationCallIndirect.TypeIndex. -type OperationCallIndirect struct { - TypeIndex, TableIndex uint32 -} - -// String implements fmt.Stringer. -func (o OperationCallIndirect) String() string { - return fmt.Sprintf("%s: type=%d, table=%d", o.Kind(), o.TypeIndex, o.TableIndex) -} - -// Kind implements Operation.Kind -func (OperationCallIndirect) Kind() OperationKind { - return OperationKindCallIndirect +func NewOperationCallIndirect(typeIndex, tableIndex uint32) UnionOperation { + return UnionOperation{OpKind: OperationKindCallIndirect, U1: uint64(typeIndex), U2: uint64(tableIndex)} } // InclusiveRange is the range which spans across the value stack starting from the top to the bottom, and @@ -1148,65 +1154,38 @@ func (OperationDrop) Kind() OperationKind { return OperationKindDrop } -// OperationSelect implements Operation. +// NewOperationSelect is a constructor for UnionOperation with Kind OperationKindSelect. // // This corresponds to wasm.OpcodeSelect. // // The engines are expected to pop three values, say [..., x2, x1, c], then if the value "c" equals zero, // "x1" is pushed back onto the stack and, otherwise "x2" is pushed back. -type OperationSelect struct { - // IsTargetVector true if the selection target value's type is wasm.ValueTypeV128. - IsTargetVector bool -} - -// String implements fmt.Stringer. -func (o OperationSelect) String() string { return o.Kind().String() } - -// Kind implements Operation.Kind -func (OperationSelect) Kind() OperationKind { - return OperationKindSelect -} - -// OperationPick implements Operation. // -// The engines are expected to copy a value pointed by OperationPick.Depth, and push the +// isTargetVector true if the selection target value's type is wasm.ValueTypeV128. +func NewOperationSelect(isTargetVector bool) UnionOperation { + return UnionOperation{OpKind: OperationKindSelect, B3: isTargetVector} +} + +// NewOperationPick is a constructor for UnionOperation with Kind OperationKindPick. +// +// The engines are expected to copy a value pointed by depth, and push the // copied value onto the top of the stack. -type OperationPick struct { - // Depth is the location of the pick target in the uint64 value stack at runtime. - // If IsTargetVector=true, this points to the location of the lower 64-bits of the vector. - Depth int - IsTargetVector bool +// +// depth is the location of the pick target in the uint64 value stack at runtime. +// If isTargetVector=true, this points to the location of the lower 64-bits of the vector. +func NewOperationPick(depth int, isTargetVector bool) UnionOperation { + return UnionOperation{OpKind: OperationKindPick, U1: uint64(depth), B3: isTargetVector} } -// String implements fmt.Stringer. -func (o OperationPick) String() string { - return fmt.Sprintf("%s %d (is_vector=%v)", o.Kind(), o.Depth, o.IsTargetVector) -} - -// Kind implements Operation.Kind -func (OperationPick) Kind() OperationKind { - return OperationKindPick -} - -// OperationSet implements Operation. +// NewOperationSet is a constructor for UnionOperation with Kind OperationKindSet. // // The engines are expected to set the top value of the stack to the location specified by -// OperationSet.Depth. -type OperationSet struct { - // Depth is the location of the set target in the uint64 value stack at runtime. - // If IsTargetVector=true, this points the location of the lower 64-bits of the vector. - Depth int - IsTargetVector bool -} - -// String implements fmt.Stringer. -func (o OperationSet) String() string { - return fmt.Sprintf("%s %d (is_vector=%v)", o.Kind(), o.Depth, o.IsTargetVector) -} - -// Kind implements Operation.Kind -func (OperationSet) Kind() OperationKind { - return OperationKindSet +// depth. +// +// depth is the location of the set target in the uint64 value stack at runtime. +// If isTargetVector=true, this points the location of the lower 64-bits of the vector. +func NewOperationSet(depth int, isTargetVector bool) UnionOperation { + return UnionOperation{OpKind: OperationKindSet, U1: uint64(depth), B3: isTargetVector} } // NewOperationGlobalGet is a constructor for UnionOperation with Kind OperationKindGlobalGet. @@ -1244,175 +1223,88 @@ type MemoryArg struct { Offset uint32 } -// OperationLoad implements Operation. +// NewOperationLoad is a constructor for UnionOperation with Kind OperationKindLoad. // // This corresponds to wasm.OpcodeI32LoadName wasm.OpcodeI64LoadName wasm.OpcodeF32LoadName and wasm.OpcodeF64LoadName. // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise load the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationLoad struct { - Type UnsignedType - Arg MemoryArg +func NewOperationLoad(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindLoad, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationLoad) String() string { - return fmt.Sprintf("%s.%s (align=%d, offset=%d)", o.Type, o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationLoad) Kind() OperationKind { - return OperationKindLoad -} - -// OperationLoad8 implements Operation. +// NewOperationLoad8 is a constructor for UnionOperation with Kind OperationKindLoad8. // // This corresponds to wasm.OpcodeI32Load8SName wasm.OpcodeI32Load8UName wasm.OpcodeI64Load8SName wasm.OpcodeI64Load8UName. // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise load the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationLoad8 struct { - Type SignedInt - Arg MemoryArg +func NewOperationLoad8(signedInt SignedInt, arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindLoad8, B1: byte(signedInt), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationLoad8) String() string { - return fmt.Sprintf("%s.%s (align=%d, offset=%d)", o.Type, o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationLoad8) Kind() OperationKind { - return OperationKindLoad8 -} - -// OperationLoad16 implements Operation. +// NewOperationLoad16 is a constructor for UnionOperation with Kind OperationKindLoad16. // // This corresponds to wasm.OpcodeI32Load16SName wasm.OpcodeI32Load16UName wasm.OpcodeI64Load16SName wasm.OpcodeI64Load16UName. // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise load the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationLoad16 struct { - Type SignedInt - Arg MemoryArg +func NewOperationLoad16(signedInt SignedInt, arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindLoad16, B1: byte(signedInt), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationLoad16) String() string { - return fmt.Sprintf("%s.%s (align=%d, offset=%d)", o.Type, o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationLoad16) Kind() OperationKind { - return OperationKindLoad16 -} - -// OperationLoad32 implements Operation. +// NewOperationLoad32 is a constructor for UnionOperation with Kind OperationKindLoad32. // // This corresponds to wasm.OpcodeI64Load32SName wasm.OpcodeI64Load32UName. // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise load the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationLoad32 struct { - Signed bool - Arg MemoryArg -} - -// String implements fmt.Stringer. -func (o OperationLoad32) String() string { - var t string - if o.Signed { - t = "i64" - } else { - t = "u64" +func NewOperationLoad32(signed bool, arg MemoryArg) UnionOperation { + sigB := byte(0) + if signed { + sigB = 1 } - return fmt.Sprintf("%s.%s (align=%d, offset=%d)", t, o.Kind(), o.Arg.Alignment, o.Arg.Offset) + return UnionOperation{OpKind: OperationKindLoad32, B1: sigB, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// Kind implements Operation.Kind -func (OperationLoad32) Kind() OperationKind { - return OperationKindLoad32 -} - -// OperationStore implements Operation. +// NewOperationStore is a constructor for UnionOperation with Kind OperationKindStore. // // # This corresponds to wasm.OpcodeI32StoreName wasm.OpcodeI64StoreName wasm.OpcodeF32StoreName wasm.OpcodeF64StoreName // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise store the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationStore struct { - Type UnsignedType - Arg MemoryArg +func NewOperationStore(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindStore, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationStore) String() string { - return fmt.Sprintf("%s.%s (align=%d, offset=%d)", o.Type, o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationStore) Kind() OperationKind { - return OperationKindStore -} - -// OperationStore8 implements Operation. +// NewOperationStore8 is a constructor for UnionOperation with Kind OperationKindStore8. // // # This corresponds to wasm.OpcodeI32Store8Name wasm.OpcodeI64Store8Name // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise store the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationStore8 struct { - Arg MemoryArg +func NewOperationStore8(arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindStore8, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationStore8) String() string { - return fmt.Sprintf("%s (align=%d, offset=%d)", o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationStore8) Kind() OperationKind { - return OperationKindStore8 -} - -// OperationStore16 implements Operation. +// NewOperationStore16 is a constructor for UnionOperation with Kind OperationKindStore16. // // # This corresponds to wasm.OpcodeI32Store16Name wasm.OpcodeI64Store16Name // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise store the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationStore16 struct { - Arg MemoryArg +func NewOperationStore16(arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindStore16, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } -// String implements fmt.Stringer. -func (o OperationStore16) String() string { - return fmt.Sprintf("%s (align=%d, offset=%d)", o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind -func (OperationStore16) Kind() OperationKind { - return OperationKindStore16 -} - -// OperationStore32 implements Operation. +// NewOperationStore32 is a constructor for UnionOperation with Kind OperationKindStore32. // // # This corresponds to wasm.OpcodeI64Store32Name // // The engines are expected to check the boundary of memory length, and exit the execution if this exceeds the boundary, // otherwise store the corresponding value following the semantics of the corresponding WebAssembly instruction. -type OperationStore32 struct { - Arg MemoryArg -} - -// String implements fmt.Stringer. -func (o OperationStore32) String() string { - return fmt.Sprintf("%s (align=%d, offset=%d)", o.Kind(), o.Arg.Alignment, o.Arg.Offset) -} - -// Kind implements Operation.Kind. -func (OperationStore32) Kind() OperationKind { - return OperationKindStore32 +func NewOperationStore32(arg MemoryArg) UnionOperation { + return UnionOperation{OpKind: OperationKindStore32, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} } // NewOperationMemorySize is a constructor for UnionOperation with Kind OperationKindMemorySize. diff --git a/internal/wazeroir/operations_test.go b/internal/wazeroir/operations_test.go index 1e5ae8f4..8fc09d35 100644 --- a/internal/wazeroir/operations_test.go +++ b/internal/wazeroir/operations_test.go @@ -16,11 +16,20 @@ func TestOperationKind_String(t *testing.T) { // TestUnionOperation_String ensures that UnionOperation's stringer is well-defined for all supported OpKinds. func TestUnionOperation_String(t *testing.T) { op := UnionOperation{} + // TODO: after done with union refactoring, use + // `for k := OperationKind(0); k < operationKindEnd; k++ { ... }` + // rather than listing all kinds here manually like TestOperationKind_String. for _, k := range []OperationKind{ OperationKindUnreachable, OperationKindCall, + OperationKindCallIndirect, + OperationKindSelect, OperationKindGlobalGet, OperationKindGlobalSet, + OperationKindLoad, + OperationKindLoad8, + OperationKindLoad16, + OperationKindLoad32, OperationKindMemorySize, OperationKindMemoryGrow, OperationKindConstI32,