1
Fork 0

Add a scheme for moving away from extern "rust-intrinsic" entirely

This commit is contained in:
Oli Scherer 2024-02-19 17:35:12 +00:00
parent f2612daf58
commit 1e57df1969
16 changed files with 131 additions and 8 deletions

View file

@ -1255,7 +1255,17 @@ fn codegen_regular_intrinsic_call<'tcx>(
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained // Unimplemented intrinsics must have a fallback body. The fallback body is obtained
// by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`. // by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`.
_ => return Err(Instance::new(instance.def_id(), instance.args)), _ => {
let intrinsic = fx.tcx.intrinsic(instance.def_id()).unwrap();
if intrinsic.must_be_overridden {
span_bug!(
source_info.span,
"intrinsic {} must be overridden by codegen_cranelift, but isn't",
intrinsic.name,
);
}
return Err(Instance::new(instance.def_id(), instance.args));
}
} }
let ret_block = fx.get_block(destination.unwrap()); let ret_block = fx.get_block(destination.unwrap());

View file

@ -16,6 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::Providers; use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy}; use rustc_session::config::{CrateType, OomStrategy};
use rustc_span::sym;
use rustc_target::spec::{SanitizerSet, TlsModel}; use rustc_target::spec::{SanitizerSet, TlsModel};
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
@ -81,6 +82,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
return library.kind.is_statically_included().then_some(def_id); return library.kind.is_statically_included().then_some(def_id);
} }
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
return None;
}
// Only consider nodes that actually have exported symbols. // Only consider nodes that actually have exported symbols.
match tcx.def_kind(def_id) { match tcx.def_kind(def_id) {
DefKind::Fn | DefKind::Static(_) => {} DefKind::Fn | DefKind::Static(_) => {}

View file

@ -903,7 +903,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MergingSucc::False MergingSucc::False
}; };
} }
Err(instance) => Some(instance), Err(instance) => {
if intrinsic.must_be_overridden {
span_bug!(
span,
"intrinsic {} must be overridden by codegen backend, but isn't",
intrinsic.name,
);
}
Some(instance)
}
} }
} }
}; };

View file

@ -867,6 +867,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, rustc_no_mir_inline, Normal, template!(Word), WarnFollowing,
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
), ),
rustc_attr!(
rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing,
"the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies",
),
// ========================================================================== // ==========================================================================
// Internal attributes, Testing: // Internal attributes, Testing:

View file

