Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 039d887

Browse files
committedJan 25, 2024
Auto merge of rust-lang#119911 - NCGThompson:is-statically-known, r=oli-obk
Replacement of rust-lang#114390: Add new intrinsic `is_var_statically_known` and optimize pow for powers of two This adds a new intrinsic `is_val_statically_known` that lowers to [``@llvm.is.constant.*`](https://llvm.org/docs/LangRef.html#llvm-is-constant-intrinsic).` It also applies the intrinsic in the int_pow methods to recognize and optimize the idiom `2isize.pow(x)`. See rust-lang#114390 for more discussion. While I have extended the scope of the power of two optimization from rust-lang#114390, I haven't added any new uses for the intrinsic. That can be done in later pull requests. Note: When testing or using the library, be sure to use `--stage 1` or higher. Otherwise, the intrinsic will be a noop and the doctests will be skipped. If you are trying out edits, you may be interested in [`--keep-stage 0`](https://rustc-dev-guide.rust-lang.org/building/suggested.html#faster-builds-with---keep-stage). Fixes rust-lang#47234 Resolves rust-lang#114390 `@Centri3`
2 parents 68411c9 + 9dccd5d commit 039d887

File tree

17 files changed

+632
-152
lines changed

17 files changed

+632
-152
lines changed
 

‎compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
443443

