Files
wazero/internal/wazeroir/compiler_test.go
Takeshi Yoneda 119b069ba2 wazeroir: backfill vector instruction unittets (#623)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
2022-06-06 10:17:21 +09:00

1931 lines
72 KiB
Go

package wazeroir
import (
"context"
"fmt"
"math"
"testing"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
"github.com/tetratelabs/wazero/internal/watzero"
)
// ctx is an arbitrary, non-default context.
var ctx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
var (
f32, f64, i32 = wasm.ValueTypeF32, wasm.ValueTypeF64, wasm.ValueTypeI32
f32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{f32}, Results: []wasm.ValueType{i32},
ParamNumInUint64: 1,
ResultNumInUint64: 1,
}
i32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32},
ParamNumInUint64: 1,
ResultNumInUint64: 1,
}
i32i32_i32 = &wasm.FunctionType{Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32},
ParamNumInUint64: 2,
ResultNumInUint64: 1,
}
v_v = &wasm.FunctionType{}
v_f64f64 = &wasm.FunctionType{Results: []wasm.ValueType{f64, f64}, ResultNumInUint64: 2}
)
func TestCompile(t *testing.T) {
tests := []struct {
name string
module *wasm.Module
expected *CompilationResult
enabledFeatures wasm.Features
}{
{
name: "nullary",
module: requireModuleText(t, `(module (func))`),
expected: &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Functions: []uint32{0},
Types: []*wasm.FunctionType{{}},
Signature: &wasm.FunctionType{},
TableTypes: []wasm.RefType{},
},
},
{
name: "identity",
module: requireModuleText(t, `(module
(func (param $x i32) (result i32) local.get 0)
)`),
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$x]
&OperationPick{Depth: 0}, // [$x, $x]
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$x]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Types: []*wasm.FunctionType{
{Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32},
ParamNumInUint64: 1,
ResultNumInUint64: 1},
},
Functions: []uint32{0},
Signature: &wasm.FunctionType{
Params: []wasm.ValueType{wasm.ValueTypeI32}, Results: []wasm.ValueType{wasm.ValueTypeI32},
ParamNumInUint64: 1,
ResultNumInUint64: 1,
},
TableTypes: []wasm.RefType{},
},
},
{
name: "memory.grow", // Ex to expose ops to grow memory
module: requireModuleText(t, `(module
(func (param $delta i32) (result (;previous_size;) i32) local.get 0 memory.grow)
)`),
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$delta]
&OperationPick{Depth: 0}, // [$delta, $delta]
&OperationMemoryGrow{}, // [$delta, $old_size]
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [$old_size]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Types: []*wasm.FunctionType{{
Params: []wasm.ValueType{i32}, Results: []wasm.ValueType{i32},
ParamNumInUint64: 1,
ResultNumInUint64: 1,
}},
Functions: []uint32{0},
Signature: &wasm.FunctionType{
Params: []wasm.ValueType{wasm.ValueTypeI32}, Results: []wasm.ValueType{wasm.ValueTypeI32},
ParamNumInUint64: 1,
ResultNumInUint64: 1,
},
TableTypes: []wasm.RefType{},
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
enabledFeatures := tc.enabledFeatures
if enabledFeatures == 0 {
enabledFeatures = wasm.Features20220419
}
res, err := CompileFunctions(ctx, enabledFeatures, tc.module)
require.NoError(t, err)
require.Equal(t, tc.expected, res[0])
})
}
}
func TestCompile_Block(t *testing.T) {
tests := []struct {
name string
module *wasm.Module
expected *CompilationResult
enabledFeatures wasm.Features
}{
{
name: "type-i32-i32",
module: &wasm.Module{
TypeSection: []*wasm.FunctionType{v_v},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeBlock, 0x40,
wasm.OpcodeBr, 0,
wasm.OpcodeI32Add,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
wasm.OpcodeEnd,
}}},
},
// Above set manually until the text compiler supports this:
// (func (export "type-i32-i32") (block (drop (i32.add (br 0)))))
expected: &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationBr{
Target: &BranchTarget{
Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
},
},
&OperationLabel{
Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
},
&OperationBr{Target: &BranchTarget{}}, // return!
},
// Note: i32.add comes after br 0 so is unreachable. Compilation succeeds when it feels like it
// shouldn't because the br instruction is stack-polymorphic. In other words, (br 0) substitutes for the
// two i32 parameters to add.
LabelCallers: map[string]uint32{".L2_cont": 1},
Functions: []uint32{0},
Types: []*wasm.FunctionType{v_v},
Signature: v_v,
TableTypes: []wasm.RefType{},
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
requireCompilationResult(t, tc.enabledFeatures, tc.expected, tc.module)
})
}
}
// TestCompile_BulkMemoryOperations uses the example from the "bulk-memory-operations" overview.
func TestCompile_BulkMemoryOperations(t *testing.T) {
// Set manually until the text compiler supports this:
// (module
// (memory 1)
// (data (i32.const 0) "hello") ;; data segment 0, is active so always copied
// (data "goodbye") ;; data segment 1, is passive
//
// (func $start
// ;; copy data segment 1 into memory 0 (the 0 is implicit)
// (memory.init 1
// (i32.const 16) ;; target offset
// (i32.const 0) ;; source offset
// (i32.const 7)) ;; length
//
// ;; The memory used by this segment is no longer needed, so this segment can
// ;; be dropped.
// (data.drop 1)
// )
// (start $start)
// )
two := uint32(2)
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{v_v},
FunctionSection: []wasm.Index{0},
MemorySection: &wasm.Memory{Min: 1},
DataSection: []*wasm.DataSegment{
{
OffsetExpression: &wasm.ConstantExpression{
Opcode: wasm.OpcodeI32Const,
Data: []byte{0x00},
},
Init: []byte("hello"),
},
{
OffsetExpression: nil, // passive
Init: []byte("goodbye"),
},
},
DataCountSection: &two,
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 16,
wasm.OpcodeI32Const, 0,
wasm.OpcodeI32Const, 7,
wasm.OpcodeMiscPrefix, wasm.OpcodeMiscMemoryInit, 1, 0, // segment 1, memory 0
wasm.OpcodeMiscPrefix, wasm.OpcodeMiscDataDrop, 1,
wasm.OpcodeEnd,
}}}}
expected := &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationConstI32{16}, // [16]
&OperationConstI32{0}, // [16, 0]
&OperationConstI32{7}, // [16, 0, 7]
&OperationMemoryInit{1}, // []
&OperationDataDrop{1}, // []
&OperationBr{Target: &BranchTarget{}}, // return!
},
HasMemory: true,
NeedsAccessToDataInstances: true,
LabelCallers: map[string]uint32{},
Signature: v_v,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{v_v},
TableTypes: []wasm.RefType{},
}
res, err := CompileFunctions(ctx, wasm.FeatureBulkMemoryOperations, module)
require.NoError(t, err)
require.Equal(t, expected, res[0])
}
func TestCompile_MultiValue(t *testing.T) {
i32i32_i32i32 := &wasm.FunctionType{
Params: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
Results: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI32},
ParamNumInUint64: 2,
ResultNumInUint64: 2,
}
_i32i64 := &wasm.FunctionType{
Results: []wasm.ValueType{wasm.ValueTypeI32, wasm.ValueTypeI64},
ParamNumInUint64: 0,
ResultNumInUint64: 2,
}
tests := []struct {
name string
module *wasm.Module
expected *CompilationResult
enabledFeatures wasm.Features
}{
{
name: "swap",
module: requireModuleText(t, `(module
(func (param $x i32) (param $y i32) (result i32 i32) local.get 1 local.get 0)
)`),
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$x, $y]
&OperationPick{Depth: 0}, // [$x, $y, $y]
&OperationPick{Depth: 2}, // [$x, $y, $y, $x]
&OperationDrop{Depth: &InclusiveRange{Start: 2, End: 3}}, // [$y, $x]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Signature: i32i32_i32i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{i32i32_i32i32},
TableTypes: []wasm.RefType{},
},
},
{
name: "br.wast - type-f64-f64-value",
module: &wasm.Module{
TypeSection: []*wasm.FunctionType{v_f64f64},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeBlock, 0, // (block (result f64 f64)
wasm.OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0x10, 0x40, // (f64.const 4)
wasm.OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0x14, 0x40, // (f64.const 5)
wasm.OpcodeBr, 0,
wasm.OpcodeF64Add,
wasm.OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0x18, 0x40, // (f64.const 6)
wasm.OpcodeEnd,
wasm.OpcodeEnd,
}}},
},
// Above set manually until the text compiler supports this:
// (func $type-f64-f64-value (result f64 f64)
// (block (result f64 f64)
// (f64.add (br 0 (f64.const 4) (f64.const 5))) (f64.const 6)
// )
// )
expected: &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationConstF64{Value: 4}, // [4]
&OperationConstF64{Value: 5}, // [4, 5]
&OperationBr{
Target: &BranchTarget{
Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
},
},
&OperationLabel{
Label: &Label{FrameID: 2, Kind: LabelKindContinuation}, // arbitrary FrameID
},
&OperationBr{Target: &BranchTarget{}}, // return!
},
// Note: f64.add comes after br 0 so is unreachable. This is why neither the add, nor its other operand
// are in the above compilation result.
LabelCallers: map[string]uint32{".L2_cont": 1}, // arbitrary label
Signature: v_f64f64,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{v_f64f64},
TableTypes: []wasm.RefType{},
},
},
{
name: "call.wast - $const-i32-i64",
module: requireModuleText(t, `(module
(func $const-i32-i64 (result i32 i64) i32.const 306 i64.const 356)
)`),
expected: &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationConstI32{Value: 306}, // [306]
&OperationConstI64{Value: 356}, // [306, 356]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Signature: _i32i64,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{_i32i64},
TableTypes: []wasm.RefType{},
},
},
{
name: "if.wast - param",
module: &wasm.Module{
TypeSection: []*wasm.FunctionType{i32_i32}, // (func (param i32) (result i32)
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 1, // (i32.const 1)
wasm.OpcodeLocalGet, 0, wasm.OpcodeIf, 0, // (if (param i32) (result i32) (local.get 0)
wasm.OpcodeI32Const, 2, wasm.OpcodeI32Add, // (then (i32.const 2) (i32.add))
wasm.OpcodeElse, wasm.OpcodeI32Const, 0x7e, wasm.OpcodeI32Add, // (else (i32.const -2) (i32.add))
wasm.OpcodeEnd, // )
wasm.OpcodeEnd, // )
}}},
},
// Above set manually until the text compiler supports this:
// (func (export "param") (param i32) (result i32)
// (i32.const 1)
// (if (param i32) (result i32) (local.get 0)
// (then (i32.const 2) (i32.add))
// (else (i32.const -2) (i32.add))
// )
// )
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$0]
&OperationConstI32{Value: 1}, // [$0, 1]
&OperationPick{Depth: 1}, // [$0, 1, $0]
&OperationBrIf{ // [$0, 1]
Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
&OperationConstI32{Value: 2}, // [$0, 1, 2]
&OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
&OperationConstI32{Value: uint32(api.EncodeI32(-2))}, // [$0, 1, -2]
&OperationAdd{Type: UnsignedTypeI32}, // [$0, -1]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
&OperationBr{Target: &BranchTarget{}},
},
LabelCallers: map[string]uint32{
".L2": 1,
".L2_cont": 2,
".L2_else": 1,
},
Signature: i32_i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{i32_i32},
TableTypes: []wasm.RefType{},
},
},
{
name: "if.wast - params",
module: &wasm.Module{
TypeSection: []*wasm.FunctionType{
i32_i32, // (func (param i32) (result i32)
i32i32_i32, // (if (param i32 i32) (result i32)
},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 1, // (i32.const 1)
wasm.OpcodeI32Const, 2, // (i32.const 2)
wasm.OpcodeLocalGet, 0, wasm.OpcodeIf, 1, // (if (param i32) (result i32) (local.get 0)
wasm.OpcodeI32Add, // (then (i32.add))
wasm.OpcodeElse, wasm.OpcodeI32Sub, // (else (i32.sub))
wasm.OpcodeEnd, // )
wasm.OpcodeEnd, // )
}}},
},
// Above set manually until the text compiler supports this:
// (func (export "params") (param i32) (result i32)
// (i32.const 1)
// (i32.const 2)
// (if (param i32 i32) (result i32) (local.get 0)
// (then (i32.add))
// (else (i32.sub))
// )
// )
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$0]
&OperationConstI32{Value: 1}, // [$0, 1]
&OperationConstI32{Value: 2}, // [$0, 1, 2]
&OperationPick{Depth: 2}, // [$0, 1, 2, $0]
&OperationBrIf{ // [$0, 1, 2]
Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
&OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
&OperationSub{Type: UnsignedTypeI32}, // [$0, -1]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
&OperationBr{Target: &BranchTarget{}},
},
LabelCallers: map[string]uint32{
".L2": 1,
".L2_cont": 2,
".L2_else": 1,
},
Signature: i32_i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{i32_i32, i32i32_i32},
TableTypes: []wasm.RefType{},
},
},
{
name: "if.wast - params-break",
module: &wasm.Module{
TypeSection: []*wasm.FunctionType{
i32_i32, // (func (param i32) (result i32)
i32i32_i32, // (if (param i32 i32) (result i32)
},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 1, // (i32.const 1)
wasm.OpcodeI32Const, 2, // (i32.const 2)
wasm.OpcodeLocalGet, 0, wasm.OpcodeIf, 1, // (if (param i32) (result i32) (local.get 0)
wasm.OpcodeI32Add, wasm.OpcodeBr, 0, // (then (i32.add) (br 0))
wasm.OpcodeElse, wasm.OpcodeI32Sub, wasm.OpcodeBr, 0, // (else (i32.sub) (br 0))
wasm.OpcodeEnd, // )
wasm.OpcodeEnd, // )
}}},
},
// Above set manually until the text compiler supports this:
// (func (export "params-break") (param i32) (result i32)
// (i32.const 1)
// (i32.const 2)
// (if (param i32 i32) (result i32) (local.get 0)
// (then (i32.add) (br 0))
// (else (i32.sub) (br 0))
// )
// )
expected: &CompilationResult{
Operations: []Operation{ // begin with params: [$0]
&OperationConstI32{Value: 1}, // [$0, 1]
&OperationConstI32{Value: 2}, // [$0, 1, 2]
&OperationPick{Depth: 2}, // [$0, 1, 2, $0]
&OperationBrIf{ // [$0, 1, 2]
Then: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindHeader}}},
Else: &BranchTargetDrop{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindElse}}},
},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindHeader}},
&OperationAdd{Type: UnsignedTypeI32}, // [$0, 3]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindElse}},
&OperationSub{Type: UnsignedTypeI32}, // [$0, -1]
&OperationBr{Target: &BranchTarget{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}}},
&OperationLabel{Label: &Label{FrameID: 2, Kind: LabelKindContinuation}},
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // .L2 = [3], .L2_else = [-1]
&OperationBr{Target: &BranchTarget{}},
},
LabelCallers: map[string]uint32{
".L2": 1,
".L2_cont": 2,
".L2_else": 1,
},
Signature: i32_i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{i32_i32, i32i32_i32},
TableTypes: []wasm.RefType{},
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
enabledFeatures := tc.enabledFeatures
if enabledFeatures == 0 {
enabledFeatures = wasm.Features20220419
}
res, err := CompileFunctions(ctx, enabledFeatures, tc.module)
require.NoError(t, err)
require.Equal(t, tc.expected, res[0])
})
}
}
// TestCompile_NonTrappingFloatToIntConversion picks an arbitrary operator from "nontrapping-float-to-int-conversion".
func TestCompile_NonTrappingFloatToIntConversion(t *testing.T) {
module := requireModuleText(t, `(module
(func (param f32) (result i32) local.get 0 i32.trunc_sat_f32_s)
)`)
expected := &CompilationResult{
Operations: []Operation{ // begin with params: [$0]
&OperationPick{Depth: 0}, // [$0, $0]
&OperationITruncFromF{ // [$0, i32.trunc_sat_f32_s($0)]
InputType: Float32,
OutputType: SignedInt32,
NonTrapping: true,
},
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [i32.trunc_sat_f32_s($0)]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Signature: f32_i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{f32_i32},
TableTypes: []wasm.RefType{},
}
res, err := CompileFunctions(ctx, wasm.FeatureNonTrappingFloatToIntConversion, module)
require.NoError(t, err)
require.Equal(t, expected, res[0])
}
// TestCompile_SignExtensionOps picks an arbitrary operator from "sign-extension-ops".
func TestCompile_SignExtensionOps(t *testing.T) {
module := requireModuleText(t, `(module
(func (param i32) (result i32) local.get 0 i32.extend8_s)
)`)
expected := &CompilationResult{
Operations: []Operation{ // begin with params: [$0]
&OperationPick{Depth: 0}, // [$0, $0]
&OperationSignExtend32From8{}, // [$0, i32.extend8_s($0)]
&OperationDrop{Depth: &InclusiveRange{Start: 1, End: 1}}, // [i32.extend8_s($0)]
&OperationBr{Target: &BranchTarget{}}, // return!
},
LabelCallers: map[string]uint32{},
Signature: i32_i32,
Functions: []wasm.Index{0},
Types: []*wasm.FunctionType{i32_i32},
TableTypes: []wasm.RefType{},
}
res, err := CompileFunctions(ctx, wasm.FeatureSignExtensionOps, module)
require.NoError(t, err)
require.Equal(t, expected, res[0])
}
func requireCompilationResult(t *testing.T, enabledFeatures wasm.Features, expected *CompilationResult, module *wasm.Module) {
if enabledFeatures == 0 {
enabledFeatures = wasm.Features20220419
}
res, err := CompileFunctions(ctx, enabledFeatures, module)
require.NoError(t, err)
require.Equal(t, expected, res[0])
}
func requireModuleText(t *testing.T, wat string) *wasm.Module {
binary, err := watzero.Wat2Wasm(wat)
require.NoError(t, err)
m, err := binaryformat.DecodeModule(binary, wasm.Features20220419, wasm.MemorySizer)
require.NoError(t, err)
return m
}
func TestCompile_CallIndirectNonZeroTableIndex(t *testing.T) {
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{v_v, v_v, v_v},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 0, // call indirect offset
wasm.OpcodeCallIndirect,
2, // Type index for call_indirect.
5, // Non-zero table index for call_indirect.
wasm.OpcodeEnd,
}}},
TableSection: []*wasm.Table{
{Type: wasm.RefTypeExternref}, {Type: wasm.RefTypeFuncref}, {Type: wasm.RefTypeFuncref},
{Type: wasm.RefTypeFuncref}, {Type: wasm.RefTypeFuncref},
{Type: wasm.RefTypeFuncref, Min: 100},
},
}
expected := &CompilationResult{
Operations: []Operation{ // begin with params: []
&OperationConstI32{},
&OperationCallIndirect{TypeIndex: 2, TableIndex: 5},
&OperationBr{Target: &BranchTarget{}}, // return!
},
HasTable: true,
LabelCallers: map[string]uint32{},
Signature: v_v,
Functions: []wasm.Index{0},
TableTypes: []wasm.RefType{
wasm.RefTypeExternref, wasm.RefTypeFuncref, wasm.RefTypeFuncref, wasm.RefTypeFuncref, wasm.RefTypeFuncref, wasm.RefTypeFuncref,
},
Types: []*wasm.FunctionType{v_v, v_v, v_v},
}
res, err := CompileFunctions(ctx, wasm.FeatureBulkMemoryOperations, module)
require.NoError(t, err)
require.Equal(t, expected, res[0])
}
func TestCompile_Refs(t *testing.T) {
tests := []struct {
name string
body []byte
expected []Operation
}{
{
name: "ref.func",
body: []byte{
wasm.OpcodeRefFunc, 100,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationRefFunc{FunctionIndex: 100},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "ref.null (externref)",
body: []byte{
wasm.OpcodeRefNull, wasm.ValueTypeExternref,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI64{Value: 0},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "ref.null (funcref)",
body: []byte{
wasm.OpcodeRefNull, wasm.ValueTypeFuncref,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI64{Value: 0},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "ref.is_null",
body: []byte{
wasm.OpcodeRefFunc, 100,
wasm.OpcodeRefIsNull,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationRefFunc{FunctionIndex: 100},
&OperationEqz{Type: UnsignedInt64},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "ref.is_null (externref)",
body: []byte{
wasm.OpcodeRefNull, wasm.ValueTypeExternref,
wasm.OpcodeRefIsNull,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI64{Value: 0},
&OperationEqz{Type: UnsignedInt64},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: tc.body}},
}
res, err := CompileFunctions(ctx, wasm.Features20220419, module)
require.NoError(t, err)
require.Equal(t, tc.expected, res[0].Operations)
})
}
}
func TestCompile_TableGetOrSet(t *testing.T) {
tests := []struct {
name string
body []byte
expected []Operation
}{
{
name: "table.get",
body: []byte{
wasm.OpcodeI32Const, 10,
wasm.OpcodeTableGet, 0,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI32{Value: 10},
&OperationTableGet{TableIndex: 0},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "table.set (externref)",
body: []byte{
wasm.OpcodeI32Const, 10,
wasm.OpcodeRefNull, wasm.ValueTypeExternref,
wasm.OpcodeTableSet, 0,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI32{Value: 10},
&OperationConstI64{Value: 0},
&OperationTableSet{TableIndex: 0},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "table.set (funcref)",
body: []byte{
wasm.OpcodeI32Const, 10,
wasm.OpcodeRefFunc, 1,
wasm.OpcodeTableSet, 0,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI32{Value: 10},
&OperationRefFunc{FunctionIndex: 1},
&OperationTableSet{TableIndex: 0},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: tc.body}},
TableSection: []*wasm.Table{{}},
}
res, err := CompileFunctions(ctx, wasm.Features20220419, module)
require.NoError(t, err)
require.Equal(t, tc.expected, res[0].Operations)
})
}
}
func TestCompile_TableGrowFillSize(t *testing.T) {
tests := []struct {
name string
body []byte
expected []Operation
}{
{
name: "table.grow",
body: []byte{
wasm.OpcodeRefNull, wasm.RefTypeFuncref,
wasm.OpcodeI32Const, 1,
wasm.OpcodeMiscPrefix, wasm.OpcodeMiscTableGrow, 1,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI64{Value: 0}, // Null ref.
&OperationConstI32{Value: 1},
&OperationTableGrow{TableIndex: 1},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "table.fill",
body: []byte{
wasm.OpcodeI32Const, 10,
wasm.OpcodeRefNull, wasm.RefTypeFuncref,
wasm.OpcodeI32Const, 1,
wasm.OpcodeMiscPrefix, wasm.OpcodeMiscTableFill, 1,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationConstI32{Value: 10},
&OperationConstI64{Value: 0}, // Null ref.
&OperationConstI32{Value: 1},
&OperationTableFill{TableIndex: 1},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "table.size",
body: []byte{
wasm.OpcodeMiscPrefix, wasm.OpcodeMiscTableSize, 1,
wasm.OpcodeEnd,
},
expected: []Operation{
&OperationTableSize{TableIndex: 1},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: tc.body}},
TableSection: []*wasm.Table{{}},
}
res, err := CompileFunctions(ctx, wasm.Features20220419, module)
require.NoError(t, err)
require.Equal(t, tc.expected, res[0].Operations)
require.True(t, res[0].HasTable)
})
}
}
func TestCompile_Locals(t *testing.T) {
tests := []struct {
name string
mod *wasm.Module
expected []Operation
}{
{
name: "local.get - func param - v128",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeV128}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeLocalGet, 0,
wasm.OpcodeEnd,
}}},
},
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]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.get - func param - i64",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeI64}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeLocalGet, 0,
wasm.OpcodeEnd,
}}},
},
expected: []Operation{
&OperationPick{Depth: 0, IsTargetVector: false}, // [param[0]] -> [param[0], param[0]]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.get - non func param - v128",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{
Body: []byte{
wasm.OpcodeLocalGet, 0,
wasm.OpcodeEnd,
},
LocalTypes: []wasm.ValueType{wasm.ValueTypeV128},
}},
},
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]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.set - func param - v128",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeV128}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeVecPrefix, wasm.OpcodeVecV128Const, // [] -> [0x01, 0x02]
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
wasm.OpcodeLocalSet, 0, // [0x01, 0x02] -> []
wasm.OpcodeEnd,
}}},
},
expected: []Operation{
// [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, p[0].lo, p[1].hi]
&OperationSwap{Depth: 3, IsTargetVector: true},
// [0x01, 0x02, p[0].lo, p[1].hi] -> [0x02, 0x01]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.set - func param - i32",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeI32}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeI32Const, 0x1, // [] -> [0x01]
wasm.OpcodeLocalSet, 0, // [0x01] -> []
wasm.OpcodeEnd,
}}},
},
expected: []Operation{
&OperationConstI32{Value: 0x1},
&OperationSwap{Depth: 1, IsTargetVector: false},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.set - non func param - v128",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{
Body: []byte{
wasm.OpcodeVecPrefix, wasm.OpcodeVecV128Const, // [] -> [0x01, 0x02]
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
wasm.OpcodeLocalSet, 0, // [0x01, 0x02] -> []
wasm.OpcodeEnd,
},
LocalTypes: []wasm.ValueType{wasm.ValueTypeV128},
}},
},
expected: []Operation{
&OperationV128Const{Lo: 0, Hi: 0},
// [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, p[0].lo, p[1].hi]
&OperationSwap{Depth: 3, IsTargetVector: true},
// [p[0].lo, 0x02, 0x01, p[1].hi] -> [0x02, 0x01]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.tee - func param - v128",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeV128}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeVecPrefix, wasm.OpcodeVecV128Const, // [] -> [0x01, 0x02]
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
wasm.OpcodeLocalTee, 0, // [0x01, 0x02] -> [0x01, 0x02]
wasm.OpcodeEnd,
}}},
},
expected: []Operation{
// [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},
// [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] -> [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi]
&OperationSwap{Depth: 5, IsTargetVector: true},
// [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi] -> [0x01, 0x02, 0x01, 0x02]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.tee - func param - f32",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{Params: []wasm.ValueType{wasm.ValueTypeF32}}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{Body: []byte{
wasm.OpcodeF32Const, 1, 0, 0, 0,
wasm.OpcodeLocalTee, 0, // [0x01, 0x02] -> [0x01, 0x02]
wasm.OpcodeEnd,
}}},
},
expected: []Operation{
&OperationConstF32{math.Float32frombits(1)},
&OperationPick{Depth: 0, IsTargetVector: false},
&OperationSwap{Depth: 2, IsTargetVector: false},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 0}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
{
name: "local.tee - non func param",
mod: &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
CodeSection: []*wasm.Code{{
Body: []byte{
wasm.OpcodeVecPrefix, wasm.OpcodeVecV128Const, // [] -> [0x01, 0x02]
1, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0,
wasm.OpcodeLocalTee, 0, // [0x01, 0x02] -> [0x01, 0x02]
wasm.OpcodeEnd,
},
LocalTypes: []wasm.ValueType{wasm.ValueTypeV128},
}},
},
expected: []Operation{
&OperationV128Const{Lo: 0, Hi: 0},
// [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},
// [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x2] -> [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi]
&OperationSwap{Depth: 5, IsTargetVector: true},
// [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi] -> [0x01, 0x02, 0x01, 0x02]
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 1}},
&OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}},
&OperationBr{Target: &BranchTarget{}}, // return!
},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
res, err := CompileFunctions(ctx, wasm.Features20220419, tc.mod)
require.NoError(t, err)
msg := fmt.Sprintf("\nhave:\n\t%s\nwant:\n\t%s", Format(res[0].Operations), Format(tc.expected))
require.Equal(t, tc.expected, res[0].Operations, msg)
})
}
}
func TestCompile_Vec(t *testing.T) {
addV128Const := func(in []byte) []byte {
return append(in, wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128Const,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1)
}
vvv2v := func(vec wasm.OpcodeVec) (ret []byte) {
ret = addV128Const(ret)
ret = addV128Const(ret)
ret = addV128Const(ret)
return append(ret, wasm.OpcodeVecPrefix, vec, wasm.OpcodeDrop, wasm.OpcodeEnd)
}
vv2v := func(vec wasm.OpcodeVec) (ret []byte) {
ret = addV128Const(ret)
ret = addV128Const(ret)
return append(ret, wasm.OpcodeVecPrefix, vec, wasm.OpcodeDrop, wasm.OpcodeEnd)
}
vi2v := func(vec wasm.OpcodeVec) (ret []byte) {
ret = addV128Const(ret)
return append(ret, wasm.OpcodeI32Const, 1,
wasm.OpcodeVecPrefix, vec,
wasm.OpcodeDrop, wasm.OpcodeEnd)
}
v2v := func(vec wasm.OpcodeVec) (ret []byte) {
ret = addV128Const(ret)
return append(ret, wasm.OpcodeVecPrefix, vec, wasm.OpcodeDrop, wasm.OpcodeEnd)
}
load := func(vec wasm.OpcodeVec, offset, align uint32) (ret []byte) {
ret = []byte{wasm.OpcodeI32Const, 1, 1, 1, 1, wasm.OpcodeVecPrefix, vec}
ret = append(ret, leb128.EncodeUint32(align)...)
ret = append(ret, leb128.EncodeUint32(offset)...)
ret = append(ret, wasm.OpcodeDrop, wasm.OpcodeEnd)
return
}
loadLane := func(vec wasm.OpcodeVec, offset, align uint32, lane byte) (ret []byte) {
ret = addV128Const([]byte{wasm.OpcodeI32Const, 1, 1, 1, 1})
ret = append(ret, wasm.OpcodeVecPrefix, vec)
ret = append(ret, leb128.EncodeUint32(align)...)
ret = append(ret, leb128.EncodeUint32(offset)...)
ret = append(ret, lane, wasm.OpcodeDrop, wasm.OpcodeEnd)
return
}
storeLane := func(vec wasm.OpcodeVec, offset, align uint32, lane byte) (ret []byte) {
ret = addV128Const([]byte{wasm.OpcodeI32Const, 0})
ret = append(ret, wasm.OpcodeVecPrefix, vec)
ret = append(ret, leb128.EncodeUint32(align)...)
ret = append(ret, leb128.EncodeUint32(offset)...)
ret = append(ret, lane, wasm.OpcodeEnd)
return
}
extractLane := func(vec wasm.OpcodeVec, lane byte) (ret []byte) {
ret = addV128Const(ret)
ret = append(ret, wasm.OpcodeVecPrefix, vec, lane, wasm.OpcodeDrop, wasm.OpcodeEnd)
return
}
replaceLane := func(vec wasm.OpcodeVec, lane byte) (ret []byte) {
ret = addV128Const(ret)
switch vec {
case wasm.OpcodeVecI8x16ReplaceLane, wasm.OpcodeVecI16x8ReplaceLane, wasm.OpcodeVecI32x4ReplaceLane:
ret = append(ret, wasm.OpcodeI32Const, 0)
case wasm.OpcodeVecI64x2ReplaceLane:
ret = append(ret, wasm.OpcodeI64Const, 0)
case wasm.OpcodeVecF32x4ReplaceLane:
ret = append(ret, wasm.OpcodeF32Const, 0, 0, 0, 0)
case wasm.OpcodeVecF64x2ReplaceLane:
ret = append(ret, wasm.OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0, 0)
}
ret = append(ret, wasm.OpcodeVecPrefix, vec, lane, wasm.OpcodeDrop, wasm.OpcodeEnd)
return
}
splat := func(vec wasm.OpcodeVec) (ret []byte) {
switch vec {
case wasm.OpcodeVecI8x16Splat, wasm.OpcodeVecI16x8Splat, wasm.OpcodeVecI32x4Splat:
ret = append(ret, wasm.OpcodeI32Const, 0)
case wasm.OpcodeVecI64x2Splat:
ret = append(ret, wasm.OpcodeI64Const, 0)
case wasm.OpcodeVecF32x4Splat:
ret = append(ret, wasm.OpcodeF32Const, 0, 0, 0, 0)
case wasm.OpcodeVecF64x2Splat:
ret = append(ret, wasm.OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0, 0)
}
ret = append(ret, wasm.OpcodeVecPrefix, vec, wasm.OpcodeDrop, wasm.OpcodeEnd)
return
}
tests := []struct {
name string
body []byte
expected Operation
needDropBeforeReturn bool
}{
{
name: "i8x16 add",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI8x16Add),
expected: &OperationV128Add{Shape: ShapeI8x16},
},
{
name: "i8x16 add",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI16x8Add),
expected: &OperationV128Add{Shape: ShapeI16x8},
},
{
name: "i32x2 add",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI64x2Add),
expected: &OperationV128Add{Shape: ShapeI64x2},
},
{
name: "i64x2 add",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI64x2Add),
expected: &OperationV128Add{Shape: ShapeI64x2},
},
{
name: "i8x16 sub",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI8x16Sub),
expected: &OperationV128Sub{Shape: ShapeI8x16},
},
{
name: "i16x8 sub",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI16x8Sub),
expected: &OperationV128Sub{Shape: ShapeI16x8},
},
{
name: "i32x2 sub",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI64x2Sub),
expected: &OperationV128Sub{Shape: ShapeI64x2},
},
{
name: "i64x2 sub",
needDropBeforeReturn: true,
body: vv2v(wasm.OpcodeVecI64x2Sub),
expected: &OperationV128Sub{Shape: ShapeI64x2},
},
{
name: wasm.OpcodeVecV128LoadName, body: load(wasm.OpcodeVecV128Load, 0, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type128, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{
name: wasm.OpcodeVecV128LoadName + "/align=4", body: load(wasm.OpcodeVecV128Load, 0, 4),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type128, Arg: &MemoryArg{Alignment: 4, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load8x8SName, body: load(wasm.OpcodeVecV128Load8x8s, 1, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type8x8s, Arg: &MemoryArg{Alignment: 0, Offset: 1}},
},
{
name: wasm.OpcodeVecV128Load8x8SName + "/align=1", body: load(wasm.OpcodeVecV128Load8x8s, 0, 1),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type8x8s, Arg: &MemoryArg{Alignment: 1, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load8x8UName, body: load(wasm.OpcodeVecV128Load8x8u, 0, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type8x8u, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load8x8UName + "/align=1", body: load(wasm.OpcodeVecV128Load8x8u, 0, 1),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type8x8u, Arg: &MemoryArg{Alignment: 1, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load16x4SName, body: load(wasm.OpcodeVecV128Load16x4s, 1, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type16x4s, Arg: &MemoryArg{Alignment: 0, Offset: 1}},
},
{
name: wasm.OpcodeVecV128Load16x4SName + "/align=2", body: load(wasm.OpcodeVecV128Load16x4s, 0, 2),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type16x4s, Arg: &MemoryArg{Alignment: 2, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load16x4UName, body: load(wasm.OpcodeVecV128Load16x4u, 0, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type16x4u, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load16x4UName + "/align=2", body: load(wasm.OpcodeVecV128Load16x4u, 0, 2),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type16x4u, Arg: &MemoryArg{Alignment: 2, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load32x2SName, body: load(wasm.OpcodeVecV128Load32x2s, 1, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32x2s, Arg: &MemoryArg{Alignment: 0, Offset: 1}},
},
{
name: wasm.OpcodeVecV128Load32x2SName + "/align=3", body: load(wasm.OpcodeVecV128Load32x2s, 0, 3),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32x2s, Arg: &MemoryArg{Alignment: 3, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load32x2UName, body: load(wasm.OpcodeVecV128Load32x2u, 0, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32x2u, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load32x2UName + "/align=3", body: load(wasm.OpcodeVecV128Load32x2u, 0, 3),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32x2u, Arg: &MemoryArg{Alignment: 3, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load8SplatName, body: load(wasm.OpcodeVecV128Load8Splat, 2, 0),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type8Splat, Arg: &MemoryArg{Alignment: 0, Offset: 2}},
},
{
name: wasm.OpcodeVecV128Load16SplatName, body: load(wasm.OpcodeVecV128Load16Splat, 0, 1),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type16Splat, Arg: &MemoryArg{Alignment: 1, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load32SplatName, body: load(wasm.OpcodeVecV128Load32Splat, 3, 2),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32Splat, Arg: &MemoryArg{Alignment: 2, Offset: 3}},
},
{
name: wasm.OpcodeVecV128Load64SplatName, body: load(wasm.OpcodeVecV128Load64Splat, 0, 3),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type64Splat, Arg: &MemoryArg{Alignment: 3, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load32zeroName, body: load(wasm.OpcodeVecV128Load32zero, 0, 2),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type32zero, Arg: &MemoryArg{Alignment: 2, Offset: 0}},
},
{
name: wasm.OpcodeVecV128Load64zeroName, body: load(wasm.OpcodeVecV128Load64zero, 5, 3),
needDropBeforeReturn: true,
expected: &OperationV128Load{Type: LoadV128Type64zero, Arg: &MemoryArg{Alignment: 3, Offset: 5}},
},
{name: wasm.OpcodeVecV128Load8LaneName, needDropBeforeReturn: true,
body: loadLane(wasm.OpcodeVecV128Load8Lane, 5, 0, 10),
expected: &OperationV128LoadLane{LaneIndex: 10, LaneSize: 8, Arg: &MemoryArg{Alignment: 0, Offset: 5}},
},
{name: wasm.OpcodeVecV128Load16LaneName, needDropBeforeReturn: true,
body: loadLane(wasm.OpcodeVecV128Load16Lane, 100, 1, 7),
expected: &OperationV128LoadLane{LaneIndex: 7, LaneSize: 16, Arg: &MemoryArg{Alignment: 1, Offset: 100}},
},
{name: wasm.OpcodeVecV128Load32LaneName, needDropBeforeReturn: true,
body: loadLane(wasm.OpcodeVecV128Load32Lane, 0, 2, 3),
expected: &OperationV128LoadLane{LaneIndex: 3, LaneSize: 32, Arg: &MemoryArg{Alignment: 2, Offset: 0}},
},
{name: wasm.OpcodeVecV128Load64LaneName, needDropBeforeReturn: true,
body: loadLane(wasm.OpcodeVecV128Load64Lane, 0, 3, 1),
expected: &OperationV128LoadLane{LaneIndex: 1, LaneSize: 64, Arg: &MemoryArg{Alignment: 3, Offset: 0}},
},
{
name: wasm.OpcodeVecV128StoreName, body: []byte{
wasm.OpcodeI32Const,
1, 1, 1, 1,
wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128Const,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128Store,
4, // alignment
10, // offset
wasm.OpcodeEnd,
},
expected: &OperationV128Store{Arg: &MemoryArg{Alignment: 4, Offset: 10}},
},
{name: wasm.OpcodeVecV128Store8LaneName,
body: storeLane(wasm.OpcodeVecV128Store8Lane, 0, 0, 0),
expected: &OperationV128StoreLane{LaneIndex: 0, LaneSize: 8, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{name: wasm.OpcodeVecV128Store8LaneName + "/lane=15",
body: storeLane(wasm.OpcodeVecV128Store8Lane, 100, 0, 15),
expected: &OperationV128StoreLane{LaneIndex: 15, LaneSize: 8, Arg: &MemoryArg{Alignment: 0, Offset: 100}},
},
{name: wasm.OpcodeVecV128Store16LaneName,
body: storeLane(wasm.OpcodeVecV128Store16Lane, 0, 0, 0),
expected: &OperationV128StoreLane{LaneIndex: 0, LaneSize: 16, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{name: wasm.OpcodeVecV128Store16LaneName + "/lane=7/align=1",
body: storeLane(wasm.OpcodeVecV128Store16Lane, 100, 1, 7),
expected: &OperationV128StoreLane{LaneIndex: 7, LaneSize: 16, Arg: &MemoryArg{Alignment: 1, Offset: 100}},
},
{name: wasm.OpcodeVecV128Store32LaneName,
body: storeLane(wasm.OpcodeVecV128Store32Lane, 0, 0, 0),
expected: &OperationV128StoreLane{LaneIndex: 0, LaneSize: 32, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{name: wasm.OpcodeVecV128Store32LaneName + "/lane=3/align=2",
body: storeLane(wasm.OpcodeVecV128Store32Lane, 100, 2, 3),
expected: &OperationV128StoreLane{LaneIndex: 3, LaneSize: 32, Arg: &MemoryArg{Alignment: 2, Offset: 100}},
},
{name: wasm.OpcodeVecV128Store64LaneName,
body: storeLane(wasm.OpcodeVecV128Store64Lane, 0, 0, 0),
expected: &OperationV128StoreLane{LaneIndex: 0, LaneSize: 64, Arg: &MemoryArg{Alignment: 0, Offset: 0}},
},
{name: wasm.OpcodeVecV128Store64LaneName + "/lane=1/align=3",
body: storeLane(wasm.OpcodeVecV128Store64Lane, 50, 3, 1),
expected: &OperationV128StoreLane{LaneIndex: 1, LaneSize: 64, Arg: &MemoryArg{Alignment: 3, Offset: 50}},
},
{name: wasm.OpcodeVecI8x16ExtractLaneSName,
body: extractLane(wasm.OpcodeVecI8x16ExtractLaneS, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Signed: true, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16ExtractLaneSName + "/lane=15",
body: extractLane(wasm.OpcodeVecI8x16ExtractLaneS, 15),
expected: &OperationV128ExtractLane{LaneIndex: 15, Signed: true, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16ExtractLaneUName,
body: extractLane(wasm.OpcodeVecI8x16ExtractLaneU, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Signed: false, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16ExtractLaneUName + "/lane=15",
body: extractLane(wasm.OpcodeVecI8x16ExtractLaneU, 15),
expected: &OperationV128ExtractLane{LaneIndex: 15, Signed: false, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ExtractLaneSName,
body: extractLane(wasm.OpcodeVecI16x8ExtractLaneS, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Signed: true, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ExtractLaneSName + "/lane=7",
body: extractLane(wasm.OpcodeVecI16x8ExtractLaneS, 7),
expected: &OperationV128ExtractLane{LaneIndex: 7, Signed: true, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ExtractLaneUName,
body: extractLane(wasm.OpcodeVecI16x8ExtractLaneU, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Signed: false, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ExtractLaneUName + "/lane=7",
body: extractLane(wasm.OpcodeVecI16x8ExtractLaneU, 7),
expected: &OperationV128ExtractLane{LaneIndex: 7, Signed: false, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI32x4ExtractLaneName,
body: extractLane(wasm.OpcodeVecI32x4ExtractLane, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Shape: ShapeI32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI32x4ExtractLaneName + "/lane=3",
body: extractLane(wasm.OpcodeVecI32x4ExtractLane, 3),
expected: &OperationV128ExtractLane{LaneIndex: 3, Shape: ShapeI32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI64x2ExtractLaneName,
body: extractLane(wasm.OpcodeVecI64x2ExtractLane, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Shape: ShapeI64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI64x2ExtractLaneName + "/lane=1",
body: extractLane(wasm.OpcodeVecI64x2ExtractLane, 1),
expected: &OperationV128ExtractLane{LaneIndex: 1, Shape: ShapeI64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF32x4ExtractLaneName,
body: extractLane(wasm.OpcodeVecF32x4ExtractLane, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Shape: ShapeF32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF32x4ExtractLaneName + "/lane=3",
body: extractLane(wasm.OpcodeVecF32x4ExtractLane, 3),
expected: &OperationV128ExtractLane{LaneIndex: 3, Shape: ShapeF32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF64x2ExtractLaneName,
body: extractLane(wasm.OpcodeVecF64x2ExtractLane, 0),
expected: &OperationV128ExtractLane{LaneIndex: 0, Shape: ShapeF64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF64x2ExtractLaneName + "/lane=1",
body: extractLane(wasm.OpcodeVecF64x2ExtractLane, 1),
expected: &OperationV128ExtractLane{LaneIndex: 1, Shape: ShapeF64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecI8x16ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16ReplaceLaneName + "/lane=15",
body: replaceLane(wasm.OpcodeVecI8x16ReplaceLane, 15),
expected: &OperationV128ReplaceLane{LaneIndex: 15, Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecI16x8ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8ReplaceLaneName + "/lane=7",
body: replaceLane(wasm.OpcodeVecI16x8ReplaceLane, 7),
expected: &OperationV128ReplaceLane{LaneIndex: 7, Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI32x4ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecI32x4ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeI32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI32x4ReplaceLaneName + "/lane=3",
body: replaceLane(wasm.OpcodeVecI32x4ReplaceLane, 3),
expected: &OperationV128ReplaceLane{LaneIndex: 3, Shape: ShapeI32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI64x2ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecI64x2ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeI64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI64x2ReplaceLaneName + "/lane=1",
body: replaceLane(wasm.OpcodeVecI64x2ReplaceLane, 1),
expected: &OperationV128ReplaceLane{LaneIndex: 1, Shape: ShapeI64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF32x4ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecF32x4ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeF32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF32x4ReplaceLaneName + "/lane=3",
body: replaceLane(wasm.OpcodeVecF32x4ReplaceLane, 3),
expected: &OperationV128ReplaceLane{LaneIndex: 3, Shape: ShapeF32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF64x2ReplaceLaneName,
body: replaceLane(wasm.OpcodeVecF64x2ReplaceLane, 0),
expected: &OperationV128ReplaceLane{LaneIndex: 0, Shape: ShapeF64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF64x2ReplaceLaneName + "/lane=1",
body: replaceLane(wasm.OpcodeVecF64x2ReplaceLane, 1),
expected: &OperationV128ReplaceLane{LaneIndex: 1, Shape: ShapeF64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16SplatName, body: splat(wasm.OpcodeVecI8x16Splat),
expected: &OperationV128Splat{Shape: ShapeI8x16},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI16x8SplatName, body: splat(wasm.OpcodeVecI16x8Splat),
expected: &OperationV128Splat{Shape: ShapeI16x8},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI32x4SplatName, body: splat(wasm.OpcodeVecI32x4Splat),
expected: &OperationV128Splat{Shape: ShapeI32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI64x2SplatName, body: splat(wasm.OpcodeVecI64x2Splat),
expected: &OperationV128Splat{Shape: ShapeI64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF32x4SplatName, body: splat(wasm.OpcodeVecF32x4Splat),
expected: &OperationV128Splat{Shape: ShapeF32x4},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecF64x2SplatName, body: splat(wasm.OpcodeVecF64x2Splat),
expected: &OperationV128Splat{Shape: ShapeF64x2},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecI8x16SwizzleName, body: vv2v(wasm.OpcodeVecI8x16Swizzle),
expected: &OperationV128Swizzle{},
needDropBeforeReturn: true,
},
{
name: wasm.OpcodeVecV128i8x16ShuffleName, body: []byte{
wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128Const, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128Const, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
wasm.OpcodeVecPrefix,
wasm.OpcodeVecV128i8x16Shuffle,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
wasm.OpcodeDrop,
wasm.OpcodeEnd,
},
expected: &OperationV128Shuffle{Lanes: [16]byte{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
}},
needDropBeforeReturn: true,
},
{name: wasm.OpcodeVecV128NotName, body: v2v(wasm.OpcodeVecV128Not),
needDropBeforeReturn: true,
expected: &OperationV128Not{},
},
{name: wasm.OpcodeVecV128AndName, body: vv2v(wasm.OpcodeVecV128And),
needDropBeforeReturn: true,
expected: &OperationV128And{},
},
{name: wasm.OpcodeVecV128AndNotName, body: vv2v(wasm.OpcodeVecV128AndNot),
needDropBeforeReturn: true,
expected: &OperationV128AndNot{},
},
{name: wasm.OpcodeVecV128OrName, body: vv2v(wasm.OpcodeVecV128Or),
needDropBeforeReturn: true,
expected: &OperationV128Or{},
},
{name: wasm.OpcodeVecV128XorName, body: vv2v(wasm.OpcodeVecV128Xor),
needDropBeforeReturn: true,
expected: &OperationV128Xor{},
},
{name: wasm.OpcodeVecV128BitselectName, body: vvv2v(wasm.OpcodeVecV128Bitselect),
needDropBeforeReturn: true,
expected: &OperationV128Bitselect{},
},
{name: wasm.OpcodeVecI8x16ShlName, body: vi2v(wasm.OpcodeVecI8x16Shl),
needDropBeforeReturn: true,
expected: &OperationV128Shl{Shape: ShapeI8x16},
},
{name: wasm.OpcodeVecI8x16ShrSName, body: vi2v(wasm.OpcodeVecI8x16ShrS),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI8x16, Signed: true},
},
{name: wasm.OpcodeVecI8x16ShrUName, body: vi2v(wasm.OpcodeVecI8x16ShrU),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI8x16, Signed: false},
},
{name: wasm.OpcodeVecI16x8ShlName, body: vi2v(wasm.OpcodeVecI16x8Shl),
needDropBeforeReturn: true,
expected: &OperationV128Shl{Shape: ShapeI16x8},
},
{name: wasm.OpcodeVecI16x8ShrSName, body: vi2v(wasm.OpcodeVecI16x8ShrS),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI16x8, Signed: true},
},
{name: wasm.OpcodeVecI16x8ShrUName, body: vi2v(wasm.OpcodeVecI16x8ShrU),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI16x8, Signed: false},
},
{name: wasm.OpcodeVecI32x4ShlName, body: vi2v(wasm.OpcodeVecI32x4Shl),
needDropBeforeReturn: true,
expected: &OperationV128Shl{Shape: ShapeI32x4},
},
{name: wasm.OpcodeVecI32x4ShrSName, body: vi2v(wasm.OpcodeVecI32x4ShrS),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI32x4, Signed: true},
},
{name: wasm.OpcodeVecI32x4ShrUName, body: vi2v(wasm.OpcodeVecI32x4ShrU),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI32x4, Signed: false},
},
{name: wasm.OpcodeVecI64x2ShlName, body: vi2v(wasm.OpcodeVecI64x2Shl),
needDropBeforeReturn: true,
expected: &OperationV128Shl{Shape: ShapeI64x2},
},
{name: wasm.OpcodeVecI64x2ShrSName, body: vi2v(wasm.OpcodeVecI64x2ShrS),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI64x2, Signed: true},
},
{name: wasm.OpcodeVecI64x2ShrUName, body: vi2v(wasm.OpcodeVecI64x2ShrU),
needDropBeforeReturn: true,
expected: &OperationV128Shr{Shape: ShapeI64x2, Signed: false},
},
{name: wasm.OpcodeVecI8x16EqName, body: vv2v(wasm.OpcodeVecI8x16Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16Eq},
},
{name: wasm.OpcodeVecI8x16NeName, body: vv2v(wasm.OpcodeVecI8x16Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16Ne},
},
{name: wasm.OpcodeVecI8x16LtSName, body: vv2v(wasm.OpcodeVecI8x16LtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16LtS},
},
{name: wasm.OpcodeVecI8x16LtUName, body: vv2v(wasm.OpcodeVecI8x16LtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16LtU},
},
{name: wasm.OpcodeVecI8x16GtSName, body: vv2v(wasm.OpcodeVecI8x16GtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16GtS},
},
{name: wasm.OpcodeVecI8x16GtUName, body: vv2v(wasm.OpcodeVecI8x16GtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16GtU},
},
{name: wasm.OpcodeVecI8x16LeSName, body: vv2v(wasm.OpcodeVecI8x16LeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16LeS},
},
{name: wasm.OpcodeVecI8x16LeUName, body: vv2v(wasm.OpcodeVecI8x16LeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16LeU},
},
{name: wasm.OpcodeVecI8x16GeSName, body: vv2v(wasm.OpcodeVecI8x16GeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16GeS},
},
{name: wasm.OpcodeVecI8x16GeUName, body: vv2v(wasm.OpcodeVecI8x16GeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI8x16GeU},
},
{name: wasm.OpcodeVecI16x8EqName, body: vv2v(wasm.OpcodeVecI16x8Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8Eq},
},
{name: wasm.OpcodeVecI16x8NeName, body: vv2v(wasm.OpcodeVecI16x8Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8Ne},
},
{name: wasm.OpcodeVecI16x8LtSName, body: vv2v(wasm.OpcodeVecI16x8LtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8LtS},
},
{name: wasm.OpcodeVecI16x8LtUName, body: vv2v(wasm.OpcodeVecI16x8LtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8LtU},
},
{name: wasm.OpcodeVecI16x8GtSName, body: vv2v(wasm.OpcodeVecI16x8GtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8GtS},
},
{name: wasm.OpcodeVecI16x8GtUName, body: vv2v(wasm.OpcodeVecI16x8GtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8GtU},
},
{name: wasm.OpcodeVecI16x8LeSName, body: vv2v(wasm.OpcodeVecI16x8LeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8LeS},
},
{name: wasm.OpcodeVecI16x8LeUName, body: vv2v(wasm.OpcodeVecI16x8LeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8LeU},
},
{name: wasm.OpcodeVecI16x8GeSName, body: vv2v(wasm.OpcodeVecI16x8GeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8GeS},
},
{name: wasm.OpcodeVecI16x8GeUName, body: vv2v(wasm.OpcodeVecI16x8GeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI16x8GeU},
},
{name: wasm.OpcodeVecI32x4EqName, body: vv2v(wasm.OpcodeVecI32x4Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4Eq},
},
{name: wasm.OpcodeVecI32x4NeName, body: vv2v(wasm.OpcodeVecI32x4Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4Ne},
},
{name: wasm.OpcodeVecI32x4LtSName, body: vv2v(wasm.OpcodeVecI32x4LtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4LtS},
},
{name: wasm.OpcodeVecI32x4LtUName, body: vv2v(wasm.OpcodeVecI32x4LtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4LtU},
},
{name: wasm.OpcodeVecI32x4GtSName, body: vv2v(wasm.OpcodeVecI32x4GtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4GtS},
},
{name: wasm.OpcodeVecI32x4GtUName, body: vv2v(wasm.OpcodeVecI32x4GtU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4GtU},
},
{name: wasm.OpcodeVecI32x4LeSName, body: vv2v(wasm.OpcodeVecI32x4LeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4LeS},
},
{name: wasm.OpcodeVecI32x4LeUName, body: vv2v(wasm.OpcodeVecI32x4LeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4LeU},
},
{name: wasm.OpcodeVecI32x4GeSName, body: vv2v(wasm.OpcodeVecI32x4GeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4GeS},
},
{name: wasm.OpcodeVecI32x4GeUName, body: vv2v(wasm.OpcodeVecI32x4GeU),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI32x4GeU},
},
{name: wasm.OpcodeVecI64x2EqName, body: vv2v(wasm.OpcodeVecI64x2Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2Eq},
},
{name: wasm.OpcodeVecI64x2NeName, body: vv2v(wasm.OpcodeVecI64x2Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2Ne},
},
{name: wasm.OpcodeVecI64x2LtSName, body: vv2v(wasm.OpcodeVecI64x2LtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2LtS},
},
{name: wasm.OpcodeVecI64x2GtSName, body: vv2v(wasm.OpcodeVecI64x2GtS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2GtS},
},
{name: wasm.OpcodeVecI64x2LeSName, body: vv2v(wasm.OpcodeVecI64x2LeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2LeS},
},
{name: wasm.OpcodeVecI64x2GeSName, body: vv2v(wasm.OpcodeVecI64x2GeS),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeI64x2GeS},
},
{name: wasm.OpcodeVecF32x4EqName, body: vv2v(wasm.OpcodeVecF32x4Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Eq},
},
{name: wasm.OpcodeVecF32x4NeName, body: vv2v(wasm.OpcodeVecF32x4Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Ne},
},
{name: wasm.OpcodeVecF32x4LtName, body: vv2v(wasm.OpcodeVecF32x4Lt),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Lt},
},
{name: wasm.OpcodeVecF32x4GtName, body: vv2v(wasm.OpcodeVecF32x4Gt),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Gt},
},
{name: wasm.OpcodeVecF32x4LeName, body: vv2v(wasm.OpcodeVecF32x4Le),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Le},
},
{name: wasm.OpcodeVecF32x4GeName, body: vv2v(wasm.OpcodeVecF32x4Ge),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF32x4Ge},
},
{name: wasm.OpcodeVecF64x2EqName, body: vv2v(wasm.OpcodeVecF64x2Eq),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Eq},
},
{name: wasm.OpcodeVecF64x2NeName, body: vv2v(wasm.OpcodeVecF64x2Ne),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Ne},
},
{name: wasm.OpcodeVecF64x2LtName, body: vv2v(wasm.OpcodeVecF64x2Lt),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Lt},
},
{name: wasm.OpcodeVecF64x2GtName, body: vv2v(wasm.OpcodeVecF64x2Gt),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Gt},
},
{name: wasm.OpcodeVecF64x2LeName, body: vv2v(wasm.OpcodeVecF64x2Le),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Le},
},
{name: wasm.OpcodeVecF64x2GeName, body: vv2v(wasm.OpcodeVecF64x2Ge),
needDropBeforeReturn: true,
expected: &OperationV128Cmp{Type: V128CmpTypeF64x2Ge},
},
{name: wasm.OpcodeVecI8x16AllTrueName, body: v2v(wasm.OpcodeVecI8x16AllTrue),
needDropBeforeReturn: true,
expected: &OperationV128AllTrue{Shape: ShapeI8x16},
},
{name: wasm.OpcodeVecI16x8AllTrueName, body: v2v(wasm.OpcodeVecI16x8AllTrue),
needDropBeforeReturn: true,
expected: &OperationV128AllTrue{Shape: ShapeI16x8},
},
{name: wasm.OpcodeVecI32x4AllTrueName, body: v2v(wasm.OpcodeVecI32x4AllTrue),
needDropBeforeReturn: true,
expected: &OperationV128AllTrue{Shape: ShapeI32x4},
},
{name: wasm.OpcodeVecI64x2AllTrueName, body: v2v(wasm.OpcodeVecI64x2AllTrue),
needDropBeforeReturn: true,
expected: &OperationV128AllTrue{Shape: ShapeI64x2},
},
{name: wasm.OpcodeVecI8x16BitMaskName, body: v2v(wasm.OpcodeVecI8x16BitMask),
needDropBeforeReturn: true, expected: &OperationV128BitMask{Shape: ShapeI8x16},
},
{name: wasm.OpcodeVecI16x8BitMaskName, body: v2v(wasm.OpcodeVecI16x8BitMask),
needDropBeforeReturn: true, expected: &OperationV128BitMask{Shape: ShapeI16x8},
},
{name: wasm.OpcodeVecI32x4BitMaskName, body: v2v(wasm.OpcodeVecI32x4BitMask),
needDropBeforeReturn: true, expected: &OperationV128BitMask{Shape: ShapeI32x4},
},
{name: wasm.OpcodeVecI64x2BitMaskName, body: v2v(wasm.OpcodeVecI64x2BitMask),
needDropBeforeReturn: true, expected: &OperationV128BitMask{Shape: ShapeI64x2},
},
{name: wasm.OpcodeVecV128AnyTrueName, body: v2v(wasm.OpcodeVecV128AnyTrue),
needDropBeforeReturn: true,
expected: &OperationV128AnyTrue{},
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
module := &wasm.Module{
TypeSection: []*wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
MemorySection: &wasm.Memory{},
CodeSection: []*wasm.Code{{Body: tc.body}},
}
res, err := CompileFunctions(ctx, wasm.Features20220419, module)
require.NoError(t, err)
var actual Operation
if tc.needDropBeforeReturn {
// If the drop operation is inserted, the target op exits at -3
// as the operations looks like: [... target, drop, br(to return)].
actual = res[0].Operations[len(res[0].Operations)-3]
} else {
// If the drop operation is not inserted, the target op exits at -2
// as the operations looks like: [... target, br(to return)].
actual = res[0].Operations[len(res[0].Operations)-2]
}
require.Equal(t, tc.expected, actual)
})
}
}