Skip to content

Move more intrinsics to rustc_intrinsic #122037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -757,13 +757,6 @@ fn codegen_regular_intrinsic_call<'tcx>(
ret.write_cvalue(fx, val);
}

sym::ptr_guaranteed_cmp => {
intrinsic_args!(fx, args => (a, b); intrinsic);

let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b).load_scalar(fx);
ret.write_cvalue(fx, CValue::by_val(val, fx.layout_of(fx.tcx.types.u8)));
}

sym::caller_location => {
intrinsic_args!(fx, args => (); intrinsic);

4 changes: 0 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
@@ -81,10 +81,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
return library.kind.is_statically_included().then_some(def_id);
}

if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
return None;
}

// Only consider nodes that actually have exported symbols.
match tcx.def_kind(def_id) {
DefKind::Fn | DefKind::Static { .. } => {}
7 changes: 0 additions & 7 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::operand::{OperandRef, OperandValue};
use super::place::PlaceRef;
use super::FunctionCx;
use crate::common::IntPredicate;
use crate::errors;
use crate::errors::InvalidMonomorphization;
use crate::meth;
@@ -456,12 +455,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return Ok(());
}

sym::ptr_guaranteed_cmp => {
let a = args[0].immediate();
let b = args[1].immediate();
bx.icmp(IntPredicate::IntEQ, a, b)
}

sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
let ty = fn_args.type_at(0);
let pointee_size = bx.layout_of(ty).size;
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
@@ -441,7 +441,7 @@ pub fn check_intrinsic_type(

sym::ptr_guaranteed_cmp => (
1,
0,
1,
vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
tcx.types.u8,
),
@@ -579,7 +579,7 @@ pub fn check_intrinsic_type(

sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool),

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

sym::vtable_size | sym::vtable_align => {
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
5 changes: 1 addition & 4 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
@@ -1067,14 +1067,11 @@ fn should_encode_mir(
// Full-fledged functions + closures
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
let generics = tcx.generics_of(def_id);
let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir
let opt = tcx.sess.opts.unstable_opts.always_encode_mir
|| (tcx.sess.opts.output_types.should_codegen()
&& reachable_set.contains(&def_id)
&& (generics.requires_monomorphization(tcx)
|| tcx.cross_crate_inlinable(def_id)));
if let Some(intrinsic) = tcx.intrinsic(def_id) {
opt &= !intrinsic.must_be_overridden;
}
// The function has a `const` modifier or is in a `#[const_trait]`.
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|| tcx.is_const_default_method(def_id.to_def_id());
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
#![feature(array_windows)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(closure_track_caller)]
#![feature(core_intrinsics)]
#![feature(const_type_name)]
#![feature(discriminant_kind)]
7 changes: 6 additions & 1 deletion compiler/rustc_middle/src/ty/context/tls.rs
Original file line number Diff line number Diff line change
@@ -85,6 +85,7 @@ where

/// Allows access to the current `ImplicitCtxt` in a closure if one is available.
#[inline]
#[track_caller]
pub fn with_context_opt<F, R>(f: F) -> R
where
F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R,
@@ -147,9 +148,13 @@ where
/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
/// The closure is passed None if there is no `ImplicitCtxt` available.
#[inline]
#[track_caller]
pub fn with_opt<F, R>(f: F) -> R
where
F: for<'tcx> FnOnce(Option<TyCtxt<'tcx>>) -> R,
{
with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
with_context_opt(
#[track_caller]
|opt_context| f(opt_context.map(|context| context.tcx)),
)
}
19 changes: 11 additions & 8 deletions compiler/rustc_middle/src/util/bug.rs
Original file line number Diff line number Diff line change
@@ -28,14 +28,17 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
args: fmt::Arguments<'_>,
location: &Location<'_>,
) -> ! {
tls::with_opt(move |tcx| {
let msg = format!("{location}: {args}");
match (tcx, span) {
(Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg),
(Some(tcx), None) => tcx.dcx().bug(msg),
(None, _) => panic_any(msg),
}
})
tls::with_opt(
#[track_caller]
move |tcx| {
let msg = format!("{location}: {args}");
match (tcx, span) {
(Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg),
(Some(tcx), None) => tcx.dcx().bug(msg),
(None, _) => panic_any(msg),
}
},
)
}

/// A query to trigger a delayed bug. Clearly, if one has a `tcx` one can already trigger a
9 changes: 7 additions & 2 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
@@ -1013,8 +1013,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(source_scope) = scope {
self.source_scope = source_scope;
}

self.expr_into_dest(Place::return_place(), block, expr_id)
if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) {
let source_info = self.source_info(rustc_span::DUMMY_SP);
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
self.cfg.start_new_block().unit()
} else {
self.expr_into_dest(Place::return_place(), block, expr_id)
}
}