@ -1051,13 +1051,18 @@ fn should_encode_mir(
// Coroutines require optimized MIR to compute layout. // Coroutines require optimized MIR to compute layout.
DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => (false, true), DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => (false, true),
// Full-fledged functions + closures // Full-fledged functions + closures
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => { def_kind @ (DefKind::AssocFn | DefKind::Fn | DefKind::Closure) => {
let generics = tcx.generics_of(def_id); let generics = tcx.generics_of(def_id);
let opt = tcx.sess.opts.unstable_opts.always_encode_mir let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir
|| (tcx.sess.opts.output_types.should_codegen() || (tcx.sess.opts.output_types.should_codegen()
&& reachable_set.contains(&def_id) && reachable_set.contains(&def_id)
&& (generics.requires_monomorphization(tcx) && (generics.requires_monomorphization(tcx)
|| tcx.cross_crate_inlinable(def_id))); || tcx.cross_crate_inlinable(def_id)));
if matches!(def_kind, DefKind::AssocFn | DefKind::Fn) {
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]`. // 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()) let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|| tcx.is_const_default_method(def_id.to_def_id()); || tcx.is_const_default_method(def_id.to_def_id());

View file

@ -5,6 +5,8 @@ use super::TyCtxt;
#[derive(Copy, Clone, Debug, Decodable, Encodable, HashStable)] #[derive(Copy, Clone, Debug, Decodable, Encodable, HashStable)]
pub struct IntrinsicDef { pub struct IntrinsicDef {
pub name: Symbol, pub name: Symbol,
/// Whether the intrinsic has no meaningful body and all backends need to shim all calls to it.
pub must_be_overridden: bool,
} }
impl TyCtxt<'_> { impl TyCtxt<'_> {

View file

@ -1646,7 +1646,10 @@ pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::IntrinsicDef
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic) if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic)
|| tcx.has_attr(def_id, sym::rustc_intrinsic) || tcx.has_attr(def_id, sym::rustc_intrinsic)
{ {
Some(ty::IntrinsicDef { name: tcx.item_name(def_id.into()) }) Some(ty::IntrinsicDef {
name: tcx.item_name(def_id.into()),
must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden),
})
} else { } else {
None None
} }

View file

@ -23,6 +23,10 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
return false; return false;
} }
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
return false;
}
// This just reproduces the logic from Instance::requires_inline. // This just reproduces the logic from Instance::requires_inline.
match tcx.def_kind(def_id) { match tcx.def_kind(def_id) {
DefKind::Ctor(..) | DefKind::Closure => return true, DefKind::Ctor(..) | DefKind::Closure => return true,

View file

@ -632,6 +632,12 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> {
} }
fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
if let Some(attr) = tcx.get_attr(did, sym::rustc_intrinsic_must_be_overridden) {
span_bug!(
attr.span,
"this intrinsic must be overridden by the codegen backend, it has no meaningful body",
)
}
if tcx.is_constructor(did.to_def_id()) { if tcx.is_constructor(did.to_def_id()) {
// There's no reason to run all of the MIR passes on constructors when // 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 // we can just output the MIR we want directly. This also saves const

View file

@ -1019,6 +1019,11 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
return false; return false;
} }
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
// These are implemented by backends directly and have no meaningful body.
return false;
}
if def_id.is_local() { if def_id.is_local() {
// Local items cannot be referred to locally without monomorphizing them locally. // Local items cannot be referred to locally without monomorphizing them locally.
return true; return true;

View file

@ -1525,6 +1525,7 @@ symbols! {
rustc_inherit_overflow_checks, rustc_inherit_overflow_checks,
rustc_insignificant_dtor, rustc_insignificant_dtor,
rustc_intrinsic, rustc_intrinsic,
rustc_intrinsic_must_be_overridden,
rustc_layout, rustc_layout,
rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_end,
rustc_layout_scalar_valid_range_start, rustc_layout_scalar_valid_range_start,

View file

@ -2499,9 +2499,8 @@ extern "rust-intrinsic" {
#[rustc_nounwind] #[rustc_nounwind]
pub fn black_box<T>(dummy: T) -> T; pub fn black_box<T>(dummy: T) -> T;
/// `ptr` must point to a vtable.
/// The intrinsic will return the size stored in that vtable.
#[rustc_nounwind] #[rustc_nounwind]
#[cfg(bootstrap)]
pub fn vtable_size(ptr: *const ()) -> usize; pub fn vtable_size(ptr: *const ()) -> usize;
/// `ptr` must point to a vtable. /// `ptr` must point to a vtable.
@ -2681,6 +2680,17 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
#[cfg_attr(bootstrap, inline)] #[cfg_attr(bootstrap, inline)]
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
/// `ptr` must point to a vtable.
/// 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)]
#[cfg(not(bootstrap))]
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
unreachable!()
}
// Some functions are defined here because they accidentally got made // 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>. // 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 // (`transmute` also falls into this category, but it cannot be wrapped due to the

View file

@ -52,12 +52,23 @@ with any regular function.
Various intrinsics have native MIR operations that they correspond to. Instead of requiring Various intrinsics have native MIR operations that they correspond to. Instead of requiring
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
at all. at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist
anymore after MIR analyses.
## Intrinsics without fallback logic ## Intrinsics without fallback logic
These must be implemented by all backends. These must be implemented by all backends.
### `#[rustc_intrinsic]` declarations
These are written like intrinsics with fallback bodies, but the body is irrelevant.
Use `loop {}` for the body or call the intrinsic recursively and add
`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
invoke the body.
### Legacy extern ABI based intrinsics
These are imported as if they were FFI functions, with the special These are imported as if they were FFI functions, with the special
`rust-intrinsic` ABI. For example, if one was in a freestanding `rust-intrinsic` ABI. For example, if one was in a freestanding
context, but wished to be able to `transmute` between types, and context, but wished to be able to `transmute` between types, and

View file

@ -0,0 +1,20 @@
//! Check that `vtable_size` gets overridden by llvm backend even if there is no
//! `rustc_intrinsic_must_be_overridden` attribute on this usage.
#![feature(rustc_attrs)]
//@run-pass
#[rustc_intrinsic]
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
panic!();
}
trait Trait {}
impl Trait for () {}
fn main() {
let x: &dyn Trait = &();
unsafe {
let (_data, vtable): (*const (), *const ()) = core::mem::transmute(x);
assert_eq!(vtable_size(vtable), 0);
}
}

View file

@ -0,0 +1,18 @@
//! Check that intrinsics that do not get overridden, but are marked as such,
//! cause an error instead of silently invoking the body.
#![feature(rustc_attrs, effects)]
//@ build-fail
//@ failure-status:101
//@ normalize-stderr-test ".*note: .*\n\n" -> ""
//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
//@ normalize-stderr-test "internal compiler error:.*: intrinsic const_deallocate " -> ""
//@ rustc-env:RUST_BACKTRACE=0
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
fn main() {
unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
//~^ ERROR: must be overridden
}

View file

@ -0,0 +1,10 @@
error: must be overridden by codegen backend, but isn't
--> $DIR/not-overridden.rs:16:14
|
LL | unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
query stack during panic:
end of query stack
error: aborting due to 1 previous error