Repository: bytecodealliance/wasmtime
Pull request #5896 (2023-02-28 22:56:01)

x64: Add support for `phadd{w,d}` instructions
This commit adds support for the bare lowering of the `iadd_pairwise` instruction with `i16x8` and `i32x4` types on the x64 backend. These lowerings are achieved with the `phaddw` and `phaddd` instructions, respectively. Additionally AVX encodings of these instructions are added too.

The motivation for these new lowerings comes from the relaxed-simd proposal which will use them in the deterministic lowering of some instructions on the x64 backend.

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->



Patch:
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index ca27ac8c5..4cee85ed5 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -1910,21 +1910,20 @@
 (decl lower_cond_br (IntCC ValueRegs VecMachLabel Type) Unit)
 (extern constructor lower_cond_br lower_cond_br)
 
+(decl intcc_to_extend_op (IntCC) ExtendOp)
+(extern constructor intcc_to_extend_op intcc_to_extend_op)
+
 ;; Normalize a value for comparision.
 ;;
 ;; This ensures that types smaller than a register don't accidentally
 ;; pass undefined high bits when being compared as a full register.
-(decl normalize_cmp_value (Type ValueRegs) ValueRegs)
+(decl normalize_cmp_value (Type ValueRegs ExtendOp) ValueRegs)
 
-(rule (normalize_cmp_value $I8 r)
-      (value_reg (alu_rr_imm12 (AluOPRRI.Andi) r (imm12_const 255))))
-(rule (normalize_cmp_value $I16 r)
-      (value_reg (alu_rrr (AluOPRRR.And) r (imm $I16 65535))))
-(rule (normalize_cmp_value $I32 r)
-      (value_reg (alu_rr_imm12 (AluOPRRI.Addiw) r (imm12_const 0))))
+(rule 1 (normalize_cmp_value (fits_in_32 ity) r op)
+      (extend r op ity $I64))
 
-(rule (normalize_cmp_value $I64  r) r)
-(rule (normalize_cmp_value $I128 r) r)
+(rule (normalize_cmp_value $I64  r _) r)
+(rule (normalize_cmp_value $I128 r _) r)
 
 ;; Convert a truthy value, possibly of more than one register (an
 ;; I128), to one register. If narrower than 64 bits, must have already
