Skip to content

Commit 8a63333

Browse files
committed
Auto merge of rust-lang#119452 - AngelicosPhosphoros:make_nonzeroint_get_assume_nonzero, r=<try>
Add assume into `NonZeroIntX::get` LLVM currently don't support range metadata for function arguments so it fails to optimize non zero integers using their invariant if they are provided using by-value function arguments. Related to rust-lang#119422 Related to llvm/llvm-project#76628 Related to rust-lang#49572
2 parents 5431404 + 8f432d4 commit 8a63333

File tree

2 files changed

+101
-4
lines changed

2 files changed

+101
-4
lines changed

library/core/src/num/nonzero.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ macro_rules! nonzero_integers {
104104
#[inline]
105105
#[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")]
106106
pub const fn get(self) -> $Int {
107+
// FIXME: Remove this after LLVM supports `!range` metadata for function
108+
// arguments https://github.com/llvm/llvm-project/issues/76628
109+
//
110+
// Rustc can set range metadata only if it loads `self` from
111+
// memory somewhere. If the value of `self` was from by-value argument
112+
// of some not-inlined function, LLVM don't have range metadata
113+
// to understand that the value cannot be zero.
114+
115+
// SAFETY: It is an invariant of this type.
116+
unsafe {
117+
intrinsics::assume(self.0 != 0);
118+
}
107119
self.0
108120
}
109121

@@ -114,7 +126,9 @@ macro_rules! nonzero_integers {
114126
#[doc = concat!("Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`")]
115127
#[inline]
116128
fn from(nonzero: $Ty) -> Self {
117-
nonzero.0
129+
// Call nonzero to keep information range information
130+
// from get method.
131+
nonzero.get()
118132
}
119133
}
120134

@@ -233,7 +247,7 @@ macro_rules! nonzero_leading_trailing_zeros {
233247
#[inline]
234248
pub const fn leading_zeros(self) -> u32 {
235249
// SAFETY: since `self` cannot be zero, it is safe to call `ctlz_nonzero`.
236-
unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 }
250+
unsafe { intrinsics::ctlz_nonzero(self.get() as $Uint) as u32 }
237251
}
238252

239253
/// Returns the number of trailing zeros in the binary representation
@@ -257,7 +271,7 @@ macro_rules! nonzero_leading_trailing_zeros {
257271
#[inline]
258272
pub const fn trailing_zeros(self) -> u32 {
259273
// SAFETY: since `self` cannot be zero, it is safe to call `cttz_nonzero`.
260-
unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 }
274+
unsafe { intrinsics::cttz_nonzero(self.get() as $Uint) as u32 }
261275
}
262276

263277
}
@@ -515,7 +529,7 @@ macro_rules! nonzero_unsigned_operations {
515529
without modifying the original"]
516530
#[inline]
517531
pub const fn ilog10(self) -> u32 {
518-
super::int_log10::$Int(self.0)
532+
super::int_log10::$Int(self.get())
519533
}
520534

521535
/// Calculates the middle point of `self` and `rhs`.

tests/codegen/issues/issue-119422.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//! This test checks that compiler don't generate useless compares to zeros
2+
//! for NonZero integer types.
3+
4+
// compile-flags: -O --edition=2021 -Zmerge-functions=disabled
5+
// only-64bit (because the LLVM type of i64 for usize shows up)
6+
7+
#![crate_type = "lib"]
8+
9+
use core::num::*;
10+
use core::ptr::NonNull;
11+
12+
// CHECK-LABEL: @check_non_null
13+
#[no_mangle]
14+
pub fn check_non_null(x: NonNull<u8>) -> bool {
15+
// CHECK: ret i1 false
16+
x.as_ptr().is_null()
17+
}
18+
19+
// CHECK-LABEL: @equals_zero_is_false_u8
20+
#[no_mangle]
21+
pub fn equals_zero_is_false_u8(x: NonZeroU8) -> bool {
22+
// CHECK-NOT: br
23+
// CHECK: ret i1 false
24+
// CHECK-NOT: br
25+
x.get() == 0
26+
}
27+
28+
// CHECK-LABEL: @not_equals_zero_is_true_u8
29+
#[no_mangle]
30+
pub fn not_equals_zero_is_true_u8(x: NonZeroU8) -> bool {
31+
// CHECK-NOT: br
32+
// CHECK: ret i1 true
33+
// CHECK-NOT: br
34+
x.get() != 0
35+
}
36+
37+
// CHECK-LABEL: @equals_zero_is_false_i8
38+
#[no_mangle]
39+
pub fn equals_zero_is_false_i8(x: NonZeroI8) -> bool {
40+
// CHECK-NOT: br
41+
// CHECK: ret i1 false
42+
// CHECK-NOT: br
43+
x.get() == 0
44+
}
45+
46+
// CHECK-LABEL: @not_equals_zero_is_true_i8
47+
#[no_mangle]
48+
pub fn not_equals_zero_is_true_i8(x: NonZeroI8) -> bool {
49+
// CHECK-NOT: br
50+
// CHECK: ret i1 true
51+
// CHECK-NOT: br
52+
x.get() != 0
53+
}
54+
55+
// CHECK-LABEL: @usize_try_from_u32
56+
#[no_mangle]
57+
pub fn usize_try_from_u32(x: NonZeroU32) -> NonZeroUsize {
58+
// CHECK-NOT: br
59+
// CHECK: zext i32 %{{.*}} to i64
60+
// CHECK-NOT: br
61+
// CHECK: ret i64
62+
x.try_into().unwrap()
63+
}
64+
65+
// CHECK-LABEL: @isize_try_from_i32
66+
#[no_mangle]
67+
pub fn isize_try_from_i32(x: NonZeroI32) -> NonZeroIsize {
68+
// CHECK-NOT: br
69+
// CHECK: sext i32 %{{.*}} to i64
70+
// CHECK-NOT: br
71+
// CHECK: ret i64
72+
x.try_into().unwrap()
73+
}
74+
75+
// CHECK-LABEL: @u64_from_nonzero_is_not_zero
76+
#[no_mangle]
77+
pub fn u64_from_nonzero_is_not_zero(x: NonZeroU64)->bool {
78+
// CHECK-NOT: br
79+
// CHECK: ret i1 false
80+
// CHECK-NOT: br
81+
let v: u64 = x.into();
82+
v == 0
83+
}

0 commit comments

Comments
 (0)