Repository: tetratelabs/wazero
Pull request #609 (2022-06-01 02:35:07)

Backfill func validation unit tests for SIMD load, store, and lane manipulations


Patch:
diff --git a/internal/asm/amd64/impl.go b/internal/asm/amd64/impl.go
index 9ffd30c1..2fcd94c0 100644
--- a/internal/asm/amd64/impl.go
+++ b/internal/asm/amd64/impl.go
@@ -214,11 +214,13 @@ func (o OperandTypes) String() string {
 // AssemblerImpl implements Assembler.
 type AssemblerImpl struct {
 	asm.BaseAssemblerImpl
-	EnablePadding                  bool
-	Root, Current                  *NodeImpl
-	nodeCount                      int
-	Buf                            *bytes.Buffer
-	ForceReAssemble                bool
+	EnablePadding   bool
+	Root, Current   *NodeImpl
+	nodeCount       int
+	Buf             *bytes.Buffer
+	ForceReAssemble bool
+	// MaxDisplacementForConstantPool is fixed to defaultMaxDisplacementForConstantPool
+	// but have it as a field here for testability.
 	MaxDisplacementForConstantPool int
 
 	pool constPool
diff --git a/internal/asm/arm64/impl.go b/internal/asm/arm64/impl.go
index b4e16713..197fcb50 100644
--- a/internal/asm/arm64/impl.go
+++ b/internal/asm/arm64/impl.go
@@ -212,11 +212,13 @@ func (o OperandTypes) String() string {
 // AssemblerImpl implements Assembler.
 type AssemblerImpl struct {
 	asm.BaseAssemblerImpl
-	Root, Current                  *NodeImpl
-	Buf                            *bytes.Buffer
-	temporaryRegister              asm.Register
-	nodeCount                      int
-	pool                           constPool
+	Root, Current     *NodeImpl
+	Buf               *bytes.Buffer
+	temporaryRegister asm.Register
+	nodeCount         int
+	pool              constPool
+	// MaxDisplacementForConstantPool is fixed to defaultMaxDisplacementForConstPool
+	// but have it as a field here for testability.
 	MaxDisplacementForConstantPool int
 }
 
diff --git a/internal/engine/compiler/impl_vec_arm64.go b/internal/engine/compiler/impl_vec_arm64.go
index aa732ba6..9ed47a62 100644
--- a/internal/engine/compiler/impl_vec_arm64.go
+++ b/internal/engine/compiler/impl_vec_arm64.go
@@ -6,6 +6,7 @@ import (
 	"github.com/tetratelabs/wazero/internal/wazeroir"
 )
 
+// compileV128Const implements compiler.compileV128Const for arm64.
 func (c *arm64Compiler) compileV128Const(o *wazeroir.OperationV128Const) error {
 	c.maybeCompileMoveTopConditionalToFreeGeneralPurposeRegister()
 
@@ -37,6 +38,7 @@ func (c *arm64Compiler) compileV128Const(o *wazeroir.OperationV128Const) error {
 	return nil
 }
 
+// compileV128Add implements compiler.compileV128Add for arm64.
 func (c *arm64Compiler) compileV128Add(o *wazeroir.OperationV128Add) error {
 	x2 := c.locationStack.popV128()
 	if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil {
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index 448976ef..6d17cd6c 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -1093,6 +1093,9 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				OpcodeVecV128Load32x2s, OpcodeVecV128Load32x2u, OpcodeVecV128Load8Splat, OpcodeVecV128Load16Splat,
 				OpcodeVecV128Load32Splat, OpcodeVecV128Load64Splat,
 				OpcodeVecV128Load32zero, OpcodeVecV128Load64zero:
+				if memory == nil {
+					return fmt.Errorf("memory must exist for %s", VectorInstructionName(vecOpcode))
+				}
 				pc++
 				align, _, read, err := readMemArg(pc, body)
 				if err != nil {
@@ -1119,14 +1122,18 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				case OpcodeVecV128Load64zero:
 					maxAlign = 64 / 8
 				}
+
 				if 1<<align > maxAlign {
-					return fmt.Errorf("invalid memory alignment %d for %s", align, OpcodeVecV128StoreName)
+					return fmt.Errorf("invalid memory alignment %d for %s", align, VectorInstructionName(vecOpcode))
 				}
 				if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil {
-					return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecV128LoadName, err)
+					return fmt.Errorf("cannot pop the operand for %s: %v", VectorInstructionName(vecOpcode), err)
 				}
 				valueTypeStack.push(ValueTypeV128)
 			case OpcodeVecV128Store:
+				if memory == nil {
+					return fmt.Errorf("memory must exist for %s", VectorInstructionName(vecOpcode))
+				}
 				pc++
 				align, _, read, err := readMemArg(pc, body)
 				if err != nil {
@@ -1143,6 +1150,9 @@ func (m *Module) validateFunctionWithMaxStackValues(
 					return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecV128StoreName, err)
 				}
 			case OpcodeVecV128Load8Lane, OpcodeVecV128Load16Lane, OpcodeVecV128Load32Lane, OpcodeVecV128Load64Lane:
+				if memory == nil {
+					return fmt.Errorf("memory must exist for %s", VectorInstructionName(vecOpcode))
+				}
 				attr := vecLoadLanes[vecOpcode]
 				pc++
 				align, _, read, err := readMemArg(pc, body)
@@ -1150,7 +1160,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 					return err
 				}
 				if 1<<align > attr.alignMax {
-					return fmt.Errorf("invalid memory alignment %d for %s", align, OpcodeVecV128Load64LaneName)
+					return fmt.Errorf("invalid memory alignment %d for %s", align, vectorInstructionName[vecOpcode])
 				}
 				pc += read
 				if pc >= uint64(len(body)) {
@@ -1168,6 +1178,9 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				}
 				valueTypeStack.push(ValueTypeV128)
 			case OpcodeVecV128Store8Lane, OpcodeVecV128Store16Lane, OpcodeVecV128Store32Lane, OpcodeVecV128Store64Lane:
+				if memory == nil {
+					return fmt.Errorf("memory must exist for %s", VectorInstructionName(vecOpcode))
+				}
 				attr := vecStoreLanes[vecOpcode]
 				pc++
 				align, _, read, err := readMemArg(pc, body)
@@ -1183,7 +1196,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				}
 				lane := body[pc]
 				if lane >= attr.laneCeil {
-					return fmt.Errorf("invalid lane index %d >= %d", lane, attr.laneCeil)
+					return fmt.Errorf("invalid lane index %d >= %d for %s", lane, attr.laneCeil, vectorInstructionName[vecOpcode])
 				}
 				if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil {
 					return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err)
@@ -1247,7 +1260,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				valueTypeStack.push(ValueTypeV128)
 			case OpcodeVecV128i8x16Shuffle:
 				pc++
-				if pc+16 >= uint64(len(body)) {
+				if pc+15 >= uint64(len(body)) {
 					return fmt.Errorf("16 lane indexes for %s not found", vectorInstructionName[vecOpcode])
 				}
 				lanes := body[pc : pc+16]
diff --git a/internal/wasm/instruction.go b/internal/wasm/instruction.go
index f4f93378..8cab0bd2 100644
--- a/internal/wasm/instruction.go
+++ b/internal/wasm/instruction.go
@@ -1094,9 +1094,9 @@ const (
 	OpcodeVecI8x16ExtractLaneSName         = "i8x16.extract_lane_s"
 	OpcodeVecI8x16ExtractLaneUName         = "i8x16.extract_lane_u"
 	OpcodeVecI8x16ReplaceLaneName          = "i8x16.replace_lane"
-	OpcodeVecI16x8ExtractLaneSName         = "i16x4.extract_lane_s"
-	OpcodeVecI16x8ExtractLaneUName         = "i16x4.extract_lane_u"
-	OpcodeVecI16x8ReplaceLaneName          = "i16x4.replace_lane"
+	OpcodeVecI16x8ExtractLaneSName         = "i16x8.extract_lane_s"
+	OpcodeVecI16x8ExtractLaneUName         = "i16x8.extract_lane_u"
+	OpcodeVecI16x8ReplaceLaneName          = "i16x8.replace_lane"
 	OpcodeVecI32x4ExtractLaneName          = "i32x4.extract_lane"
 	OpcodeVecI32x4ReplaceLaneName          = "i32x4.replace_lane"
 	OpcodeVecI64x2ExtractLaneName          = "i64x2.extract_lane"


Test patch:
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index 11cb2075..b8fd9350 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
@@ -2667,6 +2668,160 @@ func TestModule_funcValidation_Select_error(t *testing.T) {
 }
 
 func TestModule_funcValidation_SIMD(t *testing.T) {
+	vv2v := func(vec OpcodeVec) []byte {
+		return []byte{
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+			OpcodeDrop,
+			OpcodeEnd,
+		}
+	}
+
+	v2v := func(vec OpcodeVec) []byte {
+		return []byte{
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+			OpcodeDrop,
+			OpcodeEnd,
+		}
+	}
+
+	load := func(vec OpcodeVec, offset, align uint32) (ret []byte) {
+		ret = []byte{
+			OpcodeI32Const,
+			1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+		}
+
+		ret = append(ret, leb128.EncodeUint32(align)...)
+		ret = append(ret, leb128.EncodeUint32(offset)...)
+		ret = append(ret,
+			OpcodeDrop,
+			OpcodeEnd,
+		)
+		return
+	}
+
+	loadLane := func(vec OpcodeVec, offset, align uint32, lane byte) (ret []byte) {
+		ret = []byte{
+			OpcodeI32Const,
+			1, 1, 1, 1,
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+		}
+
+		ret = append(ret, leb128.EncodeUint32(align)...)
+		ret = append(ret, leb128.EncodeUint32(offset)...)
+		ret = append(ret,
+			lane,
+			OpcodeDrop,
+			OpcodeEnd,
+		)
+		return
+	}
+
+	storeLane := func(vec OpcodeVec, offset, align uint32, lane byte) (ret []byte) {
+		ret = []byte{
+			OpcodeI32Const,
+			1, 1, 1, 1,
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+		}
+		ret = append(ret, leb128.EncodeUint32(align)...)
+		ret = append(ret, leb128.EncodeUint32(offset)...)
+		ret = append(ret,
+			lane,
+			OpcodeEnd,
+		)
+		return
+	}
+
+	extractLane := func(vec OpcodeVec, lane byte) (ret []byte) {
+		ret = []byte{
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			OpcodeVecPrefix,
+			vec,
+			lane,
+			OpcodeDrop,
+			OpcodeEnd,
+		}
+		return
+	}
+
+	replaceLane := func(vec OpcodeVec, lane byte) (ret []byte) {
+		ret = []byte{
+			OpcodeVecPrefix,
+			OpcodeVecV128Const,
+			1, 1, 1, 1, 1, 1, 1, 1,
+			1, 1, 1, 1, 1, 1, 1, 1,
+		}
+
+		switch vec {
+		case OpcodeVecI8x16ReplaceLane, OpcodeVecI16x8ReplaceLane, OpcodeVecI32x4ReplaceLane:
+			ret = append(ret, OpcodeI32Const, 0, 0, 0, 0)
+		case OpcodeVecI64x2ReplaceLane:
+			ret = append(ret, OpcodeI64Const, 0, 0, 0, 0, 0, 0, 0, 0)
+		case OpcodeVecF32x4ReplaceLane:
+			ret = append(ret, OpcodeF32Const, 0, 0, 0, 0)
+		case OpcodeVecF64x2ReplaceLane:
+			ret = append(ret, OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0, 0)
+		}
+
+		ret = append(ret,
+			OpcodeVecPrefix,
+			vec,
+			lane,
+			OpcodeDrop,
+			OpcodeEnd,
+		)
+		return
+	}
+
+	splat := func(vec OpcodeVec) (ret []byte) {
+		switch vec {
+		case OpcodeVecI8x16Splat, OpcodeVecI16x8Splat, OpcodeVecI32x4Splat:
+			ret = append(ret, OpcodeI32Const, 0, 0, 0, 0)
+		case OpcodeVecI64x2Splat:
+			ret = append(ret, OpcodeI64Const, 0, 0, 0, 0, 0, 0, 0, 0)
+		case OpcodeVecF32x4Splat:
+			ret = append(ret, OpcodeF32Const, 0, 0, 0, 0)
+		case OpcodeVecF64x2Splat:
+			ret = append(ret, OpcodeF64Const, 0, 0, 0, 0, 0, 0, 0, 0)
+		}
+
+		ret = append(ret,
+			OpcodeVecPrefix,
+			vec,
+			OpcodeDrop,
+			OpcodeEnd,
+		)
+		return
+	}
+
 	tests := []struct {
 		name        string
 		body        []byte
@@ -2683,26 +2838,103 @@ func TestModule_funcValidation_SIMD(t *testing.T) {
 				OpcodeEnd,
 			},
 		},
-		{
-			name: "i32x4.add",
-			body: []byte{
-				OpcodeVecPrefix,
-				OpcodeVecV128Const,
-				1, 1, 1, 1, 1, 1, 1, 1,
-				1, 1, 1, 1, 1, 1, 1, 1,
+		{name: OpcodeVecI8x16AddName, body: vv2v(OpcodeVecI8x16Add)},
+		{name: OpcodeVecI16x8AddName, body: vv2v(OpcodeVecI16x8Add)},
+		{name: OpcodeVecI32x4AddName, body: vv2v(OpcodeVecI32x4Add)},
+		{name: OpcodeVecI64x2AddName, body: vv2v(OpcodeVecI64x2Add)},
+		{name: OpcodeVecI8x16SubName, body: vv2v(OpcodeVecI8x16Sub)},
+		{name: OpcodeVecI16x8SubName, body: vv2v(OpcodeVecI16x8Sub)},
+		{name: OpcodeVecI32x4SubName, body: vv2v(OpcodeVecI32x4Sub)},
+		{name: OpcodeVecI64x2SubName, body: vv2v(OpcodeVecI64x2Sub)},
+		{name: OpcodeVecV128AnyTrueName, body: v2v(OpcodeVecV128AnyTrue)},
+		{name: OpcodeVecV128AnyTrueName, body: v2v(OpcodeVecI8x16AllTrue)},
+		{name: OpcodeVecI8x16AllTrueName, body: v2v(OpcodeVecI16x8AllTrue)},
+		{name: OpcodeVecI16x8AllTrueName, body: v2v(OpcodeVecI32x4AllTrue)},
+		{name: OpcodeVecI32x4AllTrueName, body: v2v(OpcodeVecI64x2AllTrue)},
+		{name: OpcodeVecV128LoadName, body: load(OpcodeVecV128Load, 0, 0)},
+		{name: OpcodeVecV128LoadName + "/align=4", body: load(OpcodeVecV128Load, 0, 4)},
+		{name: OpcodeVecV128Load8x8SName, body: load(OpcodeVecV128Load8x8s, 1, 0)},
+		{name: OpcodeVecV128Load8x8SName + "/align=1", body: load(OpcodeVecV128Load8x8s, 0, 1)},
+		{name: OpcodeVecV128Load8x8UName, body: load(OpcodeVecV128Load8x8u, 0, 0)},
+		{name: OpcodeVecV128Load8x8UName + "/align=1", body: load(OpcodeVecV128Load8x8u, 0, 1)},
+		{name: OpcodeVecV128Load16x4SName, body: load(OpcodeVecV128Load16x4s, 1, 0)},
+		{name: OpcodeVecV128Load16x4SName + "/align=2", body: load(OpcodeVecV128Load16x4s, 0, 2)},
+		{name: OpcodeVecV128Load16x4UName, body: load(OpcodeVecV128Load16x4u, 0, 0)},
+		{name: OpcodeVecV128Load16x4UName + "/align=2", body: load(OpcodeVecV128Load16x4u, 0, 2)},
+		{name: OpcodeVecV128Load32x2SName, body: load(OpcodeVecV128Load32x2s, 1, 0)},
+		{name: OpcodeVecV128Load32x2SName + "/align=3", body: load(OpcodeVecV128Load32x2s, 0, 3)},
+		{name: OpcodeVecV128Load32x2UName, body: load(OpcodeVecV128Load32x2u, 0, 0)},
+		{name: OpcodeVecV128Load32x2UName + "/align=3", body: load(OpcodeVecV128Load32x2u, 0, 3)},
+		{name: OpcodeVecV128Load8SplatName, body: load(OpcodeVecV128Load8Splat, 2, 0)},
+		{name: OpcodeVecV128Load16SplatName, body: load(OpcodeVecV128Load16Splat, 0, 1)},
+		{name: OpcodeVecV128Load32SplatName, body: load(OpcodeVecV128Load32Splat, 3, 2)},
+		{name: OpcodeVecV128Load64SplatName, body: load(OpcodeVecV128Load64Splat, 0, 3)},
+		{name: OpcodeVecV128Load32zeroName, body: load(OpcodeVecV128Load32zero, 0, 2)},
+		{name: OpcodeVecV128Load64zeroName, body: load(OpcodeVecV128Load64zero, 5, 3)},
+		{name: OpcodeVecV128Load8LaneName, body: loadLane(OpcodeVecV128Load8Lane, 5, 0, 10)},
+		{name: OpcodeVecV128Load16LaneName, body: loadLane(OpcodeVecV128Load16Lane, 100, 1, 7)},
+		{name: OpcodeVecV128Load32LaneName, body: loadLane(OpcodeVecV128Load32Lane, 0, 2, 3)},
+		{name: OpcodeVecV128Load64LaneName, body: loadLane(OpcodeVecV128Load64Lane, 0, 3, 1)},
+		{
+			name: OpcodeVecV128StoreName, body: []byte{
+				OpcodeI32Const,
+				1, 1, 1, 1,
 				OpcodeVecPrefix,
 				OpcodeVecV128Const,
 				1, 1, 1, 1, 1, 1, 1, 1,
 				1, 1, 1, 1, 1, 1, 1, 1,
 				OpcodeVecPrefix,
-				OpcodeVecI32x4Add,
-				OpcodeDrop,
+				OpcodeVecV128Store,
+				4,  // alignment
+				10, // offset
 				OpcodeEnd,
 			},
 		},
-		{
-			name: "i64x2.add",
-			body: []byte{
+		{name: OpcodeVecV128Store8LaneName, body: storeLane(OpcodeVecV128Store8Lane, 0, 0, 0)},
+		{name: OpcodeVecV128Store8LaneName + "/lane=15", body: storeLane(OpcodeVecV128Store8Lane, 100, 0, 15)},
+		{name: OpcodeVecV128Store16LaneName, body: storeLane(OpcodeVecV128Store16Lane, 0, 0, 0)},
+		{name: OpcodeVecV128Store16LaneName + "/lane=7/align=1", body: storeLane(OpcodeVecV128Store16Lane, 100, 1, 7)},
+		{name: OpcodeVecV128Store32LaneName, body: storeLane(OpcodeVecV128Store32Lane, 0, 0, 0)},
+		{name: OpcodeVecV128Store32LaneName + "/lane=3/align=2", body: storeLane(OpcodeVecV128Store32Lane, 100, 2, 3)},
+		{name: OpcodeVecV128Store64LaneName, body: storeLane(OpcodeVecV128Store64Lane, 0, 0, 0)},
+		{name: OpcodeVecV128Store64LaneName + "/lane=1/align=3", body: storeLane(OpcodeVecV128Store64Lane, 50, 3, 1)},
+		{name: OpcodeVecI8x16ExtractLaneSName, body: extractLane(OpcodeVecI8x16ExtractLaneS, 0)},
+		{name: OpcodeVecI8x16ExtractLaneSName + "/lane=15", body: extractLane(OpcodeVecI8x16ExtractLaneS, 15)},
+		{name: OpcodeVecI8x16ExtractLaneUName, body: extractLane(OpcodeVecI8x16ExtractLaneU, 0)},
+		{name: OpcodeVecI8x16ExtractLaneUName + "/lane=15", body: extractLane(OpcodeVecI8x16ExtractLaneU, 15)},
+		{name: OpcodeVecI16x8ExtractLaneSName, body: extractLane(OpcodeVecI16x8ExtractLaneS, 0)},
+		{name: OpcodeVecI16x8ExtractLaneSName + "/lane=7", body: extractLane(OpcodeVecI16x8ExtractLaneS, 7)},
+		{name: OpcodeVecI16x8ExtractLaneUName, body: extractLane(OpcodeVecI16x8ExtractLaneU, 0)},
+		{name: OpcodeVecI16x8ExtractLaneUName + "/lane=8", body: extractLane(OpcodeVecI16x8ExtractLaneU, 7)},
+		{name: OpcodeVecI32x4ExtractLaneName, body: extractLane(OpcodeVecI32x4ExtractLane, 0)},
+		{name: OpcodeVecI32x4ExtractLaneName + "/lane=3", body: extractLane(OpcodeVecI32x4ExtractLane, 3)},
+		{name: OpcodeVecI64x2ExtractLaneName, body: extractLane(OpcodeVecI64x2ExtractLane, 0)},
+		{name: OpcodeVecI64x2ExtractLaneName + "/lane=1", body: extractLane(OpcodeVecI64x2ExtractLane, 1)},
+		{name: OpcodeVecF32x4ExtractLaneName, body: extractLane(OpcodeVecF32x4ExtractLane, 0)},
+		{name: OpcodeVecF32x4ExtractLaneName + "/lane=3", body: extractLane(OpcodeVecF32x4ExtractLane, 3)},
+		{name: OpcodeVecF64x2ExtractLaneName, body: extractLane(OpcodeVecF64x2ExtractLane, 0)},
+		{name: OpcodeVecF64x2ExtractLaneName + "/lane=1", body: extractLane(OpcodeVecF64x2ExtractLane, 1)},
+		{name: OpcodeVecI8x16ReplaceLaneName, body: replaceLane(OpcodeVecI8x16ReplaceLane, 0)},
+		{name: OpcodeVecI8x16ReplaceLaneName + "/lane=15", body: replaceLane(OpcodeVecI8x16ReplaceLane, 15)},
+		{name: OpcodeVecI16x8ReplaceLaneName, body: replaceLane(OpcodeVecI16x8ReplaceLane, 0)},
+		{name: OpcodeVecI16x8ReplaceLaneName + "/lane=7", body: replaceLane(OpcodeVecI16x8ReplaceLane, 7)},
+		{name: OpcodeVecI32x4ReplaceLaneName, body: replaceLane(OpcodeVecI32x4ReplaceLane, 0)},
+		{name: OpcodeVecI32x4ReplaceLaneName + "/lane=3", body: replaceLane(OpcodeVecI32x4ReplaceLane, 3)},
+		{name: OpcodeVecI64x2ReplaceLaneName, body: replaceLane(OpcodeVecI64x2ReplaceLane, 0)},
+		{name: OpcodeVecI64x2ReplaceLaneName + "/lane=1", body: replaceLane(OpcodeVecI64x2ReplaceLane, 1)},
+		{name: OpcodeVecF32x4ReplaceLaneName, body: replaceLane(OpcodeVecF32x4ReplaceLane, 0)},
+		{name: OpcodeVecF32x4ReplaceLaneName + "/lane=3", body: replaceLane(OpcodeVecF32x4ReplaceLane, 3)},
+		{name: OpcodeVecF64x2ReplaceLaneName, body: replaceLane(OpcodeVecF64x2ReplaceLane, 0)},
+		{name: OpcodeVecF64x2ReplaceLaneName + "/lane=1", body: replaceLane(OpcodeVecF64x2ReplaceLane, 1)},
+		{name: OpcodeVecI8x16SplatName, body: splat(OpcodeVecI8x16Splat)},
+		{name: OpcodeVecI16x8SplatName, body: splat(OpcodeVecI16x8Splat)},
+		{name: OpcodeVecI32x4SplatName, body: splat(OpcodeVecI32x4Splat)},
+		{name: OpcodeVecI64x2SplatName, body: splat(OpcodeVecI64x2Splat)},
+		{name: OpcodeVecF32x4SplatName, body: splat(OpcodeVecF32x4Splat)},
+		{name: OpcodeVecF64x2SplatName, body: splat(OpcodeVecF64x2Splat)},
+		{name: OpcodeVecI8x16SwizzleName, body: vv2v(OpcodeVecI8x16Swizzle)},
+		{
+			name: OpcodeVecV128i8x16ShuffleName, body: []byte{
 				OpcodeVecPrefix,
 				OpcodeVecV128Const,
 				1, 1, 1, 1, 1, 1, 1, 1,
@@ -2712,7 +2944,8 @@ func TestModule_funcValidation_SIMD(t *testing.T) {
 				1, 1, 1, 1, 1, 1, 1, 1,
 				1, 1, 1, 1, 1, 1, 1, 1,
 				OpcodeVecPrefix,
-				OpcodeVecI64x2Add,
+				OpcodeVecV128i8x16Shuffle,
+				1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
 				OpcodeDrop,
 				OpcodeEnd,
 			},
@@ -2727,19 +2960,21 @@ func TestModule_funcValidation_SIMD(t *testing.T) {
 				FunctionSection: []Index{0},
 				CodeSection:     []*Code{{Body: tc.body}},
 			}
-			err := m.validateFunction(FeatureSIMD, 0, []Index{0}, nil, nil, nil, nil)
+			err := m.validateFunction(FeatureSIMD, 0, []Index{0}, nil, &Memory{}, nil, nil)
 			require.NoError(t, err)
 		})
 	}
 }
 
 func TestModule_funcValidation_SIMD_error(t *testing.T) {
-	tests := []struct {
+	type testCase struct {
 		name        string
 		body        []byte
 		flag        Features
 		expectedErr string
-	}{
+	}
+
+	tests := []testCase{
 		{
 			name: "simd disabled",
 			body: []byte{
@@ -2790,6 +3025,26 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 			flag:        FeatureSIMD,
 			expectedErr: "cannot pop the operand for i64x2.add: v128 missing",
 		},
+		{
+			name: "shuffle lane index not found",
+			flag: FeatureSIMD,
+			body: []byte{
+				OpcodeVecPrefix,
+				OpcodeVecV128i8x16Shuffle,
+			},
+			expectedErr: "16 lane indexes for v128.shuffle not found",
+		},
+		{
+			name: "shuffle lane index not found",
+			flag: FeatureSIMD,
+			body: []byte{
+				OpcodeVecPrefix,
+				OpcodeVecV128i8x16Shuffle,
+				0xff, 0, 0, 0, 0, 0, 0, 0,
+				0, 0, 0, 0, 0, 0, 0, 0,
+			},
+			expectedErr: "invalid lane index[0] 255 >= 32 for v128.shuffle",
+		},
 		{
 			// TODO delete this case after SIMD impl completion.
 			name: "unimplemented",
@@ -2802,6 +3057,56 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 		},
 	}
 
+	addExtractOrReplaceLaneOutOfIndexCase := func(op OpcodeVec, lane, laneCeil byte) {
+		n := VectorInstructionName(op)
+		tests = append(tests, testCase{
+			name: n + "/lane index out of range",
+			flag: FeatureSIMD,
+			body: []byte{
+				OpcodeVecPrefix, op, lane,
+			},
+			expectedErr: fmt.Sprintf("invalid lane index %d >= %d for %s", lane, laneCeil, n),
+		})
+	}
+
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI8x16ExtractLaneS, 16, 16)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI8x16ExtractLaneU, 20, 16)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI16x8ExtractLaneS, 8, 8)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI16x8ExtractLaneU, 8, 8)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI32x4ExtractLane, 4, 4)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecF32x4ExtractLane, 4, 4)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI64x2ExtractLane, 2, 2)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecF64x2ExtractLane, 2, 2)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI8x16ReplaceLane, 16, 16)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI16x8ReplaceLane, 8, 8)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI32x4ReplaceLane, 4, 4)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecI64x2ReplaceLane, 2, 2)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecF32x4ReplaceLane, 10, 4)
+	addExtractOrReplaceLaneOutOfIndexCase(OpcodeVecF64x2ReplaceLane, 3, 2)
+
+	addStoreOrLoadLaneOutOfIndexCase := func(op OpcodeVec, lane, laneCeil byte) {
+		n := VectorInstructionName(op)
+		tests = append(tests, testCase{
+			name: n + "/lane index out of range",
+			flag: FeatureSIMD,
+			body: []byte{
+				OpcodeVecPrefix, op,
+				0, 0, // align and offset.
+				lane,
+			},
+			expectedErr: fmt.Sprintf("invalid lane index %d >= %d for %s", lane, laneCeil, n),
+		})
+	}
+
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Load8Lane, 16, 16)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Load16Lane, 8, 8)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Load32Lane, 4, 4)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Load64Lane, 2, 2)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Store8Lane, 16, 16)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Store16Lane, 8, 8)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Store32Lane, 4, 4)
+	addStoreOrLoadLaneOutOfIndexCase(OpcodeVecV128Store64Lane, 2, 2)
+
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
@@ -2810,7 +3115,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 				FunctionSection: []Index{0},
 				CodeSection:     []*Code{{Body: tc.body}},
 			}
-			err := m.validateFunction(tc.flag, 0, []Index{0}, nil, nil, nil, nil)
+			err := m.validateFunction(tc.flag, 0, []Index{0}, nil, &Memory{}, nil, nil)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}