@@ -1940,7 +1939,7 @@
 ;; Default behavior for branching based on an input value.
 (rule
   (lower_branch (brif v @ (value_type ty) _ _) targets)
-  (lower_cond_br (IntCC.NotEqual) (normalize_cmp_value ty v) targets ty))
+  (lower_cond_br (IntCC.NotEqual) (normalize_cmp_value ty v (ExtendOp.Zero)) targets ty))
 
 ;; Special case for SI128 to reify the comparison value and branch on it.
 (rule 2
@@ -2118,7 +2117,7 @@
 (rule
   0
   (lower_bmask (fits_in_64 _) (fits_in_64 in_ty) val)
-  (let ((input Reg (normalize_cmp_value in_ty val))
+  (let ((input Reg (normalize_cmp_value in_ty val (ExtendOp.Zero)))
         (zero Reg (zero_reg))
         (ones Reg (load_imm12 -1)))
   (value_reg (gen_select_reg (IntCC.Equal) zero input zero ones))))
diff --git a/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs b/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
index d62d8ba4a..1803ecfec 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/unwind/systemv.rs
@@ -143,7 +143,7 @@ mod tests {
 
         assert_eq!(
             format!("{:?}", fde),
-            "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [] }"
+            "FrameDescriptionEntry { address: Constant(4321), length: 20, lsda: None, instructions: [] }"
         );
     }
 
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index 9d1f43af5..b08d6558f 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -626,12 +626,12 @@
 ;;;;;  Rules for `select`;;;;;;;;;
 (rule
   (lower (has_type ty (select c @ (value_type cty) x y)))
-  (gen_select ty (truthy_to_reg cty (normalize_cmp_value cty c)) x y))
+  (gen_select ty (truthy_to_reg cty (normalize_cmp_value cty c (ExtendOp.Zero))) x y))
 
 (rule 1
   (lower (has_type (fits_in_64 ty) (select (icmp cc a b @ (value_type in_ty)) x y)))
-  (let ((a Reg (normalize_cmp_value in_ty a))
-        (b Reg (normalize_cmp_value in_ty b)))
+  (let ((a Reg (normalize_cmp_value in_ty a (intcc_to_extend_op cc)))
+        (b Reg (normalize_cmp_value in_ty b (intcc_to_extend_op cc))))
     (gen_select_reg cc a b x y)))
 
 ;;;;;  Rules for `bitselect`;;;;;;;;;
@@ -851,7 +851,7 @@
 
 (rule -1
   (lower (has_type ty (select_spectre_guard c @ (value_type cty) x y)))
-  (gen_select ty (truthy_to_reg cty (normalize_cmp_value cty c)) x y))
+  (gen_select ty (truthy_to_reg cty (normalize_cmp_value cty c (ExtendOp.Zero))) x y))
 
 ;;;;;  Rules for `bmask`;;;;;;;;;
 (rule
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index f1b3fed92..1c088b4be 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -3,7 +3,7 @@
 // Pull in the ISLE generated code.
 #[allow(unused)]
 pub mod generated_code;
-use generated_code::{Context, MInst};
+use generated_code::{Context, ExtendOp, MInst};
 
 // Types that the generated ISLE code uses via `use super::*`.
 use super::{writable_zero_reg, zero_reg};
@@ -60,7 +60,22 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, Riscv64Backend> {
             _ => unreachable!(),
         }
     }
-
+    fn intcc_to_extend_op(&mut self, cc: &IntCC) -> ExtendOp {
+        use IntCC::*;
+        match *cc {
+            Equal
+            | NotEqual
+            | UnsignedLessThan
+            | UnsignedGreaterThanOrEqual
+            | UnsignedGreaterThan
+            | UnsignedLessThanOrEqual => ExtendOp::Zero,
+
+            SignedLessThan
+            | SignedGreaterThanOrEqual
+            | SignedGreaterThan
+            | SignedLessThanOrEqual => ExtendOp::Signed,
+        }
+    }
     fn lower_cond_br(
         &mut self,
         cc: &IntCC,
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index d25cc7efc..993b4984d 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -860,7 +860,10 @@
             Ucomisd
             Unpcklps
             Xorps
-            Xorpd))
+            Xorpd
+            Phaddw
+            Phaddd
+          ))
 
 (type CmpOpcode extern
       (enum Cmp
@@ -1356,6 +1359,8 @@
             Vcvtps2pd
             Vcvttpd2dq
             Vcvttps2dq
+            Vphaddw
+            Vphaddd
           ))
 
 (type Avx512Opcode extern
@@ -2482,6 +2487,22 @@
       (if-let $true (has_avx))
       (xmm_rmir_vex (AvxOpcode.Vpaddsw) src1 src2))
 
