Repository: bytecodealliance/wasmtime
Pull request #9114 (2024-08-12 16:28:55)

Winch aarch64 extend, wrap, popcnt, promote & demote
Hey 👋

This PR implements `extend`, `wrap`, `popcnt`, `promote` & `demote` instructions for winch targeting aarch64.

#8321

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->


saulecabrera: I can help reviewing this one. 

Patch:
diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs
index e2a281958..d8b1ba085 100644
--- a/winch/codegen/src/isa/aarch64/asm.rs
+++ b/winch/codegen/src/isa/aarch64/asm.rs
@@ -1,23 +1,22 @@
 //! Assembler library implementation for Aarch64.
 
 use super::{address::Address, regs};
-use crate::masm::{FloatCmpKind, IntCmpKind, RoundingMode, ShiftKind};
+use crate::masm::{ExtendKind, FloatCmpKind, IntCmpKind, RoundingMode, ShiftKind};
 use crate::{masm::OperandSize, reg::Reg};
 use cranelift_codegen::isa::aarch64::inst::{
     BitOp, BranchTarget, Cond, CondBrKind, FPULeftShiftImm, FPUOp1, FPUOp2,
     FPUOpRI::{self, UShr32, UShr64},
     FPUOpRIMod, FPURightShiftImm, FpuRoundMode, ImmLogic, ImmShift, ScalarSize,
 };
-use cranelift_codegen::MachInst;
 use cranelift_codegen::{
     ir::{MemFlags, SourceLoc},
     isa::aarch64::inst::{
         self,
         emit::{EmitInfo, EmitState},
-        ALUOp, ALUOp3, AMode, ExtendOp, Imm12, Inst, PairAMode,
+        ALUOp, ALUOp3, AMode, ExtendOp, Imm12, Inst, PairAMode, VecLanesOp, VecMisc2, VectorSize,
     },
-    settings, Final, MachBuffer, MachBufferFinalized, MachInstEmit, MachInstEmitState, MachLabel,
-    Writable,
+    settings, Final, MachBuffer, MachBufferFinalized, MachInst, MachInstEmit, MachInstEmitState,
+    MachLabel, Writable,
 };
 
 impl From<OperandSize> for inst::OperandSize {
@@ -312,6 +311,15 @@ impl Assembler {
         });
     }
 
+    pub fn mov_from_vec(&mut self, rn: Reg, rd: Reg, idx: u8, size: OperandSize) {
+        self.emit(Inst::MovFromVec {
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            idx,
+            size: size.into(),
+        });
+    }
+
     /// Add with three registers.
     pub fn add_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
         self.emit_alu_rrr_extend(ALUOp::Add, rm, rn, rd, size);
@@ -329,6 +337,16 @@ impl Assembler {
         }
     }
 
+    /// Add across Vector.
+    pub fn addv(&mut self, rn: Reg, rd: Reg, size: VectorSize) {
+        self.emit(Inst::VecLanes {
+            op: VecLanesOp::Addv,
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            size,
+        });
+    }
+
     /// Subtract with three registers.
     pub fn sub_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
         self.emit_alu_rrr_extend(ALUOp::Sub, rm, rn, rd, size);
@@ -557,6 +575,27 @@ impl Assembler {
         })
     }
 
+    /// Change precision of float.
+    pub fn cvt_float_to_float(
+        &mut self,
+        rn: Reg,
+        rd: Reg,
+        src_size: OperandSize,
+        dst_size: OperandSize,
+    ) {
+        let (fpu_op, size) = match (src_size, dst_size) {
+            (OperandSize::S32, OperandSize::S64) => (FPUOp1::Cvt32To64, ScalarSize::Size32),
+            (OperandSize::S64, OperandSize::S32) => (FPUOp1::Cvt64To32, ScalarSize::Size64),
+            _ => unimplemented!(),
+        };
+        self.emit(Inst::FpuRR {
+            fpu_op,
+            size,
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+        });
+    }
+
     /// Return instruction.
     pub fn ret(&mut self) {
         self.emit(Inst::Ret {});
@@ -611,6 +650,26 @@ impl Assembler {
         });
     }
 
+    // Population Count per byte.
+    pub fn cnt(&mut self, rd: Reg) {
+        self.emit(Inst::VecMisc {
+            op: VecMisc2::Cnt,
+            rd: Writable::from_reg(rd.into()),
+            rn: rd.into(),
+            size: VectorSize::Size8x8,
+        });
+    }
+
+    pub fn extend(&mut self, rn: Reg, rd: Reg, kind: ExtendKind) {
+        self.emit(Inst::Extend {
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            signed: kind.signed(),
+            from_bits: kind.from_bits(),
+            to_bits: kind.to_bits(),
+        })
+    }
+
     /// Bitwise AND (shifted register), setting flags.
     pub fn ands_rr(&mut self, rn: Reg, rm: Reg, size: OperandSize) {
         self.emit_alu_rrr(ALUOp::AndS, rm, rn, regs::zero(), size);
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index a59649f8e..aa519f6df 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -12,6 +12,7 @@ use crate::{
 use cranelift_codegen::{
     binemit::CodeOffset,
     ir::{RelSourceLoc, SourceLoc},
+    isa::aarch64::inst::VectorSize,
     settings, Final, MachBufferFinalized, MachLabel,
 };
 use regalloc2::RegClass;
@@ -435,8 +436,14 @@ impl Masm for MacroAssembler {
         self.asm.load_constant(0, reg);
     }
 
-    fn popcnt(&mut self, _context: &mut CodeGenContext, _size: OperandSize) {
-        todo!()
+    fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize) {
+        let src = context.pop_to_reg(self, None);
+        let tmp = regs::float_scratch();
+        self.asm.mov_to_fpu(src.into(), tmp, size);
+        self.asm.cnt(tmp);
+        self.asm.addv(tmp, tmp, VectorSize::Size8x8);
+        self.asm.mov_from_vec(tmp, src.into(), 0, OperandSize::S8);
+        context.stack.push(src.into());
     }
 
     fn signed_truncate(
@@ -491,12 +498,14 @@ impl Masm for MacroAssembler {
         todo!()
     }
 
-    fn demote(&mut self, _src: Reg, _dst: Reg) {
-        todo!()
+    fn demote(&mut self, src: Reg, dst: Reg) {
+        self.asm
+            .cvt_float_to_float(src.into(), dst.into(), OperandSize::S64, OperandSize::S32);
     }
 
-    fn promote(&mut self, _src: Reg, _dst: Reg) {
-        todo!()
+    fn promote(&mut self, src: Reg, dst: Reg) {
+        self.asm
+            .cvt_float_to_float(src.into(), dst.into(), OperandSize::S32, OperandSize::S64);
     }
 
     fn push(&mut self, reg: Reg, _size: OperandSize) -> StackSlot {
@@ -558,12 +567,12 @@ impl Masm for MacroAssembler {
         self.asm.clz(scratch, dst, size);
     }
 
-    fn wrap(&mut self, _src: Reg, _dst: Reg) {
-        todo!()
+    fn wrap(&mut self, src: Reg, dst: Reg) {
+        self.asm.mov_rr(src, dst, OperandSize::S32);
     }
 
-    fn extend(&mut self, _src: Reg, _dst: Reg, _kind: ExtendKind) {
-        todo!()
+    fn extend(&mut self, src: Reg, dst: Reg, kind: ExtendKind) {
+        self.asm.extend(src, dst, kind);
     }
 
     fn get_label(&mut self) -> MachLabel {
diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs
index 8540ad8da..98d75b3be 100644
--- a/winch/codegen/src/masm.rs
+++ b/winch/codegen/src/masm.rs
@@ -171,6 +171,35 @@ pub(crate) enum ExtendKind {
     I64Extend32S,
 }
 
+impl ExtendKind {
+    pub fn signed(&self) -> bool {
+        if let Self::I64ExtendI32U = self {
+            false
+        } else {
+            true
+        }
+    }
+
+    pub fn from_bits(&self) -> u8 {
+        match self {
+            Self::I64ExtendI32S | Self::I64ExtendI32U | Self::I64Extend32S => 32,
+            Self::I32Extend8S | Self::I64Extend8S => 8,
+            Self::I32Extend16S | Self::I64Extend16S => 16,
+        }
+    }
+
+    pub fn to_bits(&self) -> u8 {
+        match self {
+            Self::I64ExtendI32S
+            | Self::I64ExtendI32U
+            | Self::I64Extend8S
+            | Self::I64Extend16S
+            | Self::I64Extend32S => 64,
+            Self::I32Extend8S | Self::I32Extend16S => 32,
+        }
+    }
+}
+
 /// Operand size, in bits.
 #[derive(Copy, Debug, Clone, Eq, PartialEq)]
 pub(crate) enum OperandSize {


Test patch:
diff --git a/tests/disas/winch/aarch64/f32_demote_f64/const.wat b/tests/disas/winch/aarch64/f32_demote_f64/const.wat
new file mode 100644
index 000000000..2417d4ec9
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_demote_f64/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f64.const 1.0)
+        (f32.demote_f64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x3ff0000000000000
+;;       fmov    d0, x16
+;;       fcvt    s0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_demote_f64/locals.wat b/tests/disas/winch/aarch64/f32_demote_f64/locals.wat
new file mode 100644
index 000000000..8a6bde991
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_demote_f64/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (local f64)  
+
+        (local.get 0)
+        (f32.demote_f64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    d0, [x28]
+;;       fcvt    s0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_demote_f64/params.wat b/tests/disas/winch/aarch64/f32_demote_f64/params.wat
new file mode 100644
index 000000000..8e69ab7be
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_demote_f64/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f32)
+        (local.get 0)
+        (f32.demote_f64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    d0, [x28]
+;;       ldur    d0, [x28]
+;;       fcvt    s0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_promote_f32/const.wat b/tests/disas/winch/aarch64/f64_promote_f32/const.wat
new file mode 100644
index 000000000..7dedd414a
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_promote_f32/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f32.const 1.0)
+        (f64.promote_f32)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x3f800000
+;;       fmov    s0, w16
+;;       fcvt    d0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_promote_f32/locals.wat b/tests/disas/winch/aarch64/f64_promote_f32/locals.wat
new file mode 100644
index 000000000..e4c97cc74
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_promote_f32/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (local f32)  
+
+        (local.get 0)
+        (f64.promote_f32)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    s0, [x28, #4]
+;;       fcvt    d0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_promote_f32/params.wat b/tests/disas/winch/aarch64/f64_promote_f32/params.wat
new file mode 100644
index 000000000..e1b99a198
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_promote_f32/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f64)
+        (local.get 0)
+        (f64.promote_f32)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    s0, [x28, #4]
+;;       ldur    s0, [x28, #4]
+;;       fcvt    d0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_16_s/const.wat b/tests/disas/winch/aarch64/i32_extend_16_s/const.wat
new file mode 100644
index 000000000..0ca89a2a5
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_16_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       sxth    w0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_16_s/locals.wat b/tests/disas/winch/aarch64/i32_extend_16_s/locals.wat
new file mode 100644
index 000000000..35a1edad1
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_16_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local i32)
+
+        (local.get 0)
+        (i32.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    w0, [x28, #4]
+;;       sxth    w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_16_s/params.wat b/tests/disas/winch/aarch64/i32_extend_16_s/params.wat
new file mode 100644
index 000000000..5cea51c0a
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_16_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (result i32)
+        (local.get 0)
+        (i32.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       sxth    w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_8_s/const.wat b/tests/disas/winch/aarch64/i32_extend_8_s/const.wat
new file mode 100644
index 000000000..d0648cac5
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_8_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       sxtb    w0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_8_s/locals.wat b/tests/disas/winch/aarch64/i32_extend_8_s/locals.wat
new file mode 100644
index 000000000..628a93401
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_8_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local i32)
+
+        (local.get 0)
+        (i32.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    w0, [x28, #4]
+;;       sxtb    w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_extend_8_s/params.wat b/tests/disas/winch/aarch64/i32_extend_8_s/params.wat
new file mode 100644
index 000000000..d5557babd
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_extend_8_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (result i32)
+        (local.get 0)
+        (i32.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       sxtb    w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_popcnt/const.wat b/tests/disas/winch/aarch64/i32_popcnt/const.wat
new file mode 100644
index 000000000..c9ce796c3
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_popcnt/const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+      i32.const 3
+      i32.popcnt
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #3
+;;       mov     w0, w16
+;;       fmov    s31, w0
+;;       cnt     v31.8b, v31.8b
+;;       addv    b31, v31.8b
+;;       umov    w0, v31.b[0]
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_popcnt/reg.wat b/tests/disas/winch/aarch64/i32_popcnt/reg.wat
new file mode 100644
index 000000000..ef040ec5e
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_popcnt/reg.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (result i32)
+      local.get 0
+      i32.popcnt
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       fmov    s31, w0
+;;       cnt     v31.8b, v31.8b
+;;       addv    b31, v31.8b
+;;       umov    w0, v31.b[0]
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_wrap_i64/const.wat b/tests/disas/winch/aarch64/i32_wrap_i64/const.wat
new file mode 100644
index 000000000..4d4fca824
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_wrap_i64/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i64.const 1)
+        (i32.wrap_i64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       mov     w0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_wrap_i64/locals.wat b/tests/disas/winch/aarch64/i32_wrap_i64/locals.wat
new file mode 100644
index 000000000..eacaa9979
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_wrap_i64/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local i64)
+
+        (local.get 0)
+        (i32.wrap_i64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    x0, [x28]
+;;       mov     w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_wrap_i64/params.wat b/tests/disas/winch/aarch64/i32_wrap_i64/params.wat
new file mode 100644
index 000000000..de8a217ad
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_wrap_i64/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (result i32)
+        (local.get 0)
+        (i32.wrap_i64)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x2, [x28]
+;;       ldur    x0, [x28]
+;;       mov     w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_16_s/const.wat b/tests/disas/winch/aarch64/i64_extend_16_s/const.wat
new file mode 100644
index 000000000..f045ac6d6
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_16_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       sxth    x0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_16_s/locals.wat b/tests/disas/winch/aarch64/i64_extend_16_s/locals.wat
new file mode 100644
index 000000000..43d05bfaa
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_16_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    x0, [x28]
+;;       sxth    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_16_s/params.wat b/tests/disas/winch/aarch64/i64_extend_16_s/params.wat
new file mode 100644
index 000000000..343d6f99a
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_16_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend16_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x2, [x28]
+;;       ldur    x0, [x28]
+;;       sxth    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_32_s/const.wat b/tests/disas/winch/aarch64/i64_extend_32_s/const.wat
new file mode 100644
index 000000000..1462c5fbc
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_32_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_32_s/locals.wat b/tests/disas/winch/aarch64/i64_extend_32_s/locals.wat
new file mode 100644
index 000000000..4b0848de1
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_32_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    x0, [x28]
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_32_s/params.wat b/tests/disas/winch/aarch64/i64_extend_32_s/params.wat
new file mode 100644
index 000000000..dde5a053c
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_32_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x2, [x28]
+;;       ldur    x0, [x28]
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_8_s/const.wat b/tests/disas/winch/aarch64/i64_extend_8_s/const.wat
new file mode 100644
index 000000000..b7771a8ba
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_8_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       sxtb    x0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_8_s/locals.wat b/tests/disas/winch/aarch64/i64_extend_8_s/locals.wat
new file mode 100644
index 000000000..caa7ffd46
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_8_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    x0, [x28]
+;;       sxtb    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_8_s/params.wat b/tests/disas/winch/aarch64/i64_extend_8_s/params.wat
new file mode 100644
index 000000000..6e1cf4555
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_8_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend8_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x2, [x28]
+;;       ldur    x0, [x28]
+;;       sxtb    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_s/const.wat b/tests/disas/winch/aarch64/i64_extend_i32_s/const.wat
new file mode 100644
index 000000000..8dff976c7
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_s/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i32.const 1)
+        (i64.extend_i32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_s/locals.wat b/tests/disas/winch/aarch64/i64_extend_i32_s/locals.wat
new file mode 100644
index 000000000..ec2c84737
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_s/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local i32)
+
+        (local.get 0)
+        (i64.extend_i32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    w0, [x28, #4]
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_s/params.wat b/tests/disas/winch/aarch64/i64_extend_i32_s/params.wat
new file mode 100644
index 000000000..cabe373d6
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_s/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (result i64)
+        (local.get 0)
+        (i64.extend_i32_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       sxtw    x0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_u/const.wat b/tests/disas/winch/aarch64/i64_extend_i32_u/const.wat
new file mode 100644
index 000000000..5772d7e5c
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_u/const.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i32.const 1)
+        (i64.extend_i32_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       mov     w0, w0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_u/locals.wat b/tests/disas/winch/aarch64/i64_extend_i32_u/locals.wat
new file mode 100644
index 000000000..3be1cdf16
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_u/locals.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local i32)
+
+        (local.get 0)
+        (i64.extend_i32_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       ldur    w0, [x28, #4]
+;;       mov     w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_extend_i32_u/params.wat b/tests/disas/winch/aarch64/i64_extend_i32_u/params.wat
new file mode 100644
index 000000000..530a50c37
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_extend_i32_u/params.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (result i64)
+        (local.get 0)
+        (i64.extend_i32_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       mov     w0, w0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_popcnt/const.wat b/tests/disas/winch/aarch64/i64_popcnt/const.wat
new file mode 100644
index 000000000..21ea6f006
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_popcnt/const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+      i64.const 3
+      i64.popcnt
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #3
+;;       mov     x0, x16
+;;       fmov    d31, x0
+;;       cnt     v31.8b, v31.8b
+;;       addv    b31, v31.8b
+;;       umov    w0, v31.b[0]
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_popcnt/reg.wat b/tests/disas/winch/aarch64/i64_popcnt/reg.wat
new file mode 100644
index 000000000..f2115db0a
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_popcnt/reg.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (result i64)
+      local.get 0
+      i64.popcnt
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x2, [x28]
+;;       ldur    x0, [x28]
+;;       fmov    d31, x0
+;;       cnt     v31.8b, v31.8b
+;;       addv    b31, v31.8b
+;;       umov    w0, v31.b[0]
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret