Skip to content

Commit b8b92cb

Browse files
Centri3NCGThompson
authored andcommitted
Make MIRI choose the path randomly and rename the intrinsic
1 parent 9d96d1f commit b8b92cb

File tree

12 files changed

+129
-87
lines changed

12 files changed

+129
-87
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
119119
sym::likely => {
120120
self.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(true)])
121121
}
122-
sym::is_constant => self.call_intrinsic("llvm.is.constant", &[args[0].immediate()]),
122+
sym::is_compile_time_known => {
123+
self.call_intrinsic("llvm.is.constant", &[args[0].immediate()])
124+
}
123125
sym::unlikely => self
124126
.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]),
125127
kw::Try => {

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
531531
)?;
532532
}
533533
}
534+
sym::is_compile_time_known => ecx.write_scalar(Scalar::from_bool(false), dest)?,
534535
_ => {
535536
throw_unsup_format!(
536537
"intrinsic `{intrinsic_name}` is not supported at compile-time"

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
216216
sym::copy => {
217217
self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?;
218218
}
219-
sym::is_constant => self.write_scalar(Scalar::from_bool(true), dest)?,
219+
sym::is_compile_time_known => self.write_scalar(Scalar::from_bool(false), dest)?,
220220
sym::write_bytes => {
221221
self.write_bytes_intrinsic(&args[0], &args[1], &args[2])?;
222222
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir
112112
| sym::forget
113113
| sym::black_box
114114
| sym::variant_count
115-
| sym::ptr_mask
116-
| sym::is_constant => hir::Unsafety::Normal,
115+
| sym::ptr_mask => hir::Unsafety::Normal,
117116
_ => hir::Unsafety::Unsafe,
118117
};
119118

@@ -454,10 +453,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
454453

455454
sym::black_box => (1, vec![param(0)], param(0)),
456455

457-
sym::is_constant => {
458-
// FIXME: ZSTs cause an ICE. We should check for this.
459-
(1, vec![param(0)], tcx.types.bool)
460-
}
456+
sym::is_compile_time_known => (1, vec![param(0)], tcx.types.bool),
461457

462458
sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
463459

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ symbols! {
907907
io_stderr,
908908
io_stdout,
909909
irrefutable_let_patterns,
910-
is_constant,
910+
is_compile_time_known,
911911
isa_attribute,
912912
isize,
913913
issue,

library/core/src/intrinsics.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,24 +2512,29 @@ extern "rust-intrinsic" {
25122512
G: FnOnce<ARG, Output = RET>,
25132513
F: FnOnce<ARG, Output = RET>;
25142514

2515-
/// Returns whether the argument is known at compile-time. This opens the
2516-
/// door for additional optimizations, in that LLVM can then optimize
2517-
/// following checks to either `true` or `false`. This is often paired with
2518-
/// an `if-else` statement, as LLVM will only keep one branch (if
2519-
/// optimizations are on).
2515+
/// Returns whether the argument is known at compile-time.
25202516
///
2521-
/// "Constant" in this context is not the same as a constant in Rust. As
2522-
/// such, this should only be used for optimizations.
2517+
/// This is useful when there is a way of writing the code that will
2518+
/// be *faster* when some variables have known values, but *slower*
2519+
/// in the general case: an `if is_compile_time_known(var)` can be used
2520+
/// to select between these two variants. The `if` will be optimized away
2521+
/// and only the desired branch remains.
25232522
///
2524-
/// Note that, unlike most intrinsics, this is safe to call;
2525-
/// it does not require an `unsafe` block.
2526-
/// Therefore, implementations must not require the user to uphold
2527-
/// any safety invariants.
2528-
#[rustc_const_stable(feature = "todo", since = "never")]
2529-
#[rustc_safe_intrinsic]
2523+
/// Formally speaking, this function non-deterministically returns `true`
2524+
/// or `false`, and the caller has to ensure sound behavior for both cases.
2525+
/// In other words, the following code has *Undefined Behavior*:
2526+
///
2527+
/// ```rust
2528+
/// if !is_compile_time_known(0) { unreachable_unchecked(); }
2529+
/// ```
2530+
///
2531+
/// Unsafe code may not rely on `is_compile_time_known` returning any
2532+
/// particular value, ever. However, the compiler will generally make it
2533+
/// return `true` only if the value of the argument is actually known.
2534+
#[rustc_const_unstable(feature = "is_compile_time_known", issue = "none")]
25302535
#[rustc_nounwind]
25312536
#[cfg(not(bootstrap))]
2532-
pub fn is_constant<T>(arg: T) -> bool;
2537+
pub fn is_compile_time_known<T>(arg: T) -> bool;
25332538
}
25342539

25352540
// Some functions are defined here because they accidentally got made

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@
197197
//
198198
// Language features:
199199
// tidy-alphabetical-start
200+
#![cfg_attr(not(bootstrap), feature(is_compile_time_known))]
200201
#![feature(abi_unadjusted)]
201202
#![feature(adt_const_params)]
202203
#![feature(allow_internal_unsafe)]

library/core/src/num/int_macros.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,9 +2088,14 @@ macro_rules! int_impl {
20882088
without modifying the original"]
20892089
#[inline]
20902090
#[rustc_inherit_overflow_checks]
2091+
#[rustc_allow_const_fn_unstable(is_compile_time_known)]
20912092
pub const fn pow(self, mut exp: u32) -> Self {
20922093
#[cfg(not(bootstrap))]
2093-
if intrinsics::is_constant(self) && self > 0 && (self & (self - 1) == 0) {
2094+
// SAFETY: This path has the same behavior as the other.
2095+
if unsafe { intrinsics::is_compile_time_known(self) }
2096+
&& self > 0
2097+
&& (self & (self - 1) == 0)
2098+
{
20942099
let power_used = match self.checked_ilog2() {
20952100
Some(v) => v,
20962101
// SAFETY: We just checked this is a power of two. and above zero.
@@ -2099,7 +2104,9 @@ macro_rules! int_impl {
20992104
// So it panics. Have to use `overflowing_mul` to efficiently set the
21002105
// result to 0 if not.
21012106
#[cfg(debug_assertions)]
2102-
power_used * exp;
2107+
{
2108+
_ = power_used * exp;
2109+
}
21032110
let (num_shl, overflowed) = power_used.overflowing_mul(exp);
21042111
let fine = !overflowed
21052112
& (num_shl < (mem::size_of::<Self>() * 8) as u32);

library/core/src/num/uint_macros.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,6 +1973,7 @@ macro_rules! uint_impl {
19731973
without modifying the original"]
19741974
#[inline]
19751975
#[rustc_inherit_overflow_checks]
1976+
#[rustc_allow_const_fn_unstable(is_compile_time_known)]
19761977
pub const fn pow(self, mut exp: u32) -> Self {
19771978
// LLVM now knows that `self` is a constant value, but not a
19781979
// constant in Rust. This allows us to compute the power used at
@@ -1986,7 +1987,8 @@ macro_rules! uint_impl {
19861987
// instruction, but we must add a couple more checks for parity with
19871988
// our own `pow`.
19881989
#[cfg(not(bootstrap))]
1989-
if intrinsics::is_constant(self) && self.is_power_of_two() {
1990+
// SAFETY: This path has the same behavior as the other.
1991+
if unsafe { intrinsics::is_compile_time_known(self) } && self.is_power_of_two() {
19901992
let power_used = match self.checked_ilog2() {
19911993
Some(v) => v,
19921994
// SAFETY: We just checked this is a power of two. `0` is not a
@@ -1996,7 +1998,9 @@ macro_rules! uint_impl {
19961998
// So it panics. Have to use `overflowing_mul` to efficiently set the
19971999
// result to 0 if not.
19982000
#[cfg(debug_assertions)]
1999-
power_used * exp;
2001+
{
2002+
_ = power_used * exp;
2003+
}
20002004
let (num_shl, overflowed) = power_used.overflowing_mul(exp);
20012005
let fine = !overflowed
20022006
& (num_shl < (mem::size_of::<Self>() * 8) as u32);

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141141
this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
142142
}
143143

144+
// The default implementation always returns `false`, but we want
145+
// to return either `true` or `false` at random.
146+
"is_comptile_time_known" => {
147+
let rand = 0;
148+
_ = getrandom::getrandom(&mut [rand]);
149+
150+
this.write_scalar(
151+
Scalar::from_bool(if (rand % 1) == 1 { true } else { false }),
152+
dest,
153+
)?;
154+
}
155+
144156
// Floating-point operations
145157
"fabsf32" => {
146158
let [f] = check_arg_count(args)?;
@@ -417,4 +429,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
417429

418430
Ok(())
419431
}
432+
<<<<<<< HEAD
433+
=======
434+
435+
fn float_to_int_unchecked<F>(
436+
&self,
437+
f: F,
438+
dest_ty: Ty<'tcx>,
439+
) -> InterpResult<'tcx, Scalar<Provenance>>
440+
where
441+
F: Float + Into<Scalar<Provenance>>,
442+
{
443+
let this = self.eval_context_ref();
444+
445+
// Step 1: cut off the fractional part of `f`. The result of this is
446+
// guaranteed to be precisely representable in IEEE floats.
447+
let f = f.round_to_integral(Round::TowardZero).value;
448+
449+
// Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
450+
Ok(match dest_ty.kind() {
451+
// Unsigned
452+
ty::Uint(t) => {
453+
let size = Integer::from_uint_ty(this, *t).size();
454+
let res = f.to_u128(size.bits_usize());
455+
if res.status.is_empty() {
456+
// No status flags means there was no further rounding or other loss of precision.
457+
Scalar::from_uint(res.value, size)
458+
} else {
459+
// `f` was not representable in this integer type.
460+
throw_ub_format!(
461+
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`",
462+
);
463+
}
464+
}
465+
// Signed
466+
ty::Int(t) => {
467+
let size = Integer::from_int_ty(this, *t).size();
468+
let res = f.to_i128(size.bits_usize());
469+
if res.status.is_empty() {
470+
// No status flags means there was no further rounding or other loss of precision.
471+
Scalar::from_int(res.value, size)
472+
} else {
473+
// `f` was not representable in this integer type.
474+
throw_ub_format!(
475+
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`",
476+
);
477+
}
478+
}
479+
// Nothing else
480+
_ => span_bug!(
481+
this.cur_span(),
482+
"`float_to_int_unchecked` called with non-int output type {dest_ty:?}"
483+
),
484+
})
485+
}
486+
>>>>>>> acbf90a9489 (Make MIRI choose the path randomly and rename the intrinsic)
420487
}

tests/codegen/is_constant.rs

Lines changed: 0 additions & 47 deletions
This file was deleted.

tests/codegen/pow_of_two.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
pub fn a(exp: u32) -> u64 {
77
// CHECK: %[[R:.+]] = and i32 %exp, 63
88
// CHECK: %[[R:.+]] = zext i32 %[[R:.+]] to i64
9-
// CHECK: %[[R:.+]].i = shl nuw i64 1, %[[R:.+]]
10-
// CHECK: ret i64 %[[R:.+]].i
9+
// CHECK: %[[R:.+]] = shl nuw i64 %[[R:.+]].i, %[[R:.+]]
10+
// CHECK: ret i64 %[[R:.+]]
1111
2u64.pow(exp)
1212
}
1313

@@ -21,37 +21,43 @@ pub fn b(exp: u32) -> i64 {
2121
pub fn c(exp: u32) -> u32 {
2222
// CHECK: %[[R:.+]].0.i = shl i32 %exp, 1
2323
// CHECK: %[[R:.+]].1.i = icmp sgt i32 %exp, -1
24-
// CHECK: %[[R:.+]] = and i32 %[[R:.+]].0.i, 30
25-
// CHECK: %[[R:.+]].i = zext i1 %[[R:.+]].1.i to i32
26-
// CHECK: %[[R:.+]] = shl nuw nsw i32 %[[R:.+]].i, %[[R:.+]]
24+
// CHECK: %[[R:.+]].i = icmp ult i32 %[[R:.+]].0.i, 32
25+
// CHECK: %fine.i = and i1 %[[R:.+]].1.i, %[[R:.+]].i
26+
// CHECK: %0 = and i32 %[[R:.+]].0.i, 30
27+
// CHECK: %[[R:.+]].i = zext i1 %fine.i to i32
28+
// CHECK: %[[R:.+]] = shl nuw nsw i32 %[[R:.+]].i, %0
2729
// CHECK: ret i32 %[[R:.+]]
2830
4u32.pow(exp)
2931
}
3032

3133
// CHECK-LABEL: @d(
3234
#[no_mangle]
3335
pub fn d(exp: u32) -> u32 {
34-
// CHECK: %[[R:.+]] = tail call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %exp, i32 5)
36+
// CHECK: tail call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %exp, i32 5)
3537
// CHECK: %[[R:.+]].0.i = extractvalue { i32, i1 } %[[R:.+]], 0
3638
// CHECK: %[[R:.+]].1.i = extractvalue { i32, i1 } %[[R:.+]], 1
3739
// CHECK: %[[R:.+]].i = xor i1 %[[R:.+]].1.i, true
40+
// CHECK: %[[R:.+]].i = icmp ult i32 %[[R:.+]].0.i, 32
41+
// CHECK: %fine.i = and i1 %[[R:.+]].i, %[[R:.+]].i
3842
// CHECK: %[[R:.+]] = and i32 %[[R:.+]].0.i, 31
39-
// CHECK: %[[R:.+]].i = zext i1 %[[R:.+]].i to i32
40-
// CHECK: %[[R:.+]] = shl nuw i32 %[[R:.+]].i, %[[R:.+]]
43+
// CHECK: %[[R:.+]].i = zext i1 %fine.i to i32
44+
// CHECK: %[[R:.+]] = shl nuw i32 %[[R:.+]].i, %1
4145
// CHECK: ret i32 %[[R:.+]]
4246
32u32.pow(exp)
4347
}
4448

4549
// CHECK-LABEL: @e(
4650
#[no_mangle]
4751
pub fn e(exp: u32) -> i32 {
48-
// CHECK: %[[R:.+]] = tail call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %exp, i32 5)
52+
// CHECK: tail call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %exp, i32 5)
53+
// CHECK: %[[R:.+]].0.i = extractvalue { i32, i1 } %[[R:.+]], 0
54+
// CHECK: %[[R:.+]].i = icmp ult i32 %[[R:.+]].0.i, 32
4955
// CHECK: %[[R:.+]].1.i = extractvalue { i32, i1 } %[[R:.+]], 1
5056
// CHECK: %[[R:.+]].i = xor i1 %[[R:.+]].1.i, true
51-
// CHECK: %[[R:.+]].i = zext i1 %[[R:.+]].i to i32
52-
// CHECK: %[[R:.+]].0.i = extractvalue { i32, i1 } %[[R:.+]], 0
57+
// CHECK: %fine.i = and i1 %[[R:.+]].i, %[[R:.+]].i
58+
// CHECK: %[[R:.+]].i = zext i1 %fine.i to i32
5359
// CHECK: %[[R:.+]] = and i32 %[[R:.+]].0.i, 31
54-
// CHECK: %[[R:.+]].010.i = shl nuw i32 %[[R:.+]].i, %[[R:.+]]
55-
// CHECK: ret i32 %[[R:.+]].010.i
60+
// CHECK: %[[R:.+]] = shl nuw i32 %[[R:.+]].i, %1
61+
// CHECK: ret i32 %[[R:.+]]
5662
32i32.pow(exp)
5763
}

0 commit comments

Comments
 (0)