Repository: tetratelabs/wazero
Pull request #2049 (2024-02-12 23:45:42)

wazevo(amd64): passes q15mulr_sat_s/load_zero/pmin/pmax


Patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr.go b/internal/engine/wazevo/backend/isa/amd64/instr.go
index 6e064388..d6ed0339 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr.go
@@ -1576,6 +1576,7 @@ const (
 	sseOpcodeUcomisd
 	sseOpcodeXorps
 	sseOpcodeXorpd
+	sseOpcodePmulhrsw
 )
 
 func (s sseOpcode) String() string {
@@ -1910,6 +1911,8 @@ func (s sseOpcode) String() string {
 		return "xorps"
 	case sseOpcodeXorpd:
 		return "xorpd"
+	case sseOpcodePmulhrsw:
+		return "pmulhrsw"
 	default:
 		panic("BUG")
 	}
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
index b7463179..820acfe7 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding.go
@@ -323,6 +323,8 @@ func (i *instruction) encode(c backend.Compiler) (needsLabelResolution bool) {
 			legPrex, opcode, opcodeNum = legacyPrefixesNone, 0x0F57, 2
 		case sseOpcodeXorpd:
 			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F57, 2
+		case sseOpcodePmulhrsw:
+			legPrex, opcode, opcodeNum = legacyPrefixes0x66, 0x0F380B, 3
 		default:
 			panic(fmt.Sprintf("Unsupported sseOpcode: %s", op))
 		}
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine.go b/internal/engine/wazevo/backend/isa/amd64/machine.go
index 84c7fc85..85c5eac1 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine.go
@@ -29,6 +29,7 @@ func NewBackend() backend.Machine {
 		spillSlots:            map[regalloc.VRegID]int64{},
 		amodePool:             wazevoapi.NewPool[amode](nil),
 		swizzleMaskConstIndex: -1,
+		sqmulRoundSatIndex:    -1,
 	}
 }
 
@@ -59,7 +60,7 @@ type (
 		jmpTableTargets [][]uint32
 		consts          []_const
 
-		swizzleMaskConstIndex int
+		swizzleMaskConstIndex, sqmulRoundSatIndex int
 	}
 
 	_const struct {
@@ -103,6 +104,7 @@ func (m *machine) Reset() {
 	m.amodePool.Reset()
 	m.jmpTableTargets = m.jmpTableTargets[:0]
 	m.swizzleMaskConstIndex = -1
+	m.sqmulRoundSatIndex = -1
 }
 
 // ExecutableContext implements backend.Machine.
@@ -723,6 +725,51 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		x, lane := instr.ArgWithLane()
 		m.lowerSplat(x, instr.Return(), lane)
 
+	case ssa.OpcodeSqmulRoundSat:
+		x, y := instr.Arg2()
+		m.lowerSqmulRoundSat(x, y, instr.Return())
+
+	case ssa.OpcodeVZeroExtLoad:
+		ptr, offset, typ := instr.VZeroExtLoadData()
+		var sseOp sseOpcode
+		// Both movss and movsd clears the higher bits of the destination register upt 128 bits.
+		// https://www.felixcloutier.com/x86/movss
+		// https://www.felixcloutier.com/x86/movsd
+		if typ == ssa.TypeF32 {
+			sseOp = sseOpcodeMovss
+		} else {
+			sseOp = sseOpcodeMovsd
+		}
+		mem := m.lowerToAddressMode(ptr, offset)
+		dst := m.c.VRegOf(instr.Return())
+		m.insert(m.allocateInstr().asXmmUnaryRmR(sseOp, newOperandMem(mem), dst))
+
+	case ssa.OpcodeVMinPseudo:
+		x, y, lane := instr.Arg2WithLane()
+		var vecOp sseOpcode
+		switch lane {
+		case ssa.VecLaneF32x4:
+			vecOp = sseOpcodeMinps
+		case ssa.VecLaneF64x2:
+			vecOp = sseOpcodeMinpd
+		default:
+			panic("BUG: unexpected lane type")
+		}
+		m.lowerVbBinOp(vecOp, y, x, instr.Return())
+
+	case ssa.OpcodeVMaxPseudo:
+		x, y, lane := instr.Arg2WithLane()
+		var vecOp sseOpcode
+		switch lane {
+		case ssa.VecLaneF32x4:
+			vecOp = sseOpcodeMaxps
+		case ssa.VecLaneF64x2:
+			vecOp = sseOpcodeMaxpd
+		default:
+			panic("BUG: unexpected lane type")
+		}
+		m.lowerVbBinOp(vecOp, y, x, instr.Return())
+
 	case ssa.OpcodeVIabs:
 		m.lowerVIabs(instr)
 	case ssa.OpcodeVIpopcnt:
@@ -2891,6 +2938,34 @@ func (m *machine) lowerVbnot(instr *ssa.Instruction) {
 	m.copyTo(tmp, rd)
 }
 
+var sqmulRoundSat = [16]byte{
+	0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
+	0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
+}
+
+func (m *machine) lowerSqmulRoundSat(x, y, ret ssa.Value) {
+	// See https://github.com/WebAssembly/simd/pull/365 for the following logic.
+	maskIndex := m.sqmulRoundSatIndex
+	if maskIndex < 0 {
+		maskIndex = len(m.consts)
+		m.consts = append(m.consts, _const{_var: sqmulRoundSat[:], label: m.allocateLabel()})
+		m.sqmulRoundSatIndex = maskIndex
+	}
+
+	tmp := m.c.AllocateVReg(ssa.TypeV128)
+	loadMask := m.allocateInstr().asXmmUnaryRmR(sseOpcodeMovdqu, newOperandMem(m.newAmodeRipRel(m.consts[maskIndex].label.L)), tmp)
+	m.insert(loadMask)
+
+	xx, yy := m.getOperand_Reg(m.c.ValueDefinition(x)), m.getOperand_Mem_Reg(m.c.ValueDefinition(y))
+	tmpX := m.copyToTmp(xx.reg())
+
+	m.insert(m.allocateInstr().asXmmRmR(sseOpcodePmulhrsw, yy, tmpX))
+	m.insert(m.allocateInstr().asXmmRmR(sseOpcodePcmpeqd, newOperandReg(tmpX), tmp))
+	m.insert(m.allocateInstr().asXmmRmR(sseOpcodePxor, newOperandReg(tmp), tmpX))
+
+	m.copyTo(tmpX, m.c.VRegOf(ret))
+}
+
 func (m *machine) lowerSplat(x, ret ssa.Value, lane ssa.VecLane) {
 	tmpDst := m.c.AllocateVReg(ssa.TypeV128)
 	m.insert(m.allocateInstr().asDefineUninitializedReg(tmpDst))


Test patch:
diff --git a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
index bbe47b84..a8cdb29c 100644
--- a/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/instr_encoding_test.go
@@ -4120,6 +4120,11 @@ func TestInstruction_format_encode(t *testing.T) {
 			want:       "660f3a09c119",
 			wantFormat: "roundpd $25, %xmm1, %xmm0",
 		},
+		{
+			setup:      func(i *instruction) { i.asXmmRmR(sseOpcodePmulhrsw, newOperandReg(xmm1VReg), xmm0VReg) },
+			want:       "660f380bc1",
+			wantFormat: "pmulhrsw %xmm1, %xmm0",
+		},
 	} {
 		tc := tc
 		t.Run(tc.wantFormat, func(t *testing.T) {
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index bb0682cc..683d186a 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -1523,8 +1523,8 @@ func TestSpectestV2(t *testing.T) {
 		{"simd_f64x2_cmp"},
 		//{"simd_f32x4_rounding"},
 		//{"simd_f64x2_rounding"},
-		//{"simd_f32x4_pmin_pmax"},
-		//{"simd_f64x2_pmin_pmax"},
+		{"simd_f32x4_pmin_pmax"},
+		{"simd_f64x2_pmin_pmax"},
 		//{"simd_i32x4_trunc_sat_f32x4"},
 		//{"simd_i32x4_trunc_sat_f64x2"},
 		//{"simd_i32x4_dot_i16x8"},
@@ -1533,12 +1533,12 @@ func TestSpectestV2(t *testing.T) {
 		//{"simd_i32x4_extmul_i16x8"},
 		//{"simd_i16x8_extadd_pairwise_i8x16"},
 		//{"simd_i32x4_extadd_pairwise_i16x8"},
-		//{"simd_i16x8_q15mulr_sat_s"},
+		{"simd_i16x8_q15mulr_sat_s"},
 		//{"simd_int_to_int_extend"},
 		//{"simd_load"},
 		//{"simd_load_extend"},
 		//{"simd_load_splat"},
-		//{"simd_load_zero"},
+		{"simd_load_zero"},
 		{"simd_load8_lane"},
 		{"simd_load16_lane"},
 		{"simd_load32_lane"},