444444
ret.write_cvalue(fx, a);
445445
}
446+
sym::is_val_statically_known => {
447+
intrinsic_args!(fx, args => (_a); intrinsic);
448+
449+
let res = fx.bcx.ins().iconst(types::I8, 0);
450+
ret.write_cvalue(fx, CValue::by_val(res, ret.layout()));
451+
}
446452
sym::breakpoint => {
447453
intrinsic_args!(fx, args => (); intrinsic);
448454

‎compiler/rustc_codegen_gcc/src/context.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,16 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
196196

197197
let mut functions = FxHashMap::default();
198198
let builtins = [
199-
"__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow",
200-
"__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/
199+
"__builtin_unreachable", "abort", "__builtin_expect", /*"__builtin_expect_with_probability",*/
200+
"__builtin_constant_p", "__builtin_add_overflow", "__builtin_mul_overflow", "__builtin_saddll_overflow",
201+
/*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/
201202
"__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow",
202203
"__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow",
203204
"__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos",
204205
"powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf",
205206
"fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf",
206207
"ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round",
207-
"__builtin_expect_with_probability",
208+
208209
];
209210

210211
for builtin in builtins.iter() {

‎compiler/rustc_codegen_gcc/src/intrinsic/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
123123
sym::unlikely => {
124124
self.expect(args[0].immediate(), false)
125125
}
126+
sym::is_val_statically_known => {
127+
let a = args[0].immediate();
128+
let builtin = self.context.get_builtin_function("__builtin_constant_p");
129+
let res = self.context.new_call(None, builtin, &[a]);
130+
self.icmp(IntPredicate::IntEQ, res, self.const_i32(0))
131+
}
126132
kw::Try => {
127133
try_intrinsic(
128134
self,

‎compiler/rustc_codegen_llvm/src/context.rs

+14
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,20 @@ impl<'ll> CodegenCx<'ll, '_> {
916916
ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void);
917917
ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void);
918918

919+
// FIXME: This is an infinitesimally small portion of the types you can
920+
// pass to this intrinsic, if we can ever lazily register intrinsics we
921+
// should register these when they're used, that way any type can be
922+
// passed.
923+
ifn!("llvm.is.constant.i1", fn(i1) -> i1);
924+
ifn!("llvm.is.constant.i8", fn(t_i8) -> i1);
925+
ifn!("llvm.is.constant.i16", fn(t_i16) -> i1);
926+
ifn!("llvm.is.constant.i32", fn(t_i32) -> i1);
927+
ifn!("llvm.is.constant.i64", fn(t_i64) -> i1);
928+
ifn!("llvm.is.constant.i128", fn(t_i128) -> i1);
929+
ifn!("llvm.is.constant.isize", fn(t_isize) -> i1);
930+
ifn!("llvm.is.constant.f32", fn(t_f32) -> i1);
931+
ifn!("llvm.is.constant.f64", fn(t_f64) -> i1);
932+
919933
ifn!("llvm.expect.i1", fn(i1, i1) -> i1);
920934
ifn!("llvm.eh.typeid.for", fn(ptr) -> t_i32);
921935
ifn!("llvm.localescape", fn(...) -> void);

‎compiler/rustc_codegen_llvm/src/intrinsic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ 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_val_statically_known => self.call_intrinsic(
123+
&format!("llvm.is.constant.{:?}", args[0].layout.immediate_llvm_type(self.cx)),
124+
&[args[0].immediate()],
125+
),
122126
sym::unlikely => self
123127
.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]),
124128
kw::Try => {

‎compiler/rustc_const_eval/src/const_eval/machine.rs

+5
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
531531
)?;
532532
}
533533
}
534+
// The intrinsic represents whether the value is known to the optimizer (LLVM).
535+
// We're not doing any optimizations here, so there is no optimizer that could know the value.
536+
// (We know the value here in the machine of course, but this is the runtime of that code,
537+
// not the optimization stage.)
538+
sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?,
534539
_ => {
535540
throw_unsup_format!(
536541
"intrinsic `{intrinsic_name}` is not supported at compile-time"

‎compiler/rustc_hir_analysis/src/check/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
453453

454454
sym::black_box => (1, vec![param(0)], param(0)),
455455

456+
sym::is_val_statically_known => (1, vec![param(0)], tcx.types.bool),
457+
456458
sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
457459

458460
sym::vtable_size | sym::vtable_align => {

‎compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,7 @@ symbols! {
910910
io_stderr,
911911
io_stdout,
912912
irrefutable_let_patterns,
913+
is_val_statically_known,
913914
isa_attribute,
914915
isize,
915916
issue,

‎library/core/src/intrinsics.rs

+60
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,66 @@ extern "rust-intrinsic" {
25172517
where
25182518
G: FnOnce<ARG, Output = RET>,
25192519
F: FnOnce<ARG, Output = RET>;
2520+
2521+
/// Returns whether the argument's value is statically known at
2522+
/// compile-time.
2523+
///
2524+
/// This is useful when there is a way of writing the code that will
2525+
/// be *faster* when some variables have known values, but *slower*
2526+
/// in the general case: an `if is_val_statically_known(var)` can be used
2527+
/// to select between these two variants. The `if` will be optimized away
2528+
/// and only the desired branch remains.
2529+
///
2530+
/// Formally speaking, this function non-deterministically returns `true`
2531+
/// or `false`, and the caller has to ensure sound behavior for both cases.
2532+
/// In other words, the following code has *Undefined Behavior*:
2533+
///
2534+
/// ```
2535+
/// #![feature(is_val_statically_known)]
2536+
/// #![feature(core_intrinsics)]
2537+
/// # #![allow(internal_features)]
2538+
/// use std::hint::unreachable_unchecked;
2539+
/// use std::intrinsics::is_val_statically_known;
2540+
///
2541+
/// unsafe {
2542+
/// if !is_val_statically_known(0) { unreachable_unchecked(); }
2543+
/// }
2544+
/// ```
2545+
///
2546+
/// This also means that the following code's behavior is unspecified; it
2547+
/// may panic, or it may not:
2548+
///
2549+
/// ```no_run
2550+
/// #![feature(is_val_statically_known)]
2551+
/// #![feature(core_intrinsics)]
2552+
/// # #![allow(internal_features)]
2553+
/// use std::intrinsics::is_val_statically_known;
2554+
///
2555+
/// unsafe {
2556+
/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0));
2557+
/// }
2558+
/// ```
2559+
///
2560+
/// Unsafe code may not rely on `is_val_statically_known` returning any
2561+
/// particular value, ever. However, the compiler will generally make it
2562+
/// return `true` only if the value of the argument is actually known.
2563+
///
2564+
/// When calling this in a `const fn`, both paths must be semantically
2565+
/// equivalent, that is, the result of the `true` branch and the `false`
2566+
/// branch must return the same value and have the same side-effects *no
2567+
/// matter what*.
2568+
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
2569+
#[rustc_nounwind]
2570+
#[cfg(not(bootstrap))]
2571+
pub fn is_val_statically_known<T: Copy>(arg: T) -> bool;
2572+
}
2573+
2574+
// FIXME: Seems using `unstable` here completely ignores `rustc_allow_const_fn_unstable`
2575+
// and thus compiling stage0 core doesn't work.
2576+
#[rustc_const_stable(feature = "is_val_statically_known", since = "0.0.0")]
2577+
#[cfg(bootstrap)]
2578+
pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
2579+
false
25202580
}
25212581

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

‎library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
//
201201
// Language features:
202202
// tidy-alphabetical-start
203+
#![cfg_attr(not(bootstrap), feature(is_val_statically_known))]
203204
#![feature(abi_unadjusted)]
204205
#![feature(adt_const_params)]
205206
#![feature(allow_internal_unsafe)]

0 commit comments

Comments
 (0)
Please sign in to comment.