CVE-2022-31169: Incorrect Calculation (CWE-682)
value: Wasmtime is a standalone runtime for WebAssembly. There is a bug in Wasmtime's code generator, Cranelift, for AArch64 targets where constant divisors can result in incorrect division results at runtime. This affects Wasmtime prior to version 0.38.2 and Cranelift prior to 0.85.2. This issue only affects the AArch64 platform. Other platforms are not affected. The translation rules for constants did not take into account whether sign or zero-extension should happen which resulted in an incorrect value being placed into a register when a division was encountered. The impact of this bug is that programs executing within the WebAssembly sandbox would not behave according to the WebAssembly specification. This means that it is hypothetically possible for execution within the sandbox to go awry and WebAssembly programs could produce unexpected results. This should not impact hosts executing WebAssembly but does affect the correctness of guest programs. This bug has been patched in Wasmtime version 0.38.2 and cranelift-codegen 0.85.2. There are no known workarounds.
CWE description: The product performs a calculation that generates incorrect or unintended results that are later used in security-critical decisions or resource management.
CVSS v3 base score: 7.5
Published: 2022-07-22T04:15Z
Repository: https://github.com/bytecodealliance/wasmtime
Language: Rust
Files changed: cranelift/codegen/src/isa/aarch64/inst.isle, cranelift/codegen/src/isa/aarch64/inst/imms.rs, cranelift/codegen/src/isa/aarch64/lower.isle, cranelift/codegen/src/isa/aarch64/lower/isle.rs, cranelift/filetests/filetests/isa/aarch64/arithmetic.clif, cranelift/filetests/filetests/runtests/arithmetic.clif
Commit date: 2022-07-20 11:53:56 -0500
Commit message: Merge pull request from GHSA-7f6x-jwh5-m9r4

Copyright (c) 2022, Arm Limited.
Diff: diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle
index d19da6bf25..3824a4ec99 100644
--- a/cranelift/codegen/src/isa/aarch64/inst.isle
+++ b/cranelift/codegen/src/isa/aarch64/inst.isle
@@ -1323,16 +1323,10 @@
 (decl use_lse () Inst)
 (extern extractor use_lse use_lse)
 
 ;; Extractor helpers for various immmediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(decl move_wide_const_from_u64 (MoveWideConst) u64)
-(extern extractor move_wide_const_from_u64 move_wide_const_from_u64)
-
-(decl move_wide_const_from_negated_u64 (MoveWideConst) u64)
-(extern extractor move_wide_const_from_negated_u64 move_wide_const_from_negated_u64)
-
 (decl pure imm_logic_from_u64 (Type u64) ImmLogic)
 (extern constructor imm_logic_from_u64 imm_logic_from_u64)
 
 (decl pure imm_logic_from_imm64 (Type Imm64) ImmLogic)
 (extern constructor imm_logic_from_imm64 imm_logic_from_imm64)
@@ -2023,31 +2017,40 @@
 (rule (udf use_allocated_encoding trap_code)
       (SideEffectNoResult.Inst (MInst.Udf use_allocated_encoding trap_code)))
 
 ;; Immediate value helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(decl imm (Type u64) Reg)
-
-;; 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVZ
-(rule (imm (integral_ty _ty) (move_wide_const_from_u64 n))
-      (movz n (OperandSize.Size64)))
-
-;; 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVN
-(rule (imm (integral_ty _ty) (move_wide_const_from_negated_u64 n))
-      (movn n (OperandSize.Size64)))
-
-;; Weird logical-instruction immediate in ORI using zero register
-(rule (imm (integral_ty _ty) k)
-      (if-let n (imm_logic_from_u64 $I64 k))
-      (orr_imm $I64 (zero_reg) n))
-
-(decl load_constant64_full (u64) Reg)
+;; Type of extension performed by an immediate helper
+(type ImmExtend
+  (enum
+    (Sign)
+    (Zero)))
+
+;; Arguments:
+;; * Immediate type
+;; * Way to extend the immediate value to the full width of the destination
+;;   register
+;; * Immediate value - only the bits that fit within the type are used and
+;;   extended, while the rest are ignored
+;;
+;; Note that, unlike the convention in the AArch64 backend, this helper leaves
+;; all bits in the destination register in a defined state, i.e. smaller types
+;; such as `I8` are either sign- or zero-extended.
+(decl imm (Type ImmExtend u64) Reg)
+
+;; Weird logical-instruction immediate in ORI using zero register; to simplify,
+;; we only match when we are zero-extending the value.
+(rule (imm (integral_ty ty) (ImmExtend.Zero) k)
+      (if-let n (imm_logic_from_u64 ty k))
+      (orr_imm ty (zero_reg) n))
+
+(decl load_constant64_full (Type ImmExtend u64) Reg)
 (extern constructor load_constant64_full load_constant64_full)
 
-;; Fallback for integral 64-bit constants that uses lots of `movk`
-(rule (imm (integral_ty _ty) n)
-      (load_constant64_full n))
+;; Fallback for integral 64-bit constants
+(rule (imm (integral_ty ty) extend n)
+      (load_constant64_full ty extend n))
 
 ;; Sign extension helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Place a `Value` into a register, sign extending it to 32-bits
 (decl put_in_reg_sext32 (Value) Reg)
diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
index 4505d56c4c..47a30b40a3 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
@@ -616,10 +616,14 @@ impl MoveWideConst {
                 bits: imm,
                 shift: shift_enc,
             })
         }
     }
+
+    pub fn zero() -> MoveWideConst {
+        MoveWideConst { bits: 0, shift: 0 }
+    }
 }
 
 /// Advanced SIMD modified immediate as used by MOVI/MVNI.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct ASIMDMovModImm {
diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle
index c4332418e6..eada9d0bdf 100644
--- a/cranelift/codegen/src/isa/aarch64/lower.isle
+++ b/cranelift/codegen/src/isa/aarch64/lower.isle
@@ -5,24 +5,24 @@
 (decl lower (Inst) InstOutput)
 
 ;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (rule (lower (has_type ty (iconst (u64_from_imm64 n))))
-      (imm ty n))
+      (imm ty (ImmExtend.Zero) n))
 
 ;;;; Rules for `bconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (rule (lower (has_type ty (bconst $false)))
-      (imm ty 0))
+      (imm ty (ImmExtend.Zero) 0))
 
 (rule (lower (has_type ty (bconst $true)))
-      (imm ty 1))
+      (imm ty (ImmExtend.Zero) 1))
 
 ;;;; Rules for `null` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (rule (lower (has_type ty (null)))
-      (imm ty 0))
+      (imm ty (ImmExtend.Zero) 0))
 
 ;;;; Rules for `iadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; `i64` and smaller
 
@@ -531,11 +531,11 @@
 
 ;; Special case where if a `Value` is known to be nonzero we can trivially
 ;; move it into a register.
 (rule (put_nonzero_in_reg_zext64 (and (value_type ty)
                                       (iconst (nonzero_u64_from_imm64 n))))
-      (imm ty n))
+      (imm ty (ImmExtend.Zero) n))
 
 ;;;; Rules for `sdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; TODO: Add SDiv32 to implement 32-bit directly, rather
 ;; than extending the input.
@@ -566,22 +566,22 @@
 (extern extractor safe_divisor_from_imm64 safe_divisor_from_imm64)
 
 ;; Special case for `sdiv` where no checks are needed due to division by a
 ;; constant meaning the checks are always passed.
 (rule (lower (has_type (fits_in_64 ty) (sdiv x (iconst (safe_divisor_from_imm64 y)))))
-      (a64_sdiv $I64 (put_in_reg_sext64 x) (imm ty y)))
+      (a64_sdiv $I64 (put_in_reg_sext64 x) (imm ty (ImmExtend.Sign) y)))
 
 ;; Helper for placing a `Value` into a `Reg` and validating that it's nonzero.
 (decl put_nonzero_in_reg_sext64 (Value) Reg)
 (rule (put_nonzero_in_reg_sext64 val)
       (trap_if_zero_divisor (put_in_reg_sext64 val)))
 
 ;; Note that this has a special case where if the `Value` is a constant that's
 ;; not zero we can skip the zero check.
 (rule (put_nonzero_in_reg_sext64 (and (value_type ty)
                                       (iconst (nonzero_u64_from_imm64 n))))
-      (imm ty n))
+      (imm ty (ImmExtend.Sign) n))
 
 ;;;; Rules for `urem` and `srem` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Remainder (x % y) is implemented as:
 ;;
@@ -644,18 +644,18 @@
       (load_acquire in (sink_atomic_load addr)))
 
 ;; Conversion to 128-bit needs a zero-extension of the lower bits and the upper
 ;; bits are all zero.
 (rule (lower (has_type $I128 (uextend x)))
-      (value_regs (put_in_reg_zext64 x) (imm $I64 0)))
+      (value_regs (put_in_reg_zext64 x) (imm $I64 (ImmExtend.Zero) 0)))
 
 ;; Like above where vector extraction automatically zero-extends extending to
 ;; i128 only requires generating a 0 constant for the upper bits.
 (rule (lower (has_type $I128
                        (uextend (extractlane vec @ (value_type in)
                                              (u8_from_uimm8 lane)))))
-      (value_regs (mov_from_vec (put_in_reg vec) lane (vector_size in)) (imm $I64 0)))
+      (value_regs (mov_from_vec (put_in_reg vec) lane (vector_size in)) (imm $I64 (ImmExtend.Zero) 0)))
 
 ;;;; Rules for `sextend` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; General rule for extending input to an output which fits in a single
 ;; register.
@@ -1035,11 +1035,11 @@
 ;;
 ;; TODO: much better codegen is possible with a constant amount.
 (rule (lower (has_type $I128 (rotl x y)))
       (let ((val ValueRegs x)
             (amt Reg (value_regs_get y 0))
-            (neg_amt Reg (sub $I64 (imm $I64 128) amt))
+            (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt))
             (lshift ValueRegs (lower_shl128 val amt))
             (rshift ValueRegs (lower_ushr128 val neg_amt)))
         (value_regs
           (orr $I64 (value_regs_get lshift 0) (value_regs_get rshift 0))
           (orr $I64 (value_regs_get lshift 1) (value_regs_get rshift 1)))))
@@ -1119,11 +1119,11 @@
 ;;
 ;; TODO: much better codegen is possible with a constant amount.
 (rule (lower (has_type $I128 (rotr x y)))
       (let ((val ValueRegs x)
             (amt Reg (value_regs_get y 0))
-            (neg_amt Reg (sub $I64 (imm $I64 128) amt))
+            (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt))
             (rshift ValueRegs (lower_ushr128 val amt))
             (lshift ValueRegs (lower_shl128 val neg_amt))
             (hi Reg (orr $I64 (value_regs_get rshift 1) (value_regs_get lshift 1)))
             (lo Reg (orr $I64 (value_regs_get rshift 0) (value_regs_get lshift 0))))
         (value_regs lo hi)))
@@ -1174,11 +1174,11 @@
 (decl lower_clz128 (ValueRegs) ValueRegs)
 (rule (lower_clz128 val)
       (let ((hi_clz Reg (a64_clz $I64 (value_regs_get val 1)))
             (lo_clz Reg (a64_clz $I64 (value_regs_get val 0)))
             (tmp Reg (lsr_imm $I64 hi_clz (imm_shift_from_u8 6))))
-        (value_regs (madd $I64 lo_clz tmp hi_clz) (imm $I64 0))))
+        (value_regs (madd $I64 lo_clz tmp hi_clz) (imm $I64 (ImmExtend.Zero) 0))))
 
 ;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Note that all `ctz` instructions are implemented by reversing the bits and
 ;; then using a `clz` instruction since the tail zeros are the same as the
@@ -1226,11 +1226,11 @@
             (sign_eq Reg (lsr_imm $I64 sign_eq_eon (imm_shift_from_u8 63)))
             (lo_sign_bits Reg (madd $I64 lo_cls sign_eq sign_eq))
             (maybe_lo Reg (with_flags_reg
                            (cmp64_imm hi_cls (u8_into_imm12 63))
                            (csel (Cond.Eq) lo_sign_bits (zero_reg)))))
-        (value_regs (add $I64 maybe_lo hi_cls) (imm $I64 0))))
+        (value_regs (add $I64 maybe_lo hi_cls) (imm $I64 (ImmExtend.Zero) 0))))
 
 (rule (lower (has_type ty (cls x)))
       (a64_cls ty x))
 
 ;;;; Rules for `bint` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1240,11 +1240,11 @@
 
 (rule (lower (has_type $I128 (bint x)))
       (let ((val ValueRegs x)
             (in_lo Reg (value_regs_get val 0))
             (dst_lo Reg (and_imm $I32 in_lo (u64_into_imm_logic $I32 1)))
-            (dst_hi Reg (imm $I64 0)))
+            (dst_hi Reg (imm $I64 (ImmExtend.Zero) 0)))
         (value_regs dst_lo dst_hi)))
 
 (rule (lower (bint x))
       (and_imm $I32 x (u64_into_imm_logic $I32 1)))
 
@@ -1335,11 +1335,11 @@
       (let ((val ValueRegs x)
             (tmp_half Reg (mov_to_fpu (value_regs_get val 0) (ScalarSize.Size64)))
             (tmp Reg (mov_to_vec tmp_half (value_regs_get val 1) 1 (VectorSize.Size64x2)))
             (nbits Reg (vec_cnt tmp (VectorSize.Size8x16)))
             (added Reg (addv nbits (VectorSize.Size8x16))))
-        (value_regs (mov_from_vec added 0 (VectorSize.Size8x16)) (imm $I64 0))))
+        (value_regs (mov_from_vec added 0 (VectorSize.Size8x16)) (imm $I64 (ImmExtend.Zero) 0))))
 
 (rule (lower (has_type $I8X16 (popcnt x)))
       (vec_cnt x (VectorSize.Size8x16)))
 
 ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index e6d51db796..fa4556f03d 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -73,24 +73,16 @@ where
         } else {
             None
         }
     }
 
-    fn move_wide_const_from_u64(&mut self, n: u64) -> Option<MoveWideConst> {
-        MoveWideConst::maybe_from_u64(n)
-    }
-
-    fn move_wide_const_from_negated_u64(&mut self, n: u64) -> Option<MoveWideConst> {
-        MoveWideConst::maybe_from_u64(!n)
-    }
-
     fn imm_logic_from_u64(&mut self, ty: Type, n: u64) -> Option<ImmLogic> {
-        let ty = if ty.bits() < 32 { I32 } else { ty };
         ImmLogic::maybe_from_u64(n, ty)
     }
 
     fn imm_logic_from_imm64(&mut self, ty: Type, n: Imm64) -> Option<ImmLogic> {
+        let ty = if ty.bits() < 32 { I32 } else { ty };
         self.imm_logic_from_u64(ty, n.bits() as u64)
     }
 
     fn imm12_from_u64(&mut self, n: u64) -> Option<Imm12> {
         Imm12::maybe_from_u64(n)
@@ -134,11 +126,49 @@ where
     /// This is the fallback case for loading a 64-bit integral constant into a
     /// register.
     ///
     /// The logic here is nontrivial enough that it's not really worth porting
     /// this over to ISLE.
-    fn load_constant64_full(&mut self, value: u64) -> Reg {
+    fn load_constant64_full(
+        &mut self,
+        ty: Type,
+        extend: &generated_code::ImmExtend,
+        value: u64,
+    ) -> Reg {
+        let bits = ty.bits();
+        let value = if bits < 64 {
+            if *extend == generated_code::ImmExtend::Sign {
+                let shift = 64 - bits;
+                let value = value as i64;
+
+                ((value << shift) >> shift) as u64
+            } else {
+                value & !(u64::MAX << bits)
+            }
+        } else {
+            value
+        };
+        let rd = self.temp_writable_reg(I64);
+
+        if value == 0 {
+            self.emit(&MInst::MovWide {
+                op: MoveWideOp::MovZ,
+                rd,
+                imm: MoveWideConst::zero(),
+                size: OperandSize::Size64,
+            });
+            return rd.to_reg();
+        } else if value == u64::MAX {
+            self.emit(&MInst::MovWide {
+                op: MoveWideOp::MovN,
+                rd,
+                imm: MoveWideConst::zero(),
+                size: OperandSize::Size64,
+            });
+            return rd.to_reg();
+        };
+
         // If the top 32 bits are zero, use 32-bit `mov` operations.
         let (num_half_words, size, negated) = if value >> 32 == 0 {
             (2, OperandSize::Size32, (!value << 32) >> 32)
         } else {
             (4, OperandSize::Size64, !value)
@@ -150,12 +180,10 @@ where
         // Either 0xffff or 0x0000 half words can be skipped, depending on the first
         // instruction used.
         let ignored_halfword = if first_is_inverted { 0xffff } else { 0 };
         let mut first_mov_emitted = false;
 
-        let rd = self.temp_writable_reg(I64);
-
         for i in 0..num_half_words {
             let imm16 = (value >> (16 * i)) & 0xffff;
             if imm16 != ignored_halfword {
                 if !first_mov_emitted {
                     first_mov_emitted = true;
diff --git a/cranelift/filetests/filetests/isa/aarch64/arithmetic.clif b/cranelift/filetests/filetests/isa/aarch64/arithmetic.clif
index 7c47f19798..b50b482b25 100644
--- a/cranelift/filetests/filetests/isa/aarch64/arithmetic.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/arithmetic.clif
@@ -72,11 +72,11 @@ block0(v0: i64):
   v2 = sdiv.i64 v0, v1
   return v2
 }
 
 ; block0:
-;   orr x3, xzr, #2
+;   movz w3, #2
 ;   sdiv x0, x0, x3
 ;   ret
 
 function %f8(i64, i64) -> i64 {
 block0(v0: i64, v1: i64):
@@ -148,11 +148,11 @@ block0(v0: i32):
   return v2
 }
 
 ; block0:
 ;   sxtw x3, w0
-;   orr x5, xzr, #2
+;   movz w5, #2
 ;   sdiv x0, x3, x5
 ;   ret
 
 function %f14(i32, i32) -> i32 {
 block0(v0: i32, v1: i32):
@@ -174,11 +174,11 @@ block0(v0: i32):
   return v2
 }
 
 ; block0:
 ;   mov w3, w0
-;   orr x5, xzr, #2
+;   orr w5, wzr, #2
 ;   udiv x0, x3, x5
 ;   ret
 
 function %f16(i32, i32) -> i32 {
 block0(v0: i32, v1: i32):
@@ -458,11 +458,11 @@ block0(v0: i64):
   v2 = srem.i64 v0, v1
   return v2
 }
 
 ; block0:
-;   orr x3, xzr, #2
+;   movz w3, #2
 ;   sdiv x5, x0, x3
 ;   msub x0, x5, x3, x0
 ;   ret
 
 function %urem_const (i64) -> i64 {
diff --git a/cranelift/filetests/filetests/runtests/arithmetic.clif b/cranelift/filetests/filetests/runtests/arithmetic.clif
index fdcdc760ba..9fd5149b09 100644
--- a/cranelift/filetests/filetests/runtests/arithmetic.clif
+++ b/cranelift/filetests/filetests/runtests/arithmetic.clif
@@ -345,10 +345,20 @@ block0(v0: i64,v1: i64):
 ; run: %sdiv_i64(-19, 7) == -2
 ; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0xF81FFDDD_DBD96000
 ; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0x7E002222_426A000
 ; run: %sdiv_i64(0x80000000_00000000, -2) == 0x40000000_00000000
 
+function %sdiv_i64_const(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i64 -2
+    v2 = sdiv v0, v1
+    return v2
+}
+; run: %sdiv_i64_const(0) == 0
+; run: %sdiv_i64_const(-1) == 0
+; run: %sdiv_i64_const(0xFFFFFFFF_FFFFFFFE) == 1
+
 function %sdiv_i32(i32, i32) -> i32 {
 block0(v0: i32,v1: i32):
     v2 = sdiv v0, v1
     return v2
 }
@@ -361,10 +371,20 @@ block0(v0: i32,v1: i32):
 ; run: %sdiv_i32(-19, 7) == -2
 ; run: %sdiv_i32(0xC0FFEEEE, 8) == 0xF81FFDDE
 ; run: %sdiv_i32(0xC0FFEEEE, -8) == 0x7E00222
 ; run: %sdiv_i32(0x80000000, -2) == 0x40000000
 
+function %sdiv_i32_const(i32) -> i32 {
+block0(v0: i32):
+    v1 = iconst.i32 -2
+    v2 = sdiv v0, v1
+    return v2
+}
+; run: %sdiv_i32_const(0) == 0
+; run: %sdiv_i32_const(-1) == 0
+; run: %sdiv_i32_const(0xFFFFFFFE) == 1
+
 function %sdiv_i16(i16, i16) -> i16 {
 block0(v0: i16,v1: i16):
     v2 = sdiv v0, v1
     return v2
 }
@@ -377,10 +397,20 @@ block0(v0: i16,v1: i16):
 ; run: %sdiv_i16(-19, 7) == -2
 ; run: %sdiv_i16(0xC0FF, 8) == 0xF820
 ; run: %sdiv_i16(0xC0FF, -8) == 0x07E0
 ; run: %sdiv_i16(0x8000, -2) == 0x4000
 
+function %sdiv_i16_const(i16) -> i16 {
+block0(v0: i16):
+    v1 = iconst.i16 -2
+    v2 = sdiv v0, v1
+    return v2
+}
+; run: %sdiv_i16_const(0) == 0
+; run: %sdiv_i16_const(-1) == 0
+; run: %sdiv_i16_const(0xFFFE) == 1
+
 function %sdiv_i8(i8, i8) -> i8 {
 block0(v0: i8,v1: i8):
     v2 = sdiv v0, v1
     return v2
 }
@@ -393,10 +423,19 @@ block0(v0: i8,v1: i8):
 ; run: %sdiv_i8(-19, 7) == -2
 ; run: %sdiv_i8(0xC0, 8) == 0xF8
 ; run: %sdiv_i8(0xC0, -8) == 0x08
 ; run: %sdiv_i8(0x80, -2) == 0x40
 
+function %sdiv_i8_const(i8) -> i8 {
+block0(v0: i8):
+    v1 = iconst.i8 -2
+    v2 = sdiv v0, v1
+    return v2
+}
+; run: %sdiv_i8_const(0) == 0
+; run: %sdiv_i8_const(-1) == 0
+; run: %sdiv_i8_const(0xFE) == 1
 
 function %udiv_i64(i64, i64) -> i64 {
 block0(v0: i64,v1: i64):
     v2 = udiv v0, v1
     return v2
@@ -411,10 +450,20 @@ block0(v0: i64,v1: i64):
 ; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0x181FFDDD_DBD95FFF
 ; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0
 ; run: %udiv_i64(0x80000000_00000000, -1) == 0
 ; run: %udiv_i64(0x80000000_00000000, -2) == 0
 
+function %udiv_i64_const(i64) -> i64 {
+block0(v0: i64):
+    v1 = iconst.i64 -2
+    v2 = udiv v0, v1
+    return v2
+}
+; run: %udiv_i64_const(0) == 0
+; run: %udiv_i64_const(-1) == 1
+; run: %udiv_i64_const(0xFFFFFFFF_FFFFFFFE) == 1
+
 function %udiv_i32(i32, i32) -> i32 {
 block0(v0: i32,v1: i32):
     v2 = udiv v0, v1
     return v2
 }
@@ -428,10 +477,20 @@ block0(v0: i32,v1: i32):
 ; run: %udiv_i32(0xC0FFEEEE, 8) == 0x181FFDDD
 ; run: %udiv_i32(0xC0FFEEEE, -8) == 0
 ; run: %udiv_i32(0x80000000, -1) == 0
 ; run: %udiv_i32(0x80000000, -2) == 0
 
+function %udiv_i32_const(i32) -> i32 {
+block0(v0: i32):
+    v1 = iconst.i32 -2
+    v2 = udiv v0, v1
+    return v2
+}
+; run: %udiv_i32_const(0) == 0
+; run: %udiv_i32_const(-1) == 1
+; run: %udiv_i32_const(0xFFFFFFFE) == 1
+
 function %udiv_i16(i16, i16) -> i16 {
 block0(v0: i16,v1: i16):
     v2 = udiv v0, v1
     return v2
 }
@@ -445,10 +504,20 @@ block0(v0: i16,v1: i16):
 ; run: %udiv_i16(0xC0FF, 8) == 0x181F
 ; run: %udiv_i16(0xC0FF, -8) == 0
 ; run: %udiv_i16(0x8000, -1) == 0
 ; run: %udiv_i16(0x8000, -2) == 0
 
+function %udiv_i16_const(i16) -> i16 {
+block0(v0: i16):
+    v1 = iconst.i16 -2
+    v2 = udiv v0, v1
+    return v2
+}
+; run: %udiv_i16_const(0) == 0
+; run: %udiv_i16_const(-1) == 1
+; run: %udiv_i16_const(0xFFFE) == 1
+
 function %udiv_i8(i8, i8) -> i8 {
 block0(v0: i8,v1: i8):
     v2 = udiv v0, v1
     return v2
 }
@@ -461,5 +530,15 @@ block0(v0: i8,v1: i8):
 ; run: %udiv_i8(-19, 7) == 0x21
 ; run: %udiv_i8(0xC0, 8) == 0x18
 ; run: %udiv_i8(0xC0, -8) == 0
 ; run: %udiv_i8(0x80, -1) == 0
 ; run: %udiv_i8(0x80, -2) == 0
+
+function %udiv_i8_const(i8) -> i8 {
+block0(v0: i8):
+    v1 = iconst.i8 -2
+    v2 = udiv v0, v1
+    return v2
+}
+; run: %udiv_i8_const(0) == 0
+; run: %udiv_i8_const(-1) == 1
+; run: %udiv_i8_const(0xFE) == 1

Vulnerable code: (decl move_wide_const_from_u64 (MoveWideConst) u64)
(extern extractor move_wide_const_from_u64 move_wide_const_from_u64)

(decl move_wide_const_from_negated_u64 (MoveWideConst) u64)
(extern extractor move_wide_const_from_negated_u64 move_wide_const_from_negated_u64)

(decl imm (Type u64) Reg)

;; 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVZ
(rule (imm (integral_ty _ty) (move_wide_const_from_u64 n))
      (movz n (OperandSize.Size64)))

;; 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVN
(rule (imm (integral_ty _ty) (move_wide_const_from_negated_u64 n))
      (movn n (OperandSize.Size64)))

;; Weird logical-instruction immediate in ORI using zero register
(rule (imm (integral_ty _ty) k)
      (if-let n (imm_logic_from_u64 $I64 k))
      (orr_imm $I64 (zero_reg) n))

(decl load_constant64_full (u64) Reg)
;; Fallback for integral 64-bit constants that uses lots of `movk`
(rule (imm (integral_ty _ty) n)
      (load_constant64_full n))
      (imm ty n))
      (imm ty 0))
      (imm ty 1))
      (imm ty 0))
      (imm ty n))
      (a64_sdiv $I64 (put_in_reg_sext64 x) (imm ty y)))
      (imm ty n))
      (value_regs (put_in_reg_zext64 x) (imm $I64 0)))
      (value_regs (mov_from_vec (put_in_reg vec) lane (vector_size in)) (imm $I64 0)))
            (neg_amt Reg (sub $I64 (imm $I64 128) amt))
            (neg_amt Reg (sub $I64 (imm $I64 128) amt))
        (value_regs (madd $I64 lo_clz tmp hi_clz) (imm $I64 0))))
        (value_regs (add $I64 maybe_lo hi_cls) (imm $I64 0))))
            (dst_hi Reg (imm $I64 0)))
        (value_regs (mov_from_vec added 0 (VectorSize.Size8x16)) (imm $I64 0))))
    fn move_wide_const_from_u64(&mut self, n: u64) -> Option<MoveWideConst> {
        MoveWideConst::maybe_from_u64(n)
    }

    fn move_wide_const_from_negated_u64(&mut self, n: u64) -> Option<MoveWideConst> {
        MoveWideConst::maybe_from_u64(!n)
    }

        let ty = if ty.bits() < 32 { I32 } else { ty };
    fn load_constant64_full(&mut self, value: u64) -> Reg {
        let rd = self.temp_writable_reg(I64);

;   orr x3, xzr, #2
;   orr x5, xzr, #2
;   orr x5, xzr, #2
;   orr x3, xzr, #2
Fixed code: ;; Type of extension performed by an immediate helper
(type ImmExtend
  (enum
    (Sign)
    (Zero)))

;; Arguments:
;; * Immediate type
;; * Way to extend the immediate value to the full width of the destination
;;   register
;; * Immediate value - only the bits that fit within the type are used and
;;   extended, while the rest are ignored
;;
;; Note that, unlike the convention in the AArch64 backend, this helper leaves
;; all bits in the destination register in a defined state, i.e. smaller types
;; such as `I8` are either sign- or zero-extended.
(decl imm (Type ImmExtend u64) Reg)

;; Weird logical-instruction immediate in ORI using zero register; to simplify,
;; we only match when we are zero-extending the value.
(rule (imm (integral_ty ty) (ImmExtend.Zero) k)
      (if-let n (imm_logic_from_u64 ty k))
      (orr_imm ty (zero_reg) n))

(decl load_constant64_full (Type ImmExtend u64) Reg)
;; Fallback for integral 64-bit constants
(rule (imm (integral_ty ty) extend n)
      (load_constant64_full ty extend n))

    pub fn zero() -> MoveWideConst {
        MoveWideConst { bits: 0, shift: 0 }
    }
      (imm ty (ImmExtend.Zero) n))
      (imm ty (ImmExtend.Zero) 0))
      (imm ty (ImmExtend.Zero) 1))
      (imm ty (ImmExtend.Zero) 0))
      (imm ty (ImmExtend.Zero) n))
      (a64_sdiv $I64 (put_in_reg_sext64 x) (imm ty (ImmExtend.Sign) y)))
      (imm ty (ImmExtend.Sign) n))
      (value_regs (put_in_reg_zext64 x) (imm $I64 (ImmExtend.Zero) 0)))
      (value_regs (mov_from_vec (put_in_reg vec) lane (vector_size in)) (imm $I64 (ImmExtend.Zero) 0)))
            (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt))
            (neg_amt Reg (sub $I64 (imm $I64 (ImmExtend.Zero) 128) amt))
        (value_regs (madd $I64 lo_clz tmp hi_clz) (imm $I64 (ImmExtend.Zero) 0))))
        (value_regs (add $I64 maybe_lo hi_cls) (imm $I64 (ImmExtend.Zero) 0))))
            (dst_hi Reg (imm $I64 (ImmExtend.Zero) 0)))
        (value_regs (mov_from_vec added 0 (VectorSize.Size8x16)) (imm $I64 (ImmExtend.Zero) 0))))
        let ty = if ty.bits() < 32 { I32 } else { ty };
    fn load_constant64_full(
        &mut self,
        ty: Type,
        extend: &generated_code::ImmExtend,
        value: u64,
    ) -> Reg {
        let bits = ty.bits();
        let value = if bits < 64 {
            if *extend == generated_code::ImmExtend::Sign {
                let shift = 64 - bits;
                let value = value as i64;

                ((value << shift) >> shift) as u64
            } else {
                value & !(u64::MAX << bits)
            }
        } else {
            value
        };
        let rd = self.temp_writable_reg(I64);

        if value == 0 {
            self.emit(&MInst::MovWide {
                op: MoveWideOp::MovZ,
                rd,
                imm: MoveWideConst::zero(),
                size: OperandSize::Size64,
            });
            return rd.to_reg();
        } else if value == u64::MAX {
            self.emit(&MInst::MovWide {
                op: MoveWideOp::MovN,
                rd,
                imm: MoveWideConst::zero(),
                size: OperandSize::Size64,
            });
            return rd.to_reg();
        };

;   movz w3, #2
;   movz w5, #2
;   orr w5, wzr, #2
;   movz w3, #2
function %sdiv_i64_const(i64) -> i64 {
block0(v0: i64):
    v1 = iconst.i64 -2
    v2 = sdiv v0, v1
    return v2
}
; run: %sdiv_i64_const(0) == 0
; run: %sdiv_i64_const(-1) == 0
; run: %sdiv_i64_const(0xFFFFFFFF_FFFFFFFE) == 1

function %sdiv_i32_const(i32) -> i32 {
block0(v0: i32):
    v1 = iconst.i32 -2
    v2 = sdiv v0, v1
    return v2
}
; run: %sdiv_i32_const(0) == 0
; run: %sdiv_i32_const(-1) == 0
; run: %sdiv_i32_const(0xFFFFFFFE) == 1

function %sdiv_i16_const(i16) -> i16 {
block0(v0: i16):
    v1 = iconst.i16 -2
    v2 = sdiv v0, v1
    return v2
}
; run: %sdiv_i16_const(0) == 0
; run: %sdiv_i16_const(-1) == 0
; run: %sdiv_i16_const(0xFFFE) == 1

function %sdiv_i8_const(i8) -> i8 {
block0(v0: i8):
    v1 = iconst.i8 -2
    v2 = sdiv v0, v1
    return v2
}
; run: %sdiv_i8_const(0) == 0
; run: %sdiv_i8_const(-1) == 0
; run: %sdiv_i8_const(0xFE) == 1
function %udiv_i64_const(i64) -> i64 {
block0(v0: i64):
    v1 = iconst.i64 -2
    v2 = udiv v0, v1
    return v2
}
; run: %udiv_i64_const(0) == 0
; run: %udiv_i64_const(-1) == 1
; run: %udiv_i64_const(0xFFFFFFFF_FFFFFFFE) == 1

function %udiv_i32_const(i32) -> i32 {
block0(v0: i32):
    v1 = iconst.i32 -2
    v2 = udiv v0, v1
    return v2
}
; run: %udiv_i32_const(0) == 0
; run: %udiv_i32_const(-1) == 1
; run: %udiv_i32_const(0xFFFFFFFE) == 1

function %udiv_i16_const(i16) -> i16 {
block0(v0: i16):
    v1 = iconst.i16 -2
    v2 = udiv v0, v1
    return v2
}
; run: %udiv_i16_const(0) == 0
; run: %udiv_i16_const(-1) == 1
; run: %udiv_i16_const(0xFFFE) == 1


function %udiv_i8_const(i8) -> i8 {
block0(v0: i8):
    v1 = iconst.i8 -2
    v2 = udiv v0, v1
    return v2
}
; run: %udiv_i8_const(0) == 0
; run: %udiv_i8_const(-1) == 1
; run: %udiv_i8_const(0xFE) == 1