Repository: bytecodealliance/wasmtime
Pull request #7683 (2023-12-13 20:44:57)

Winch: Integer conversion instructions
<!--
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
-->
Adds support for the following integer conversion instructions to Winch:
- `i32.wrap_i64`
- `i64.extend_i32_s`
- `i64.extend_i32_u`
- `i32.extend8_s`
- `i32.extend16_s`
- `i64.extend8_s`
- `i64.extend16_s`
- `i64.extend32_s`


Patch:
diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs
index 648923768..e166fa069 100644
--- a/fuzz/fuzz_targets/differential.rs
+++ b/fuzz/fuzz_targets/differential.rs
@@ -369,6 +369,14 @@ fn winch_supports_module(module: &[u8]) -> bool {
                         | I64Ctz { .. }
                         | I32Popcnt { .. }
                         | I64Popcnt { .. }
+                        | I32WrapI64 { .. }
+                        | I64ExtendI32S { .. }
+                        | I64ExtendI32U { .. }
+                        | I32Extend8S { .. }
+                        | I32Extend16S { .. }
+                        | I64Extend8S { .. }
+                        | I64Extend16S { .. }
+                        | I64Extend32S { .. }
                         | LocalGet { .. }
                         | LocalSet { .. }
                         | LocalTee { .. }
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index 06dfef326..74ce2d96e 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -4,8 +4,9 @@ use crate::{
     codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData},
     isa::reg::Reg,
     masm::{
-        CalleeKind, DivKind, FloatCmpKind, Imm as I, IntCmpKind, MacroAssembler as Masm,
-        OperandSize, RegImm, RemKind, RoundingMode, SPOffset, ShiftKind, StackSlot, TrapCode,
+        CalleeKind, DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind,
+        MacroAssembler as Masm, OperandSize, RegImm, RemKind, RoundingMode, SPOffset, ShiftKind,
+        StackSlot, TrapCode,
     },
 };
 use cranelift_codegen::{settings, Final, MachBufferFinalized, MachLabel};
@@ -382,6 +383,14 @@ impl Masm for MacroAssembler {
         todo!()
     }
 
+    fn wrap(&mut self, _src: Reg, _dst: Reg) {
+        todo!()
+    }
+
+    fn extend(&mut self, _src: Reg, _dst: Reg, _kind: ExtendKind) {
+        todo!()
+    }
+
     fn get_label(&mut self) -> MachLabel {
         self.asm.get_label()
     }
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index f4ce1be6d..9fa1d1cb5 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -2,7 +2,7 @@
 
 use crate::{
     isa::reg::Reg,
-    masm::{DivKind, IntCmpKind, OperandSize, RemKind, RoundingMode, ShiftKind},
+    masm::{DivKind, ExtendKind, IntCmpKind, OperandSize, RemKind, RoundingMode, ShiftKind},
 };
 use cranelift_codegen::{
     entity::EntityRef,
@@ -132,6 +132,20 @@ impl From<ShiftKind> for CraneliftShiftKind {
     }
 }
 
+impl From<ExtendKind> for ExtMode {
+    fn from(value: ExtendKind) -> Self {
+        match value {
+            ExtendKind::I64ExtendI32S | ExtendKind::I64ExtendI32U | ExtendKind::I64Extend32S => {
+                ExtMode::LQ
+            }
+            ExtendKind::I32Extend8S => ExtMode::BL,
+            ExtendKind::I32Extend16S => ExtMode::WL,
+            ExtendKind::I64Extend8S => ExtMode::BQ,
+            ExtendKind::I64Extend16S => ExtMode::WQ,
+        }
+    }
+}
+
 /// Low level assembler implementation for x64.
 pub(crate) struct Assembler {
     /// The machine instruction buffer.
@@ -301,6 +315,24 @@ impl Assembler {
         }
     }
 
