mirror of https://github.com/rust-lang/rust.git
Implement intrinsics with fallback bodies
This commit is contained in:
parent
0eee945680
commit
92281c7e81
|
@ -787,7 +787,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
|
||||
// Handle intrinsics old codegen wants Expr's for, ourselves.
|
||||
let intrinsic = match def {
|
||||
Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id)),
|
||||
Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -788,6 +788,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
|
||||
"the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe"
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_intrinsic, Normal, template!(Word), ErrorFollowing,
|
||||
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Testing:
|
||||
|
|
|
@ -1746,8 +1746,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
self.root.tables.attr_flags.get(self, index)
|
||||
}
|
||||
|
||||
fn get_intrinsic(self, index: DefIndex) -> bool {
|
||||
self.root.tables.intrinsic.get(self, index)
|
||||
fn get_intrinsic(self, index: DefIndex) -> Option<Symbol> {
|
||||
self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self))
|
||||
}
|
||||
|
||||
fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap {
|
||||
|
|
|
@ -348,7 +348,7 @@ provide! { tcx, def_id, other, cdata,
|
|||
cdata.get_stability_implications(tcx).iter().copied().collect()
|
||||
}
|
||||
stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) }
|
||||
intrinsic => { cdata.get_intrinsic(def_id.index).then(|| tcx.item_name(def_id)) }
|
||||
intrinsic => { cdata.get_intrinsic(def_id.index) }
|
||||
defined_lang_items => { cdata.get_lang_items(tcx) }
|
||||
diagnostic_items => { cdata.get_diagnostic_items() }
|
||||
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
|
||||
|
|
|
@ -1411,7 +1411,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
if let DefKind::Fn | DefKind::AssocFn = def_kind {
|
||||
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
|
||||
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
|
||||
self.tables.intrinsic.set(def_id.index, tcx.intrinsic(def_id).is_some());
|
||||
if let Some(name) = tcx.intrinsic(def_id) {
|
||||
record!(self.tables.intrinsic[def_id] <- name);
|
||||
}
|
||||
}
|
||||
if let DefKind::TyParam = def_kind {
|
||||
let default = self.tcx.object_lifetime_default(def_id);
|
||||
|
|
|
@ -375,7 +375,7 @@ macro_rules! define_tables {
|
|||
|
||||
define_tables! {
|
||||
- defaulted:
|
||||
intrinsic: Table<DefIndex, bool>,
|
||||
intrinsic: Table<DefIndex, Option<LazyValue<Symbol>>>,
|
||||
is_macro_rules: Table<DefIndex, bool>,
|
||||
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
||||
type_alias_is_lazy: Table<DefIndex, bool>,
|
||||
|
|
|
@ -1549,9 +1549,10 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
|
||||
}
|
||||
|
||||
/// Determines whether an item is an intrinsic by Abi.
|
||||
/// Determines whether an item is an intrinsic by Abi. or by whether it has a `rustc_intrinsic` attribute
|
||||
pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Symbol> {
|
||||
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
|
||||
|| tcx.has_attr(def_id, sym::rustc_intrinsic)
|
||||
{
|
||||
Some(tcx.item_name(def_id.into()))
|
||||
} else {
|
||||
|
|
|
@ -1418,6 +1418,7 @@ symbols! {
|
|||
rustc_if_this_changed,
|
||||
rustc_inherit_overflow_checks,
|
||||
rustc_insignificant_dtor,
|
||||
rustc_intrinsic,
|
||||
rustc_layout,
|
||||
rustc_layout_scalar_valid_range_end,
|
||||
rustc_layout_scalar_valid_range_start,
|
||||
|
|
|
@ -2517,79 +2517,79 @@ extern "rust-intrinsic" {
|
|||
where
|
||||
G: FnOnce<ARG, Output = RET>,
|
||||
F: FnOnce<ARG, Output = RET>;
|
||||
|
||||
/// Returns whether the argument's value is statically known at
|
||||
/// compile-time.
|
||||
///
|
||||
/// This is useful when there is a way of writing the code that will
|
||||
/// be *faster* when some variables have known values, but *slower*
|
||||
/// in the general case: an `if is_val_statically_known(var)` can be used
|
||||
/// to select between these two variants. The `if` will be optimized away
|
||||
/// and only the desired branch remains.
|
||||
///
|
||||
/// Formally speaking, this function non-deterministically returns `true`
|
||||
/// or `false`, and the caller has to ensure sound behavior for both cases.
|
||||
/// In other words, the following code has *Undefined Behavior*:
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(is_val_statically_known)]
|
||||
/// #![feature(core_intrinsics)]
|
||||
/// # #![allow(internal_features)]
|
||||
/// use std::hint::unreachable_unchecked;
|
||||
/// use std::intrinsics::is_val_statically_known;
|
||||
///
|
||||
/// unsafe {
|
||||
/// if !is_val_statically_known(0) { unreachable_unchecked(); }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This also means that the following code's behavior is unspecified; it
|
||||
/// may panic, or it may not:
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(is_val_statically_known)]
|
||||
/// #![feature(core_intrinsics)]
|
||||
/// # #![allow(internal_features)]
|
||||
/// use std::intrinsics::is_val_statically_known;
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Unsafe code may not rely on `is_val_statically_known` returning any
|
||||
/// particular value, ever. However, the compiler will generally make it
|
||||
/// return `true` only if the value of the argument is actually known.
|
||||
///
|
||||
/// When calling this in a `const fn`, both paths must be semantically
|
||||
/// equivalent, that is, the result of the `true` branch and the `false`
|
||||
/// branch must return the same value and have the same side-effects *no
|
||||
/// matter what*.
|
||||
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
|
||||
#[rustc_nounwind]
|
||||
pub fn is_val_statically_known<T: Copy>(arg: T) -> bool;
|
||||
|
||||
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
|
||||
/// macro expansion.
|
||||
///
|
||||
/// This always returns `false` in const eval and Miri. The interpreter provides better
|
||||
/// diagnostics than the checks that this is used to implement. However, this means
|
||||
/// you should only be using this intrinsic to guard requirements that, if violated,
|
||||
/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those
|
||||
/// checks entirely.
|
||||
///
|
||||
/// Since this is evaluated after monomorphization, branching on this value can be used to
|
||||
/// implement debug assertions that are included in the precompiled standard library, but can
|
||||
/// be optimized out by builds that monomorphize the standard library code with debug
|
||||
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
|
||||
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
||||
#[rustc_safe_intrinsic]
|
||||
#[cfg(not(bootstrap))]
|
||||
pub(crate) fn debug_assertions() -> bool;
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
/// Returns whether the argument's value is statically known at
|
||||
/// compile-time.
|
||||
///
|
||||
/// This is useful when there is a way of writing the code that will
|
||||
/// be *faster* when some variables have known values, but *slower*
|
||||
/// in the general case: an `if is_val_statically_known(var)` can be used
|
||||
/// to select between these two variants. The `if` will be optimized away
|
||||
/// and only the desired branch remains.
|
||||
///
|
||||
/// Formally speaking, this function non-deterministically returns `true`
|
||||
/// or `false`, and the caller has to ensure sound behavior for both cases.
|
||||
/// In other words, the following code has *Undefined Behavior*:
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(is_val_statically_known)]
|
||||
/// #![feature(core_intrinsics)]
|
||||
/// # #![allow(internal_features)]
|
||||
/// use std::hint::unreachable_unchecked;
|
||||
/// use std::intrinsics::is_val_statically_known;
|
||||
///
|
||||
/// unsafe {
|
||||
/// if !is_val_statically_known(0) { unreachable_unchecked(); }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This also means that the following code's behavior is unspecified; it
|
||||
/// may panic, or it may not:
|
||||
///
|
||||
/// ```no_run
|
||||
/// #![feature(is_val_statically_known)]
|
||||
/// #![feature(core_intrinsics)]
|
||||
/// # #![allow(internal_features)]
|
||||
/// use std::intrinsics::is_val_statically_known;
|
||||
///
|
||||
/// unsafe {
|
||||
/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Unsafe code may not rely on `is_val_statically_known` returning any
|
||||
/// particular value, ever. However, the compiler will generally make it
|
||||
/// return `true` only if the value of the argument is actually known.
|
||||
///
|
||||
/// When calling this in a `const fn`, both paths must be semantically
|
||||
/// equivalent, that is, the result of the `true` branch and the `false`
|
||||
/// branch must return the same value and have the same side-effects *no
|
||||
/// matter what*.
|
||||
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
|
||||
#[rustc_nounwind]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
|
||||
/// macro expansion.
|
||||
///
|
||||
/// This always returns `false` in const eval and Miri. The interpreter provides better
|
||||
/// diagnostics than the checks that this is used to implement. However, this means
|
||||
/// you should only be using this intrinsic to guard requirements that, if violated,
|
||||
/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those
|
||||
/// checks entirely.
|
||||
///
|
||||
/// Since this is evaluated after monomorphization, branching on this value can be used to
|
||||
/// implement debug assertions that are included in the precompiled standard library, but can
|
||||
/// be optimized out by builds that monomorphize the standard library code with debug
|
||||
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
|
||||
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
pub(crate) const fn debug_assertions() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,60 @@
|
|||
|
||||
The tracking issue for this feature is: None.
|
||||
|
||||
Intrinsics are never intended to be stable directly, but intrinsics are often
|
||||
Intrinsics are rarely intended to be stable directly, but are usually
|
||||
exported in some sort of stable manner. Prefer using the stable interfaces to
|
||||
the intrinsic directly when you can.
|
||||
|
||||
------------------------
|
||||
|
||||
|
||||
## Intrinsics with fallback logic
|
||||
|
||||
Many intrinsics can be written in pure rust, albeit inefficiently or without supporting
|
||||
some features that only exist on some backends. Backends can simply not implement those
|
||||
intrinsics without causing any code miscompilations or failures to compile.
|
||||
|
||||
```rust
|
||||
#![feature(rustc_attrs, effects)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[rustc_intrinsic]
|
||||
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
|
||||
```
|
||||
|
||||
Since these are just regular functions, it is perfectly ok to create the intrinsic twice:
|
||||
|
||||
```rust
|
||||
#![feature(rustc_attrs, effects)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
#[rustc_intrinsic]
|
||||
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
|
||||
|
||||
mod foo {
|
||||
#[rustc_intrinsic]
|
||||
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
|
||||
panic!("noisy const dealloc")
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The behaviour on backends that override the intrinsic is exactly the same. On other
|
||||
backends, the intrinsic behaviour depends on which implementation is called, just like
|
||||
with any regular function.
|
||||
|
||||
## Intrinsics lowered to MIR instructions
|
||||
|
||||
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
|
||||
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
|
||||
at all.
|
||||
|
||||
## Intrinsics without fallback logic
|
||||
|
||||
These must be implemented by all backends.
|
||||
|
||||
These are imported as if they were FFI functions, with the special
|
||||
`rust-intrinsic` ABI. For example, if one was in a freestanding
|
||||
context, but wished to be able to `transmute` between types, and
|
||||
|
@ -27,4 +74,5 @@ extern "rust-intrinsic" {
|
|||
}
|
||||
```
|
||||
|
||||
As with any other FFI functions, these are always `unsafe` to call.
|
||||
As with any other FFI functions, these are by default always `unsafe` to call.
|
||||
You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call.
|
||||
|
|
|
@ -643,7 +643,7 @@ impl Item {
|
|||
let abi = tcx.fn_sig(def_id).skip_binder().abi();
|
||||
hir::FnHeader {
|
||||
unsafety: if abi == Abi::RustIntrinsic {
|
||||
intrinsic_operation_unsafety(tcx, self.def_id().unwrap())
|
||||
intrinsic_operation_unsafety(tcx, def_id.expect_local())
|
||||
} else {
|
||||
hir::Unsafety::Unsafe
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// run-pass
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(is_val_statically_known)]
|
||||
#![feature(core_intrinsics, is_val_statically_known)]
|
||||
|
||||
use std::intrinsics::is_val_statically_known;
|
||||
|
||||
|
|
Loading…
Reference in New Issue