Implement intrinsics with fallback bodies
This commit is contained in:
parent
0eee945680
commit
92281c7e81
12 changed files with 137 additions and 82 deletions
|
@ -787,7 +787,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
// Handle intrinsics old codegen wants Expr's for, ourselves.
|
// Handle intrinsics old codegen wants Expr's for, ourselves.
|
||||||
let intrinsic = match def {
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -788,6 +788,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
|
rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
|
||||||
"the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe"
|
"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:
|
// Internal attributes, Testing:
|
||||||
|
|
|
@ -1746,8 +1746,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||||
self.root.tables.attr_flags.get(self, index)
|
self.root.tables.attr_flags.get(self, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_intrinsic(self, index: DefIndex) -> bool {
|
fn get_intrinsic(self, index: DefIndex) -> Option<Symbol> {
|
||||||
self.root.tables.intrinsic.get(self, index)
|
self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap {
|
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()
|
cdata.get_stability_implications(tcx).iter().copied().collect()
|
||||||
}
|
}
|
||||||
stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) }
|
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) }
|
defined_lang_items => { cdata.get_lang_items(tcx) }
|
||||||
diagnostic_items => { cdata.get_diagnostic_items() }
|
diagnostic_items => { cdata.get_diagnostic_items() }
|
||||||
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
|
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 {
|
if let DefKind::Fn | DefKind::AssocFn = def_kind {
|
||||||
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
|
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));
|
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 {
|
if let DefKind::TyParam = def_kind {
|
||||||
let default = self.tcx.object_lifetime_default(def_id);
|
let default = self.tcx.object_lifetime_default(def_id);
|
||||||
|
|
|
@ -375,7 +375,7 @@ macro_rules! define_tables {
|
||||||
|
|
||||||
define_tables! {
|
define_tables! {
|
||||||
- defaulted:
|
- defaulted:
|
||||||
intrinsic: Table<DefIndex, bool>,
|
intrinsic: Table<DefIndex, Option<LazyValue<Symbol>>>,
|
||||||
is_macro_rules: Table<DefIndex, bool>,
|
is_macro_rules: Table<DefIndex, bool>,
|
||||||
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
is_type_alias_impl_trait: Table<DefIndex, bool>,
|
||||||
type_alias_is_lazy: 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)))
|
.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> {
|
pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Symbol> {
|
||||||
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
|
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()))
|
Some(tcx.item_name(def_id.into()))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1418,6 +1418,7 @@ symbols! {
|
||||||
rustc_if_this_changed,
|
rustc_if_this_changed,
|
||||||
rustc_inherit_overflow_checks,
|
rustc_inherit_overflow_checks,
|
||||||
rustc_insignificant_dtor,
|
rustc_insignificant_dtor,
|
||||||
|
rustc_intrinsic,
|
||||||
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,
|
||||||
|
|
|
@ -2517,6 +2517,7 @@ extern "rust-intrinsic" {
|
||||||
where
|
where
|
||||||
G: FnOnce<ARG, Output = RET>,
|
G: FnOnce<ARG, Output = RET>,
|
||||||
F: FnOnce<ARG, Output = RET>;
|
F: FnOnce<ARG, Output = RET>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the argument's value is statically known at
|
/// Returns whether the argument's value is statically known at
|
||||||
/// compile-time.
|
/// compile-time.
|
||||||
|
@ -2567,7 +2568,11 @@ extern "rust-intrinsic" {
|
||||||
/// matter what*.
|
/// matter what*.
|
||||||
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
|
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
|
||||||
#[rustc_nounwind]
|
#[rustc_nounwind]
|
||||||
pub fn is_val_statically_known<T: Copy>(arg: T) -> bool;
|
#[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
|
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
|
||||||
/// macro expansion.
|
/// macro expansion.
|
||||||
|
@ -2583,13 +2588,8 @@ extern "rust-intrinsic" {
|
||||||
/// be optimized out by builds that monomorphize the standard library code with debug
|
/// be optimized out by builds that monomorphize the standard library code with debug
|
||||||
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
|
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
|
||||||
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
||||||
#[rustc_safe_intrinsic]
|
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||||
pub(crate) fn debug_assertions() -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(bootstrap)]
|
|
||||||
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
|
|
||||||
pub(crate) const fn debug_assertions() -> bool {
|
pub(crate) const fn debug_assertions() -> bool {
|
||||||
cfg!(debug_assertions)
|
cfg!(debug_assertions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,60 @@
|
||||||
|
|
||||||
The tracking issue for this feature is: None.
|
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
|
exported in some sort of stable manner. Prefer using the stable interfaces to
|
||||||
the intrinsic directly when you can.
|
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
|
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
|
||||||
|
@ -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();
|
let abi = tcx.fn_sig(def_id).skip_binder().abi();
|
||||||
hir::FnHeader {
|
hir::FnHeader {
|
||||||
unsafety: if abi == Abi::RustIntrinsic {
|
unsafety: if abi == Abi::RustIntrinsic {
|
||||||
intrinsic_operation_unsafety(tcx, self.def_id().unwrap())
|
intrinsic_operation_unsafety(tcx, def_id.expect_local())
|
||||||
} else {
|
} else {
|
||||||
hir::Unsafety::Unsafe
|
hir::Unsafety::Unsafe
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics, is_val_statically_known)]
|
||||||
#![feature(is_val_statically_known)]
|
|
||||||
|
|
||||||
use std::intrinsics::is_val_statically_known;
|
use std::intrinsics::is_val_statically_known;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue