about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs38
-rw-r--r--src/tools/miri/tests/pass/intrinsics-x86.rs23
2 files changed, 45 insertions, 16 deletions
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 16eac81fa0a..2db2f2e19ff 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -5,7 +5,6 @@ use rustc_target::spec::abi::Abi;
 
 use crate::*;
 use helpers::bool_to_simd_element;
-use helpers::convert::Truncate as _;
 use shims::foreign_items::EmulateByNameResult;
 
 mod sse;
@@ -26,23 +25,30 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
         // Prefix should have already been checked.
         let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
         match unprefixed_name {
-            "addcarry.64" if this.tcx.sess.target.arch == "x86_64" => {
-                // Computes u8+u64+u64, returning tuple (u8,u64) comprising the output carry and truncated sum.
+            // Used to implement the `_addcarry_u32` and `_addcarry_u64` functions.
+            // Computes u8+uX+uX (uX is u32 or u64), returning tuple (u8,uX) comprising
+            // the output carry and truncated sum.
+            "addcarry.32" | "addcarry.64" => {
+                if unprefixed_name == "addcarry.64" && this.tcx.sess.target.arch != "x86_64" {
+                    return Ok(EmulateByNameResult::NotSupported);
+                }
+
                 let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
                 let c_in = this.read_scalar(c_in)?.to_u8()?;
-                let a = this.read_scalar(a)?.to_u64()?;
-                let b = this.read_scalar(b)?.to_u64()?;
-
-                #[allow(clippy::arithmetic_side_effects)]
-                // adding two u64 and a u8 cannot wrap in a u128
-                let wide_sum = u128::from(c_in) + u128::from(a) + u128::from(b);
-                #[allow(clippy::arithmetic_side_effects)] // it's a u128, we can shift by 64
-                let (c_out, sum) = ((wide_sum >> 64).truncate::<u8>(), wide_sum.truncate::<u64>());
-
-                let c_out_field = this.project_field(dest, 0)?;
-                this.write_scalar(Scalar::from_u8(c_out), &c_out_field)?;
-                let sum_field = this.project_field(dest, 1)?;
-                this.write_scalar(Scalar::from_u64(sum), &sum_field)?;
+                let a = this.read_immediate(a)?;
+                let b = this.read_immediate(b)?;
+
+                let (sum, overflow1) = this.overflowing_binary_op(mir::BinOp::Add, &a, &b)?;
+                let (sum, overflow2) = this.overflowing_binary_op(
+                    mir::BinOp::Add,
+                    &sum,
+                    &ImmTy::from_uint(c_in, a.layout),
+                )?;
+                #[allow(clippy::arithmetic_side_effects)] // adding two bools into a u8
+                let c_out = u8::from(overflow1) + u8::from(overflow2);
+
+                this.write_scalar(Scalar::from_u8(c_out), &this.project_field(dest, 0)?)?;
+                this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
             }
 
             name if name.starts_with("sse.") => {
diff --git a/src/tools/miri/tests/pass/intrinsics-x86.rs b/src/tools/miri/tests/pass/intrinsics-x86.rs
index 88cd782e70a..2e947200989 100644
--- a/src/tools/miri/tests/pass/intrinsics-x86.rs
+++ b/src/tools/miri/tests/pass/intrinsics-x86.rs
@@ -1,3 +1,24 @@
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod x86 {
+    #[cfg(target_arch = "x86")]
+    use core::arch::x86 as arch;
+    #[cfg(target_arch = "x86_64")]
+    use core::arch::x86_64 as arch;
+
+    fn adc(c_in: u8, a: u32, b: u32) -> (u8, u32) {
+        let mut sum = 0;
+        // SAFETY: There are no safety requirements for calling `_addcarry_u32`.
+        // It's just unsafe for API consistency with other intrinsics.
+        let c_out = unsafe { arch::_addcarry_u32(c_in, a, b, &mut sum) };
+        (c_out, sum)
+    }
+
+    pub fn main() {
+        assert_eq!(adc(1, 1, 1), (0, 3));
+        assert_eq!(adc(3, u32::MAX, u32::MAX), (2, 1));
+    }
+}
+
 #[cfg(target_arch = "x86_64")]
 mod x86_64 {
     use core::arch::x86_64 as arch;
@@ -17,6 +38,8 @@ mod x86_64 {
 }
 
 fn main() {
+    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+    x86::main();
     #[cfg(target_arch = "x86_64")]
     x86_64::main();
 }