Skip to content

Commit 6ec63ed

Browse files
committed
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
1 parent 3560399 commit 6ec63ed

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

src/tools/miri/src/shims/x86/mod.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,17 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
2626
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap();
2727
match unprefixed_name {
2828
// Used to implement the `_addcarry_u32` and `_addcarry_u64` functions.
29-
// Computes u8+uX+uX (uX is u32 or u64), returning tuple (u8,uX) comprising
30-
// the output carry and truncated sum.
29+
// Computes a + b with input and output carry. The input carry is an 8-bit
30+
// value, which is interpreted as 1 if it is non-zero. The output carry is
31+
// an 8-bit value that will be 0 or 1.
32+
// https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/addcarry-u32-addcarry-u64.html
3133
"addcarry.32" | "addcarry.64" => {
3234
if unprefixed_name == "addcarry.64" && this.tcx.sess.target.arch != "x86_64" {
3335
return Ok(EmulateByNameResult::NotSupported);
3436
}
3537

3638
let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
37-
let c_in = this.read_scalar(c_in)?.to_u8()?;
39+
let c_in = this.read_scalar(c_in)?.to_u8()? != 0;
3840
let a = this.read_immediate(a)?;
3941
let b = this.read_immediate(b)?;
4042

@@ -44,10 +46,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
4446
&sum,
4547
&ImmTy::from_uint(c_in, a.layout),
4648
)?;
47-
#[allow(clippy::arithmetic_side_effects)] // adding two bools into a u8
48-
let c_out = u8::from(overflow1) + u8::from(overflow2);
49+
let c_out = overflow1 | overflow2;
4950

50-
this.write_scalar(Scalar::from_u8(c_out), &this.project_field(dest, 0)?)?;
51+
this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?;
5152
this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
5253
}
5354

src/tools/miri/tests/pass/intrinsics-x86.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ mod x86 {
1414
}
1515

1616
pub fn main() {
17+
assert_eq!(adc(0, 1, 1), (0, 2));
1718
assert_eq!(adc(1, 1, 1), (0, 3));
18-
assert_eq!(adc(3, u32::MAX, u32::MAX), (2, 1));
19+
assert_eq!(adc(2, 1, 1), (0, 3)); // any non-zero carry acts as 1!
20+
assert_eq!(adc(u8::MAX, 1, 1), (0, 3));
21+
assert_eq!(adc(0, u32::MAX, u32::MAX), (1, u32::MAX - 1));
22+
assert_eq!(adc(1, u32::MAX, u32::MAX), (1, u32::MAX));
23+
assert_eq!(adc(2, u32::MAX, u32::MAX), (1, u32::MAX));
24+
assert_eq!(adc(u8::MAX, u32::MAX, u32::MAX), (1, u32::MAX));
1925
}
2026
}
2127

@@ -32,8 +38,14 @@ mod x86_64 {
3238
}
3339

3440
pub fn main() {
41+
assert_eq!(adc(0, 1, 1), (0, 2));
3542
assert_eq!(adc(1, 1, 1), (0, 3));
36-
assert_eq!(adc(3, u64::MAX, u64::MAX), (2, 1));
43+
assert_eq!(adc(2, 1, 1), (0, 3)); // any non-zero carry acts as 1!
44+
assert_eq!(adc(u8::MAX, 1, 1), (0, 3));
45+
assert_eq!(adc(0, u64::MAX, u64::MAX), (1, u64::MAX - 1));
46+
assert_eq!(adc(1, u64::MAX, u64::MAX), (1, u64::MAX));
47+
assert_eq!(adc(2, u64::MAX, u64::MAX), (1, u64::MAX));
48+
assert_eq!(adc(u8::MAX, u64::MAX, u64::MAX), (1, u64::MAX));
3749
}
3850
}
3951

0 commit comments

Comments
 (0)