Repository: tetratelabs/wazero
Pull request #617 (2022-06-02 12:32:58)

SIMD: implements comparison instructions 
This implements all the vector comparison operators for the interpreter
and compiler(amd64).

Notably, now our engines pass `simd_*_cmp.wast` spec tests.

part of #484 

mathetake: ok done with interpreter....
mathetake: will do the wazeroir/compiler_test backfilling in another PR as this is already huge!

Patch:
diff --git a/internal/asm/amd64/consts.go b/internal/asm/amd64/consts.go
index 55444881..ec71cb86 100644
--- a/internal/asm/amd64/consts.go
+++ b/internal/asm/amd64/consts.go
@@ -1,6 +1,10 @@
 package amd64
 
-import "github.com/tetratelabs/wazero/internal/asm"
+import (
+	"fmt"
+
+	"github.com/tetratelabs/wazero/internal/asm"
+)
 
 // AMD64-specific conditional register states.
 //
@@ -81,9 +85,9 @@ const (
 	CVTSL2SD
 	// CVTSL2SS is the CVTSI2SS instruction in 32-bit mode. https://www.felixcloutier.com/x86/cvtsi2ss
 	CVTSL2SS
-	// CVTSL2SD is the CVTSI2SD instruction in 64-bit mode. https://www.felixcloutier.com/x86/cvtsi2sd
+	// CVTSQ2SD is the CVTSI2SD instruction in 64-bit mode. https://www.felixcloutier.com/x86/cvtsi2sd
 	CVTSQ2SD
-	// CVTSL2SS is the CVTSI2SS instruction in 64-bit mode. https://www.felixcloutier.com/x86/cvtsi2ss
+	// CVTSQ2SS is the CVTSI2SS instruction in 64-bit mode. https://www.felixcloutier.com/x86/cvtsi2ss
 	CVTSQ2SS
 	// CVTSS2SD is the CVTSS2SD instruction. https://www.felixcloutier.com/x86/cvtss2sd
 	CVTSS2SD
@@ -409,6 +413,45 @@ const (
 	PUNPCKLBW
 	// PUNPCKHBW is the PUNPCKHBW instruction https://www.felixcloutier.com/x86/punpckhbw:punpckhwd:punpckhdq:punpckhqdq
 	PUNPCKHBW
+	// CMPPS is the CMPPS instruction https://www.felixcloutier.com/x86/cmpps
+	CMPPS
+	// CMPPD is the https://www.felixcloutier.com/x86/cmppd
+	CMPPD
+	// PCMPGTQ is the PCMPGTQ instruction https://www.felixcloutier.com/x86/pcmpgtq
+	PCMPGTQ
+	// PCMPGTD is the PCMPGTD instruction https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTD
+	// PCMPGTW is the PCMPGTW instruction https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTW
+	// PCMPGTB is the PCMPGTB instruction https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTB
+	// PMINSD is the PMINSD instruction https://www.felixcloutier.com/x86/pminsd:pminsq
+	PMINSD
+	// PMINSW is the PMINSW instruction https://www.felixcloutier.com/x86/pminsb:pminsw
+	PMINSW
+	// PMINSB is the PMINSB instruction https://www.felixcloutier.com/x86/pminsb:pminsw
+	PMINSB
+	// PMAXSD is the PMAXSD instruction https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSD
+	// PMAXSW is the PMAXSW instruction https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSW
+	// PMAXSB is the PMAXSB instruction https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSB
+	// PMINUD is the PMINUD instruction https://www.felixcloutier.com/x86/pminud:pminuq
+	PMINUD
+	// PMINUW is the PMINUW instruction https://www.felixcloutier.com/x86/pminub:pminuw
+	PMINUW
+	// PMINUB is the PMINUB instruction https://www.felixcloutier.com/x86/pminub:pminuw
+	PMINUB
+	// PMAXUD is the PMAXUD instruction https://www.felixcloutier.com/x86/pmaxud:pmaxuq
+	PMAXUD
+	// PMAXUW is the PMAXUW instruction https://www.felixcloutier.com/x86/pmaxub:pmaxuw
+	PMAXUW
+	// PMAXUB is the PMAXUB instruction https://www.felixcloutier.com/x86/pmaxub:pmaxuw
+	PMAXUB
+
+	// instructionEnd is always placed at the bottom of this iota definition to be used in the test.
+	instructionEnd
 )
 
 // InstructionName returns the name for an instruction
@@ -784,8 +827,44 @@ func InstructionName(instruction asm.Instruction) string {
 		return "NEGQ"
 	case NONE:
 		return "NONE"
+	case CMPPS:
+		return "CMPPS"
+	case CMPPD:
+		return "CMPPD"
+	case PCMPGTQ:
+		return "PCMPGTQ"
+	case PCMPGTD:
+		return "PCMPGTD"
+	case PMINSD:
+		return "PMINSD"
+	case PMAXSD:
+		return "PMAXSD"
+	case PMINSW:
+		return "PMINSW"
+	case PCMPGTB:
+		return "PCMPGTB"
+	case PMINSB:
+		return "PMINSB"
+	case PMINUD:
+		return "PMINUD"
+	case PMINUW:
+		return "PMINUW"
+	case PMINUB:
+		return "PMINUB"
+	case PMAXUD:
+		return "PMAXUD"
+	case PMAXUW:
+		return "PMAXUW"
+	case PMAXUB:
+		return "PMAXUB"
+	case PCMPGTW:
+		return "PCMPGTW"
+	case PMAXSW:
+		return "PMAXSW"
+	case PMAXSB:
+		return "PMAXSB"
 	}
-	panic(instruction)
+	panic(fmt.Errorf("unknown instruction %d", instruction))
 }
 
 // Amd64-specific registers.
diff --git a/internal/asm/amd64/impl.go b/internal/asm/amd64/impl.go
index cc58f074..c9b213ae 100644
--- a/internal/asm/amd64/impl.go
+++ b/internal/asm/amd64/impl.go
@@ -1294,6 +1294,42 @@ var registerToRegisterOpcode = map[asm.Instruction]struct {
 	PUNPCKLBW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x60}, requireSrcFloat: true, requireDstFloat: true},
 	// https://www.felixcloutier.com/x86/punpckhbw:punpckhwd:punpckhdq:punpckhqdq
 	PUNPCKHBW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x68}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/cmpps
+	CMPPS: {opcode: []byte{0x0f, 0xc2}, requireSrcFloat: true, requireDstFloat: true, needArg: true},
+	// https://www.felixcloutier.com/x86/cmppd
+	CMPPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xc2}, requireSrcFloat: true, requireDstFloat: true, needArg: true},
+	// https://www.felixcloutier.com/x86/pcmpgtq
+	PCMPGTQ: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x37}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x66}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x65}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pcmpgtb:pcmpgtw:pcmpgtd
+	PCMPGTB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x64}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminsd:pminsq
+	PMINSD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x39}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3d}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xee}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxsb:pmaxsw:pmaxsd:pmaxsq
+	PMAXSB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3c}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminsb:pminsw
+	PMINSW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xea}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminsb:pminsw
+	PMINSB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x38}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminud:pminuq
+	PMINUD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3b}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminub:pminuw
+	PMINUW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3a}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pminub:pminuw
+	PMINUB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xda}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxud:pmaxuq
+	PMAXUD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3f}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxub:pmaxuw
+	PMAXUW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x3e}, requireSrcFloat: true, requireDstFloat: true},
+	// https://www.felixcloutier.com/x86/pmaxub:pmaxuw
+	PMAXUB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xde}, requireSrcFloat: true, requireDstFloat: true},
 }
 
 var RegisterToRegisterShiftOpcode = map[asm.Instruction]struct {
diff --git a/internal/asm/arm64/consts.go b/internal/asm/arm64/consts.go
index 022c1bc6..85e5c1be 100644
--- a/internal/asm/arm64/consts.go
+++ b/internal/asm/arm64/consts.go
@@ -1,6 +1,8 @@
 package arm64
 
 import (
+	"fmt"
+
 	"github.com/tetratelabs/wazero/internal/asm"
 )
 
@@ -725,6 +727,9 @@ const (
 	TBL1
 	// TBL2 is the TBL instruction whose source is two vectors. https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/TBL--Table-vector-Lookup-
 	TBL2
+
+	// instructionEnd is always placed at the bottom of this iota definition to be used in the test.
+	instructionEnd
 )
 
 // VectorArrangement is the arrangement of data within a vector register.
@@ -1082,6 +1087,10 @@ func InstructionName(i asm.Instruction) string {
 		return "CMEQ"
 	case ADDP:
 		return "ADDP"
+	case TBL1:
+		return "TBL1"
+	case TBL2:
+		return "TBL2"
 	}
-	panic("unknown instruction")
+	panic(fmt.Errorf("unknown instruction %d", i))
 }
diff --git a/internal/engine/compiler/compiler.go b/internal/engine/compiler/compiler.go
index 2d58a0da..8e57a6ea 100644
--- a/internal/engine/compiler/compiler.go
+++ b/internal/engine/compiler/compiler.go
@@ -470,8 +470,20 @@ type compiler interface {
 	// See wasm.OpcodeVecI8x16ShrSName wasm.OpcodeVecI8x16ShrUName wasm.OpcodeVecI16x8ShrSName
 	// wasm.OpcodeVecI16x8ShrUName wasm.OpcodeVecI32x4ShrSName wasm.OpcodeVecI32x4ShrUName.
 	// wasm.OpcodeVecI64x2ShrSName wasm.OpcodeVecI64x2ShrUName.
-	compileV128Shr(shr *wazeroir.OperationV128Shr) error
+	compileV128Shr(*wazeroir.OperationV128Shr) error
 	// compileV128Shl adds instructions which are equivalent to wasm.OpcodeVecXXXShl instructions.
 	// See wasm.OpcodeVecI8x16ShlName wasm.OpcodeVecI16x8ShlName wasm.OpcodeVecI32x4ShlName wasm.OpcodeVecI64x2ShlName
-	compileV128Shl(shr *wazeroir.OperationV128Shl) error
+	compileV128Shl(*wazeroir.OperationV128Shl) error
+	// compileV128Cmp adds instructions which are equivalent to various vector comparison instructions.
+	// See wasm.OpcodeVecI8x16EqName, wasm.OpcodeVecI8x16NeName, wasm.OpcodeVecI8x16LtSName, wasm.OpcodeVecI8x16LtUName, wasm.OpcodeVecI8x16GtSName,
+	//	wasm.OpcodeVecI8x16GtUName, wasm.OpcodeVecI8x16LeSName, wasm.OpcodeVecI8x16LeUName, wasm.OpcodeVecI8x16GeSName, wasm.OpcodeVecI8x16GeUName,
+	//	wasm.OpcodeVecI16x8EqName, wasm.OpcodeVecI16x8NeName, wasm.OpcodeVecI16x8LtSName, wasm.OpcodeVecI16x8LtUName, wasm.OpcodeVecI16x8GtSName,
+	//	wasm.OpcodeVecI16x8GtUName, wasm.OpcodeVecI16x8LeSName, wasm.OpcodeVecI16x8LeUName, wasm.OpcodeVecI16x8GeSName, wasm.OpcodeVecI16x8GeUName,
+	//	wasm.OpcodeVecI32x4EqName, wasm.OpcodeVecI32x4NeName, wasm.OpcodeVecI32x4LtSName, wasm.OpcodeVecI32x4LtUName, wasm.OpcodeVecI32x4GtSName,
+	//	wasm.OpcodeVecI32x4GtUName, wasm.OpcodeVecI32x4LeSName, wasm.OpcodeVecI32x4LeUName, wasm.OpcodeVecI32x4GeSName, wasm.OpcodeVecI32x4GeUName,
+	//	wasm.OpcodeVecI64x2EqName, wasm.OpcodeVecI64x2NeName, wasm.OpcodeVecI64x2LtSName, wasm.OpcodeVecI64x2GtSName, wasm.OpcodeVecI64x2LeSName,
+	//	wasm.OpcodeVecI64x2GeSName, wasm.OpcodeVecF32x4EqName, wasm.OpcodeVecF32x4NeName, wasm.OpcodeVecF32x4LtName, wasm.OpcodeVecF32x4GtName,
+	//	wasm.OpcodeVecF32x4LeName, wasm.OpcodeVecF32x4GeName, wasm.OpcodeVecF64x2EqName, wasm.OpcodeVecF64x2NeName, wasm.OpcodeVecF64x2LtName,
+	//	wasm.OpcodeVecF64x2GtName, wasm.OpcodeVecF64x2LeName, wasm.OpcodeVecF64x2GeName
+	compileV128Cmp(*wazeroir.OperationV128Cmp) error
 }
diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go
index aef6037a..fbf7c078 100644
--- a/internal/engine/compiler/engine.go
+++ b/internal/engine/compiler/engine.go
@@ -1092,6 +1092,8 @@ func compileWasmFunction(_ wasm.Features, ir *wazeroir.CompilationResult) (*code
 			err = compiler.compileV128Shr(o)
 		case *wazeroir.OperationV128Shl:
 			err = compiler.compileV128Shl(o)
+		case *wazeroir.OperationV128Cmp:
+			err = compiler.compileV128Cmp(o)
 		default:
 			err = errors.New("unsupported")
 		}
diff --git a/internal/engine/compiler/impl_vec_amd64.go b/internal/engine/compiler/impl_vec_amd64.go
index 5b6f0bb5..d280faa0 100644
--- a/internal/engine/compiler/impl_vec_amd64.go
+++ b/internal/engine/compiler/impl_vec_amd64.go
@@ -1063,3 +1063,270 @@ func (c *amd64Compiler) compileV128Shl(o *wazeroir.OperationV128Shl) error {
 	c.pushVectorRuntimeValueLocationOnRegister(x1.register)
 	return nil
 }
+
+// compileV128Cmp implements compiler.compileV128Cmp for amd64.
+func (c *amd64Compiler) compileV128Cmp(o *wazeroir.OperationV128Cmp) error {
+	x2 := c.locationStack.popV128()
+	if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil {
+		return err
+	}
+
+	x1 := c.locationStack.popV128()
+	if err := c.compileEnsureOnGeneralPurposeRegister(x1); err != nil {
+		return err
+	}
+
+	const (
+		// See https://www.felixcloutier.com/x86/cmppd and https://www.felixcloutier.com/x86/cmpps
+		floatEqualArg           = 0
+		floatLessThanArg        = 1
+		floatLessThanOrEqualArg = 2
+		floatNotEqualARg        = 4
+	)
+
+	x1Reg, x2Reg, result := x1.register, x2.register, asm.NilRegister
+	switch o.Type {
+	case wazeroir.V128CmpTypeF32x4Eq:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x2Reg, x1Reg, floatEqualArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF32x4Ne:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x2Reg, x1Reg, floatNotEqualARg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF32x4Lt:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x2Reg, x1Reg, floatLessThanArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF32x4Gt:
+		// Without AVX, there's no float Gt instruction, so we swap the register and use Lt instead.
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x1Reg, x2Reg, floatLessThanArg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeF32x4Le:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x2Reg, x1Reg, floatLessThanOrEqualArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF32x4Ge:
+		// Without AVX, there's no float Ge instruction, so we swap the register and use Le instead.
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPS, x1Reg, x2Reg, floatLessThanOrEqualArg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeF64x2Eq:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x2Reg, x1Reg, floatEqualArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF64x2Ne:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x2Reg, x1Reg, floatNotEqualARg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF64x2Lt:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x2Reg, x1Reg, floatLessThanArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF64x2Gt:
+		// Without AVX, there's no float Gt instruction, so we swap the register and use Lt instead.
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x1Reg, x2Reg, floatLessThanArg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeF64x2Le:
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x2Reg, x1Reg, floatLessThanOrEqualArg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeF64x2Ge:
+		// Without AVX, there's no float Ge instruction, so we swap the register and use Le instead.
+		c.assembler.CompileRegisterToRegisterWithArg(amd64.CMPPD, x1Reg, x2Reg, floatLessThanOrEqualArg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeI8x16Eq:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQB, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI8x16Ne:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQB, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI8x16LtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTB, x1Reg, x2Reg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeI8x16LtU, wazeroir.V128CmpTypeI8x16GtU:
+		// Take the unsigned min/max values on each byte on x1 and x2 onto x1Reg.
+		if o.Type == wazeroir.V128CmpTypeI8x16LtU {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUB, x2Reg, x1Reg)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUB, x2Reg, x1Reg)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQB, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI8x16GtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTB, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI8x16LeS, wazeroir.V128CmpTypeI8x16LeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		// Copy the value on the src to tmp.
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI8x16LeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMINSB, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUB, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQB, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI8x16GeS, wazeroir.V128CmpTypeI8x16GeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI8x16GeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXSB, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUB, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQB, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8Eq:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQW, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8Ne:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQW, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8LtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTW, x1Reg, x2Reg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeI16x8LtU, wazeroir.V128CmpTypeI16x8GtU:
+		// Take the unsigned min/max values on each byte on x1 and x2 onto x1Reg.
+		if o.Type == wazeroir.V128CmpTypeI16x8LtU {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUW, x2Reg, x1Reg)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUW, x2Reg, x1Reg)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQW, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8GtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTW, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8LeS, wazeroir.V128CmpTypeI16x8LeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		// Copy the value on the src to tmp.
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI16x8LeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMINSW, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUW, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQW, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI16x8GeS, wazeroir.V128CmpTypeI16x8GeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI16x8GeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXSW, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUW, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQW, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4Eq:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4Ne:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4LtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTD, x1Reg, x2Reg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeI32x4LtU, wazeroir.V128CmpTypeI32x4GtU:
+		// Take the unsigned min/max values on each byte on x1 and x2 onto x1Reg.
+		if o.Type == wazeroir.V128CmpTypeI32x4LtU {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUD, x2Reg, x1Reg)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUD, x2Reg, x1Reg)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4GtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTD, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4LeS, wazeroir.V128CmpTypeI32x4LeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		// Copy the value on the src to tmp.
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI32x4LeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMINSD, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMINUD, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI32x4GeS, wazeroir.V128CmpTypeI32x4GeU:
+		tmp, err := c.allocateRegister(registerTypeVector)
+		if err != nil {
+			return err
+		}
+		c.assembler.CompileRegisterToRegister(amd64.MOVDQA, x1Reg, tmp)
+		if o.Type == wazeroir.V128CmpTypeI32x4GeS {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXSD, x2Reg, tmp)
+		} else {
+			c.assembler.CompileRegisterToRegister(amd64.PMAXUD, x2Reg, tmp)
+		}
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, tmp, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI64x2Eq:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQQ, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI64x2Ne:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQQ, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI64x2LtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTQ, x1Reg, x2Reg)
+		result = x2Reg
+	case wazeroir.V128CmpTypeI64x2GtS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTQ, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI64x2LeS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTQ, x2Reg, x1Reg)
+		// Set all bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x2Reg, x2Reg)
+		// Swap the bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x2Reg, x1Reg)
+		result = x1Reg
+	case wazeroir.V128CmpTypeI64x2GeS:
+		c.assembler.CompileRegisterToRegister(amd64.PCMPGTQ, x1Reg, x2Reg)
+		// Set all bits on x1Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PCMPEQD, x1Reg, x1Reg)
+		// Swap the bits on x2Reg register.
+		c.assembler.CompileRegisterToRegister(amd64.PXOR, x1Reg, x2Reg)
+		result = x2Reg
+	}
+
+	c.locationStack.markRegisterUnused(x1Reg, x2Reg)
+	c.pushVectorRuntimeValueLocationOnRegister(result)
+	return nil
+}
diff --git a/internal/engine/compiler/impl_vec_arm64.go b/internal/engine/compiler/impl_vec_arm64.go
index 2825ae85..ee1d368c 100644
--- a/internal/engine/compiler/impl_vec_arm64.go
+++ b/internal/engine/compiler/impl_vec_arm64.go
@@ -697,3 +697,8 @@ func (c *arm64Compiler) compileV128Shr(o *wazeroir.OperationV128Shr) error {
 func (c *arm64Compiler) compileV128Shl(o *wazeroir.OperationV128Shl) error {
 	return fmt.Errorf("TODO: %s is not implemented yet on arm64 compiler", o.Kind())
 }
+
+// compileV128Cmp implements compiler.compileV128Cmp for arm64.
+func (c *arm64Compiler) compileV128Cmp(o *wazeroir.OperationV128Cmp) error {
+	return fmt.Errorf("TODO: %s is not implemented yet on arm64 compiler", o.Kind())
+}
diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go
index 047f560a..75dfd41a 100644
--- a/internal/engine/interpreter/interpreter.go
+++ b/internal/engine/interpreter/interpreter.go
@@ -643,6 +643,8 @@ func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) {
 			op.b3 = o.Signed
 		case *wazeroir.OperationV128Shl:
 			op.b1 = o.Shape
+		case *wazeroir.OperationV128Cmp:
+			op.b1 = o.Type
 		default:
 			panic(fmt.Errorf("BUG: unimplemented operation %s", op.kind.String()))
 		}
@@ -2551,10 +2553,377 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, callCtx *wasm.CallCont
 					lo = lo >> s
 					hi = hi >> s
 				}
+
 			}
 			ce.pushValue(lo)
 			ce.pushValue(hi)
 			frame.pc++
+		case wazeroir.OperationKindV128Cmp:
+			x2Hi, x2Lo := ce.popValue(), ce.popValue()
+			x1Hi, x1Lo := ce.popValue(), ce.popValue()
+			var result []bool
+			switch op.b1 {
+			case wazeroir.V128CmpTypeI8x16Eq:
+				result = []bool{
+					byte(x1Lo>>0) == byte(x2Lo>>0), byte(x1Lo>>8) == byte(x2Lo>>8),
+					byte(x1Lo>>16) == byte(x2Lo>>16), byte(x1Lo>>24) == byte(x2Lo>>24),
+					byte(x1Lo>>32) == byte(x2Lo>>32), byte(x1Lo>>40) == byte(x2Lo>>40),
+					byte(x1Lo>>48) == byte(x2Lo>>48), byte(x1Lo>>56) == byte(x2Lo>>56),
+					byte(x1Hi>>0) == byte(x2Hi>>0), byte(x1Hi>>8) == byte(x2Hi>>8),
+					byte(x1Hi>>16) == byte(x2Hi>>16), byte(x1Hi>>24) == byte(x2Hi>>24),
+					byte(x1Hi>>32) == byte(x2Hi>>32), byte(x1Hi>>40) == byte(x2Hi>>40),
+					byte(x1Hi>>48) == byte(x2Hi>>48), byte(x1Hi>>56) == byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16Ne:
+				result = []bool{
+					byte(x1Lo>>0) != byte(x2Lo>>0), byte(x1Lo>>8) != byte(x2Lo>>8),
+					byte(x1Lo>>16) != byte(x2Lo>>16), byte(x1Lo>>24) != byte(x2Lo>>24),
+					byte(x1Lo>>32) != byte(x2Lo>>32), byte(x1Lo>>40) != byte(x2Lo>>40),
+					byte(x1Lo>>48) != byte(x2Lo>>48), byte(x1Lo>>56) != byte(x2Lo>>56),
+					byte(x1Hi>>0) != byte(x2Hi>>0), byte(x1Hi>>8) != byte(x2Hi>>8),
+					byte(x1Hi>>16) != byte(x2Hi>>16), byte(x1Hi>>24) != byte(x2Hi>>24),
+					byte(x1Hi>>32) != byte(x2Hi>>32), byte(x1Hi>>40) != byte(x2Hi>>40),
+					byte(x1Hi>>48) != byte(x2Hi>>48), byte(x1Hi>>56) != byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16LtS:
+				result = []bool{
+					int8(x1Lo>>0) < int8(x2Lo>>0), int8(x1Lo>>8) < int8(x2Lo>>8),
+					int8(x1Lo>>16) < int8(x2Lo>>16), int8(x1Lo>>24) < int8(x2Lo>>24),
+					int8(x1Lo>>32) < int8(x2Lo>>32), int8(x1Lo>>40) < int8(x2Lo>>40),
+					int8(x1Lo>>48) < int8(x2Lo>>48), int8(x1Lo>>56) < int8(x2Lo>>56),
+					int8(x1Hi>>0) < int8(x2Hi>>0), int8(x1Hi>>8) < int8(x2Hi>>8),
+					int8(x1Hi>>16) < int8(x2Hi>>16), int8(x1Hi>>24) < int8(x2Hi>>24),
+					int8(x1Hi>>32) < int8(x2Hi>>32), int8(x1Hi>>40) < int8(x2Hi>>40),
+					int8(x1Hi>>48) < int8(x2Hi>>48), int8(x1Hi>>56) < int8(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16LtU:
+				result = []bool{
+					byte(x1Lo>>0) < byte(x2Lo>>0), byte(x1Lo>>8) < byte(x2Lo>>8),
+					byte(x1Lo>>16) < byte(x2Lo>>16), byte(x1Lo>>24) < byte(x2Lo>>24),
+					byte(x1Lo>>32) < byte(x2Lo>>32), byte(x1Lo>>40) < byte(x2Lo>>40),
+					byte(x1Lo>>48) < byte(x2Lo>>48), byte(x1Lo>>56) < byte(x2Lo>>56),
+					byte(x1Hi>>0) < byte(x2Hi>>0), byte(x1Hi>>8) < byte(x2Hi>>8),
+					byte(x1Hi>>16) < byte(x2Hi>>16), byte(x1Hi>>24) < byte(x2Hi>>24),
+					byte(x1Hi>>32) < byte(x2Hi>>32), byte(x1Hi>>40) < byte(x2Hi>>40),
+					byte(x1Hi>>48) < byte(x2Hi>>48), byte(x1Hi>>56) < byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16GtS:
+				result = []bool{
+					int8(x1Lo>>0) > int8(x2Lo>>0), int8(x1Lo>>8) > int8(x2Lo>>8),
+					int8(x1Lo>>16) > int8(x2Lo>>16), int8(x1Lo>>24) > int8(x2Lo>>24),
+					int8(x1Lo>>32) > int8(x2Lo>>32), int8(x1Lo>>40) > int8(x2Lo>>40),
+					int8(x1Lo>>48) > int8(x2Lo>>48), int8(x1Lo>>56) > int8(x2Lo>>56),
+					int8(x1Hi>>0) > int8(x2Hi>>0), int8(x1Hi>>8) > int8(x2Hi>>8),
+					int8(x1Hi>>16) > int8(x2Hi>>16), int8(x1Hi>>24) > int8(x2Hi>>24),
+					int8(x1Hi>>32) > int8(x2Hi>>32), int8(x1Hi>>40) > int8(x2Hi>>40),
+					int8(x1Hi>>48) > int8(x2Hi>>48), int8(x1Hi>>56) > int8(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16GtU:
+				result = []bool{
+					byte(x1Lo>>0) > byte(x2Lo>>0), byte(x1Lo>>8) > byte(x2Lo>>8),
+					byte(x1Lo>>16) > byte(x2Lo>>16), byte(x1Lo>>24) > byte(x2Lo>>24),
+					byte(x1Lo>>32) > byte(x2Lo>>32), byte(x1Lo>>40) > byte(x2Lo>>40),
+					byte(x1Lo>>48) > byte(x2Lo>>48), byte(x1Lo>>56) > byte(x2Lo>>56),
+					byte(x1Hi>>0) > byte(x2Hi>>0), byte(x1Hi>>8) > byte(x2Hi>>8),
+					byte(x1Hi>>16) > byte(x2Hi>>16), byte(x1Hi>>24) > byte(x2Hi>>24),
+					byte(x1Hi>>32) > byte(x2Hi>>32), byte(x1Hi>>40) > byte(x2Hi>>40),
+					byte(x1Hi>>48) > byte(x2Hi>>48), byte(x1Hi>>56) > byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16LeS:
+				result = []bool{
+					int8(x1Lo>>0) <= int8(x2Lo>>0), int8(x1Lo>>8) <= int8(x2Lo>>8),
+					int8(x1Lo>>16) <= int8(x2Lo>>16), int8(x1Lo>>24) <= int8(x2Lo>>24),
+					int8(x1Lo>>32) <= int8(x2Lo>>32), int8(x1Lo>>40) <= int8(x2Lo>>40),
+					int8(x1Lo>>48) <= int8(x2Lo>>48), int8(x1Lo>>56) <= int8(x2Lo>>56),
+					int8(x1Hi>>0) <= int8(x2Hi>>0), int8(x1Hi>>8) <= int8(x2Hi>>8),
+					int8(x1Hi>>16) <= int8(x2Hi>>16), int8(x1Hi>>24) <= int8(x2Hi>>24),
+					int8(x1Hi>>32) <= int8(x2Hi>>32), int8(x1Hi>>40) <= int8(x2Hi>>40),
+					int8(x1Hi>>48) <= int8(x2Hi>>48), int8(x1Hi>>56) <= int8(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16LeU:
+				result = []bool{
+					byte(x1Lo>>0) <= byte(x2Lo>>0), byte(x1Lo>>8) <= byte(x2Lo>>8),
+					byte(x1Lo>>16) <= byte(x2Lo>>16), byte(x1Lo>>24) <= byte(x2Lo>>24),
+					byte(x1Lo>>32) <= byte(x2Lo>>32), byte(x1Lo>>40) <= byte(x2Lo>>40),
+					byte(x1Lo>>48) <= byte(x2Lo>>48), byte(x1Lo>>56) <= byte(x2Lo>>56),
+					byte(x1Hi>>0) <= byte(x2Hi>>0), byte(x1Hi>>8) <= byte(x2Hi>>8),
+					byte(x1Hi>>16) <= byte(x2Hi>>16), byte(x1Hi>>24) <= byte(x2Hi>>24),
+					byte(x1Hi>>32) <= byte(x2Hi>>32), byte(x1Hi>>40) <= byte(x2Hi>>40),
+					byte(x1Hi>>48) <= byte(x2Hi>>48), byte(x1Hi>>56) <= byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16GeS:
+				result = []bool{
+					int8(x1Lo>>0) >= int8(x2Lo>>0), int8(x1Lo>>8) >= int8(x2Lo>>8),
+					int8(x1Lo>>16) >= int8(x2Lo>>16), int8(x1Lo>>24) >= int8(x2Lo>>24),
+					int8(x1Lo>>32) >= int8(x2Lo>>32), int8(x1Lo>>40) >= int8(x2Lo>>40),
+					int8(x1Lo>>48) >= int8(x2Lo>>48), int8(x1Lo>>56) >= int8(x2Lo>>56),
+					int8(x1Hi>>0) >= int8(x2Hi>>0), int8(x1Hi>>8) >= int8(x2Hi>>8),
+					int8(x1Hi>>16) >= int8(x2Hi>>16), int8(x1Hi>>24) >= int8(x2Hi>>24),
+					int8(x1Hi>>32) >= int8(x2Hi>>32), int8(x1Hi>>40) >= int8(x2Hi>>40),
+					int8(x1Hi>>48) >= int8(x2Hi>>48), int8(x1Hi>>56) >= int8(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI8x16GeU:
+				result = []bool{
+					byte(x1Lo>>0) >= byte(x2Lo>>0), byte(x1Lo>>8) >= byte(x2Lo>>8),
+					byte(x1Lo>>16) >= byte(x2Lo>>16), byte(x1Lo>>24) >= byte(x2Lo>>24),
+					byte(x1Lo>>32) >= byte(x2Lo>>32), byte(x1Lo>>40) >= byte(x2Lo>>40),
+					byte(x1Lo>>48) >= byte(x2Lo>>48), byte(x1Lo>>56) >= byte(x2Lo>>56),
+					byte(x1Hi>>0) >= byte(x2Hi>>0), byte(x1Hi>>8) >= byte(x2Hi>>8),
+					byte(x1Hi>>16) >= byte(x2Hi>>16), byte(x1Hi>>24) >= byte(x2Hi>>24),
+					byte(x1Hi>>32) >= byte(x2Hi>>32), byte(x1Hi>>40) >= byte(x2Hi>>40),
+					byte(x1Hi>>48) >= byte(x2Hi>>48), byte(x1Hi>>56) >= byte(x2Hi>>56),
+				}
+			case wazeroir.V128CmpTypeI16x8Eq:
+				result = []bool{
+					uint16(x1Lo>>0) == uint16(x2Lo>>0), uint16(x1Lo>>16) == uint16(x2Lo>>16),
+					uint16(x1Lo>>32) == uint16(x2Lo>>32), uint16(x1Lo>>48) == uint16(x2Lo>>48),
+					uint16(x1Hi>>0) == uint16(x2Hi>>0), uint16(x1Hi>>16) == uint16(x2Hi>>16),
+					uint16(x1Hi>>32) == uint16(x2Hi>>32), uint16(x1Hi>>48) == uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8Ne:
+				result = []bool{
+					uint16(x1Lo>>0) != uint16(x2Lo>>0), uint16(x1Lo>>16) != uint16(x2Lo>>16),
+					uint16(x1Lo>>32) != uint16(x2Lo>>32), uint16(x1Lo>>48) != uint16(x2Lo>>48),
+					uint16(x1Hi>>0) != uint16(x2Hi>>0), uint16(x1Hi>>16) != uint16(x2Hi>>16),
+					uint16(x1Hi>>32) != uint16(x2Hi>>32), uint16(x1Hi>>48) != uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8LtS:
+				result = []bool{
+					int16(x1Lo>>0) < int16(x2Lo>>0), int16(x1Lo>>16) < int16(x2Lo>>16),
+					int16(x1Lo>>32) < int16(x2Lo>>32), int16(x1Lo>>48) < int16(x2Lo>>48),
+					int16(x1Hi>>0) < int16(x2Hi>>0), int16(x1Hi>>16) < int16(x2Hi>>16),
+					int16(x1Hi>>32) < int16(x2Hi>>32), int16(x1Hi>>48) < int16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8LtU:
+				result = []bool{
+					uint16(x1Lo>>0) < uint16(x2Lo>>0), uint16(x1Lo>>16) < uint16(x2Lo>>16),
+					uint16(x1Lo>>32) < uint16(x2Lo>>32), uint16(x1Lo>>48) < uint16(x2Lo>>48),
+					uint16(x1Hi>>0) < uint16(x2Hi>>0), uint16(x1Hi>>16) < uint16(x2Hi>>16),
+					uint16(x1Hi>>32) < uint16(x2Hi>>32), uint16(x1Hi>>48) < uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8GtS:
+				result = []bool{
+					int16(x1Lo>>0) > int16(x2Lo>>0), int16(x1Lo>>16) > int16(x2Lo>>16),
+					int16(x1Lo>>32) > int16(x2Lo>>32), int16(x1Lo>>48) > int16(x2Lo>>48),
+					int16(x1Hi>>0) > int16(x2Hi>>0), int16(x1Hi>>16) > int16(x2Hi>>16),
+					int16(x1Hi>>32) > int16(x2Hi>>32), int16(x1Hi>>48) > int16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8GtU:
+				result = []bool{
+					uint16(x1Lo>>0) > uint16(x2Lo>>0), uint16(x1Lo>>16) > uint16(x2Lo>>16),
+					uint16(x1Lo>>32) > uint16(x2Lo>>32), uint16(x1Lo>>48) > uint16(x2Lo>>48),
+					uint16(x1Hi>>0) > uint16(x2Hi>>0), uint16(x1Hi>>16) > uint16(x2Hi>>16),
+					uint16(x1Hi>>32) > uint16(x2Hi>>32), uint16(x1Hi>>48) > uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8LeS:
+				result = []bool{
+					int16(x1Lo>>0) <= int16(x2Lo>>0), int16(x1Lo>>16) <= int16(x2Lo>>16),
+					int16(x1Lo>>32) <= int16(x2Lo>>32), int16(x1Lo>>48) <= int16(x2Lo>>48),
+					int16(x1Hi>>0) <= int16(x2Hi>>0), int16(x1Hi>>16) <= int16(x2Hi>>16),
+					int16(x1Hi>>32) <= int16(x2Hi>>32), int16(x1Hi>>48) <= int16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8LeU:
+				result = []bool{
+					uint16(x1Lo>>0) <= uint16(x2Lo>>0), uint16(x1Lo>>16) <= uint16(x2Lo>>16),
+					uint16(x1Lo>>32) <= uint16(x2Lo>>32), uint16(x1Lo>>48) <= uint16(x2Lo>>48),
+					uint16(x1Hi>>0) <= uint16(x2Hi>>0), uint16(x1Hi>>16) <= uint16(x2Hi>>16),
+					uint16(x1Hi>>32) <= uint16(x2Hi>>32), uint16(x1Hi>>48) <= uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8GeS:
+				result = []bool{
+					int16(x1Lo>>0) >= int16(x2Lo>>0), int16(x1Lo>>16) >= int16(x2Lo>>16),
+					int16(x1Lo>>32) >= int16(x2Lo>>32), int16(x1Lo>>48) >= int16(x2Lo>>48),
+					int16(x1Hi>>0) >= int16(x2Hi>>0), int16(x1Hi>>16) >= int16(x2Hi>>16),
+					int16(x1Hi>>32) >= int16(x2Hi>>32), int16(x1Hi>>48) >= int16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI16x8GeU:
+				result = []bool{
+					uint16(x1Lo>>0) >= uint16(x2Lo>>0), uint16(x1Lo>>16) >= uint16(x2Lo>>16),
+					uint16(x1Lo>>32) >= uint16(x2Lo>>32), uint16(x1Lo>>48) >= uint16(x2Lo>>48),
+					uint16(x1Hi>>0) >= uint16(x2Hi>>0), uint16(x1Hi>>16) >= uint16(x2Hi>>16),
+					uint16(x1Hi>>32) >= uint16(x2Hi>>32), uint16(x1Hi>>48) >= uint16(x2Hi>>48),
+				}
+			case wazeroir.V128CmpTypeI32x4Eq:
+				result = []bool{
+					uint32(x1Lo>>0) == uint32(x2Lo>>0), uint32(x1Lo>>32) == uint32(x2Lo>>32),
+					uint32(x1Hi>>0) == uint32(x2Hi>>0), uint32(x1Hi>>32) == uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4Ne:
+				result = []bool{
+					uint32(x1Lo>>0) != uint32(x2Lo>>0), uint32(x1Lo>>32) != uint32(x2Lo>>32),
+					uint32(x1Hi>>0) != uint32(x2Hi>>0), uint32(x1Hi>>32) != uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4LtS:
+				result = []bool{
+					int32(x1Lo>>0) < int32(x2Lo>>0), int32(x1Lo>>32) < int32(x2Lo>>32),
+					int32(x1Hi>>0) < int32(x2Hi>>0), int32(x1Hi>>32) < int32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4LtU:
+				result = []bool{
+					uint32(x1Lo>>0) < uint32(x2Lo>>0), uint32(x1Lo>>32) < uint32(x2Lo>>32),
+					uint32(x1Hi>>0) < uint32(x2Hi>>0), uint32(x1Hi>>32) < uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4GtS:
+				result = []bool{
+					int32(x1Lo>>0) > int32(x2Lo>>0), int32(x1Lo>>32) > int32(x2Lo>>32),
+					int32(x1Hi>>0) > int32(x2Hi>>0), int32(x1Hi>>32) > int32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4GtU:
+				result = []bool{
+					uint32(x1Lo>>0) > uint32(x2Lo>>0), uint32(x1Lo>>32) > uint32(x2Lo>>32),
+					uint32(x1Hi>>0) > uint32(x2Hi>>0), uint32(x1Hi>>32) > uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4LeS:
+				result = []bool{
+					int32(x1Lo>>0) <= int32(x2Lo>>0), int32(x1Lo>>32) <= int32(x2Lo>>32),
+					int32(x1Hi>>0) <= int32(x2Hi>>0), int32(x1Hi>>32) <= int32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4LeU:
+				result = []bool{
+					uint32(x1Lo>>0) <= uint32(x2Lo>>0), uint32(x1Lo>>32) <= uint32(x2Lo>>32),
+					uint32(x1Hi>>0) <= uint32(x2Hi>>0), uint32(x1Hi>>32) <= uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4GeS:
+				result = []bool{
+					int32(x1Lo>>0) >= int32(x2Lo>>0), int32(x1Lo>>32) >= int32(x2Lo>>32),
+					int32(x1Hi>>0) >= int32(x2Hi>>0), int32(x1Hi>>32) >= int32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI32x4GeU:
+				result = []bool{
+					uint32(x1Lo>>0) >= uint32(x2Lo>>0), uint32(x1Lo>>32) >= uint32(x2Lo>>32),
+					uint32(x1Hi>>0) >= uint32(x2Hi>>0), uint32(x1Hi>>32) >= uint32(x2Hi>>32),
+				}
+			case wazeroir.V128CmpTypeI64x2Eq:
+				result = []bool{x1Lo == x2Lo, x1Hi == x2Hi}
+			case wazeroir.V128CmpTypeI64x2Ne:
+				result = []bool{x1Lo != x2Lo, x1Hi != x2Hi}
+			case wazeroir.V128CmpTypeI64x2LtS:
+				result = []bool{int64(x1Lo) < int64(x2Lo), int64(x1Hi) < int64(x2Hi)}
+			case wazeroir.V128CmpTypeI64x2GtS:
+				result = []bool{int64(x1Lo) > int64(x2Lo), int64(x1Hi) > int64(x2Hi)}
+			case wazeroir.V128CmpTypeI64x2LeS:
+				result = []bool{int64(x1Lo) <= int64(x2Lo), int64(x1Hi) <= int64(x2Hi)}
+			case wazeroir.V128CmpTypeI64x2GeS:
+				result = []bool{int64(x1Lo) >= int64(x2Lo), int64(x1Hi) >= int64(x2Hi)}
+			case wazeroir.V128CmpTypeF32x4Eq:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) == math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) == math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) == math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) == math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF32x4Ne:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) != math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) != math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) != math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) != math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF32x4Lt:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) < math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) < math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) < math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) < math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF32x4Gt:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) > math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) > math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) > math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) > math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF32x4Le:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) <= math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) <= math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) <= math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) <= math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF32x4Ge:
+				result = []bool{
+					math.Float32frombits(uint32(x1Lo>>0)) >= math.Float32frombits(uint32(x2Lo>>0)),
+					math.Float32frombits(uint32(x1Lo>>32)) >= math.Float32frombits(uint32(x2Lo>>32)),
+					math.Float32frombits(uint32(x1Hi>>0)) >= math.Float32frombits(uint32(x2Hi>>0)),
+					math.Float32frombits(uint32(x1Hi>>32)) >= math.Float32frombits(uint32(x2Hi>>32)),
+				}
+			case wazeroir.V128CmpTypeF64x2Eq:
+				result = []bool{
+					math.Float64frombits(x1Lo) == math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) == math.Float64frombits(x2Hi),
+				}
+			case wazeroir.V128CmpTypeF64x2Ne:
+				result = []bool{
+					math.Float64frombits(x1Lo) != math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) != math.Float64frombits(x2Hi),
+				}
+			case wazeroir.V128CmpTypeF64x2Lt:
+				result = []bool{
+					math.Float64frombits(x1Lo) < math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) < math.Float64frombits(x2Hi),
+				}
+			case wazeroir.V128CmpTypeF64x2Gt:
+				result = []bool{
+					math.Float64frombits(x1Lo) > math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) > math.Float64frombits(x2Hi),
+				}
+			case wazeroir.V128CmpTypeF64x2Le:
+				result = []bool{
+					math.Float64frombits(x1Lo) <= math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) <= math.Float64frombits(x2Hi),
+				}
+			case wazeroir.V128CmpTypeF64x2Ge:
+				result = []bool{
+					math.Float64frombits(x1Lo) >= math.Float64frombits(x2Lo),
+					math.Float64frombits(x1Hi) >= math.Float64frombits(x2Hi),
+				}
+			}
+
+			var retLo, retHi uint64
+			laneNum := len(result)
+			switch laneNum {
+			case 16:
+				for i, b := range result {
+					if b {
+						if i < 8 {
+							retLo |= 0xff << (i * 8)
+						} else {
+							retHi |= 0xff << ((i - 8) * 8)
+						}
+					}
+				}
+			case 8:
+				for i, b := range result {
+					if b {
+						if i < 4 {
+							retLo |= 0xffff << (i * 16)
+						} else {
+							retHi |= 0xffff << ((i - 4) * 16)
+						}
+					}
+				}
+			case 4:
+				for i, b := range result {
+					if b {
+						if i < 2 {
+							retLo |= 0xffff_ffff << (i * 32)
+						} else {
+							retHi |= 0xffff_ffff << ((i - 2) * 32)
+						}
+					}
+				}
+			case 2:
+				if result[0] {
+					retLo = ^uint64(0)
+				}
+				if result[1] {
+					retHi = ^uint64(0)
+				}
+			}
+
+			ce.pushValue(retLo)
+			ce.pushValue(retHi)
+			frame.pc++
 		}
 	}
 	ce.popFrame()
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index 4b21f6f0..899edda0 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -1300,6 +1300,23 @@ func (m *Module) validateFunctionWithMaxStackValues(
 					return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err)
 				}
 				valueTypeStack.push(ValueTypeV128)
+			case OpcodeVecI8x16Eq, OpcodeVecI8x16Ne, OpcodeVecI8x16LtS, OpcodeVecI8x16LtU, OpcodeVecI8x16GtS,
+				OpcodeVecI8x16GtU, OpcodeVecI8x16LeS, OpcodeVecI8x16LeU, OpcodeVecI8x16GeS, OpcodeVecI8x16GeU,
+				OpcodeVecI16x8Eq, OpcodeVecI16x8Ne, OpcodeVecI16x8LtS, OpcodeVecI16x8LtU, OpcodeVecI16x8GtS,
+				OpcodeVecI16x8GtU, OpcodeVecI16x8LeS, OpcodeVecI16x8LeU, OpcodeVecI16x8GeS, OpcodeVecI16x8GeU,
+				OpcodeVecI32x4Eq, OpcodeVecI32x4Ne, OpcodeVecI32x4LtS, OpcodeVecI32x4LtU, OpcodeVecI32x4GtS,
+				OpcodeVecI32x4GtU, OpcodeVecI32x4LeS, OpcodeVecI32x4LeU, OpcodeVecI32x4GeS, OpcodeVecI32x4GeU,
+				OpcodeVecI64x2Eq, OpcodeVecI64x2Ne, OpcodeVecI64x2LtS, OpcodeVecI64x2GtS, OpcodeVecI64x2LeS,
+				OpcodeVecI64x2GeS, OpcodeVecF32x4Eq, OpcodeVecF32x4Ne, OpcodeVecF32x4Lt, OpcodeVecF32x4Gt,
+				OpcodeVecF32x4Le, OpcodeVecF32x4Ge, OpcodeVecF64x2Eq, OpcodeVecF64x2Ne, OpcodeVecF64x2Lt,
+				OpcodeVecF64x2Gt, OpcodeVecF64x2Le, OpcodeVecF64x2Ge:
+				if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil {
+					return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err)
+				}
+				if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil {
+					return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err)
+				}
+				valueTypeStack.push(ValueTypeV128)
 			default:
 				return fmt.Errorf("TODO: SIMD instruction %s will be implemented in #506", vectorInstructionName[vecOpcode])
 			}
diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go
index 2e18e7db..0aa1a817 100644
--- a/internal/wazeroir/compiler.go
+++ b/internal/wazeroir/compiler.go
@@ -2180,6 +2180,198 @@ operatorSwitch:
 			c.emit(
 				&OperationV128Shr{Shape: ShapeI64x2, Signed: false},
 			)
+		case wasm.OpcodeVecI8x16Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16Eq},
+			)
+		case wasm.OpcodeVecI8x16Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16Ne},
+			)
+		case wasm.OpcodeVecI8x16LtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16LtS},
+			)
+		case wasm.OpcodeVecI8x16LtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16LtU},
+			)
+		case wasm.OpcodeVecI8x16GtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16GtS},
+			)
+		case wasm.OpcodeVecI8x16GtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16GtU},
+			)
+		case wasm.OpcodeVecI8x16LeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16LeS},
+			)
+		case wasm.OpcodeVecI8x16LeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16LeU},
+			)
+		case wasm.OpcodeVecI8x16GeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16GeS},
+			)
+		case wasm.OpcodeVecI8x16GeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI8x16GeU},
+			)
+		case wasm.OpcodeVecI16x8Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8Eq},
+			)
+		case wasm.OpcodeVecI16x8Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8Ne},
+			)
+		case wasm.OpcodeVecI16x8LtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8LtS},
+			)
+		case wasm.OpcodeVecI16x8LtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8LtU},
+			)
+		case wasm.OpcodeVecI16x8GtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8GtS},
+			)
+		case wasm.OpcodeVecI16x8GtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8GtU},
+			)
+		case wasm.OpcodeVecI16x8LeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8LeS},
+			)
+		case wasm.OpcodeVecI16x8LeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8LeU},
+			)
+		case wasm.OpcodeVecI16x8GeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8GeS},
+			)
+		case wasm.OpcodeVecI16x8GeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI16x8GeU},
+			)
+		case wasm.OpcodeVecI32x4Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4Eq},
+			)
+		case wasm.OpcodeVecI32x4Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4Ne},
+			)
+		case wasm.OpcodeVecI32x4LtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4LtS},
+			)
+		case wasm.OpcodeVecI32x4LtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4LtU},
+			)
+		case wasm.OpcodeVecI32x4GtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4GtS},
+			)
+		case wasm.OpcodeVecI32x4GtU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4GtU},
+			)
+		case wasm.OpcodeVecI32x4LeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4LeS},
+			)
+		case wasm.OpcodeVecI32x4LeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4LeU},
+			)
+		case wasm.OpcodeVecI32x4GeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4GeS},
+			)
+		case wasm.OpcodeVecI32x4GeU:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI32x4GeU},
+			)
+		case wasm.OpcodeVecI64x2Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2Eq},
+			)
+		case wasm.OpcodeVecI64x2Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2Ne},
+			)
+		case wasm.OpcodeVecI64x2LtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2LtS},
+			)
+		case wasm.OpcodeVecI64x2GtS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2GtS},
+			)
+		case wasm.OpcodeVecI64x2LeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2LeS},
+			)
+		case wasm.OpcodeVecI64x2GeS:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeI64x2GeS},
+			)
+		case wasm.OpcodeVecF32x4Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Eq},
+			)
+		case wasm.OpcodeVecF32x4Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Ne},
+			)
+		case wasm.OpcodeVecF32x4Lt:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Lt},
+			)
+		case wasm.OpcodeVecF32x4Gt:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Gt},
+			)
+		case wasm.OpcodeVecF32x4Le:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Le},
+			)
+		case wasm.OpcodeVecF32x4Ge:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF32x4Ge},
+			)
+		case wasm.OpcodeVecF64x2Eq:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Eq},
+			)
+		case wasm.OpcodeVecF64x2Ne:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Ne},
+			)
+		case wasm.OpcodeVecF64x2Lt:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Lt},
+			)
+		case wasm.OpcodeVecF64x2Gt:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Gt},
+			)
+		case wasm.OpcodeVecF64x2Le:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Le},
+			)
+		case wasm.OpcodeVecF64x2Ge:
+			c.emit(
+				&OperationV128Cmp{Type: V128CmpTypeF64x2Ge},
+			)
 		default:
 			return fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp))
 		}
diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go
index 741fab77..db25340b 100644
--- a/internal/wazeroir/operations.go
+++ b/internal/wazeroir/operations.go
@@ -319,25 +319,37 @@ func (o OperationKind) String() (ret string) {
 	case OperationKindV128AllTrue:
 		ret = "V128AllTrue"
 	case OperationKindV128And:
-		return "V128And"
+		ret = "V128And"
 	case OperationKindV128Not:
-		return "V128Not"
+		ret = "V128Not"
 	case OperationKindV128Or:
-		return "V128Or"
+		ret = "V128Or"
 	case OperationKindV128Xor:
-		return "V128Xor"
+		ret = "V128Xor"
 	case OperationKindV128Bitselect:
-		return "V128Bitselect"
+		ret = "V128Bitselect"
 	case OperationKindV128AndNot:
-		return "V128AndNot"
+		ret = "V128AndNot"
 	case OperationKindV128BitMask:
-		return "V128BitMask"
+		ret = "V128BitMask"
 	case OperationKindV128Shl:
-		return "V128Shl"
+		ret = "V128Shl"
 	case OperationKindV128Shr:
-		return "V128Shr"
+		ret = "V128Shr"
+	case OperationKindV128Cmp:
+		ret = "V128Cmp"
+	case OperationKindSignExtend32From8:
+		ret = "SignExtend32From8"
+	case OperationKindSignExtend32From16:
+		ret = "SignExtend32From16"
+	case OperationKindSignExtend64From8:
+		ret = "SignExtend64From8"
+	case OperationKindSignExtend64From16:
+		ret = "SignExtend64From16"
+	case OperationKindSignExtend64From32:
+		ret = "SignExtend64From32"
 	default:
-		panic("BUG")
+		panic(fmt.Errorf("unknown operation %d", o))
 	}
 	return
 }
@@ -456,6 +468,10 @@ const (
 	OperationKindV128AndNot
 	OperationKindV128Shl
 	OperationKindV128Shr
+	OperationKindV128Cmp
+
+	// operationKindEnd is always placed at the bottom of this iota definition to be used in the test.
+	operationKindEnd
 )
 
 type Label struct {
@@ -1524,3 +1540,114 @@ type OperationV128Shr struct {
 func (o *OperationV128Shr) Kind() OperationKind {
 	return OperationKindV128Shr
 }
+
+// OperationV128Cmp implements Operation.
+type OperationV128Cmp struct {
+	Type V128CmpType
+}
+
+type V128CmpType = byte
+
+const (
+	// V128CmpTypeI8x16Eq corresponds to wasm.OpcodeVecI8x16EqName.
+	V128CmpTypeI8x16Eq V128CmpType = iota
+	// V128CmpTypeI8x16Ne corresponds to wasm.OpcodeVecI8x16NeName.
+	V128CmpTypeI8x16Ne
+	// V128CmpTypeI8x16LtS corresponds to wasm.OpcodeVecI8x16LtSName.
+	V128CmpTypeI8x16LtS
+	// V128CmpTypeI8x16LtU corresponds to wasm.OpcodeVecI8x16LtUName.
+	V128CmpTypeI8x16LtU
+	// V128CmpTypeI8x16GtS corresponds to wasm.OpcodeVecI8x16GtSName.
+	V128CmpTypeI8x16GtS
+	// V128CmpTypeI8x16GtU corresponds to wasm.OpcodeVecI8x16GtUName.
+	V128CmpTypeI8x16GtU
+	// V128CmpTypeI8x16LeS corresponds to wasm.OpcodeVecI8x16LeSName.
+	V128CmpTypeI8x16LeS
+	// V128CmpTypeI8x16LeU corresponds to wasm.OpcodeVecI8x16LeUName.
+	V128CmpTypeI8x16LeU
+	// V128CmpTypeI8x16GeS corresponds to wasm.OpcodeVecI8x16GeSName.
+	V128CmpTypeI8x16GeS
+	// V128CmpTypeI8x16GeU corresponds to wasm.OpcodeVecI8x16GeUName.
+	V128CmpTypeI8x16GeU
+	// V128CmpTypeI16x8Eq corresponds to wasm.OpcodeVecI16x8EqName.
+	V128CmpTypeI16x8Eq
+	// V128CmpTypeI16x8Ne corresponds to wasm.OpcodeVecI16x8NeName.
+	V128CmpTypeI16x8Ne
+	// V128CmpTypeI16x8LtS corresponds to wasm.OpcodeVecI16x8LtSName.
+	V128CmpTypeI16x8LtS
+	// V128CmpTypeI16x8LtU corresponds to wasm.OpcodeVecI16x8LtUName.
+	V128CmpTypeI16x8LtU
+	// V128CmpTypeI16x8GtS corresponds to wasm.OpcodeVecI16x8GtSName.
+	V128CmpTypeI16x8GtS
+	// V128CmpTypeI16x8GtU corresponds to wasm.OpcodeVecI16x8GtUName.
+	V128CmpTypeI16x8GtU
+	// V128CmpTypeI16x8LeS corresponds to wasm.OpcodeVecI16x8LeSName.
+	V128CmpTypeI16x8LeS
+	// V128CmpTypeI16x8LeU corresponds to wasm.OpcodeVecI16x8LeUName.
+	V128CmpTypeI16x8LeU
+	// V128CmpTypeI16x8GeS corresponds to wasm.OpcodeVecI16x8GeSName.
+	V128CmpTypeI16x8GeS
+	// V128CmpTypeI16x8GeU corresponds to wasm.OpcodeVecI16x8GeUName.
+	V128CmpTypeI16x8GeU
+	// V128CmpTypeI32x4Eq corresponds to wasm.OpcodeVecI32x4EqName.
+	V128CmpTypeI32x4Eq
+	// V128CmpTypeI32x4Ne corresponds to wasm.OpcodeVecI32x4NeName.
+	V128CmpTypeI32x4Ne
+	// V128CmpTypeI32x4LtS corresponds to wasm.OpcodeVecI32x4LtSName.
+	V128CmpTypeI32x4LtS
+	// V128CmpTypeI32x4LtU corresponds to wasm.OpcodeVecI32x4LtUName.
+	V128CmpTypeI32x4LtU
+	// V128CmpTypeI32x4GtS corresponds to wasm.OpcodeVecI32x4GtSName.
+	V128CmpTypeI32x4GtS
+	// V128CmpTypeI32x4GtU corresponds to wasm.OpcodeVecI32x4GtUName.
+	V128CmpTypeI32x4GtU
+	// V128CmpTypeI32x4LeS corresponds to wasm.OpcodeVecI32x4LeSName.
+	V128CmpTypeI32x4LeS
+	// V128CmpTypeI32x4LeU corresponds to wasm.OpcodeVecI32x4LeUName.
+	V128CmpTypeI32x4LeU
+	// V128CmpTypeI32x4GeS corresponds to wasm.OpcodeVecI32x4GeSName.
+	V128CmpTypeI32x4GeS
+	// V128CmpTypeI32x4GeU corresponds to wasm.OpcodeVecI32x4GeUName.
+	V128CmpTypeI32x4GeU
+	// V128CmpTypeI64x2Eq corresponds to wasm.OpcodeVecI64x2EqName.
+	V128CmpTypeI64x2Eq
+	// V128CmpTypeI64x2Ne corresponds to wasm.OpcodeVecI64x2NeName.
+	V128CmpTypeI64x2Ne
+	// V128CmpTypeI64x2LtS corresponds to wasm.OpcodeVecI64x2LtSName.
+	V128CmpTypeI64x2LtS
+	// V128CmpTypeI64x2GtS corresponds to wasm.OpcodeVecI64x2GtSName.
+	V128CmpTypeI64x2GtS
+	// V128CmpTypeI64x2LeS corresponds to wasm.OpcodeVecI64x2LeSName.
+	V128CmpTypeI64x2LeS
+	// V128CmpTypeI64x2GeS corresponds to wasm.OpcodeVecI64x2GeSName.
+	V128CmpTypeI64x2GeS
+	// V128CmpTypeF32x4Eq corresponds to wasm.OpcodeVecF32x4EqName.
+	V128CmpTypeF32x4Eq
+	// V128CmpTypeF32x4Ne corresponds to wasm.OpcodeVecF32x4NeName.
+	V128CmpTypeF32x4Ne
+	// V128CmpTypeF32x4Lt corresponds to wasm.OpcodeVecF32x4LtName.
+	V128CmpTypeF32x4Lt
+	// V128CmpTypeF32x4Gt corresponds to wasm.OpcodeVecF32x4GtName.
+	V128CmpTypeF32x4Gt
+	// V128CmpTypeF32x4Le corresponds to wasm.OpcodeVecF32x4LeName.
+	V128CmpTypeF32x4Le
+	// V128CmpTypeF32x4Ge corresponds to wasm.OpcodeVecF32x4GeName.
+	V128CmpTypeF32x4Ge
+	// V128CmpTypeF64x2Eq corresponds to wasm.OpcodeVecF64x2EqName.
+	V128CmpTypeF64x2Eq
+	// V128CmpTypeF64x2Ne corresponds to wasm.OpcodeVecF64x2NeName.
+	V128CmpTypeF64x2Ne
+	// V128CmpTypeF64x2Lt corresponds to wasm.OpcodeVecF64x2LtName.
+	V128CmpTypeF64x2Lt
+	// V128CmpTypeF64x2Gt corresponds to wasm.OpcodeVecF64x2GtName.
+	V128CmpTypeF64x2Gt
+	// V128CmpTypeF64x2Le corresponds to wasm.OpcodeVecF64x2LeName.
+	V128CmpTypeF64x2Le
+	// V128CmpTypeF64x2Ge corresponds to wasm.OpcodeVecF64x2GeName.
+	V128CmpTypeF64x2Ge
+)
+
+//Kind implements Operation.Kind.
+func (o *OperationV128Cmp) Kind() OperationKind {
+	return OperationKindV128Cmp
+}
diff --git a/internal/wazeroir/signature.go b/internal/wazeroir/signature.go
index 924baac3..f3ff5ac6 100644
--- a/internal/wazeroir/signature.go
+++ b/internal/wazeroir/signature.go
@@ -536,6 +536,17 @@ func (c *compiler) wasmOpcodeSignature(op wasm.Opcode, index uint32) (*signature
 			return signature_V128_V128, nil
 		case wasm.OpcodeVecV128Bitselect:
 			return signature_V128V128V128_V32, nil
+		case wasm.OpcodeVecI8x16Eq, wasm.OpcodeVecI8x16Ne, wasm.OpcodeVecI8x16LtS, wasm.OpcodeVecI8x16LtU, wasm.OpcodeVecI8x16GtS,
+			wasm.OpcodeVecI8x16GtU, wasm.OpcodeVecI8x16LeS, wasm.OpcodeVecI8x16LeU, wasm.OpcodeVecI8x16GeS, wasm.OpcodeVecI8x16GeU,
+			wasm.OpcodeVecI16x8Eq, wasm.OpcodeVecI16x8Ne, wasm.OpcodeVecI16x8LtS, wasm.OpcodeVecI16x8LtU, wasm.OpcodeVecI16x8GtS,
+			wasm.OpcodeVecI16x8GtU, wasm.OpcodeVecI16x8LeS, wasm.OpcodeVecI16x8LeU, wasm.OpcodeVecI16x8GeS, wasm.OpcodeVecI16x8GeU,
+			wasm.OpcodeVecI32x4Eq, wasm.OpcodeVecI32x4Ne, wasm.OpcodeVecI32x4LtS, wasm.OpcodeVecI32x4LtU, wasm.OpcodeVecI32x4GtS,
+			wasm.OpcodeVecI32x4GtU, wasm.OpcodeVecI32x4LeS, wasm.OpcodeVecI32x4LeU, wasm.OpcodeVecI32x4GeS, wasm.OpcodeVecI32x4GeU,
+			wasm.OpcodeVecI64x2Eq, wasm.OpcodeVecI64x2Ne, wasm.OpcodeVecI64x2LtS, wasm.OpcodeVecI64x2GtS, wasm.OpcodeVecI64x2LeS,
+			wasm.OpcodeVecI64x2GeS, wasm.OpcodeVecF32x4Eq, wasm.OpcodeVecF32x4Ne, wasm.OpcodeVecF32x4Lt, wasm.OpcodeVecF32x4Gt,
+			wasm.OpcodeVecF32x4Le, wasm.OpcodeVecF32x4Ge, wasm.OpcodeVecF64x2Eq, wasm.OpcodeVecF64x2Ne, wasm.OpcodeVecF64x2Lt,
+			wasm.OpcodeVecF64x2Gt, wasm.OpcodeVecF64x2Le, wasm.OpcodeVecF64x2Ge:
+			return signature_V128V128_V128, nil
 		default:
 			return nil, fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp))
 		}


Test patch:
diff --git a/internal/asm/amd64/consts_test.go b/internal/asm/amd64/consts_test.go
new file mode 100644
index 00000000..6d9964b3
--- /dev/null
+++ b/internal/asm/amd64/consts_test.go
@@ -0,0 +1,15 @@
+package amd64
+
+import (
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/asm"
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+// TestInstructionName ensures that all the instruction's name is defined.
+func TestInstructionName(t *testing.T) {
+	for inst := asm.Instruction(0); inst < instructionEnd; inst++ {
+		require.NotEqual(t, "", InstructionName(inst))
+	}
+}
diff --git a/internal/asm/amd64/impl_test.go b/internal/asm/amd64/impl_test.go
index df9d2e73..0a49b7ab 100644
--- a/internal/asm/amd64/impl_test.go
+++ b/internal/asm/amd64/impl_test.go
@@ -733,6 +733,210 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) {
 			},
 			exp: []byte{0x66, 0x45, 0xf, 0xf3, 0xdf},
 		},
+		{
+			name: "cmpeqps xmm11, xmm15",
+			n: &NodeImpl{
+				Instruction: CMPPS,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX15,
+				DstReg:      RegX11,
+				Arg:         0, // CMPPS with arg=0 == Pseudo-Op CMPEQPS.
+			},
+			exp: []byte{0x45, 0xf, 0xc2, 0xdf, 0x0},
+		},
+		{
+			name: "cmpordps xmm1, xmm5",
+			n: &NodeImpl{
+				Instruction: CMPPS,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX5,
+				DstReg:      RegX1,
+				Arg:         7, // CMPPS with arg=7 == Pseudo-Op CMPORDPS.
+			},
+			exp: []byte{0xf, 0xc2, 0xcd, 0x7},
+		},
+		{
+			name: "cmplepd xmm11, xmm15",
+			n: &NodeImpl{
+				Instruction: CMPPD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX15,
+				DstReg:      RegX11,
+				Arg:         2, // CMPPD with arg=2 == Pseudo-Op CMPLEPD.
+			},
+			exp: []byte{0x66, 0x45, 0xf, 0xc2, 0xdf, 0x2},
+		},
+		{
+			name: "cmpneqpd xmm1, xmm5",
+			n: &NodeImpl{
+				Instruction: CMPPD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX5,
+				DstReg:      RegX1,
+				Arg:         4, // CMPPD with arg=4 == Pseudo-Op CMPNEQPD.
+			},
+			exp: []byte{0x66, 0xf, 0xc2, 0xcd, 0x4},
+		},
+		{
+			name: "pcmpgtq xmm10, xmm3",
+			n: &NodeImpl{
+				Instruction: PCMPGTQ,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX3,
+				DstReg:      RegX10,
+			},
+			exp: []byte{0x66, 0x44, 0xf, 0x38, 0x37, 0xd3},
+		},
+		{
+			name: "pcmpgtd xmm10, xmm3",
+			n: &NodeImpl{
+				Instruction: PCMPGTD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX3,
+				DstReg:      RegX10,
+			},
+			exp: []byte{0x66, 0x44, 0xf, 0x66, 0xd3},
+		},
+		{
+			name: "pminsd xmm10, xmm3",
+			n: &NodeImpl{
+				Instruction: PMINSD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX3,
+				DstReg:      RegX10,
+			},
+			exp: []byte{0x66, 0x44, 0xf, 0x38, 0x39, 0xd3},
+		},
+		{
+			name: "pmaxsd xmm1, xmm12",
+			n: &NodeImpl{
+				Instruction: PMAXSD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX12,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0x41, 0xf, 0x38, 0x3d, 0xcc},
+		},
+		{
+			name: "pmaxsw xmm1, xmm12",
+			n: &NodeImpl{
+				Instruction: PMAXSW,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX12,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0x41, 0xf, 0xee, 0xcc},
+		},
+		{
+			name: "pminsw xmm1, xmm12",
+			n: &NodeImpl{
+				Instruction: PMINSW,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX12,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0x41, 0xf, 0xea, 0xcc},
+		},
+		{
+			name: "pcmpgtb xmm1, xmm12",
+			n: &NodeImpl{
+				Instruction: PCMPGTB,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX12,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0x41, 0xf, 0x64, 0xcc},
+		},
+		{
+			name: "pminsb xmm1, xmm12",
+			n: &NodeImpl{
+				Instruction: PMINSB,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX12,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0x41, 0xf, 0x38, 0x38, 0xcc},
+		},
+		{
+			name: "pmaxsb xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMAXSB,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x38, 0x3c, 0xca},
+		},
+		{
+			name: "pminud xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMINUD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x38, 0x3b, 0xca},
+		},
+		{
+			name: "pminuw xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMINUW,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x38, 0x3a, 0xca},
+		},
+		{
+			name: "pminub xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMINUB,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0xda, 0xca},
+		},
+		{
+			name: "pmaxud xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMAXUD,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x38, 0x3f, 0xca},
+		},
+		{
+			name: "pmaxuw xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMAXUW,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x38, 0x3e, 0xca},
+		},
+		{
+			name: "pmaxub xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PMAXUB,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0xde, 0xca},
+		},
+		{
+			name: "pcmpgtw xmm1, xmm2",
+			n: &NodeImpl{
+				Instruction: PCMPGTW,
+				Types:       OperandTypesRegisterToRegister,
+				SrcReg:      RegX2,
+				DstReg:      RegX1,
+			},
+			exp: []byte{0x66, 0xf, 0x65, 0xca},
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/internal/asm/arm64/consts_test.go b/internal/asm/arm64/consts_test.go
new file mode 100644
index 00000000..93410f38
--- /dev/null
+++ b/internal/asm/arm64/consts_test.go
@@ -0,0 +1,15 @@
+package arm64
+
+import (
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/asm"
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+// TestInstructionName ensures that all the instruction's name is defined.
+func TestInstructionName(t *testing.T) {
+	for inst := asm.Instruction(0); inst < instructionEnd; inst++ {
+		require.NotEqual(t, "", InstructionName(inst))
+	}
+}
diff --git a/internal/engine/compiler/compiler_vec_test.go b/internal/engine/compiler/compiler_vec_test.go
index 3216a914..a658d72d 100644
--- a/internal/engine/compiler/compiler_vec_test.go
+++ b/internal/engine/compiler/compiler_vec_test.go
@@ -2838,3 +2838,439 @@ func TestCompiler_compileV128Shr(t *testing.T) {
 		})
 	}
 }
