Add a scheme for moving away from extern "rust-intrinsic"
entirely
This commit is contained in:
parent
f2612daf58
commit
1e57df1969
16 changed files with 131 additions and 8 deletions
|
@ -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());
|
||||||
|
|
|
@ -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(_) => {}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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<'_> {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
20
tests/ui/intrinsics/always-gets-overridden.rs
Normal file
20
tests/ui/intrinsics/always-gets-overridden.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
18
tests/ui/intrinsics/not-overridden.rs
Normal file
18
tests/ui/intrinsics/not-overridden.rs
Normal 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
|
||||||
|
}
|
10
tests/ui/intrinsics/not-overridden.stderr
Normal file
10
tests/ui/intrinsics/not-overridden.stderr
Normal 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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue