Skip to content

Commit 9abab80

Browse files
committed
Implement llvm.x86.subborrow.32 and llvm.x86.subborrow.64
1 parent b50f80f commit 9abab80

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

src/shims/x86/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,32 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
5151
this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?;
5252
this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
5353
}
54+
// Used to implement the `_subborrow_u32` and `_subborrow_u64` functions.
55+
// Computes a - b with input and output borrow. The input borrow is an 8-bit
56+
// value, which is interpreted as 1 if it is non-zero. The output borrow is
57+
// an 8-bit value that will be 0 or 1.
58+
// https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html
59+
"subborrow.32" | "subborrow.64" => {
60+
if unprefixed_name == "subborrow.64" && this.tcx.sess.target.arch != "x86_64" {
61+
return Ok(EmulateByNameResult::NotSupported);
62+
}
63+
64+
let [b_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
65+
let b_in = this.read_scalar(b_in)?.to_u8()? != 0;
66+
let a = this.read_immediate(a)?;
67+
let b = this.read_immediate(b)?;
68+
69+
let (sub, overflow1) = this.overflowing_binary_op(mir::BinOp::Sub, &a, &b)?;
70+
let (sub, overflow2) = this.overflowing_binary_op(
71+
mir::BinOp::Sub,
72+
&sub,
73+
&ImmTy::from_uint(b_in, a.layout),
74+
)?;
75+
let b_out = overflow1 | overflow2;
76+
77+
this.write_scalar(Scalar::from_u8(b_out.into()), &this.project_field(dest, 0)?)?;
78+
this.write_immediate(*sub, &this.project_field(dest, 1)?)?;
79+
}
5480

5581
name if name.starts_with("sse.") => {
5682
return sse::EvalContextExt::emulate_x86_sse_intrinsic(

tests/pass/intrinsics-x86.rs

+42
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ mod x86 {
1313
(c_out, sum)
1414
}
1515

16+
fn sbb(b_in: u8, a: u32, b: u32) -> (u8, u32) {
17+
let mut sum = 0;
18+
// SAFETY: There are no safety requirements for calling `_subborrow_u32`.
19+
// It's just unsafe for API consistency with other intrinsics.
20+
let b_out = unsafe { arch::_subborrow_u32(b_in, a, b, &mut sum) };
21+
(b_out, sum)
22+
}
23+
1624
pub fn main() {
1725
assert_eq!(adc(0, 1, 1), (0, 2));
1826
assert_eq!(adc(1, 1, 1), (0, 3));
@@ -22,6 +30,19 @@ mod x86 {
2230
assert_eq!(adc(1, u32::MAX, u32::MAX), (1, u32::MAX));
2331
assert_eq!(adc(2, u32::MAX, u32::MAX), (1, u32::MAX));
2432
assert_eq!(adc(u8::MAX, u32::MAX, u32::MAX), (1, u32::MAX));
33+
34+
assert_eq!(sbb(0, 1, 1), (0, 0));
35+
assert_eq!(sbb(1, 1, 1), (1, u32::MAX));
36+
assert_eq!(sbb(2, 1, 1), (1, u32::MAX)); // any non-zero borrow acts as 1!
37+
assert_eq!(sbb(u8::MAX, 1, 1), (1, u32::MAX));
38+
assert_eq!(sbb(0, 2, 1), (0, 1));
39+
assert_eq!(sbb(1, 2, 1), (0, 0));
40+
assert_eq!(sbb(2, 2, 1), (0, 0));
41+
assert_eq!(sbb(u8::MAX, 2, 1), (0, 0));
42+
assert_eq!(sbb(0, 1, 2), (1, u32::MAX));
43+
assert_eq!(sbb(1, 1, 2), (1, u32::MAX - 1));
44+
assert_eq!(sbb(2, 1, 2), (1, u32::MAX - 1));
45+
assert_eq!(sbb(u8::MAX, 1, 2), (1, u32::MAX - 1));
2546
}
2647
}
2748

@@ -37,6 +58,14 @@ mod x86_64 {
3758
(c_out, sum)
3859
}
3960

61+
fn sbb(b_in: u8, a: u64, b: u64) -> (u8, u64) {
62+
let mut sum = 0;
63+
// SAFETY: There are no safety requirements for calling `_subborrow_u64`.
64+
// It's just unsafe for API consistency with other intrinsics.
65+
let b_out = unsafe { arch::_subborrow_u64(b_in, a, b, &mut sum) };
66+
(b_out, sum)
67+
}
68+
4069
pub fn main() {
4170
assert_eq!(adc(0, 1, 1), (0, 2));
4271
assert_eq!(adc(1, 1, 1), (0, 3));
@@ -46,6 +75,19 @@ mod x86_64 {
4675
assert_eq!(adc(1, u64::MAX, u64::MAX), (1, u64::MAX));
4776
assert_eq!(adc(2, u64::MAX, u64::MAX), (1, u64::MAX));
4877
assert_eq!(adc(u8::MAX, u64::MAX, u64::MAX), (1, u64::MAX));
78+
79+
assert_eq!(sbb(0, 1, 1), (0, 0));
80+
assert_eq!(sbb(1, 1, 1), (1, u64::MAX));
81+
assert_eq!(sbb(2, 1, 1), (1, u64::MAX)); // any non-zero borrow acts as 1!
82+
assert_eq!(sbb(u8::MAX, 1, 1), (1, u64::MAX));
83+
assert_eq!(sbb(0, 2, 1), (0, 1));
84+
assert_eq!(sbb(1, 2, 1), (0, 0));
85+
assert_eq!(sbb(2, 2, 1), (0, 0));
86+
assert_eq!(sbb(u8::MAX, 2, 1), (0, 0));
87+
assert_eq!(sbb(0, 1, 2), (1, u64::MAX));
88+
assert_eq!(sbb(1, 1, 2), (1, u64::MAX - 1));
89+
assert_eq!(sbb(2, 1, 2), (1, u64::MAX - 1));
90+
assert_eq!(sbb(u8::MAX, 1, 2), (1, u64::MAX - 1));
4991
}
5092
}
5193

0 commit comments

Comments
 (0)