Skip to content

Commit 92281c7

Browse files
committed
Implement intrinsics with fallback bodies
1 parent 0eee945 commit 92281c7

File tree

12 files changed

+136
-81
lines changed

12 files changed

+136
-81
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
787787

788788
// Handle intrinsics old codegen wants Expr's for, ourselves.
789789
let intrinsic = match def {
790-
Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id)),
790+
Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()),
791791
_ => None,
792792
};
793793

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
788788
rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing,
789789
"the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe"
790790
),
791+
rustc_attr!(
792+
rustc_intrinsic, Normal, template!(Word), ErrorFollowing,
793+
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
794+
),
791795

792796
// ==========================================================================
793797
// Internal attributes, Testing:

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,8 +1746,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
17461746
self.root.tables.attr_flags.get(self, index)
17471747
}
17481748

1749-
fn get_intrinsic(self, index: DefIndex) -> bool {
1750-
self.root.tables.intrinsic.get(self, index)
1749+
fn get_intrinsic(self, index: DefIndex) -> Option<Symbol> {
1750+
self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self))
17511751
}
17521752

17531753
fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap {

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ provide! { tcx, def_id, other, cdata,
348348
cdata.get_stability_implications(tcx).iter().copied().collect()
349349
}
350350
stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) }
351-
intrinsic => { cdata.get_intrinsic(def_id.index).then(|| tcx.item_name(def_id)) }
351+
intrinsic => { cdata.get_intrinsic(def_id.index) }
352352
defined_lang_items => { cdata.get_lang_items(tcx) }
353353
diagnostic_items => { cdata.get_diagnostic_items() }
354354
missing_lang_items => { cdata.get_missing_lang_items(tcx) }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
14111411
if let DefKind::Fn | DefKind::AssocFn = def_kind {
14121412
self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
14131413
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
1414-
self.tables.intrinsic.set(def_id.index, tcx.intrinsic(def_id).is_some());
1414+
if let Some(name) = tcx.intrinsic(def_id) {
1415+
record!(self.tables.intrinsic[def_id] <- name);
1416+
}
14151417
}
14161418
if let DefKind::TyParam = def_kind {
14171419
let default = self.tcx.object_lifetime_default(def_id);

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ macro_rules! define_tables {
375375

376376
define_tables! {
377377
- defaulted:
378-
intrinsic: Table<DefIndex, bool>,
378+
intrinsic: Table<DefIndex, Option<LazyValue<Symbol>>>,
379379
is_macro_rules: Table<DefIndex, bool>,
380380
is_type_alias_impl_trait: Table<DefIndex, bool>,
381381
type_alias_is_lazy: Table<DefIndex, bool>,

compiler/rustc_middle/src/ty/util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1549,9 +1549,10 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
15491549
.any(|items| items.iter().any(|item| item.has_name(sym::notable_trait)))
15501550
}
15511551

1552-
/// Determines whether an item is an intrinsic by Abi.
1552+
/// Determines whether an item is an intrinsic by Abi. or by whether it has a `rustc_intrinsic` attribute
15531553
pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Symbol> {
15541554
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
1555+
|| tcx.has_attr(def_id, sym::rustc_intrinsic)
15551556
{
15561557
Some(tcx.item_name(def_id.into()))
15571558
} else {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,7 @@ symbols! {
14181418
rustc_if_this_changed,
14191419
rustc_inherit_overflow_checks,
14201420
rustc_insignificant_dtor,
1421+
rustc_intrinsic,
14211422
rustc_layout,
14221423
rustc_layout_scalar_valid_range_end,
14231424
rustc_layout_scalar_valid_range_start,

library/core/src/intrinsics.rs

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,79 +2517,79 @@ extern "rust-intrinsic" {
25172517
where
25182518
G: FnOnce<ARG, Output = RET>,
25192519
F: FnOnce<ARG, Output = RET>;
2520+
}
25202521

2521-
/// Returns whether the argument's value is statically known at
2522-
/// compile-time.
2523-
///
2524-
/// This is useful when there is a way of writing the code that will
2525-
/// be *faster* when some variables have known values, but *slower*
2526-
/// in the general case: an `if is_val_statically_known(var)` can be used
2527-
/// to select between these two variants. The `if` will be optimized away
2528-
/// and only the desired branch remains.
2529-
///
2530-
/// Formally speaking, this function non-deterministically returns `true`
2531-
/// or `false`, and the caller has to ensure sound behavior for both cases.
2532-
/// In other words, the following code has *Undefined Behavior*:
2533-
///
2534-
/// ```no_run
2535-
/// #![feature(is_val_statically_known)]
2536-
/// #![feature(core_intrinsics)]
2537-
/// # #![allow(internal_features)]
2538-
/// use std::hint::unreachable_unchecked;
2539-
/// use std::intrinsics::is_val_statically_known;
2540-
///
2541-
/// unsafe {
2542-
/// if !is_val_statically_known(0) { unreachable_unchecked(); }
2543-
/// }
2544-
/// ```
2545-
///
2546-
/// This also means that the following code's behavior is unspecified; it
2547-
/// may panic, or it may not:
2548-
///
2549-
/// ```no_run
2550-
/// #![feature(is_val_statically_known)]
2551-
/// #![feature(core_intrinsics)]
2552-
/// # #![allow(internal_features)]
2553-
/// use std::intrinsics::is_val_statically_known;
2554-
///
2555-
/// unsafe {
2556-
/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0));
2557-
/// }
2558-
/// ```
2559-
///
2560-
/// Unsafe code may not rely on `is_val_statically_known` returning any
2561-
/// particular value, ever. However, the compiler will generally make it
2562-
/// return `true` only if the value of the argument is actually known.
2563-
///
2564-
/// When calling this in a `const fn`, both paths must be semantically
2565-
/// equivalent, that is, the result of the `true` branch and the `false`
2566-
/// branch must return the same value and have the same side-effects *no
2567-
/// matter what*.
2568-
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
2569-
#[rustc_nounwind]
2570-
pub fn is_val_statically_known<T: Copy>(arg: T) -> bool;
2571-
2572-
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
2573-
/// macro expansion.
2574-
///
2575-
/// This always returns `false` in const eval and Miri. The interpreter provides better
2576-
/// diagnostics than the checks that this is used to implement. However, this means
2577-
/// you should only be using this intrinsic to guard requirements that, if violated,
2578-
/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those
2579-
/// checks entirely.
2580-
///
2581-
/// Since this is evaluated after monomorphization, branching on this value can be used to
2582-
/// implement debug assertions that are included in the precompiled standard library, but can
2583-
/// be optimized out by builds that monomorphize the standard library code with debug
2584-
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
2585-
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
2586-
#[rustc_safe_intrinsic]
2587-
#[cfg(not(bootstrap))]
2588-
pub(crate) fn debug_assertions() -> bool;
2522+
/// Returns whether the argument's value is statically known at
2523+
/// compile-time.
2524+
///
2525+
/// This is useful when there is a way of writing the code that will
2526+
/// be *faster* when some variables have known values, but *slower*
2527+
/// in the general case: an `if is_val_statically_known(var)` can be used
2528+
/// to select between these two variants. The `if` will be optimized away
2529+
/// and only the desired branch remains.
2530+
///
2531+
/// Formally speaking, this function non-deterministically returns `true`
2532+
/// or `false`, and the caller has to ensure sound behavior for both cases.
2533+
/// In other words, the following code has *Undefined Behavior*:
2534+
///
2535+
/// ```no_run
2536+
/// #![feature(is_val_statically_known)]
2537+
/// #![feature(core_intrinsics)]
2538+
/// # #![allow(internal_features)]
2539+
/// use std::hint::unreachable_unchecked;
2540+
/// use std::intrinsics::is_val_statically_known;
2541+
///
2542+
/// unsafe {
2543+
/// if !is_val_statically_known(0) { unreachable_unchecked(); }
2544+
/// }
2545+
/// ```
2546+
///
2547+
/// This also means that the following code's behavior is unspecified; it
2548+
/// may panic, or it may not:
2549+
///
2550+
/// ```no_run
2551+
/// #![feature(is_val_statically_known)]
2552+
/// #![feature(core_intrinsics)]
2553+
/// # #![allow(internal_features)]
2554+
/// use std::intrinsics::is_val_statically_known;
2555+
///
2556+
/// unsafe {
2557+
/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0));
2558+
/// }
2559+
/// ```
2560+
///
2561+
/// Unsafe code may not rely on `is_val_statically_known` returning any
2562+
/// particular value, ever. However, the compiler will generally make it
2563+
/// return `true` only if the value of the argument is actually known.
2564+
///
2565+
/// When calling this in a `const fn`, both paths must be semantically
2566+
/// equivalent, that is, the result of the `true` branch and the `false`
2567+
/// branch must return the same value and have the same side-effects *no
2568+
/// matter what*.
2569+
#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")]
2570+
#[rustc_nounwind]
2571+
#[unstable(feature = "core_intrinsics", issue = "none")]
2572+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
2573+
pub const unsafe fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
2574+
false
25892575
}
25902576