+    /// Register-to-register move with zero extension.
+    pub fn movzx_rr(&mut self, src: Reg, dst: Reg, kind: ExtendKind) {
+        self.emit(Inst::MovzxRmR {
+            ext_mode: kind.into(),
+            src: src.into(),
+            dst: dst.into(),
+        })
+    }
+
+    /// Register-to-register move with sign extension.
+    pub fn movsx_rr(&mut self, src: Reg, dst: Reg, kind: ExtendKind) {
+        self.emit(Inst::MovsxRmR {
+            ext_mode: kind.into(),
+            src: src.into(),
+            dst: dst.into(),
+        });
+    }
+
     /// Integer register conditional move.
     pub fn cmov(&mut self, src: Reg, dst: Reg, cc: IntCmpKind, size: OperandSize) {
         self.emit(Inst::Cmove {
diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs
index 15c108aae..c5ea1ac88 100644
--- a/winch/codegen/src/isa/x64/masm.rs
+++ b/winch/codegen/src/isa/x64/masm.rs
@@ -6,8 +6,8 @@ use super::{
 };
 
 use crate::masm::{
-    DivKind, FloatCmpKind, Imm as I, IntCmpKind, MacroAssembler as Masm, OperandSize, RegImm,
-    RemKind, RoundingMode, ShiftKind, TrapCode,
+    DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind, MacroAssembler as Masm, OperandSize,
+    RegImm, RemKind, RoundingMode, ShiftKind, TrapCode,
 };
 use crate::{
     abi::ABI,
@@ -904,6 +904,18 @@ impl Masm for MacroAssembler {
         }
     }
 
+    fn wrap(&mut self, src: Reg, dst: Reg) {
+        self.asm.mov_rr(src.into(), dst.into(), OperandSize::S32);
+    }
+
+    fn extend(&mut self, src: Reg, dst: Reg, kind: ExtendKind) {
+        if let ExtendKind::I64ExtendI32U = kind {
+            self.asm.movzx_rr(src, dst, kind);
+        } else {
+            self.asm.movsx_rr(src, dst, kind);
+        }
+    }
+
     fn unreachable(&mut self) {
         self.asm.trap(TrapCode::UnreachableCodeReached)
     }
diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs
index b4613635a..42ea92f97 100644
--- a/winch/codegen/src/masm.rs
+++ b/winch/codegen/src/masm.rs
@@ -114,6 +114,26 @@ pub(crate) enum ShiftKind {
     Rotr,
 }
 
+/// Kinds of extends in WebAssembly. The [`masm`] implementation for each ISA
+/// is responsible for emitting the correct sequence of instructions when
+/// lowering to machine code.
+pub(crate) enum ExtendKind {
+    /// Sign extends i32 to i64.
+    I64ExtendI32S,
+    /// Zero extends i32 to i64.
+    I64ExtendI32U,
+    // Sign extends the 8 least significant bits to 32 bits.
+    I32Extend8S,
+    // Sign extends the 16 least significant bits to 32 bits.
+    I32Extend16S,
+    /// Sign extends the 8 least significant bits to 64 bits.
+    I64Extend8S,
+    /// Sign extends the 16 least significant bits to 64 bits.
+    I64Extend16S,
+    /// Sign extends the 32 least significant bits to 64 bits.
+    I64Extend32S,
+}
+
 /// Operand size, in bits.
 #[derive(Copy, Debug, Clone, Eq, PartialEq)]
 pub(crate) enum OperandSize {
@@ -580,6 +600,12 @@ pub(crate) trait MacroAssembler {
     /// this will emit multiple instructions if the `has_popcnt` flag is false.
     fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize);
 
+    /// Converts an i64 to an i32 by discarding the high 32 bits.
+    fn wrap(&mut self, src: Reg, dst: Reg);
+
+    /// Extends an integer of a given size to a larger size.
+    fn extend(&mut self, src: Reg, dst: Reg, kind: ExtendKind);
+
     /// Zero a given memory range.
     ///
     /// The default implementation divides the given memory range
diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs
index ca74d0ffc..a82ecce9a 100644
--- a/winch/codegen/src/visitor.rs
+++ b/winch/codegen/src/visitor.rs
@@ -7,8 +7,8 @@
 use crate::abi::ABI;
 use crate::codegen::{control_index, Callee, CodeGen, ControlStackFrame, FnCall};
 use crate::masm::{
-    DivKind, FloatCmpKind, IntCmpKind, MacroAssembler, OperandSize, RegImm, RemKind, RoundingMode,
-    ShiftKind,
+    DivKind, ExtendKind, FloatCmpKind, IntCmpKind, MacroAssembler, OperandSize, RegImm, RemKind,
+    RoundingMode, ShiftKind,
 };
 use crate::stack::{TypedReg, Val};
 use cranelift_codegen::ir::TrapCode;
@@ -143,6 +143,14 @@ macro_rules! def_unsupported {
     (emit I64Ctz $($rest:tt)*) => {};
     (emit I32Popcnt $($rest:tt)*) => {};
     (emit I64Popcnt $($rest:tt)*) => {};
+    (emit I32WrapI64 $($rest:tt)*) => {};
+    (emit I64ExtendI32S $($rest:tt)*) => {};
+    (emit I64ExtendI32U $($rest:tt)*) => {};
+    (emit I32Extend8S $($rest:tt)*) => {};
+    (emit I32Extend16S $($rest:tt)*) => {};
+    (emit I64Extend8S $($rest:tt)*) => {};
+    (emit I64Extend16S $($rest:tt)*) => {};
+    (emit I64Extend32S $($rest:tt)*) => {};
     (emit LocalGet $($rest:tt)*) => {};
     (emit LocalSet $($rest:tt)*) => {};
     (emit Call $($rest:tt)*) => {};
@@ -935,6 +943,78 @@ where
         self.masm.popcnt(&mut self.context, S64);
     }
 
+    fn visit_i32_wrap_i64(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S64, &mut |masm, reg, _size| {
+            masm.wrap(reg, reg);
+            TypedReg::i32(reg)
+        });
+    }
+
+    fn visit_i64_extend_i32_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S32, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I64ExtendI32S);
+            TypedReg::i64(reg)
+        });
+    }
+
+    fn visit_i64_extend_i32_u(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S32, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I64ExtendI32U);
+            TypedReg::i64(reg)
+        });
+    }
+
+    fn visit_i32_extend8_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S32, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I32Extend8S);
+            TypedReg::i32(reg)
+        });
+    }
+
+    fn visit_i32_extend16_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S32, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I32Extend16S);
+            TypedReg::i32(reg)
+        });
+    }
+
+    fn visit_i64_extend8_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S64, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I64Extend8S);
+            TypedReg::i64(reg)
+        });
+    }
+
+    fn visit_i64_extend16_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S64, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I64Extend16S);
+            TypedReg::i64(reg)
+        });
+    }
+
+    fn visit_i64_extend32_s(&mut self) {
+        use OperandSize::*;
+
+        self.context.unop(self.masm, S64, &mut |masm, reg, _size| {
+            masm.extend(reg, reg, ExtendKind::I64Extend32S);
+            TypedReg::i64(reg)
+        });
+    }
+
     fn visit_local_get(&mut self, index: u32) {
         use WasmType::*;
         let context = &mut self.context;


Test patch:
diff --git a/tests/misc_testsuite/winch/conversions.wast b/tests/misc_testsuite/winch/conversions.wast
new file mode 100644
index 000000000..6f84e57d2
--- /dev/null
+++ b/tests/misc_testsuite/winch/conversions.wast
@@ -0,0 +1,38 @@
+(module
+  (func (export "i64.extend_i32_s") (param $x i32) (result i64) (i64.extend_i32_s (local.get $x)))
+  (func (export "i64.extend_i32_u") (param $x i32) (result i64) (i64.extend_i32_u (local.get $x)))
+  (func (export "i32.wrap_i64") (param $x i64) (result i32) (i32.wrap_i64 (local.get $x)))
+)
+
+(assert_return (invoke "i64.extend_i32_s" (i32.const 0)) (i64.const 0))
+(assert_return (invoke "i64.extend_i32_s" (i32.const 10000)) (i64.const 10000))
+(assert_return (invoke "i64.extend_i32_s" (i32.const -10000)) (i64.const -10000))
+(assert_return (invoke "i64.extend_i32_s" (i32.const -1)) (i64.const -1))
+(assert_return (invoke "i64.extend_i32_s" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff))
+(assert_return (invoke "i64.extend_i32_s" (i32.const 0x80000000)) (i64.const 0xffffffff80000000))
+
+(assert_return (invoke "i64.extend_i32_u" (i32.const 0)) (i64.const 0))
+(assert_return (invoke "i64.extend_i32_u" (i32.const 10000)) (i64.const 10000))
+(assert_return (invoke "i64.extend_i32_u" (i32.const -10000)) (i64.const 0x00000000ffffd8f0))
+(assert_return (invoke "i64.extend_i32_u" (i32.const -1)) (i64.const 0xffffffff))
+(assert_return (invoke "i64.extend_i32_u" (i32.const 0x7fffffff)) (i64.const 0x000000007fffffff))
+(assert_return (invoke "i64.extend_i32_u" (i32.const 0x80000000)) (i64.const 0x0000000080000000))
+
+(assert_return (invoke "i32.wrap_i64" (i64.const -1)) (i32.const -1))
+(assert_return (invoke "i32.wrap_i64" (i64.const -100000)) (i32.const -100000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x80000000)) (i32.const 0x80000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff7fffffff)) (i32.const 0x7fffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000000)) (i32.const 0x00000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xfffffffeffffffff)) (i32.const 0xffffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0xffffffff00000001)) (i32.const 0x00000001))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0)) (i32.const 0))
+(assert_return (invoke "i32.wrap_i64" (i64.const 1311768467463790320)) (i32.const 0x9abcdef0))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x00000000ffffffff)) (i32.const 0xffffffff))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000000)) (i32.const 0x00000000))
+(assert_return (invoke "i32.wrap_i64" (i64.const 0x0000000100000001)) (i32.const 0x00000001))
+
+;; Type check
+
+(assert_invalid (module (func (result i32) (i32.wrap_i64 (f32.const 0)))) "type mismatch")
+(assert_invalid (module (func (result i64) (i64.extend_i32_s (f32.const 0)))) "type mismatch")
+(assert_invalid (module (func (result i64) (i64.extend_i32_u (f32.const 0)))) "type mismatch")
diff --git a/tests/misc_testsuite/winch/i32.wast b/tests/misc_testsuite/winch/i32.wast
index 8a06d0914..e4084be3f 100644
--- a/tests/misc_testsuite/winch/i32.wast
+++ b/tests/misc_testsuite/winch/i32.wast
@@ -19,6 +19,8 @@
   (func (export "clz") (param $x i32) (result i32) (i32.clz (local.get $x)))
   (func (export "ctz") (param $x i32) (result i32) (i32.ctz (local.get $x)))
   (func (export "popcnt") (param $x i32) (result i32) (i32.popcnt (local.get $x)))