+
+func i16x8(i1, i2, i3, i4, i5, i6, i7, i8 uint16) (ret [16]byte) {
+	binary.LittleEndian.PutUint16(ret[0:], i1)
+	binary.LittleEndian.PutUint16(ret[2:], i2)
+	binary.LittleEndian.PutUint16(ret[4:], i3)
+	binary.LittleEndian.PutUint16(ret[6:], i4)
+	binary.LittleEndian.PutUint16(ret[8:], i5)
+	binary.LittleEndian.PutUint16(ret[10:], i6)
+	binary.LittleEndian.PutUint16(ret[12:], i7)
+	binary.LittleEndian.PutUint16(ret[14:], i8)
+	return
+}
+
+func i32x4(i1, i2, i3, i4 uint32) (ret [16]byte) {
+	binary.LittleEndian.PutUint32(ret[0:], i1)
+	binary.LittleEndian.PutUint32(ret[4:], i2)
+	binary.LittleEndian.PutUint32(ret[8:], i3)
+	binary.LittleEndian.PutUint32(ret[12:], i4)
+	return
+}
+
+func f32x4(f1, f2, f3, f4 float32) (ret [16]byte) {
+	binary.LittleEndian.PutUint32(ret[0:], math.Float32bits(f1))
+	binary.LittleEndian.PutUint32(ret[4:], math.Float32bits(f2))
+	binary.LittleEndian.PutUint32(ret[8:], math.Float32bits(f3))
+	binary.LittleEndian.PutUint32(ret[12:], math.Float32bits(f4))
+	return
+}
+
+func i64x2(i1, i2 uint64) (ret [16]byte) {
+	binary.LittleEndian.PutUint64(ret[0:], i1)
+	binary.LittleEndian.PutUint64(ret[8:], i2)
+	return
+}
+
+func f64x2(f1, f2 float64) (ret [16]byte) {
+	binary.LittleEndian.PutUint64(ret[0:], math.Float64bits(f1))
+	binary.LittleEndian.PutUint64(ret[8:], math.Float64bits(f2))
+	return
+}
+
+func TestCompiler_compileV128Cmp(t *testing.T) {
+	if runtime.GOARCH != "amd64" {
+		// TODO: implement on amd64.
+		t.Skip()
+	}
+
+	tests := []struct {
+		name        string
+		cmpType     wazeroir.V128CmpType
+		x1, x2, exp [16]byte
+	}{
+		{
+			name:    "f32x4 eq",
+			cmpType: wazeroir.V128CmpTypeF32x4Eq,
+			x1:      f32x4(1.0, -123.123, 0, 3214231),
+			x2:      f32x4(0, 0, 0, 0),
+			exp:     [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0},
+		},
+		{
+			name:    "f32x4 ne",
+			cmpType: wazeroir.V128CmpTypeF32x4Ne,
+			x1:      f32x4(1.0, -123.123, 123, 3214231),
+			x2:      f32x4(2.0, 213123123.1231, 123, 0),
+			exp:     [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f32x4 lt",
+			cmpType: wazeroir.V128CmpTypeF32x4Lt,
+			x1:      f32x4(2.0, -123.123, 1234, 3214231),
+			x2:      f32x4(2.0, 213123123.1231, 123, 0),
+			exp:     [16]byte{0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+		},
+		{
+			name:    "f32x4 le",
+			cmpType: wazeroir.V128CmpTypeF32x4Le,
+			x1:      f32x4(2.0, -123.123, 1234, 3214231),
+			x2:      f32x4(2.0, 213123123.1231, 123, 0),
+			exp:     [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+		},
+		{
+			name:    "f32x4 gt",
+			cmpType: wazeroir.V128CmpTypeF32x4Gt,
+			x1:      f32x4(2.0, -123.123, 1234, 3214231),
+			x2:      f32x4(2.0, 213123123.1231, 123, 0),
+			exp:     [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f32x4 ge",
+			cmpType: wazeroir.V128CmpTypeF32x4Ge,
+			x1:      f32x4(2.0, -123.123, 1234, 3214231),
+			x2:      f32x4(2.0, 213123123.1231, 123, 0),
+			exp:     [16]byte{0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f64x2 eq",
+			cmpType: wazeroir.V128CmpTypeF64x2Eq,
+			x1:      f64x2(1.0, -123.12412),
+			x2:      f64x2(1.0, 123.123124),
+			exp:     [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+		},
+		{
+			name:    "f64x2 ne",
+			cmpType: wazeroir.V128CmpTypeF64x2Ne,
+			x1:      f64x2(1.0, -123.12412),
+			x2:      f64x2(1.0, 123.123124),
+			exp:     [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f64x2 lt",
+			cmpType: wazeroir.V128CmpTypeF64x2Lt,
+			x1:      f64x2(-123, math.Inf(-1)),
+			x2:      f64x2(-123, -1234515),
+			exp:     [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f64x2 le",
+			cmpType: wazeroir.V128CmpTypeF64x2Le,
+			x1:      f64x2(-123, 123),
+			x2:      f64x2(-123, math.MaxFloat64),
+			exp:     [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "f64x2 gt",
+			cmpType: wazeroir.V128CmpTypeF64x2Gt,
+			x1:      f64x2(math.MaxFloat64, -123.0),
+			x2:      f64x2(123, -123.0),
+			exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0, 0, 0, 0, 0, 0, 0, 0},
+		},
+		{
+			name:    "f64x2 ge",
+			cmpType: wazeroir.V128CmpTypeF64x2Ge,
+			x1:      f64x2(math.MaxFloat64, -123.0),
+			x2:      f64x2(123, -123.0),
+			exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+		},
+		{
+			name:    "i8x16 eq",
+			cmpType: wazeroir.V128CmpTypeI8x16Eq,
+			x1:      [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1},
+			x2:      [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0},
+			exp:     [16]byte{0, 0xff, 0, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xff, 0},
+		},
+		{
+			name:    "i8x16 ne",
+			cmpType: wazeroir.V128CmpTypeI8x16Ne,
+			x1:      [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1},
+			x2:      [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0},
+			exp:     [16]byte{0xff, 0, 0xff, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0xff},
+		},
+		{
+			name:    "i8x16 lt_s",
+			cmpType: wazeroir.V128CmpTypeI8x16LtS,
+			x1:      [16]byte{0: i8ToU8(-1), 15: 0},
+			x2:      [16]byte{0: 0x7f, 15: i8ToU8(-1)},
+			exp:     [16]byte{0: 0xff},
+		},
+		{
+			name:    "i8x16 lt_u",
+			cmpType: wazeroir.V128CmpTypeI8x16LtU,
+			x1:      [16]byte{0: 0xff, 15: 0},
+			x2:      [16]byte{0: 0x7f, 15: 0xff},
+			exp:     [16]byte{15: 0xff},
+		},
+		{
+			name:    "i8x16 gt_s",
+			cmpType: wazeroir.V128CmpTypeI8x16GtS,
+			x1:      [16]byte{0: i8ToU8(-1), 15: 0},
+			x2:      [16]byte{0: 0x7f, 15: i8ToU8(-1)},
+			exp:     [16]byte{15: 0xff},
+		},
+		{
+			name:    "i8x16 gt_u",
+			cmpType: wazeroir.V128CmpTypeI8x16GtU,
+			x1:      [16]byte{0: 0xff, 15: 0},
+			x2:      [16]byte{0: 0x7f, 15: 0xff},
+			exp:     [16]byte{0: 0xff},
+		},
+		{
+			name:    "i8x16 le_s",
+			cmpType: wazeroir.V128CmpTypeI8x16LeS,
+			x1:      [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)},
+			x2:      [16]byte{0: 0x7f, 15: i8ToU8(-1)},
+			exp:     [16]byte{0: 0xff, 15: 0xff},
+		},
+		{
+			name:    "i8x16 le_u",
+			cmpType: wazeroir.V128CmpTypeI8x16LeU,
+			x1:      [16]byte{0x80, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff},
+			x2:      [16]byte{0: 0x7f, 5: 0x1, 15: 0xff},
+			exp:     [16]byte{5: 0xff, 15: 0xff},
+		},
+		{
+			name:    "i8x16 ge_s",
+			cmpType: wazeroir.V128CmpTypeI8x16GeS,
+			x1:      [16]byte{0: 0x7f, 15: i8ToU8(-1)},
+			x2:      [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)},
+			exp:     [16]byte{0: 0xff, 15: 0xff},
+		},
+		{
+			name:    "i8x16 ge_u",
+			cmpType: wazeroir.V128CmpTypeI8x16GeU,
+			x1:      [16]byte{0: 0x7f, 3: 0xe, 15: 0xff},
+			x2:      [16]byte{0xff, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff},
+			exp:     [16]byte{3: 0xff, 15: 0xff},
+		},
+		{
+			name:    "i16x8 eq",
+			cmpType: wazeroir.V128CmpTypeI16x8Eq,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0xffff, 0, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0),
+		},
+		{
+			name:    "i8x16 ne",
+			cmpType: wazeroir.V128CmpTypeI16x8Ne,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0, 0xffff, 0, 0, 0xffff, 0, 0, 0xffff),
+		},
+		{
+			name:    "i8x16 lt_s",
+			cmpType: wazeroir.V128CmpTypeI16x8LtS,
+			x1:      i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0xffff, 0, 0, 0, 0xffff, 0, 0, 0),
+		},
+		{
+			name:    "i8x16 lt_u",
+			cmpType: wazeroir.V128CmpTypeI16x8LtU,
+			x1:      i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0, 0, 0, 0, 0xffff, 0, 0, 0),
+		},
+		{
+			name:    "i8x16 gt_s",
+			cmpType: wazeroir.V128CmpTypeI16x8GtS,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0xffff, 0xffff, 0, 0, 0, 0, 0, 0xffff),
+		},
+		{
+			name:    "i8x16 gt_u",
+			cmpType: wazeroir.V128CmpTypeI16x8GtU,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0, 0xffff, 0, 0, 0, 0, 0, 0xffff),
+		},
+		{
+			name:    "i8x16 le_s",
+			cmpType: wazeroir.V128CmpTypeI16x8LeS,
+			x1:      i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0xffff, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0),
+		},
+		{
+			name:    "i8x16 le_u",
+			cmpType: wazeroir.V128CmpTypeI16x8LeU,
+			x1:      i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0),
+		},
+		{
+			name:    "i8x16 ge_s",
+			cmpType: wazeroir.V128CmpTypeI16x8GeS,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff),
+		},
+		{
+			name:    "i8x16 ge_u",
+			cmpType: wazeroir.V128CmpTypeI16x8GeU,
+			x1:      i16x8(0, 1, 0, 1, 0, 1, 0, 1),
+			x2:      i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
+			exp:     i16x8(0, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff),
+		},
+		{
+			name:    "i32x4 eq",
+			cmpType: wazeroir.V128CmpTypeI32x4Eq,
+			x1:      i32x4(0, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0xffffffff, 0xffffffff, 0, 0),
+		},
+		{
+			name:    "i32x4 ne",
+			cmpType: wazeroir.V128CmpTypeI32x4Ne,
+			x1:      i32x4(0, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0, 0, 0xffffffff, 0xffffffff),
+		},
+		{
+			name:    "i32x4 lt_s",
+			cmpType: wazeroir.V128CmpTypeI32x4LtS,
+			x1:      i32x4(0xffffffff, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0xffffffff, 0, 0, 0xffffffff),
+		},
+		{
+			name:    "i32x4 lt_u",
+			cmpType: wazeroir.V128CmpTypeI32x4LtU,
+			x1:      i32x4(0xffffffff, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0, 0, 0, 0xffffffff),
+		},
+		{
+			name:    "i32x4 gt_s",
+			cmpType: wazeroir.V128CmpTypeI32x4GtS,
+			x1:      i32x4(0, 1, 1, 1),
+			x2:      i32x4(0xffffffff, 1, 0, 0),
+			exp:     i32x4(0xffffffff, 0, 0xffffffff, 0xffffffff),
+		},
+		{
+			name:    "i32x4 gt_u",
+			cmpType: wazeroir.V128CmpTypeI32x4GtU,
+			x1:      i32x4(0, 1, 1, 1),
+			x2:      i32x4(0xffffffff, 1, 0, 0),
+			exp:     i32x4(0, 0, 0xffffffff, 0xffffffff),
+		},
+		{
+			name:    "i32x4 le_s",
+			cmpType: wazeroir.V128CmpTypeI32x4LeS,
+			x1:      i32x4(0xffffffff, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0xffffffff, 0xffffffff, 0, 0xffffffff),
+		},
+		{
+			name:    "i32x4 le_u",
+			cmpType: wazeroir.V128CmpTypeI32x4LeU,
+			x1:      i32x4(0xffffffff, 1, 1, 0),
+			x2:      i32x4(0, 1, 0, 1),
+			exp:     i32x4(0, 0xffffffff, 0, 0xffffffff),
+		},
+		{
+			name:    "i32x4 ge_s",
+			cmpType: wazeroir.V128CmpTypeI32x4GeS,
+			x1:      i32x4(0, 1, 1, 0),
+			x2:      i32x4(0xffffffff, 1, 0, 1),
+			exp:     i32x4(0xffffffff, 0xffffffff, 0xffffffff, 0),
+		},
+		{
+			name:    "i32x4 ge_u",
+			cmpType: wazeroir.V128CmpTypeI32x4GeU,
+			x1:      i32x4(0, 1, 1, 0),
+			x2:      i32x4(0xffffffff, 1, 0, 1),
+			exp:     i32x4(0, 0xffffffff, 0xffffffff, 0),
+		},
+		{
+			name:    "i64x2 eq",
+			cmpType: wazeroir.V128CmpTypeI64x2Eq,
+			x1:      i64x2(1, 0),
+			x2:      i64x2(0, 0),
+			exp:     i64x2(0, 0xffffffffffffffff),
+		},
+		{
+			name:    "i64x2 ne",
+			cmpType: wazeroir.V128CmpTypeI64x2Ne,
+			x1:      i64x2(1, 0),
+			x2:      i64x2(0, 0),
+			exp:     i64x2(0xffffffffffffffff, 0),
+		},
+		{
+			name:    "i64x2 lt_s",
+			cmpType: wazeroir.V128CmpTypeI64x2LtS,
+			x1:      i64x2(0xffffffffffffffff, 0),
+			x2:      i64x2(0, 0),
+			exp:     i64x2(0xffffffffffffffff, 0),
+		},
+		{
+			name:    "i64x2 gt_s",
+			cmpType: wazeroir.V128CmpTypeI64x2GtS,
+			x1:      i64x2(123, 0),
+			x2:      i64x2(123, 0xffffffffffffffff),
+			exp:     i64x2(0, 0xffffffffffffffff),
+		},
+		{
+			name:    "i64x2 le_s",
+			cmpType: wazeroir.V128CmpTypeI64x2LeS,
+			x1:      i64x2(123, 0xffffffffffffffff),
+			x2:      i64x2(123, 0),
+			exp:     i64x2(0xffffffffffffffff, 0xffffffffffffffff),
+		},
+		{
+			name:    "i64x2 ge_s",
+			cmpType: wazeroir.V128CmpTypeI64x2GeS,
+			x1:      i64x2(123, 0),
+			x2:      i64x2(123, 0xffffffffffffffff),
+			exp:     i64x2(0xffffffffffffffff, 0xffffffffffffffff),
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			env := newCompilerEnvironment()
+			compiler := env.requireNewCompiler(t, newCompiler,
+				&wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}})
+
+			err := compiler.compilePreamble()
+			require.NoError(t, err)
+
+			err = compiler.compileV128Const(&wazeroir.OperationV128Const{
+				Lo: binary.LittleEndian.Uint64(tc.x1[:8]),
+				Hi: binary.LittleEndian.Uint64(tc.x1[8:]),
+			})
+			require.NoError(t, err)
+
+			err = compiler.compileV128Const(&wazeroir.OperationV128Const{
+				Lo: binary.LittleEndian.Uint64(tc.x2[:8]),
+				Hi: binary.LittleEndian.Uint64(tc.x2[8:]),
+			})
+			require.NoError(t, err)
+
+			err = compiler.compileV128Cmp(&wazeroir.OperationV128Cmp{Type: tc.cmpType})
+			require.NoError(t, err)
+
+			require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp)
+			require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters))
+
+			err = compiler.compileReturnFunction()
+			require.NoError(t, err)
+
+			// Generate and run the code under test.
+			code, _, _, err := compiler.compile()
+			require.NoError(t, err)
+			env.exec(code)
+
+			lo, hi := env.stackTopAsV128()
+			var actual [16]byte
+			binary.LittleEndian.PutUint64(actual[:8], lo)
+			binary.LittleEndian.PutUint64(actual[8:], hi)
+			require.Equal(t, tc.exp, actual)
+		})
+	}
+}
diff --git a/internal/integration_test/spectest/v2/spec_test.go b/internal/integration_test/spectest/v2/spec_test.go
index 663e179c..339c40d8 100644
--- a/internal/integration_test/spectest/v2/spec_test.go
+++ b/internal/integration_test/spectest/v2/spec_test.go
@@ -34,7 +34,9 @@ func TestCompiler(t *testing.T) {
 				"simd_load_splat.json", "simd_load_zero.json", "simd_store.json", "simd_store16_lane.json",
 				"simd_store32_lane.json", "simd_store64_lane.json", "simd_store8_lane.json":
 				return true
-			case "simd_bitwise.json", "simd_boolean.json", "simd_bit_shift.json":
+			case "simd_bitwise.json", "simd_boolean.json", "simd_bit_shift.json",
+				"simd_i8x16_cmp.json", "simd_i16x8_cmp.json", "simd_i32x4_cmp.json", "simd_i64x2_cmp.json",
+				"simd_f32x4_cmp.json", "simd_f64x2_cmp.json":
 				// TODO: implement on arm64.
 				return runtime.GOARCH == "amd64"
 			default:
@@ -54,7 +56,8 @@ func TestInterpreter(t *testing.T) {
 				"simd_load32_lane.json", "simd_load64_lane.json", "simd_load8_lane.json", "simd_lane.json",
 				"simd_load_extend.json", "simd_load_splat.json", "simd_load_zero.json", "simd_store.json",
 				"simd_store16_lane.json", "simd_store32_lane.json", "simd_store64_lane.json", "simd_store8_lane.json",
-				"simd_bitwise.json", "simd_boolean.json", "simd_bit_shift.json":
+				"simd_bitwise.json", "simd_boolean.json", "simd_bit_shift.json", "simd_i8x16_cmp.json", "simd_i16x8_cmp.json",
+				"simd_i32x4_cmp.json", "simd_i64x2_cmp.json", "simd_f32x4_cmp.json", "simd_f64x2_cmp.json":
 				return true
 			default:
 				return false // others not supported, yet!
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index 2b3fb8d5..4b7084d2 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -2974,6 +2974,54 @@ func TestModule_funcValidation_SIMD(t *testing.T) {
 		{name: OpcodeVecI64x2ShlName, body: vi2v(OpcodeVecI64x2Shl)},
 		{name: OpcodeVecI64x2ShrSName, body: vi2v(OpcodeVecI64x2ShrS)},
 		{name: OpcodeVecI64x2ShrUName, body: vi2v(OpcodeVecI64x2ShrU)},
+		{name: OpcodeVecI8x16EqName, body: vv2v(OpcodeVecI8x16Eq)},
+		{name: OpcodeVecI8x16NeName, body: vv2v(OpcodeVecI8x16Ne)},
+		{name: OpcodeVecI8x16LtSName, body: vv2v(OpcodeVecI8x16LtS)},
+		{name: OpcodeVecI8x16LtUName, body: vv2v(OpcodeVecI8x16LtU)},
+		{name: OpcodeVecI8x16GtSName, body: vv2v(OpcodeVecI8x16GtS)},
+		{name: OpcodeVecI8x16GtUName, body: vv2v(OpcodeVecI8x16GtU)},
+		{name: OpcodeVecI8x16LeSName, body: vv2v(OpcodeVecI8x16LeS)},
+		{name: OpcodeVecI8x16LeUName, body: vv2v(OpcodeVecI8x16LeU)},
+		{name: OpcodeVecI8x16GeSName, body: vv2v(OpcodeVecI8x16GeS)},
+		{name: OpcodeVecI8x16GeUName, body: vv2v(OpcodeVecI8x16GeU)},
+		{name: OpcodeVecI16x8EqName, body: vv2v(OpcodeVecI16x8Eq)},
+		{name: OpcodeVecI16x8NeName, body: vv2v(OpcodeVecI16x8Ne)},
+		{name: OpcodeVecI16x8LtSName, body: vv2v(OpcodeVecI16x8LtS)},
+		{name: OpcodeVecI16x8LtUName, body: vv2v(OpcodeVecI16x8LtU)},
+		{name: OpcodeVecI16x8GtSName, body: vv2v(OpcodeVecI16x8GtS)},
+		{name: OpcodeVecI16x8GtUName, body: vv2v(OpcodeVecI16x8GtU)},
+		{name: OpcodeVecI16x8LeSName, body: vv2v(OpcodeVecI16x8LeS)},
+		{name: OpcodeVecI16x8LeUName, body: vv2v(OpcodeVecI16x8LeU)},
+		{name: OpcodeVecI16x8GeSName, body: vv2v(OpcodeVecI16x8GeS)},
+		{name: OpcodeVecI16x8GeUName, body: vv2v(OpcodeVecI16x8GeU)},
+		{name: OpcodeVecI32x4EqName, body: vv2v(OpcodeVecI32x4Eq)},
+		{name: OpcodeVecI32x4NeName, body: vv2v(OpcodeVecI32x4Ne)},
+		{name: OpcodeVecI32x4LtSName, body: vv2v(OpcodeVecI32x4LtS)},
+		{name: OpcodeVecI32x4LtUName, body: vv2v(OpcodeVecI32x4LtU)},
+		{name: OpcodeVecI32x4GtSName, body: vv2v(OpcodeVecI32x4GtS)},
+		{name: OpcodeVecI32x4GtUName, body: vv2v(OpcodeVecI32x4GtU)},
+		{name: OpcodeVecI32x4LeSName, body: vv2v(OpcodeVecI32x4LeS)},
+		{name: OpcodeVecI32x4LeUName, body: vv2v(OpcodeVecI32x4LeU)},
+		{name: OpcodeVecI32x4GeSName, body: vv2v(OpcodeVecI32x4GeS)},
+		{name: OpcodeVecI32x4GeUName, body: vv2v(OpcodeVecI32x4GeU)},
+		{name: OpcodeVecI64x2EqName, body: vv2v(OpcodeVecI64x2Eq)},
+		{name: OpcodeVecI64x2NeName, body: vv2v(OpcodeVecI64x2Ne)},
+		{name: OpcodeVecI64x2LtSName, body: vv2v(OpcodeVecI64x2LtS)},
+		{name: OpcodeVecI64x2GtSName, body: vv2v(OpcodeVecI64x2GtS)},
+		{name: OpcodeVecI64x2LeSName, body: vv2v(OpcodeVecI64x2LeS)},
+		{name: OpcodeVecI64x2GeSName, body: vv2v(OpcodeVecI64x2GeS)},
+		{name: OpcodeVecF32x4EqName, body: vv2v(OpcodeVecF32x4Eq)},
+		{name: OpcodeVecF32x4NeName, body: vv2v(OpcodeVecF32x4Ne)},
+		{name: OpcodeVecF32x4LtName, body: vv2v(OpcodeVecF32x4Lt)},
+		{name: OpcodeVecF32x4GtName, body: vv2v(OpcodeVecF32x4Gt)},
+		{name: OpcodeVecF32x4LeName, body: vv2v(OpcodeVecF32x4Le)},
+		{name: OpcodeVecF32x4GeName, body: vv2v(OpcodeVecF32x4Ge)},
+		{name: OpcodeVecF64x2EqName, body: vv2v(OpcodeVecF64x2Eq)},
+		{name: OpcodeVecF64x2NeName, body: vv2v(OpcodeVecF64x2Ne)},
+		{name: OpcodeVecF64x2LtName, body: vv2v(OpcodeVecF64x2Lt)},
+		{name: OpcodeVecF64x2GtName, body: vv2v(OpcodeVecF64x2Gt)},
+		{name: OpcodeVecF64x2LeName, body: vv2v(OpcodeVecF64x2Le)},
+		{name: OpcodeVecF64x2GeName, body: vv2v(OpcodeVecF64x2Ge)},
 	}
 
 	for _, tt := range tests {
diff --git a/internal/wazeroir/operations_test.go b/internal/wazeroir/operations_test.go
new file mode 100644
index 00000000..ba5c9deb
--- /dev/null
+++ b/internal/wazeroir/operations_test.go
@@ -0,0 +1,14 @@
+package wazeroir
+
+import (
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+// TestInstructionName ensures that all the operation kind's stringer is well-defined.
+func TestOperationKind_String(t *testing.T) {
+	for k := OperationKind(0); k < operationKindEnd; k++ {
+		require.NotEqual(t, "", k.String())
+	}
+}