+;; Helper for creating `phaddw` instructions.
+(decl x64_phaddw (Xmm XmmMem) Xmm)
+(rule 0 (x64_phaddw src1 src2)
+      (xmm_rm_r (SseOpcode.Phaddw) src1 src2))
+(rule 1 (x64_phaddw src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vphaddw) src1 src2))
+
+;; Helper for creating `phaddd` instructions.
+(decl x64_phaddd (Xmm XmmMem) Xmm)
+(rule 0 (x64_phaddd src1 src2)
+      (xmm_rm_r (SseOpcode.Phaddd) src1 src2))
+(rule 1 (x64_phaddd src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vphaddd) src1 src2))
+
 ;; Helper for creating `paddusb` instructions.
 (decl x64_paddusb (Xmm XmmMem) Xmm)
 (rule 0 (x64_paddusb src1 src2)
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index 772727133..ca6e40ce5 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -1115,6 +1115,8 @@ pub enum SseOpcode {
     Unpcklps,
     Xorps,
     Xorpd,
+    Phaddw,
+    Phaddd,
 }
 
 impl SseOpcode {
@@ -1261,7 +1263,9 @@ impl SseOpcode {
             | SseOpcode::Pabsd
             | SseOpcode::Palignr
             | SseOpcode::Pmulhrsw
-            | SseOpcode::Pshufb => SSSE3,
+            | SseOpcode::Pshufb
+            | SseOpcode::Phaddw
+            | SseOpcode::Phaddd => SSSE3,
 
             SseOpcode::Blendvpd
             | SseOpcode::Blendvps
@@ -1495,6 +1499,8 @@ impl fmt::Debug for SseOpcode {
             SseOpcode::Unpcklps => "unpcklps",
             SseOpcode::Xorps => "xorps",
             SseOpcode::Xorpd => "xorpd",
+            SseOpcode::Phaddw => "phaddw",
+            SseOpcode::Phaddd => "phaddd",
         };
         write!(fmt, "{}", name)
     }
@@ -1661,7 +1667,9 @@ impl AvxOpcode {
             | AvxOpcode::Vcvtpd2ps
             | AvxOpcode::Vcvtps2pd
             | AvxOpcode::Vcvttpd2dq
-            | AvxOpcode::Vcvttps2dq => {
+            | AvxOpcode::Vcvttps2dq
+            | AvxOpcode::Vphaddw
+            | AvxOpcode::Vphaddd => {
                 smallvec![InstructionSet::AVX]
             }
         }
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index e43746c42..b92e1ecd9 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1954,6 +1954,8 @@ pub(crate) fn emit(
                 SseOpcode::Unpcklps => (LegacyPrefixes::None, 0x0F14, 2),
                 SseOpcode::Xorps => (LegacyPrefixes::None, 0x0F57, 2),
                 SseOpcode::Xorpd => (LegacyPrefixes::_66, 0x0F57, 2),
+                SseOpcode::Phaddw => (LegacyPrefixes::_66, 0x0F3801, 3),
+                SseOpcode::Phaddd => (LegacyPrefixes::_66, 0x0F3802, 3),
                 _ => unimplemented!("Opcode {:?} not implemented", op),
             };
 
@@ -2167,6 +2169,8 @@ pub(crate) fn emit(
                 AvxOpcode::Vminsd => (LP::_F2, OM::_0F, 0x5D),
                 AvxOpcode::Vmaxss => (LP::_F3, OM::_0F, 0x5F),
                 AvxOpcode::Vmaxsd => (LP::_F2, OM::_0F, 0x5F),
+                AvxOpcode::Vphaddw => (LP::_66, OM::_0F38, 0x01),
+                AvxOpcode::Vphaddd => (LP::_66, OM::_0F38, 0x02),
                 _ => panic!("unexpected rmir vex opcode {op:?}"),
             };
             VexInstruction::new()
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index 40b40f9b9..38c1f2de7 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -3173,8 +3173,14 @@
 
 ;; Rules for `iadd_pairwise` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(rule (lower (has_type $I16X8 (iadd_pairwise x y)))
+      (x64_phaddw x y))
+
+(rule (lower (has_type $I32X4 (iadd_pairwise x y)))
+      (x64_phaddd x y))
+
 ;; special case for the `i16x8.extadd_pairwise_i8x16_s` wasm instruction
-(rule (lower
+(rule 1 (lower
         (has_type $I16X8 (iadd_pairwise
                            (swiden_low val @ (value_type $I8X16))
                            (swiden_high val))))
@@ -3182,7 +3188,7 @@
         (x64_pmaddubsw mul_const val)))
 
 ;; special case for the `i32x4.extadd_pairwise_i16x8_s` wasm instruction
-(rule (lower
+(rule 1 (lower
         (has_type $I32X4 (iadd_pairwise
                            (swiden_low val @ (value_type $I16X8))
                            (swiden_high val))))
@@ -3190,7 +3196,7 @@
         (x64_pmaddwd val mul_const)))
 
 ;; special case for the `i16x8.extadd_pairwise_i8x16_u` wasm instruction
-(rule (lower
+(rule 1 (lower
         (has_type $I16X8 (iadd_pairwise
                            (uwiden_low val @ (value_type $I8X16))
                            (uwiden_high val))))
@@ -3198,7 +3204,7 @@
         (x64_pmaddubsw val mul_const)))
 
 ;; special case for the `i32x4.extadd_pairwise_i16x8_u` wasm instruction
-(rule (lower
+(rule 1 (lower
         (has_type $I32X4 (iadd_pairwise
                            (uwiden_low val @ (value_type $I16X8))
                            (uwiden_high val))))
@@ -3212,7 +3218,7 @@
         (x64_paddd dst addd_const)))
 
 ;; special case for the `i32x4.dot_i16x8_s` wasm instruction
-(rule (lower
+(rule 1 (lower
         (has_type $I32X4 (iadd_pairwise
                            (imul (swiden_low x) (swiden_low y))
                            (imul (swiden_high x) (swiden_high y)))))


Test patch:
diff --git a/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif
index 0b7a8cde0..bfaab0334 100644
--- a/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif
@@ -16,8 +16,9 @@ block2:
 
 ; VCode:
 ; block0:
-;   sext.w a2,a0
-;   bne a2,zero,taken(label1),not_taken(label2)
+;   slli a2,a0,32
+;   srli a4,a2,32
+;   bne a4,zero,taken(label1),not_taken(label2)
 ; block1:
 ;   j label3
 ; block2:
@@ -28,11 +29,12 @@ block2:
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   sext.w a2, a0
-;   bnez a2, 8
-; block1: ; offset 0x8
+;   slli a2, a0, 0x20
+;   srli a4, a2, 0x20
+;   bnez a4, 8
+; block1: ; offset 0xc
 ;   addi a0, zero, 0x61
-; block2: ; offset 0xc
+; block2: ; offset 0x10
 ;   ret
 
 function %cold_annotation(i32) -> i32 {
@@ -49,8 +51,9 @@ block2 cold:
 
 ; VCode:
 ; block0:
-;   sext.w a2,a0
-;   bne a2,zero,taken(label1),not_taken(label2)
+;   slli a2,a0,32
+;   srli a4,a2,32
+;   bne a4,zero,taken(label1),not_taken(label2)
 ; block1:
 ;   j label3
 ; block3:
@@ -61,11 +64,12 @@ block2 cold:
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   sext.w a2, a0
-;   beqz a2, 8
-; block1: ; offset 0x8
+;   slli a2, a0, 0x20
+;   srli a4, a2, 0x20
+;   beqz a4, 8
+; block1: ; offset 0xc
 ;   ret
-; block2: ; offset 0xc
+; block2: ; offset 0x10
 ;   addi a0, zero, 0x61
 ;   j -8
 
diff --git a/cranelift/filetests/filetests/isa/riscv64/condbr.clif b/cranelift/filetests/filetests/isa/riscv64/condbr.clif
index 3a0c18efe..617afdfe0 100644
--- a/cranelift/filetests/filetests/isa/riscv64/condbr.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/condbr.clif
@@ -675,10 +675,9 @@ block1:
 
 ; VCode:
 ; block0:
-;   lui a1,16
-;   addi a1,a1,4095
-;   and a3,a0,a1
-;   bne a3,zero,taken(label1),not_taken(label2)
+;   slli t2,a0,48
+;   srli a1,t2,48
+;   bne a1,zero,taken(label1),not_taken(label2)
 ; block1:
 ;   j label3
 ; block2:
@@ -688,10 +687,9 @@ block1:
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   lui a1, 0x10
-;   addi a1, a1, -1
-;   and a3, a0, a1
-; block1: ; offset 0xc
+;   slli t2, a0, 0x30
+;   srli a1, t2, 0x30
+; block1: ; offset 0x8
 ;   ret
 
 function %i32_brif(i32){
@@ -705,8 +703,9 @@ block1:
 
 ; VCode:
 ; block0:
-;   sext.w t2,a0
-;   bne t2,zero,taken(label1),not_taken(label2)
+;   slli t2,a0,32
+;   srli a1,t2,32
+;   bne a1,zero,taken(label1),not_taken(label2)
 ; block1:
 ;   j label3
 ; block2:
@@ -716,8 +715,9 @@ block1:
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   sext.w t2, a0
-; block1: ; offset 0x4
+;   slli t2, a0, 0x20
+;   srli a1, t2, 0x20
+; block1: ; offset 0x8
 ;   ret
 
 function %i64_brif(i64){
diff --git a/cranelift/filetests/filetests/isa/riscv64/condops.clif b/cranelift/filetests/filetests/isa/riscv64/condops.clif
index 35bd747ef..c2c1334ef 100644
--- a/cranelift/filetests/filetests/isa/riscv64/condops.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/condops.clif
@@ -108,18 +108,22 @@ block0(v0: i32, v1: i8, v2: i8):
 
 ; VCode:
 ; block0:
-;   sext.w a3,a0
-;   li a4,42
-;   sext.w a5,a4
-;   select_reg a0,a1,a2##condition=(a3 eq a5)
+;   slli a3,a0,32
+;   srli a3,a3,32
+;   li a5,42
+;   slli a7,a5,32
+;   srli t4,a7,32
+;   select_reg a0,a1,a2##condition=(a3 eq t4)
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   sext.w a3, a0
-;   addi a4, zero, 0x2a
-;   sext.w a5, a4
-;   beq a3, a5, 0xc
+;   slli a3, a0, 0x20
+;   srli a3, a3, 0x20
+;   addi a5, zero, 0x2a
+;   slli a7, a5, 0x20
+;   srli t4, a7, 0x20
+;   beq a3, t4, 0xc
 ;   ori a0, a2, 0
 ;   j 8
 ;   ori a0, a1, 0
diff --git a/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif b/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif
index 967338b90..e802e74d8 100644
--- a/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/i128-bmask.clif
@@ -150,17 +150,20 @@ block0(v0: i32):
 
 ; VCode:
 ; block0:
-;   sext.w t2,a0
-;   li a1,-1
-;   select_reg a1,zero,a1##condition=(zero eq t2)
+;   slli t2,a0,32
+;   srli a1,t2,32
+;   li a3,-1
+;   select_reg a1,zero,a3##condition=(zero eq a1)
 ;   mv a0,a1
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   sext.w t2, a0
-;   addi a1, zero, -1
-;   beq zero, t2, 8
+;   slli t2, a0, 0x20
+;   srli a1, t2, 0x20
+;   addi a3, zero, -1
+;   beq zero, a1, 0xc
+;   ori a1, a3, 0
 ;   j 8
 ;   ori a1, zero, 0
 ;   ori a0, a1, 0
@@ -174,22 +177,20 @@ block0(v0: i16):
 
 ; VCode:
 ; block0:
-;   lui a1,16
-;   addi a1,a1,4095
-;   and a3,a0,a1
-;   li a5,-1
-;   select_reg a1,zero,a5##condition=(zero eq a3)
+;   slli t2,a0,48
+;   srli a1,t2,48
+;   li a3,-1
+;   select_reg a1,zero,a3##condition=(zero eq a1)
 ;   mv a0,a1
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   lui a1, 0x10
-;   addi a1, a1, -1
-;   and a3, a0, a1
-;   addi a5, zero, -1
-;   beq zero, a3, 0xc
-;   ori a1, a5, 0
+;   slli t2, a0, 0x30
+;   srli a1, t2, 0x30
+;   addi a3, zero, -1
+;   beq zero, a1, 0xc
+;   ori a1, a3, 0
 ;   j 8
 ;   ori a1, zero, 0
 ;   ori a0, a1, 0
diff --git a/cranelift/filetests/filetests/isa/x64/iadd-pairwise-avx.clif b/cranelift/filetests/filetests/isa/x64/iadd-pairwise-avx.clif
new file mode 100644
index 000000000..4ad4003e8
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/iadd-pairwise-avx.clif
@@ -0,0 +1,54 @@
+test compile precise-output
+set enable_simd
+target x86_64 has_avx
+
+function %iadd_pairwise_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+  v2 = iadd_pairwise v0, v1
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vphaddw %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vphaddw %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %iadd_pairwise_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+  v2 = iadd_pairwise v0, v1
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vphaddd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vphaddd %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/iadd-pairwise.clif b/cranelift/filetests/filetests/isa/x64/iadd-pairwise.clif
new file mode 100644
index 000000000..2e1d1c85d
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/iadd-pairwise.clif
@@ -0,0 +1,54 @@
+test compile precise-output
+set enable_simd
+target x86_64
+
+function %iadd_pairwise_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+  v2 = iadd_pairwise v0, v1
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   phaddw  %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   phaddw %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %iadd_pairwise_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+  v2 = iadd_pairwise v0, v1
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   phaddd  %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   phaddd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/runtests/issue5839.clif b/cranelift/filetests/filetests/runtests/issue5839.clif
new file mode 100644
index 000000000..4e2a2c391
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/issue5839.clif
@@ -0,0 +1,20 @@
+test interpret
+test run
+set opt_level=speed
+target aarch64
+target s390x
+target x86_64
+target riscv64
+
+function %a(i8, i8) -> i32 {
+block0(v0: i8, v1: i8):
+    v2 = icmp sle v0, v1
+    v3 = uextend.i32 v2
+    v4 = iconst.i32 0
+    v5 = iconst.i32 1
+    v6 = icmp.i32 eq v3, v4  ; v4 = 0
+    v7 = select v6, v5, v3  ; v5 = 1
+    return v7
+}
+
+; run: %a(20, -11) == 1
\ No newline at end of file