+  (func (export "extend8_s") (param $x i32) (result i32) (i32.extend8_s (local.get $x)))
+  (func (export "extend16_s") (param $x i32) (result i32) (i32.extend16_s (local.get $x)))
   (func (export "eqz") (param $x i32) (result i32) (i32.eqz (local.get $x)))
   (func (export "eq") (param $x i32) (param $y i32) (result i32) (i32.eq (local.get $x) (local.get $y)))
   (func (export "ne") (param $x i32) (param $y i32) (result i32) (i32.ne (local.get $x) (local.get $y)))
@@ -265,6 +267,22 @@
 (assert_return (invoke "popcnt" (i32.const 0x55555555)) (i32.const 16))
 (assert_return (invoke "popcnt" (i32.const 0xDEADBEEF)) (i32.const 24))
 
+(assert_return (invoke "extend8_s" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "extend8_s" (i32.const 0x7f)) (i32.const 127))
+(assert_return (invoke "extend8_s" (i32.const 0x80)) (i32.const -128))
+(assert_return (invoke "extend8_s" (i32.const 0xff)) (i32.const -1))
+(assert_return (invoke "extend8_s" (i32.const 0x012345_00)) (i32.const 0))
+(assert_return (invoke "extend8_s" (i32.const 0xfedcba_80)) (i32.const -0x80))
+(assert_return (invoke "extend8_s" (i32.const -1)) (i32.const -1))
+
+(assert_return (invoke "extend16_s" (i32.const 0)) (i32.const 0))
+(assert_return (invoke "extend16_s" (i32.const 0x7fff)) (i32.const 32767))
+(assert_return (invoke "extend16_s" (i32.const 0x8000)) (i32.const -32768))
+(assert_return (invoke "extend16_s" (i32.const 0xffff)) (i32.const -1))
+(assert_return (invoke "extend16_s" (i32.const 0x0123_0000)) (i32.const 0))
+(assert_return (invoke "extend16_s" (i32.const 0xfedc_8000)) (i32.const -0x8000))
+(assert_return (invoke "extend16_s" (i32.const -1)) (i32.const -1))
+
 (assert_return (invoke "eqz" (i32.const 0)) (i32.const 1))
 (assert_return (invoke "eqz" (i32.const 1)) (i32.const 0))
 (assert_return (invoke "eqz" (i32.const 0x80000000)) (i32.const 0))
diff --git a/tests/misc_testsuite/winch/i64.wast b/tests/misc_testsuite/winch/i64.wast
index 1783c6178..73aa4c19c 100644
--- a/tests/misc_testsuite/winch/i64.wast
+++ b/tests/misc_testsuite/winch/i64.wast
@@ -19,6 +19,9 @@
   (func (export "clz") (param $x i64) (result i64) (i64.clz (local.get $x)))
   (func (export "ctz") (param $x i64) (result i64) (i64.ctz (local.get $x)))
   (func (export "popcnt") (param $x i64) (result i64) (i64.popcnt (local.get $x)))
+  (func (export "extend8_s") (param $x i64) (result i64) (i64.extend8_s (local.get $x)))
+  (func (export "extend16_s") (param $x i64) (result i64) (i64.extend16_s (local.get $x)))
+  (func (export "extend32_s") (param $x i64) (result i64) (i64.extend32_s (local.get $x)))
   (func (export "eqz") (param $x i64) (result i32) (i64.eqz (local.get $x)))
   (func (export "eq") (param $x i64) (param $y i64) (result i32) (i64.eq (local.get $x) (local.get $y)))
   (func (export "ne") (param $x i64) (param $y i64) (result i32) (i64.ne (local.get $x) (local.get $y)))
