Repository: tetratelabs/wazero
Pull request #1732 (2023-09-23 15:23:13)

wazevo: passes f32x4, f64x4 vector arithmetic spec tests
continues #1496. Passes min/max/neg/arithmetic tests for f32x4, f64x2, adding the necessary instructions. 

Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>



Patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index f2cc06fc..2273d1ea 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -1602,6 +1602,8 @@ func (b vecOp) String() string {
 		return "orr"
 	case vecOpEOR:
 		return "eor"
+	case vecOpFadd:
+		return "fadd"
 	case vecOpAdd:
 		return "add"
 	case vecOpAddp:
@@ -1610,6 +1612,8 @@ func (b vecOp) String() string {
 		return "addv"
 	case vecOpSub:
 		return "sub"
+	case vecOpFsub:
+		return "fsub"
 	case vecOpSmin:
 		return "smin"
 	case vecOpUmin:
@@ -1624,12 +1628,20 @@ func (b vecOp) String() string {
 		return "umaxp"
 	case vecOpUrhadd:
 		return "urhadd"
+	case vecOpFmul:
+		return "fmul"
 	case vecOpMul:
 		return "mul"
 	case vecOpUmlal:
 		return "umlal"
+	case vecOpFdiv:
+		return "fmul"
 	case vecOpNeg:
 		return "neg"
+	case vecOpFneg:
+		return "fneg"
+	case vecOpFsqrt:
+		return "fsqrt"
 	case vecOpRev64:
 		return "rev64"
 	case vecOpXtn:
@@ -1640,6 +1652,12 @@ func (b vecOp) String() string {
 		return "sshr"
 	case vecOpZip1:
 		return "zip1"
+	case vecOpFabs:
+		return "fabs"
+	case vecOpFmin:
+		return "fmin"
+	case vecOpFmax:
+		return "fmax"
 	}
 	panic(int(b))
 }
@@ -1661,24 +1679,33 @@ const (
 	vecOpOrr
 	vecOpEOR
 	vecOpAdd
+	vecOpFadd
 	vecOpAddv
 	vecOpSqadd
 	vecOpUqadd
 	vecOpAddp
 	vecOpSub
+	vecOpFsub
 	vecOpSqsub
 	vecOpUqsub
 	vecOpSmin
 	vecOpUmin
 	vecOpUminv
+	vecOpFmin
 	vecOpSmax
 	vecOpUmax
 	vecOpUmaxp
+	vecOpFmax
 	vecOpUrhadd
 	vecOpMul
+	vecOpFmul
 	vecOpUmlal
+	vecOpFdiv
+	vecOpFsqrt
 	vecOpAbs
+	vecOpFabs
 	vecOpNeg
+	vecOpFneg
 	vecOpRev64
 	vecOpXtn
 	vecOpShll
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index 9ef579aa..02a6f80b 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -456,6 +456,12 @@ func encodeVecRRR(op vecOp, rd, rn, rm uint32, arr vecArrangement) uint32 {
 		}
 		size, q := arrToSizeQEncoded(arr)
 		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b10000, size, 0b1, q)
+	case vecOpFmin:
+		if arr < vecArrangement2S || arr == vecArrangement1D {
+			panic("unsupported arrangement: " + arr.String())
+		}
+		size, q := arrToSizeQEncoded(arr)
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11110, size, 0b0, q)
 	case vecOpSmin:
 		if arr > vecArrangement4S {
 			panic("unsupported arrangement: " + arr.String())
@@ -468,6 +474,64 @@ func encodeVecRRR(op vecOp, rd, rn, rm uint32, arr vecArrangement) uint32 {
 		}
 		size, q := arrToSizeQEncoded(arr)
 		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b01101, size, 0b1, q)
+	case vecOpFmax:
+		var size, q uint32
+		switch arr {
+		case vecArrangement4S:
+			size, q = 0b00, 0b1
+		case vecArrangement2S:
+			size, q = 0b00, 0b0
+		case vecArrangement2D:
+			size, q = 0b01, 0b1
+		default:
+			panic("unsupported arrangement: " + arr.String())
+		}
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11110, size, 0b0, q)
+	case vecOpFadd:
+		var size, q uint32
+		switch arr {
+		case vecArrangement4S:
+			size, q = 0b00, 0b1
+		case vecArrangement2S:
+			size, q = 0b00, 0b0
+		case vecArrangement2D:
+			size, q = 0b01, 0b1
+		default:
+			panic("unsupported arrangement: " + arr.String())
+		}
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11010, size, 0b0, q)
+	case vecOpFsub:
+		if arr < vecArrangement2S || arr == vecArrangement1D {
+			panic("unsupported arrangement: " + arr.String())
+		}
+		size, q := arrToSizeQEncoded(arr)
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11010, size, 0b0, q)
+	case vecOpFmul:
+		var size, q uint32
+		switch arr {
+		case vecArrangement4S:
+			size, q = 0b00, 0b1
+		case vecArrangement2S:
+			size, q = 0b00, 0b0
+		case vecArrangement2D:
+			size, q = 0b01, 0b1
+		default:
+			panic("unsupported arrangement: " + arr.String())
+		}
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11011, size, 0b1, q)
+	case vecOpFdiv:
+		var size, q uint32
+		switch arr {
+		case vecArrangement4S:
+			size, q = 0b00, 0b1
+		case vecArrangement2S:
+			size, q = 0b00, 0b0
+		case vecArrangement2D:
+			size, q = 0b01, 0b1
+		default:
+			panic("unsupported arrangement: " + arr.String())
+		}
+		return encodeAdvancedSIMDThreeSame(rd, rn, rm, 0b11111, size, 0b1, q)
 	case vecOpSmax:
 		if arr > vecArrangement4S {
 			panic("unsupported arrangement: " + arr.String())
@@ -1652,6 +1716,27 @@ func encodeAdvancedSIMDTwoMisc(op vecOp, rd, rn uint32, arr vecArrangement) uint
 		opcode = 0b01011
 		u = 0b1
 		size, q = arrToSizeQEncoded(arr)
+	case vecOpFabs:
+		if arr < vecArrangement2S || arr == vecArrangement1D {
+			panic("unsupported arrangement: " + arr.String())
+		}
+		opcode = 0b01111
+		u = 0b0
+		size, q = arrToSizeQEncoded(arr)
+	case vecOpFneg:
+		if arr < vecArrangement2S || arr == vecArrangement1D {
+			panic("unsupported arrangement: " + arr.String())
+		}
+		opcode = 0b01111
+		u = 0b1
+		size, q = arrToSizeQEncoded(arr)
+	case vecOpFsqrt:
+		if arr < vecArrangement2S || arr == vecArrangement1D {
+			panic("unsupported arrangement: " + arr.String())
+		}
+		opcode = 0b11111
+		u = 0b1
+		size, q = arrToSizeQEncoded(arr)
 	case vecOpRev64:
 		opcode = 0b00000
 		size, q = arrToSizeQEncoded(arr)
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 8a1d45e3..ce7b3b28 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -406,6 +406,36 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		m.lowerVecMisc(vecOpNeg, instr)
 	case ssa.OpcodeVIpopcnt:
 		m.lowerVecMisc(vecOpCnt, instr)
+	case ssa.OpcodeVSqrt:
+		m.lowerVecMisc(vecOpFsqrt, instr)
+	case ssa.OpcodeVFabs:
+		m.lowerVecMisc(vecOpFabs, instr)
+	case ssa.OpcodeVFneg:
+		m.lowerVecMisc(vecOpFneg, instr)
+	case ssa.OpcodeVFmin:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFmin, x, y, instr.Return(), arr)
+	case ssa.OpcodeVFmax:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFmax, x, y, instr.Return(), arr)
+	case ssa.OpcodeVFadd:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFadd, x, y, instr.Return(), arr)
+	case ssa.OpcodeVFsub:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFsub, x, y, instr.Return(), arr)
+	case ssa.OpcodeVFmul:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFmul, x, y, instr.Return(), arr)
+	case ssa.OpcodeVFdiv:
+		x, y, lane := instr.Arg2WithLane()
+		arr := ssaLaneToArrangement(lane)
+		m.lowerVecRRR(vecOpFdiv, x, y, instr.Return(), arr)
 	default:
 		panic("TODO: lowering " + op.String())
 	}
diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go
index c6c50782..f29cf7e2 100644
--- a/internal/engine/wazevo/frontend/lower.go
+++ b/internal/engine/wazevo/frontend/lower.go
@@ -1893,6 +1893,140 @@ func (c *Compiler) lowerCurrentOpcode() {
 			ret := builder.AllocateInstruction().
 				AsVIcmp(v1, v2, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual, lane).Insert(builder).Return()
 			state.push(ret)
+		case wasm.OpcodeVecF32x4Max, wasm.OpcodeVecF64x2Max:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Max:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Max:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFmax(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Abs, wasm.OpcodeVecF64x2Abs:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Abs:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Abs:
+				lane = ssa.VecLaneF64x2
+			}
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFabs(v1, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Min, wasm.OpcodeVecF64x2Min:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Min:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Min:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFmin(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Neg, wasm.OpcodeVecF64x2Neg:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Neg:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Neg:
+				lane = ssa.VecLaneF64x2
+			}
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFneg(v1, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Sqrt, wasm.OpcodeVecF64x2Sqrt:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Sqrt:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Sqrt:
+				lane = ssa.VecLaneF64x2
+			}
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVSqrt(v1, lane).Insert(builder).Return()
+			state.push(ret)
+
+		case wasm.OpcodeVecF32x4Add, wasm.OpcodeVecF64x2Add:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Add:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Add:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFadd(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Sub, wasm.OpcodeVecF64x2Sub:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Sub:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Sub:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFsub(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Mul, wasm.OpcodeVecF64x2Mul:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Mul:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Mul:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFmul(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+		case wasm.OpcodeVecF32x4Div, wasm.OpcodeVecF64x2Div:
+			if state.unreachable {
+				break
+			}
+			var lane ssa.VecLane
+			switch vecOp {
+			case wasm.OpcodeVecF32x4Div:
+				lane = ssa.VecLaneF32x4
+			case wasm.OpcodeVecF64x2Div:
+				lane = ssa.VecLaneF64x2
+			}
+			v2 := state.pop()
+			v1 := state.pop()
+			ret := builder.AllocateInstruction().AsVFdiv(v1, v2, lane).Insert(builder).Return()
+			state.push(ret)
+
 		default:
 			panic("TODO: unsupported vector instruction: " + wasm.VectorInstructionName(vecOp))
 		}
diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go
index e40d1ddf..2ea2cedb 100644
--- a/internal/engine/wazevo/ssa/instructions.go
+++ b/internal/engine/wazevo/ssa/instructions.go
@@ -395,15 +395,42 @@ const (
 	// OpcodeVImul performs an integer multiplication: `v = VImul.lane x, y` on vector.
 	OpcodeVImul
 
-	// OpcodeVIneg negates the given vector value: `v = VIneg x`.
+	// OpcodeVIneg negates the given integer vector value: `v = VIneg x`.
 	OpcodeVIneg
 
 	// OpcodeVIpopcnt counts the number of 1-bits in the given vector: `v = VIpopcnt x`.
 	OpcodeVIpopcnt
 
-	// OpcodeVIabs returns the absolute value for the given vector value: `v = VIabs x`.
+	// OpcodeVIabs returns the absolute value for the given vector value: `v = VIabs.lane x`.
 	OpcodeVIabs
 
+	// OpcodeVFabs takes the absolute value of a floating point value: `v = VFabs.lane x on vector.
+	OpcodeVFabs
+
+	// OpcodeVFmax takes the maximum of two floating point values: `v = VFmax.lane x, y on vector.
+	OpcodeVFmax
+
+	// OpcodeVFmin takes the minimum of two floating point values: `v = VFmin.lane x, y on vector.
+	OpcodeVFmin
+
+	// OpcodeVFneg negates the given floating point vector value: `v = VFneg x`.
+	OpcodeVFneg
+
+	// OpcodeVFadd performs a floating point addition: `v = VFadd.lane x, y` on vector.
+	OpcodeVFadd
+
+	// OpcodeVFsub performs a floating point subtraction: `v = VFsub.lane x, y` on vector.
+	OpcodeVFsub
+
+	// OpcodeVFmul performs a floating point multiplication: `v = VFmul.lane x, y` on vector.
+	OpcodeVFmul
+
+	// OpcodeVFdiv performs a floating point division: `v = VFdiv.lane x, y` on vector.
+	OpcodeVFdiv
+
+	// OpcodeVSqrt takes the minimum of two floating point values: `v = VFmin.lane x, y on vector.
+	OpcodeVSqrt
+
 	// OpcodeImul performs an integer multiplication: `v = Imul x, y`.
 	OpcodeImul
 
@@ -898,6 +925,15 @@ var instructionSideEffects = [opcodeEnd]sideEffect{
 	OpcodeVIabs:              sideEffectNone,
 	OpcodeVIneg:              sideEffectNone,
 	OpcodeVIpopcnt:           sideEffectNone,
+	OpcodeVSqrt:              sideEffectNone,
+	OpcodeVFabs:              sideEffectNone,
+	OpcodeVFmin:              sideEffectNone,
+	OpcodeVFmax:              sideEffectNone,
+	OpcodeVFneg:              sideEffectNone,
+	OpcodeVFadd:              sideEffectNone,
+	OpcodeVFsub:              sideEffectNone,
+	OpcodeVFmul:              sideEffectNone,
+	OpcodeVFdiv:              sideEffectNone,
 }
 
 // sideEffect returns true if this instruction has side effects.
@@ -1038,6 +1074,15 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{
 	OpcodeFdemote:            returnTypesFnF32,
 	OpcodeFpromote:           returnTypesFnF64,
 	OpcodeVconst:             returnTypesFnV128,
+	OpcodeVFabs:              returnTypesFnV128,
+	OpcodeVSqrt:              returnTypesFnV128,
+	OpcodeVFmax:              returnTypesFnV128,
+	OpcodeVFmin:              returnTypesFnV128,
+	OpcodeVFneg:              returnTypesFnV128,
+	OpcodeVFadd:              returnTypesFnV128,
+	OpcodeVFsub:              returnTypesFnV128,
+	OpcodeVFmul:              returnTypesFnV128,
+	OpcodeVFdiv:              returnTypesFnV128,
 }
 
 // AsLoad initializes this instruction as a store instruction with OpcodeLoad.
@@ -1277,6 +1322,93 @@ func (i *Instruction) AsVIpopcnt(x Value, lane VecLane) *Instruction {
 	return i
 }
 
+// AsVSqrt initializes this instruction as a sqrt instruction with OpcodeVSqrt on a vector.
+func (i *Instruction) AsVSqrt(x Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVSqrt
+	i.v = x
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFabs initializes this instruction as a float abs instruction with OpcodeVFabs on a vector.
+func (i *Instruction) AsVFabs(x Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFabs
+	i.v = x
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFneg initializes this instruction as a float neg instruction with OpcodeVFneg on a vector.
+func (i *Instruction) AsVFneg(x Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFneg
+	i.v = x
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFmax initializes this instruction as a float max instruction with OpcodeVFmax on a vector.
+func (i *Instruction) AsVFmax(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFmax
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFmin initializes this instruction as a float min instruction with OpcodeVFmin on a vector.
+func (i *Instruction) AsVFmin(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFmin
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFadd initializes this instruction as a floating point add instruction with OpcodeVFadd on a vector.
+func (i *Instruction) AsVFadd(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFadd
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFsub initializes this instruction as a floating point subtraction instruction with OpcodeVFsub on a vector.
+func (i *Instruction) AsVFsub(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFsub
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFmul initializes this instruction as a floating point multiplication instruction with OpcodeVFmul on a vector.
+func (i *Instruction) AsVFmul(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFmul
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
+// AsVFdiv initializes this instruction as a floating point division instruction with OpcodeVFdiv on a vector.
+func (i *Instruction) AsVFdiv(x, y Value, lane VecLane) *Instruction {
+	i.opcode = OpcodeVFdiv
+	i.v = x
+	i.v2 = y
+	i.u1 = uint64(lane)
+	i.typ = TypeV128
+	return i
+}
+
 // AsImul initializes this instruction as an integer addition instruction with OpcodeImul.
 func (i *Instruction) AsImul(x, y Value) {
 	i.opcode = OpcodeImul
@@ -2088,9 +2220,12 @@ func (i *Instruction) Format(b Builder) string {
 		OpcodeCeil, OpcodeFloor, OpcodeTrunc, OpcodeNearest:
 		instSuffix = " " + i.v.Format(b)
 	case OpcodeVIadd, OpcodeVSaddSat, OpcodeVUaddSat, OpcodeVIsub, OpcodeVSsubSat, OpcodeVUsubSat,
-		OpcodeVImin, OpcodeVUmin, OpcodeVImax, OpcodeVUmax, OpcodeVImul, OpcodeVAvgRound:
+		OpcodeVImin, OpcodeVUmin, OpcodeVImax, OpcodeVUmax, OpcodeVImul, OpcodeVAvgRound,
+		OpcodeVFadd, OpcodeVFsub, OpcodeVFmul, OpcodeVFdiv,
+		OpcodeVFmin, OpcodeVFmax:
 		instSuffix = fmt.Sprintf(".%s %s, %s", VecLane(i.u1), i.v.Format(b), i.v2.Format(b))
-	case OpcodeVIabs, OpcodeVIneg, OpcodeVIpopcnt, OpcodeVhighBits, OpcodeVallTrue, OpcodeVanyTrue:
+	case OpcodeVIabs, OpcodeVIneg, OpcodeVIpopcnt, OpcodeVhighBits, OpcodeVallTrue, OpcodeVanyTrue,
+		OpcodeVFabs, OpcodeVFneg, OpcodeVSqrt:
 		instSuffix = fmt.Sprintf(".%s %s", VecLane(i.u1), i.v.Format(b))
 	default:
 		panic(fmt.Sprintf("TODO: format for %s", i.opcode))
@@ -2506,6 +2641,25 @@ func (o Opcode) String() (ret string) {
 		return "VIneg"
 	case OpcodeVIpopcnt:
 		return "VIpopcnt"
+	case OpcodeVFabs:
+		return "VFabs"
+	case OpcodeVFmax:
+		return "VFmax"
+	case OpcodeVFmin:
+		return "VFmin"
+	case OpcodeVFneg:
+		return "VFneg"
+	case OpcodeVFadd:
+		return "VFadd"
+	case OpcodeVFsub:
+		return "VFsub"
+	case OpcodeVFmul:
+		return "VFmul"
+	case OpcodeVFdiv:
+		return "VFdiv"
+	case OpcodeVSqrt:
+		return "VSqrt"
+
 	}
 	panic(fmt.Sprintf("unknown opcode %d", o))
 }


Test patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
index 2cc84cc6..54cc6e72 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -550,6 +550,60 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "413ce36e", setup: func(i *instruction) {
 			i.asVecRRR(vecOpCmhs, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
 		}},
+		{want: "41f4230e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmax, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41f4234e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmax, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41f4634e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmax, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
+		{want: "41f4a30e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmin, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41f4a34e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmin, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41f4e34e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmin, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
+		{want: "41d4230e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFadd, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41d4234e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFadd, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41d4634e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFadd, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
+		{want: "41d4a30e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFsub, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41d4a34e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFsub, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41d4e34e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFsub, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
+		{want: "41dc232e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmul, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41dc236e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmul, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41dc636e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFmul, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
+		{want: "41fc232e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFdiv, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2S)
+		}},
+		{want: "41fc236e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFdiv, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement4S)
+		}},
+		{want: "41fc636e", setup: func(i *instruction) {
+			i.asVecRRR(vecOpFdiv, operandNR(v1VReg), operandNR(v2VReg), operandNR(v3VReg), vecArrangement2D)
+		}},
 		{want: "41b8200e", setup: func(i *instruction) {
 			i.asVecMisc(vecOpAbs, operandNR(v1VReg), operandNR(v2VReg), vecArrangement8B)
 		}},
@@ -571,6 +625,15 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "41b8e04e", setup: func(i *instruction) {
 			i.asVecMisc(vecOpAbs, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2D)
 		}},
+		{want: "41f8a00e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFabs, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2S)
+		}},
+		{want: "41f8a04e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFabs, operandNR(v1VReg), operandNR(v2VReg), vecArrangement4S)
+		}},
+		{want: "41f8e04e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFabs, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2D)
+		}},
 		{want: "41b8202e", setup: func(i *instruction) {
 			i.asVecMisc(vecOpNeg, operandNR(v1VReg), operandNR(v2VReg), vecArrangement8B)
 		}},
@@ -598,6 +661,24 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "4128a10e", setup: func(i *instruction) {
 			i.asVecMisc(vecOpXtn, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2S)
 		}},
+		{want: "41f8a02e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFneg, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2S)
+		}},
+		{want: "41f8a06e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFneg, operandNR(v1VReg), operandNR(v2VReg), vecArrangement4S)
+		}},
+		{want: "41f8e06e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFneg, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2D)
+		}},
+		{want: "41f8a12e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFsqrt, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2S)
+		}},
+		{want: "41f8a16e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFsqrt, operandNR(v1VReg), operandNR(v2VReg), vecArrangement4S)
+		}},
+		{want: "41f8e16e", setup: func(i *instruction) {
+			i.asVecMisc(vecOpFsqrt, operandNR(v1VReg), operandNR(v2VReg), vecArrangement2D)
+		}},
 		{want: "4100839a", setup: func(i *instruction) { i.asCSel(operandNR(x1VReg), operandNR(x2VReg), operandNR(x3VReg), eq, true) }},
 		{want: "4110839a", setup: func(i *instruction) { i.asCSel(operandNR(x1VReg), operandNR(x2VReg), operandNR(x3VReg), ne, true) }},
 		{want: "4100831a", setup: func(i *instruction) { i.asCSel(operandNR(x1VReg), operandNR(x2VReg), operandNR(x3VReg), eq, false) }},
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index 13cf8dff..335e193b 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -158,6 +158,10 @@ func TestSpectestV2(t *testing.T) {
 		{"simd_i16x8_cmp"},
 		{"simd_i32x4_cmp"},
 		{"simd_i64x2_cmp"},
+		{"simd_f32x4"},
+		{"simd_f64x2"},
+		{"simd_f32x4_arith"},
+		{"simd_f64x2_arith"},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Run("normal", func(t *testing.T) {