fn set_correct_source_scope_for_arg(
4 changes: 0 additions & 4 deletions compiler/rustc_mir_transform/src/cross_crate_inline.rs
Original file line number Diff line number Diff line change
@@ -23,10 +23,6 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
return false;
}

if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
return false;
}

// This just reproduces the logic from Instance::requires_inline.
match tcx.def_kind(def_id) {
DefKind::Ctor(..) | DefKind::Closure => return true,
6 changes: 0 additions & 6 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -633,12 +633,6 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> {
}

fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
if tcx.intrinsic(did).is_some_and(|i| i.must_be_overridden) {
span_bug!(
tcx.def_span(did),
"this intrinsic must be overridden by the codegen backend, it has no meaningful body",
)
}
if tcx.is_constructor(did.to_def_id()) {
// There's no reason to run all of the MIR passes on constructors when
// we can just output the MIR we want directly. This also saves const
5 changes: 0 additions & 5 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
@@ -1030,11 +1030,6 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
return false;
}

if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
// These are implemented by backends directly and have no meaningful body.
return false;
}

if def_id.is_local() {
// Local items cannot be referred to locally without monomorphizing them locally.
return true;
173 changes: 106 additions & 67 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -2434,20 +2434,29 @@ extern "rust-intrinsic" {
#[rustc_nounwind]
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;

/// See documentation of `<*const T>::guaranteed_eq` for details.
/// Returns `2` if the result is unknown.
/// Returns `1` if the pointers are guaranteed equal
/// Returns `0` if the pointers are guaranteed inequal
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[rustc_safe_intrinsic]
#[rustc_nounwind]
#[cfg(bootstrap)]
pub fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8;
}

/// See documentation of `<*const T>::guaranteed_eq` for details.
/// Returns `2` if the result is unknown.
/// Returns `1` if the pointers are guaranteed equal
/// Returns `0` if the pointers are guaranteed inequal
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[cfg(not(bootstrap))]
#[rustc_nounwind]
#[rustc_do_not_const_check]
#[inline]
pub const fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8 {
(ptr == other) as u8
}

extern "rust-intrinsic" {
/// Determines whether the raw bytes of the two values are equal.
///
/// This is particularly handy for arrays, since it allows things like just
@@ -2506,64 +2515,10 @@ extern "rust-intrinsic" {
/// `ptr` must point to a vtable.
/// The intrinsic will return the alignment stored in that vtable.
#[rustc_nounwind]
#[cfg(bootstrap)]
pub fn vtable_align(ptr: *const ()) -> usize;

/// Selects which function to call depending on the context.
///
/// If this function is evaluated at compile-time, then a call to this
/// intrinsic will be replaced with a call to `called_in_const`. It gets
/// replaced with a call to `called_at_rt` otherwise.
///
/// This function is safe to call, but note the stability concerns below.
///
/// # Type Requirements
///
/// The two functions must be both function items. They cannot be function
/// pointers or closures. The first function must be a `const fn`.
///
/// `arg` will be the tupled arguments that will be passed to either one of
/// the two functions, therefore, both functions must accept the same type of
/// arguments. Both functions must return RET.
///
/// # Stability concerns
///
/// Rust has not yet decided that `const fn` are allowed to tell whether
/// they run at compile-time or at runtime. Therefore, when using this
/// intrinsic anywhere that can be reached from stable, it is crucial that
/// the end-to-end behavior of the stable `const fn` is the same for both
/// modes of execution. (Here, Undefined Behavior is considered "the same"
/// as any other behavior, so if the function exhibits UB at runtime then
/// it may do whatever it wants at compile-time.)
///
/// Here is an example of how this could cause a problem:
/// ```no_run
/// #![feature(const_eval_select)]
/// #![feature(core_intrinsics)]
/// # #![allow(internal_features)]
/// # #![cfg_attr(bootstrap, allow(unused))]
/// use std::intrinsics::const_eval_select;
///
/// // Standard library
/// # #[cfg(not(bootstrap))]
/// pub const fn inconsistent() -> i32 {
/// fn runtime() -> i32 { 1 }
/// const fn compiletime() -> i32 { 2 }
///
// // ⚠ This code violates the required equivalence of `compiletime`
/// // and `runtime`.
/// const_eval_select((), compiletime, runtime)
/// }
/// # #[cfg(bootstrap)]
/// # pub const fn inconsistent() -> i32 { 0 }
///
/// // User Crate
/// const X: i32 = inconsistent();
/// let x = inconsistent();
/// assert_eq!(x, X);
/// ```
///
/// Currently such an assertion would always succeed; until Rust decides
/// otherwise, that principle should not be violated.
#[cfg(bootstrap)]
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
#[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
pub fn const_eval_select<ARG: Tuple, F, G, RET>(
@@ -2576,6 +2531,79 @@ extern "rust-intrinsic" {
F: FnOnce<ARG, Output = RET>;
}

/// Selects which function to call depending on the context.
///
/// If this function is evaluated at compile-time, then a call to this
/// intrinsic will be replaced with a call to `called_in_const`. It gets
/// replaced with a call to `called_at_rt` otherwise.
///
/// This function is safe to call, but note the stability concerns below.
///
/// # Type Requirements
///
/// The two functions must be both function items. They cannot be function
/// pointers or closures. The first function must be a `const fn`.
///
/// `arg` will be the tupled arguments that will be passed to either one of
/// the two functions, therefore, both functions must accept the same type of
/// arguments. Both functions must return RET.
///
/// # Stability concerns
///
/// Rust has not yet decided that `const fn` are allowed to tell whether
/// they run at compile-time or at runtime. Therefore, when using this
/// intrinsic anywhere that can be reached from stable, it is crucial that
/// the end-to-end behavior of the stable `const fn` is the same for both
/// modes of execution. (Here, Undefined Behavior is considered "the same"
/// as any other behavior, so if the function exhibits UB at runtime then
/// it may do whatever it wants at compile-time.)
///
/// Here is an example of how this could cause a problem:
/// ```no_run
/// #![feature(const_eval_select)]
/// #![feature(core_intrinsics)]
/// # #![allow(internal_features)]
/// # #![cfg_attr(bootstrap, allow(unused))]
/// use std::intrinsics::const_eval_select;
///
/// // Standard library
/// # #[cfg(not(bootstrap))]
/// pub const fn inconsistent() -> i32 {
/// fn runtime() -> i32 { 1 }
/// const fn compiletime() -> i32 { 2 }
///
// // ⚠ This code violates the required equivalence of `compiletime`
/// // and `runtime`.
/// const_eval_select((), compiletime, runtime)
/// }
/// # #[cfg(bootstrap)]
/// # pub const fn inconsistent() -> i32 { 0 }
///
/// // User Crate
/// const X: i32 = inconsistent();
/// let x = inconsistent();
/// assert_eq!(x, X);
/// ```
///
/// Currently such an assertion would always succeed; until Rust decides
/// otherwise, that principle should not be violated.
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[cfg(not(bootstrap))]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const fn const_eval_select<ARG: Tuple, F, G, RET>(
_arg: ARG,
_called_in_const: F,
_called_at_rt: G,
) -> RET
where
G: FnOnce<ARG, Output = RET>,
F: FnOnce<ARG, Output = RET>,
{
unreachable!()
}

/// Returns whether the argument's value is statically known at
/// compile-time.
///
@@ -2702,13 +2730,24 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
/// The intrinsic will return the size stored in that vtable.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
#[cfg_attr(not(bootstrap), rustc_intrinsic_must_be_overridden)]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[cfg(not(bootstrap))]
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
unreachable!()
}

/// `ptr` must point to a vtable.
/// The intrinsic will return the alignment stored in that vtable.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[cfg(not(bootstrap))]
pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
unreachable!()
}

// Some functions are defined here because they accidentally got made
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
// (`transmute` also falls into this category, but it cannot be wrapped due to the
24 changes: 13 additions & 11 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
Original file line number Diff line number Diff line change
@@ -509,17 +509,19 @@ trait StructuralPartialEq {}

const fn drop<T: ~const Destruct>(_: T) {}

extern "rust-intrinsic" {
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
#[rustc_safe_intrinsic]
fn const_eval_select<ARG: Tuple, F, G, RET>(
arg: ARG,
called_in_const: F,
called_at_rt: G,
) -> RET
where
F: const FnOnce<ARG, Output = RET>,
G: FnOnce<ARG, Output = RET>;
#[rustc_const_stable(feature = "const_eval_select", since = "1.0.0")]
#[rustc_intrinsic_must_be_overridden]
#[rustc_intrinsic]
const fn const_eval_select<ARG: Tuple, F, G, RET>(
arg: ARG,
called_in_const: F,
called_at_rt: G,
) -> RET
where
F: const FnOnce<ARG, Output = RET>,
G: FnOnce<ARG, Output = RET>,
{
loop {}
}

fn test_const_eval_select() {