@@ -265,6 +268,33 @@
 (assert_return (invoke "popcnt" (i64.const 0x99999999AAAAAAAA)) (i64.const 32))
 (assert_return (invoke "popcnt" (i64.const 0xDEADBEEFDEADBEEF)) (i64.const 48))
 
+(assert_return (invoke "extend8_s" (i64.const 0)) (i64.const 0))
+(assert_return (invoke "extend8_s" (i64.const 0x7f)) (i64.const 127))
+(assert_return (invoke "extend8_s" (i64.const 0x80)) (i64.const -128))
+(assert_return (invoke "extend8_s" (i64.const 0xff)) (i64.const -1))
+(assert_return (invoke "extend8_s" (i64.const 0x01234567_89abcd_00)) (i64.const 0))
+(assert_return (invoke "extend8_s" (i64.const 0xfedcba98_765432_80)) (i64.const -0x80))
+(assert_return (invoke "extend8_s" (i64.const -1)) (i64.const -1))
+
+(assert_return (invoke "extend16_s" (i64.const 0)) (i64.const 0))
+(assert_return (invoke "extend16_s" (i64.const 0x7fff)) (i64.const 32767))
+(assert_return (invoke "extend16_s" (i64.const 0x8000)) (i64.const -32768))
+(assert_return (invoke "extend16_s" (i64.const 0xffff)) (i64.const -1))
+(assert_return (invoke "extend16_s" (i64.const 0x12345678_9abc_0000)) (i64.const 0))
+(assert_return (invoke "extend16_s" (i64.const 0xfedcba98_7654_8000)) (i64.const -0x8000))
+(assert_return (invoke "extend16_s" (i64.const -1)) (i64.const -1))
+
+(assert_return (invoke "extend32_s" (i64.const 0)) (i64.const 0))
+(assert_return (invoke "extend32_s" (i64.const 0x7fff)) (i64.const 32767))
+(assert_return (invoke "extend32_s" (i64.const 0x8000)) (i64.const 32768))
+(assert_return (invoke "extend32_s" (i64.const 0xffff)) (i64.const 65535))
+(assert_return (invoke "extend32_s" (i64.const 0x7fffffff)) (i64.const 0x7fffffff))
+(assert_return (invoke "extend32_s" (i64.const 0x80000000)) (i64.const -0x80000000))
+(assert_return (invoke "extend32_s" (i64.const 0xffffffff)) (i64.const -1))
+(assert_return (invoke "extend32_s" (i64.const 0x01234567_00000000)) (i64.const 0))
+(assert_return (invoke "extend32_s" (i64.const 0xfedcba98_80000000)) (i64.const -0x80000000))
+(assert_return (invoke "extend32_s" (i64.const -1)) (i64.const -1))
+
 (assert_return (invoke "eqz" (i64.const 0)) (i32.const 1))
 (assert_return (invoke "eqz" (i64.const 1)) (i32.const 0))
 (assert_return (invoke "eqz" (i64.const 0x8000000000000000)) (i32.const 0))
diff --git a/winch/filetests/filetests/x64/i32_extend_16_s/const.wat b/winch/filetests/filetests/x64/i32_extend_16_s/const.wat
new file mode 100644
index 000000000..fa454fd93
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_16_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 0fbfc0               	movsx	eax, ax
+;;   14:	 4883c408             	add	rsp, 8
+;;   18:	 5d                   	pop	rbp
+;;   19:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_extend_16_s/locals.wat b/winch/filetests/filetests/x64/i32_extend_16_s/locals.wat
new file mode 100644
index 000000000..d68af5140
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_16_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (local i32)
+
+        (local.get 0)
+        (i32.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   19:	 0fbfc0               	movsx	eax, ax
+;;   1c:	 4883c410             	add	rsp, 0x10
+;;   20:	 5d                   	pop	rbp
+;;   21:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_extend_16_s/params.wat b/winch/filetests/filetests/x64/i32_extend_16_s/params.wat
new file mode 100644
index 000000000..e38a190fb
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_16_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i32) (result i32)
+        (local.get 0)
+        (i32.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 897c240c             	mov	dword ptr [rsp + 0xc], edi
+;;    c:	 4c893424             	mov	qword ptr [rsp], r14
+;;   10:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   14:	 0fbfc0               	movsx	eax, ax
+;;   17:	 4883c410             	add	rsp, 0x10
+;;   1b:	 5d                   	pop	rbp
+;;   1c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_extend_8_s/const.wat b/winch/filetests/filetests/x64/i32_extend_8_s/const.wat
new file mode 100644
index 000000000..e4de257fa
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_8_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 0fbec0               	movsx	eax, al
+;;   14:	 4883c408             	add	rsp, 8
+;;   18:	 5d                   	pop	rbp
+;;   19:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_extend_8_s/locals.wat b/winch/filetests/filetests/x64/i32_extend_8_s/locals.wat
new file mode 100644
index 000000000..d6149375d
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_8_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (local i32)
+
+        (local.get 0)
+        (i32.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   19:	 0fbec0               	movsx	eax, al
+;;   1c:	 4883c410             	add	rsp, 0x10
+;;   20:	 5d                   	pop	rbp
+;;   21:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_extend_8_s/params.wat b/winch/filetests/filetests/x64/i32_extend_8_s/params.wat
new file mode 100644
index 000000000..33ffb463f
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_extend_8_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i32) (result i32)
+        (local.get 0)
+        (i32.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 897c240c             	mov	dword ptr [rsp + 0xc], edi
+;;    c:	 4c893424             	mov	qword ptr [rsp], r14
+;;   10:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   14:	 0fbec0               	movsx	eax, al
+;;   17:	 4883c410             	add	rsp, 0x10
+;;   1b:	 5d                   	pop	rbp
+;;   1c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_wrap_i64/const.wat b/winch/filetests/filetests/x64/i32_wrap_i64/const.wat
new file mode 100644
index 000000000..70e76e041
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_wrap_i64/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (i64.const 1)
+        (i32.wrap_i64)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c001000000       	mov	rax, 1
+;;   13:	 89c0                 	mov	eax, eax
+;;   15:	 4883c408             	add	rsp, 8
+;;   19:	 5d                   	pop	rbp
+;;   1a:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_wrap_i64/locals.wat b/winch/filetests/filetests/x64/i32_wrap_i64/locals.wat
new file mode 100644
index 000000000..264365c16
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_wrap_i64/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        (local i64)
+
+        (local.get 0)
+        (i32.wrap_i64)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   1a:	 89c0                 	mov	eax, eax
+;;   1c:	 4883c410             	add	rsp, 0x10
+;;   20:	 5d                   	pop	rbp
+;;   21:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_wrap_i64/params.wat b/winch/filetests/filetests/x64/i32_wrap_i64/params.wat
new file mode 100644
index 000000000..5707ffb24
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_wrap_i64/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i64) (result i32)
+        (local.get 0)
+        (i32.wrap_i64)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48897c2408           	mov	qword ptr [rsp + 8], rdi
+;;    d:	 4c893424             	mov	qword ptr [rsp], r14
+;;   11:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   16:	 89c0                 	mov	eax, eax
+;;   18:	 4883c410             	add	rsp, 0x10
+;;   1c:	 5d                   	pop	rbp
+;;   1d:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i32_wrap_i64/spilled.wat b/winch/filetests/filetests/x64/i32_wrap_i64/spilled.wat
new file mode 100644
index 000000000..bf3bc1004
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_wrap_i64/spilled.wat
@@ -0,0 +1,23 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i32)
+        i64.const 1
+        i32.wrap_i64
+        block
+        end
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c001000000       	mov	rax, 1
+;;   13:	 89c0                 	mov	eax, eax
+;;   15:	 4883ec04             	sub	rsp, 4
+;;   19:	 890424               	mov	dword ptr [rsp], eax
+;;   1c:	 8b0424               	mov	eax, dword ptr [rsp]
+;;   1f:	 4883c404             	add	rsp, 4
+;;   23:	 4883c408             	add	rsp, 8
+;;   27:	 5d                   	pop	rbp
+;;   28:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_16_s/const.wat b/winch/filetests/filetests/x64/i64_extend_16_s/const.wat
new file mode 100644
index 000000000..5ab076bd3
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_16_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c001000000       	mov	rax, 1
+;;   13:	 480fbfc0             	movsx	rax, ax
+;;   17:	 4883c408             	add	rsp, 8
+;;   1b:	 5d                   	pop	rbp
+;;   1c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_16_s/locals.wat b/winch/filetests/filetests/x64/i64_extend_16_s/locals.wat
new file mode 100644
index 000000000..a39a9990e
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_16_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   1a:	 480fbfc0             	movsx	rax, ax
+;;   1e:	 4883c410             	add	rsp, 0x10
+;;   22:	 5d                   	pop	rbp
+;;   23:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_16_s/params.wat b/winch/filetests/filetests/x64/i64_extend_16_s/params.wat
new file mode 100644
index 000000000..98a61767a
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_16_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend16_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48897c2408           	mov	qword ptr [rsp + 8], rdi
+;;    d:	 4c893424             	mov	qword ptr [rsp], r14
+;;   11:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   16:	 480fbfc0             	movsx	rax, ax
+;;   1a:	 4883c410             	add	rsp, 0x10
+;;   1e:	 5d                   	pop	rbp
+;;   1f:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_32_s/const.wat b/winch/filetests/filetests/x64/i64_extend_32_s/const.wat
new file mode 100644
index 000000000..666ebd52c
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_32_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c001000000       	mov	rax, 1
+;;   13:	 4863c0               	movsxd	rax, eax
+;;   16:	 4883c408             	add	rsp, 8
+;;   1a:	 5d                   	pop	rbp
+;;   1b:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_32_s/locals.wat b/winch/filetests/filetests/x64/i64_extend_32_s/locals.wat
new file mode 100644
index 000000000..d6cfac6d1
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_32_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   1a:	 4863c0               	movsxd	rax, eax
+;;   1d:	 4883c410             	add	rsp, 0x10
+;;   21:	 5d                   	pop	rbp
+;;   22:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_32_s/params.wat b/winch/filetests/filetests/x64/i64_extend_32_s/params.wat
new file mode 100644
index 000000000..0cf607420
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_32_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48897c2408           	mov	qword ptr [rsp + 8], rdi
+;;    d:	 4c893424             	mov	qword ptr [rsp], r14
+;;   11:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   16:	 4863c0               	movsxd	rax, eax
+;;   19:	 4883c410             	add	rsp, 0x10
+;;   1d:	 5d                   	pop	rbp
+;;   1e:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_8_s/const.wat b/winch/filetests/filetests/x64/i64_extend_8_s/const.wat
new file mode 100644
index 000000000..a77763091
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_8_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c001000000       	mov	rax, 1
+;;   13:	 480fbec0             	movsx	rax, al
+;;   17:	 4883c408             	add	rsp, 8
+;;   1b:	 5d                   	pop	rbp
+;;   1c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_8_s/locals.wat b/winch/filetests/filetests/x64/i64_extend_8_s/locals.wat
new file mode 100644
index 000000000..62b51071b
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_8_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (local i64)
+
+        (local.get 0)
+        (i64.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   1a:	 480fbec0             	movsx	rax, al
+;;   1e:	 4883c410             	add	rsp, 0x10
+;;   22:	 5d                   	pop	rbp
+;;   23:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_8_s/params.wat b/winch/filetests/filetests/x64/i64_extend_8_s/params.wat
new file mode 100644
index 000000000..4ad6018bd
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_8_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i64) (result i64)
+        (local.get 0)
+        (i64.extend8_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48897c2408           	mov	qword ptr [rsp + 8], rdi
+;;    d:	 4c893424             	mov	qword ptr [rsp], r14
+;;   11:	 488b442408           	mov	rax, qword ptr [rsp + 8]
+;;   16:	 480fbec0             	movsx	rax, al
+;;   1a:	 4883c410             	add	rsp, 0x10
+;;   1e:	 5d                   	pop	rbp
+;;   1f:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_s/const.wat b/winch/filetests/filetests/x64/i64_extend_i32_s/const.wat
new file mode 100644
index 000000000..363d69902
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_s/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (i32.const 1)
+        (i64.extend_i32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 4863c0               	movsxd	rax, eax
+;;   14:	 4883c408             	add	rsp, 8
+;;   18:	 5d                   	pop	rbp
+;;   19:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_s/locals.wat b/winch/filetests/filetests/x64/i64_extend_i32_s/locals.wat
new file mode 100644
index 000000000..ab2629b14
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_s/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (local i32)
+
+        (local.get 0)
+        (i64.extend_i32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   19:	 4863c0               	movsxd	rax, eax
+;;   1c:	 4883c410             	add	rsp, 0x10
+;;   20:	 5d                   	pop	rbp
+;;   21:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_s/params.wat b/winch/filetests/filetests/x64/i64_extend_i32_s/params.wat
new file mode 100644
index 000000000..2d4ad7213
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_s/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i32) (result i64)
+        (local.get 0)
+        (i64.extend_i32_s)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 897c240c             	mov	dword ptr [rsp + 0xc], edi
+;;    c:	 4c893424             	mov	qword ptr [rsp], r14
+;;   10:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   14:	 4863c0               	movsxd	rax, eax
+;;   17:	 4883c410             	add	rsp, 0x10
+;;   1b:	 5d                   	pop	rbp
+;;   1c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_s/spilled.wat b/winch/filetests/filetests/x64/i64_extend_i32_s/spilled.wat
new file mode 100644
index 000000000..20ba1a2c4
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_s/spilled.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        i32.const 1
+        i64.extend_i32_s
+        block
+        end
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 4863c0               	movsxd	rax, eax
+;;   14:	 50                   	push	rax
+;;   15:	 58                   	pop	rax
+;;   16:	 4883c408             	add	rsp, 8
+;;   1a:	 5d                   	pop	rbp
+;;   1b:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_u/const.wat b/winch/filetests/filetests/x64/i64_extend_i32_u/const.wat
new file mode 100644
index 000000000..6f1c21f93
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_u/const.wat
@@ -0,0 +1,17 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (i32.const 1)
+        (i64.extend_i32_u)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 8bc0                 	mov	eax, eax
+;;   13:	 4883c408             	add	rsp, 8
+;;   17:	 5d                   	pop	rbp
+;;   18:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_u/locals.wat b/winch/filetests/filetests/x64/i64_extend_i32_u/locals.wat
new file mode 100644
index 000000000..364d40325
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_u/locals.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        (local i32)
+
+        (local.get 0)
+        (i64.extend_i32_u)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 48c744240800000000   	
+;; 				mov	qword ptr [rsp + 8], 0
+;;   11:	 4c893424             	mov	qword ptr [rsp], r14
+;;   15:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   19:	 8bc0                 	mov	eax, eax
+;;   1b:	 4883c410             	add	rsp, 0x10
+;;   1f:	 5d                   	pop	rbp
+;;   20:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_u/params.wat b/winch/filetests/filetests/x64/i64_extend_i32_u/params.wat
new file mode 100644
index 000000000..3dbdc8598
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_u/params.wat
@@ -0,0 +1,18 @@
+;;! target = "x86_64"
+
+(module
+    (func (param i32) (result i64)
+        (local.get 0)
+        (i64.extend_i32_u)
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec10             	sub	rsp, 0x10
+;;    8:	 897c240c             	mov	dword ptr [rsp + 0xc], edi
+;;    c:	 4c893424             	mov	qword ptr [rsp], r14
+;;   10:	 8b44240c             	mov	eax, dword ptr [rsp + 0xc]
+;;   14:	 8bc0                 	mov	eax, eax
+;;   16:	 4883c410             	add	rsp, 0x10
+;;   1a:	 5d                   	pop	rbp
+;;   1b:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_extend_i32_u/spilled.wat b/winch/filetests/filetests/x64/i64_extend_i32_u/spilled.wat
new file mode 100644
index 000000000..4722e1b82
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_extend_i32_u/spilled.wat
@@ -0,0 +1,21 @@
+;;! target = "x86_64"
+
+(module
+    (func (result i64)
+        i32.const 1
+        i64.extend_i32_u
+        block
+        end
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b801000000           	mov	eax, 1
+;;   11:	 8bc0                 	mov	eax, eax
+;;   13:	 50                   	push	rax
+;;   14:	 58                   	pop	rax
+;;   15:	 4883c408             	add	rsp, 8
+;;   19:	 5d                   	pop	rbp
+;;   1a:	 c3                   	ret