Skip to content

Commit ffe0471

Browse files
scottmcmgitbot
authored and
gitbot
committed
Add unchecked_disjoint_bitor with fallback intrinsic implementation
1 parent 6b6ad0a commit ffe0471

File tree

4 files changed

+116
-3
lines changed

4 files changed

+116
-3
lines changed

core/src/intrinsics/fallback.rs

+40
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,43 @@ 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+
/// This is always just `assume((self & other) == 0); self | other`.
118+
///
119+
/// It's essential that the assume is there so that this is sufficient to
120+
/// specify the UB for MIRI, rather than it needing to re-implement it.
121+
///
122+
/// # Safety
123+
/// See [`super::disjoint_bitor`].
124+
unsafe fn disjoint_bitor(self, other: Self) -> Self;
125+
}
126+
macro_rules! zero {
127+
(bool) => {
128+
false
129+
};
130+
($t:ident) => {
131+
0
132+
};
133+
}
134+
macro_rules! impl_disjoint_bitor {
135+
($($t:ident,)+) => {$(
136+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
137+
impl const DisjointBitOr for $t {
138+
#[inline]
139+
unsafe fn disjoint_bitor(self, other: Self) -> Self {
140+
// SAFETY: our precondition is that there are no bits in common,
141+
// so this is just telling that to the backend.
142+
unsafe { super::assume((self & other) == zero!($t)) };
143+
self | other
144+
}
145+
}
146+
)+};
147+
}
148+
impl_disjoint_bitor! {
149+
bool,
150+
u8, u16, u32, u64, u128, usize,
151+
i8, i16, i32, i64, i128, isize,
152+
}

core/src/intrinsics/mod.rs

+19
Original file line numberDiff line numberDiff line change
@@ -3248,6 +3248,25 @@ pub const fn three_way_compare<T: Copy>(_lhs: T, _rhss: T) -> crate::cmp::Orderi
32483248
unimplemented!()
32493249
}
32503250

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

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)]

core/src/num/uint_macros.rs

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

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

23602413
/// Calculates `self` + `rhs` with a signed `rhs`.

0 commit comments

Comments
 (0)