about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduardo Sánchez Muñoz <eduardosm-dev@e64.io>2023-09-21 20:25:36 +0200
committerEduardo Sánchez Muñoz <eduardosm-dev@e64.io>2023-09-25 07:18:10 +0200
commit6ec63ed251f25d3c9b3d28321ebacee15d18fdae (patch)
treebd752743e01a6972e8e387d3e5501e6ae6b850a4
parent356039985ea8eaf41ef54b89123bd68019181ef8 (diff)
downloadrust-6ec63ed251f25d3c9b3d28321ebacee15d18fdae.tar.gz
rust-6ec63ed251f25d3c9b3d28321ebacee15d18fdae.zip
Fix the carry semantics of `_addcarry_u32` and `_addcarry_u64`
The input carry is an 8-bit value that is interpreted as 1 when it is non-zero. The output carry is an 8-bit value that will be 0 or 1.

https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/addcarry-u32-addcarry-u64.html
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs13
-rw-r--r--src/tools/miri/tests/pass/intrinsics-x86.rs16
2 files changed, 21 insertions, 8 deletions
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 2db2f2e19ff..94759eafd3c 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -26,15 +26,17 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
         let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
         match unprefixed_name {
             // 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.
+            // Computes a + b with input and output carry. The input carry is an 8-bit
+            // value, which is interpreted as 1 if it is non-zero. The output carry is
+            // an 8-bit value that will be 0 or 1.
+            // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/addcarry-u32-addcarry-u64.html
             "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 c_in = this.read_scalar(c_in)?.to_u8()? != 0;
                 let a = this.read_immediate(a)?;
                 let b = this.read_immediate(b)?;
 
@@ -44,10 +46,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
                     &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);
+                let c_out = overflow1 | overflow2;
 
-                this.write_scalar(Scalar::from_u8(c_out), &this.project_field(dest, 0)?)?;
+                this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?;
                 this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
             }
 
diff --git a/src/tools/miri/tests/pass/intrinsics-x86.rs b/src/tools/miri/tests/pass/intrinsics-x86.rs
index 2e947200989..377f962bf14 100644
--- a/src/tools/miri/tests/pass/intrinsics-x86.rs
+++ b/src/tools/miri/tests/pass/intrinsics-x86.rs
@@ -14,8 +14,14 @@ mod x86 {
     }
 
     pub fn main() {
+        assert_eq!(adc(0, 1, 1), (0, 2));
         assert_eq!(adc(1, 1, 1), (0, 3));
-        assert_eq!(adc(3, u32::MAX, u32::MAX), (2, 1));
+        assert_eq!(adc(2, 1, 1), (0, 3)); // any non-zero carry acts as 1!
+        assert_eq!(adc(u8::MAX, 1, 1), (0, 3));
+        assert_eq!(adc(0, u32::MAX, u32::MAX), (1, u32::MAX - 1));
+        assert_eq!(adc(1, u32::MAX, u32::MAX), (1, u32::MAX));
+        assert_eq!(adc(2, u32::MAX, u32::MAX), (1, u32::MAX));
+        assert_eq!(adc(u8::MAX, u32::MAX, u32::MAX), (1, u32::MAX));
     }
 }
 
@@ -32,8 +38,14 @@ mod x86_64 {
     }
 
     pub fn main() {
+        assert_eq!(adc(0, 1, 1), (0, 2));
         assert_eq!(adc(1, 1, 1), (0, 3));
-        assert_eq!(adc(3, u64::MAX, u64::MAX), (2, 1));
+        assert_eq!(adc(2, 1, 1), (0, 3)); // any non-zero carry acts as 1!
+        assert_eq!(adc(u8::MAX, 1, 1), (0, 3));
+        assert_eq!(adc(0, u64::MAX, u64::MAX), (1, u64::MAX - 1));
+        assert_eq!(adc(1, u64::MAX, u64::MAX), (1, u64::MAX));
+        assert_eq!(adc(2, u64::MAX, u64::MAX), (1, u64::MAX));
+        assert_eq!(adc(u8::MAX, u64::MAX, u64::MAX), (1, u64::MAX));
     }
 }