Skip to content

Commit 20e70db

Browse files
committed
Auto merge of #135760 - scottmcm:disjoint-bitor, r=<try>
Add `unchecked_disjoint_bitor` per ACP373 Following the names from libs-api in rust-lang/libs-team#373 (comment) Includes a fallback implementation so this doesn't have to update cg_clif or cg_gcc, and overrides it in cg_llvm to use `or disjoint`, which [is available in LLVM 18](https://releases.llvm.org/18.1.0/docs/LangRef.html#or-instruction) so hopefully we don't need any version checks. try-job: x86_64-gnu-distcheck
2 parents 6c0de7f + 5e6ae8b commit 20e70db

File tree

14 files changed

+189
-4
lines changed

14 files changed

+189
-4
lines changed

compiler/rustc_codegen_llvm/src/builder.rs

+8
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
421421
unchecked_umul(x, y) => LLVMBuildNUWMul,
422422
}
423423

424+
fn or_disjoint(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value {
425+
unsafe {
426+
let or = llvm::LLVMBuildOr(self.llbuilder, a, b, UNNAMED);
427+
llvm::LLVMSetIsDisjoint(or, True);
428+
or
429+
}
430+
}
431+
424432
set_math_builder_methods! {
425433
fadd_fast(x, y) => (LLVMBuildFAdd, LLVMRustSetFastMath),
426434
fsub_fast(x, y) => (LLVMBuildFSub, LLVMRustSetFastMath),

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,9 @@ unsafe extern "C" {
13801380
pub fn LLVMBuildFNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
13811381
pub fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
13821382

1383+
// Extra flags on arithmetic
1384+
pub fn LLVMSetIsDisjoint(Instr: &Value, IsDisjoint: Bool);
1385+
13831386
// Memory
13841387
pub fn LLVMBuildAlloca<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
13851388
pub fn LLVMBuildArrayAlloca<'a>(

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

+5
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
225225
args[1].val.unaligned_volatile_store(bx, dst);
226226
return Ok(());
227227
}
228+
sym::disjoint_bitor => {
229+
let a = args[0].immediate();
230+
let b = args[1].immediate();
231+
bx.or_disjoint(a, b)
232+
}
228233
sym::exact_div => {
229234
let ty = arg_tys[0];
230235
match int_type_width_signed(ty, bx.tcx()) {

compiler/rustc_codegen_ssa/src/traits/builder.rs

+5
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ pub trait BuilderMethods<'a, 'tcx>:
167167
fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
168168
fn and(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
169169
fn or(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
170+
/// Defaults to [`Self::or`], but guarantees `(lhs & rhs) == 0` so some backends
171+
/// can emit something more helpful for optimizations.
172+
fn or_disjoint(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
173+
self.or(lhs, rhs)
174+
}
170175
fn xor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
171176
fn neg(&mut self, v: Self::Value) -> Self::Value;
172177
fn fneg(&mut self, v: Self::Value) -> Self::Value;

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ pub fn check_intrinsic_type(
472472
vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
473473
tcx.types.usize,
474474
),
475-
sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
475+
sym::unchecked_div | sym::unchecked_rem | sym::exact_div | sym::disjoint_bitor => {
476476
(1, 0, vec![param(0), param(0)], param(0))
477477
}
478478
sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ symbols! {
776776
discriminant_kind,
777777
discriminant_type,
778778
discriminant_value,
779+
disjoint_bitor,
779780
dispatch_from_dyn,
780781
div,
781782
div_assign,

library/core/src/intrinsics/fallback.rs

+38
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,41 @@ impl const CarryingMulAdd for i128 {
110110
(low, high)
111111
}
112112
}
113+
114+
#[const_trait]
115+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
116+
pub trait DisjointBitOr: Copy + 'static {
117+
/// See [`super::disjoint_bitor`]; we just need the trait indirection to handle
118+
/// different types since calling intrinsics with generics doesn't work.
119+
unsafe fn disjoint_bitor(self, other: Self) -> Self;
120+
}
121+
macro_rules! zero {
122+
(bool) => {
123+
false
124+
};
125+
($t:ident) => {
126+
0
127+
};
128+
}
129+
macro_rules! impl_disjoint_bitor {
130+
($($t:ident,)+) => {$(
131+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
132+
impl const DisjointBitOr for $t {
133+
#[cfg_attr(miri, track_caller)]
134+
#[inline]
135+
unsafe fn disjoint_bitor(self, other: Self) -> Self {
136+
// Note that the assume here is required for UB detection in Miri!
137+
138+
// SAFETY: our precondition is that there are no bits in common,
139+
// so this is just telling that to the backend.
140+
unsafe { super::assume((self & other) == zero!($t)) };
141+
self | other
142+
}
143+
}
144+
)+};
145+
}
146+
impl_disjoint_bitor! {
147+
bool,
148+
u8, u16, u32, u64, u128, usize,
149+
i8, i16, i32, i64, i128, isize,
150+
}

library/core/src/intrinsics/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -3244,6 +3244,26 @@ pub const fn three_way_compare<T: Copy>(_lhs: T, _rhss: T) -> crate::cmp::Orderi
32443244
unimplemented!()
32453245
}
32463246

3247+
/// Combine two values which have no bits in common.
3248+
///
3249+
/// This allows the backend to implement it as `a + b` *or* `a | b`,
3250+
/// depending which is easier to implement on a specific target.
3251+
///
3252+
/// # Safety
3253+
///
3254+
/// Requires that `(a & b) == 0`, or equivalently that `(a | b) == (a + b)`.
3255+
///
3256+
/// Otherwise it's immediate UB.
3257+
#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")]
3258+
#[rustc_nounwind]
3259+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
3260+
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
3261+
#[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri
3262+
pub const unsafe fn disjoint_bitor<T: ~const fallback::DisjointBitOr>(a: T, b: T) -> T {
3263+
// SAFETY: same preconditions as this function.
3264+
unsafe { fallback::DisjointBitOr::disjoint_bitor(a, b) }
3265+
}
3266+
32473267
/// Performs checked integer addition.
32483268
///
32493269
/// Note that, unlike most intrinsics, this is safe to call;

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
#![feature(const_eval_select)]
118118
#![feature(core_intrinsics)]
119119
#![feature(coverage_attribute)]
120+
#![feature(disjoint_bitor)]
120121
#![feature(internal_impls_macro)]
121122
#![feature(ip)]
122123
#![feature(is_ascii_octdigit)]

library/core/src/num/uint_macros.rs

+54-3
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,50 @@ macro_rules! uint_impl {
11871187
self % rhs
11881188
}
11891189

1190+
/// Same value as `self | other`, but UB if any bit position is set in both inputs.
1191+
///
1192+
/// This is a situational micro-optimization for places where you'd rather
1193+
/// use addition on some platforms and bitwise or on other platforms, based
1194+
/// on exactly which instructions combine better with whatever else you're
1195+
/// doing. Note that there's no reason to bother using this for places
1196+
/// where it's clear from the operations involved that they can't overlap.
1197+
/// For example, if you're combining `u16`s into a `u32` with
1198+
/// `((a as u32) << 16) | (b as u32)`, that's fine, as the backend will
1199+
/// know those sides of the `|` are disjoint without needing help.
1200+
///
1201+
/// # Examples
1202+
///
1203+
/// ```
1204+
/// #![feature(disjoint_bitor)]
1205+
///
1206+
/// // SAFETY: `1` and `4` have no bits in common.
1207+
/// unsafe {
1208+
#[doc = concat!(" assert_eq!(1_", stringify!($SelfT), ".unchecked_disjoint_bitor(4), 5);")]
1209+
/// }
1210+
/// ```
1211+
///
1212+
/// # Safety
1213+
///
1214+
/// Requires that `(self & other) == 0`, otherwise it's immediate UB.
1215+
///
1216+
/// Equivalently, requires that `(self | other) == (self + other)`.
1217+
#[unstable(feature = "disjoint_bitor", issue = "135758")]
1218+
#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")]
1219+
#[inline]
1220+
pub const unsafe fn unchecked_disjoint_bitor(self, other: Self) -> Self {
1221+
assert_unsafe_precondition!(
1222+
check_language_ub,
1223+
concat!(stringify!($SelfT), "::unchecked_disjoint_bitor cannot have overlapping bits"),
1224+
(
1225+
lhs: $SelfT = self,
1226+
rhs: $SelfT = other,
1227+
) => (lhs & rhs) == 0,
1228+
);
1229+
1230+
// SAFETY: Same precondition
1231+
unsafe { intrinsics::disjoint_bitor(self, other) }
1232+
}
1233+
11901234
/// Returns the logarithm of the number with respect to an arbitrary base,
11911235
/// rounded down.
11921236
///
@@ -2346,15 +2390,22 @@ macro_rules! uint_impl {
23462390
/// assert_eq!((sum1, sum0), (9, 6));
23472391
/// ```
23482392
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
2393+
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
23492394
#[must_use = "this returns the result of the operation, \
23502395
without modifying the original"]
23512396
#[inline]
23522397
pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
23532398
// note: longer-term this should be done via an intrinsic, but this has been shown
23542399
// to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic
2355-
let (a, b) = self.overflowing_add(rhs);
2356-
let (c, d) = a.overflowing_add(carry as $SelfT);
2357-
(c, b | d)
2400+
let (a, c1) = self.overflowing_add(rhs);
2401+
let (b, c2) = a.overflowing_add(carry as $SelfT);
2402+
// Ideally LLVM would know this is disjoint without us telling them,
2403+
// but it doesn't <https://github.com/llvm/llvm-project/issues/118162>
2404+
// SAFETY: Only one of `c1` and `c2` can be set.
2405+
// For c1 to be set we need to have overflowed, but if we did then
2406+
// `a` is at most `MAX-1`, which means that `c2` cannot possibly
2407+
// overflow because it's adding at most `1` (since it came from `bool`)
2408+
(b, unsafe { intrinsics::disjoint_bitor(c1, c2) })
23582409
}
23592410

23602411
/// Calculates `self` + `rhs` with a signed `rhs`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![feature(core_intrinsics)]
2+
fn main() {
3+
// one bit in common
4+
unsafe { std::intrinsics::disjoint_bitor(0b01101001_u8, 0b10001110) }; //~ ERROR: Undefined Behavior
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: `assume` called with `false`
2+
--> tests/fail/intrinsics/disjoint_bitor.rs:LL:CC
3+
|
4+
LL | unsafe { std::intrinsics::disjoint_bitor(0b01101001_u8, 0b10001110) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false`
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at tests/fail/intrinsics/disjoint_bitor.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

tests/codegen/bigint-helpers.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ compile-flags: -C opt-level=3
2+
3+
#![crate_type = "lib"]
4+
#![feature(bigint_helper_methods)]
5+
6+
// CHECK-LABEL: @u32_carrying_add
7+
#[no_mangle]
8+
pub fn u32_carrying_add(a: u32, b: u32, c: bool) -> (u32, bool) {
9+
// CHECK: @llvm.uadd.with.overflow.i32
10+
// CHECK: @llvm.uadd.with.overflow.i32
11+
// CHECK: or disjoint i1
12+
u32::carrying_add(a, b, c)
13+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ compile-flags: -C no-prepopulate-passes
2+
3+
#![crate_type = "lib"]
4+
#![feature(core_intrinsics)]
5+
6+
use std::intrinsics::disjoint_bitor;
7+
8+
// CHECK-LABEL: @disjoint_bitor_signed
9+
#[no_mangle]
10+
pub unsafe fn disjoint_bitor_signed(x: i32, y: i32) -> i32 {
11+
// CHECK: or disjoint i32 %x, %y
12+
disjoint_bitor(x, y)
13+
}
14+
15+
// CHECK-LABEL: @disjoint_bitor_unsigned
16+
#[no_mangle]
17+
pub unsafe fn disjoint_bitor_unsigned(x: u64, y: u64) -> u64 {
18+
// CHECK: or disjoint i64 %x, %y
19+
disjoint_bitor(x, y)
20+
}

0 commit comments

Comments
 (0)