2591-
#[cfg(bootstrap)]
2577+
/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in
2578+
/// macro expansion.
2579+
///
2580+
/// This always returns `false` in const eval and Miri. The interpreter provides better
2581+
/// diagnostics than the checks that this is used to implement. However, this means
2582+
/// you should only be using this intrinsic to guard requirements that, if violated,
2583+
/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those
2584+
/// checks entirely.
2585+
///
2586+
/// Since this is evaluated after monomorphization, branching on this value can be used to
2587+
/// implement debug assertions that are included in the precompiled standard library, but can
2588+
/// be optimized out by builds that monomorphize the standard library code with debug
2589+
/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`].
25922590
#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")]
2591+
#[unstable(feature = "core_intrinsics", issue = "none")]
2592+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
25932593
pub(crate) const fn debug_assertions() -> bool {
25942594
cfg!(debug_assertions)
25952595
}

src/doc/unstable-book/src/language-features/intrinsics.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,60 @@
22

33
The tracking issue for this feature is: None.
44

5-
Intrinsics are never intended to be stable directly, but intrinsics are often
5+
Intrinsics are rarely intended to be stable directly, but are usually
66
exported in some sort of stable manner. Prefer using the stable interfaces to
77
the intrinsic directly when you can.
88

99
------------------------
1010

1111

12+
## Intrinsics with fallback logic
13+
14+
Many intrinsics can be written in pure rust, albeit inefficiently or without supporting
15+
some features that only exist on some backends. Backends can simply not implement those
16+
intrinsics without causing any code miscompilations or failures to compile.
17+
18+
```rust
19+
#![feature(rustc_attrs, effects)]
20+
#![allow(internal_features)]
21+
22+
#[rustc_intrinsic]
23+
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
24+
```
25+
26+
Since these are just regular functions, it is perfectly ok to create the intrinsic twice:
27+
28+
```rust
29+
#![feature(rustc_attrs, effects)]
30+
#![allow(internal_features)]
31+
32+
#[rustc_intrinsic]
33+
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
34+
35+
mod foo {
36+
#[rustc_intrinsic]
37+
const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {
38+
panic!("noisy const dealloc")
39+
}
40+
}
41+
42+
```
43+
44+
The behaviour on backends that override the intrinsic is exactly the same. On other
45+
backends, the intrinsic behaviour depends on which implementation is called, just like
46+
with any regular function.
47+
48+
## Intrinsics lowered to MIR instructions
49+
50+
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
51+
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
52+
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
53+
at all.
54+
55+
## Intrinsics without fallback logic
56+
57+
These must be implemented by all backends.
58+
1259
These are imported as if they were FFI functions, with the special
1360
`rust-intrinsic` ABI. For example, if one was in a freestanding
1461
context, but wished to be able to `transmute` between types, and
@@ -27,4 +74,5 @@ extern "rust-intrinsic" {
2774
}
2875
```
2976

30-
As with any other FFI functions, these are always `unsafe` to call.
77+
As with any other FFI functions, these are by default always `unsafe` to call.
78+
You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call.

0 commit comments

Comments
 (0)