about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-02-19 17:35:12 +0000
committerOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-03-04 16:13:50 +0000
commit1e57df19697fd4f7f1e9dfd24ccfb00051c25bda (patch)
treef0730d02b56b2eb8d180b1845f8e0a88557b3938
parentf2612daf58bfa2d747ddae02243a6ec1d6528992 (diff)
downloadrust-1e57df19697fd4f7f1e9dfd24ccfb00051c25bda.tar.gz
rust-1e57df19697fd4f7f1e9dfd24ccfb00051c25bda.zip
Add a scheme for moving away from `extern "rust-intrinsic"` entirely
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs11
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs9
-rw-r--r--compiler/rustc_middle/src/ty/intrinsic.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs5
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs4
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs6
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs5
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/intrinsics.rs14
-rw-r--r--src/doc/unstable-book/src/language-features/intrinsics.md13
-rw-r--r--tests/ui/intrinsics/always-gets-overridden.rs20
-rw-r--r--tests/ui/intrinsics/not-overridden.rs18
-rw-r--r--tests/ui/intrinsics/not-overridden.stderr10
16 files changed, 131 insertions, 8 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 84269ec2942..9b8167fa2bf 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -1255,7 +1255,17 @@ fn codegen_regular_intrinsic_call<'tcx>(
 
         // Unimplemented intrinsics must have a fallback body. The fallback body is obtained
         // 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());
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 2dba04e0bb7..347a968a303 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -16,6 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
 use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
 use rustc_middle::util::Providers;
 use rustc_session::config::{CrateType, OomStrategy};
+use rustc_span::sym;
 use rustc_target::spec::{SanitizerSet, TlsModel};
 
 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);
             }
 
+            if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
+                return None;
+            }
+
             // Only consider nodes that actually have exported symbols.
             match tcx.def_kind(def_id) {
                 DefKind::Fn | DefKind::Static(_) => {}
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index e1150904bd1..1875d06d669 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -903,7 +903,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             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)
+                    }
                 }
             }
         };
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 10867ae230a..de7ea84ffa5 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -867,6 +867,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         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_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:
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index fdb2b4f2024..8c2b5413fa0 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1051,13 +1051,18 @@ fn should_encode_mir(
         // Coroutines require optimized MIR to compute layout.
         DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => (false, true),
         // 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 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()
                     && reachable_set.contains(&def_id)
                     && (generics.requires_monomorphization(tcx)
                         || 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]`.
             let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
                 || tcx.is_const_default_method(def_id.to_def_id());
diff --git a/compiler/rustc_middle/src/ty/intrinsic.rs b/compiler/rustc_middle/src/ty/intrinsic.rs
index f5df687d923..18d08ed23a5 100644
--- a/compiler/rustc_middle/src/ty/intrinsic.rs
+++ b/compiler/rustc_middle/src/ty/intrinsic.rs
@@ -5,6 +5,8 @@ use super::TyCtxt;
 #[derive(Copy, Clone, Debug, Decodable, Encodable, HashStable)]
 pub struct IntrinsicDef {
     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<'_> {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index e64f69a4461..19724b2e83a 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -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)
         || 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 {
         None
     }
diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
index 483fd753e70..e2730c156c3 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -23,6 +23,10 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         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.
     match tcx.def_kind(def_id) {
         DefKind::Ctor(..) | DefKind::Closure => return true,
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 86f37456183..37d087a4696 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -632,6 +632,12 @@ fn 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()) {
         // 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
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 5593de60784..b3a28b546d3 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1019,6 +1019,11 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
         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() {
         // Local items cannot be referred to locally without monomorphizing them locally.
         return true;
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7e31cfc0662..73088d2cf7b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1525,6 +1525,7 @@ symbols! {
         rustc_inherit_overflow_checks,
         rustc_insignificant_dtor,
         rustc_intrinsic,
+        rustc_intrinsic_must_be_overridden,
         rustc_layout,
         rustc_layout_scalar_valid_range_end,
         rustc_layout_scalar_valid_range_start,
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 96e667d63c5..fd9dc4c46bd 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2499,9 +2499,8 @@ extern "rust-intrinsic" {
     #[rustc_nounwind]
     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]
+    #[cfg(bootstrap)]
     pub fn vtable_size(ptr: *const ()) -> usize;
 
     /// `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)]
 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
 // 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
diff --git a/src/doc/unstable-book/src/language-features/intrinsics.md b/src/doc/unstable-book/src/language-features/intrinsics.md
index 1a8c1c0b36a..02a009d56d3 100644
--- a/src/doc/unstable-book/src/language-features/intrinsics.md
+++ b/src/doc/unstable-book/src/language-features/intrinsics.md
@@ -52,12 +52,23 @@ with any regular function.
 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.
+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
 
 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
 `rust-intrinsic` ABI. For example, if one was in a freestanding
 context, but wished to be able to `transmute` between types, and
diff --git a/tests/ui/intrinsics/always-gets-overridden.rs b/tests/ui/intrinsics/always-gets-overridden.rs
new file mode 100644
index 00000000000..ad2c2be4daa
--- /dev/null
+++ b/tests/ui/intrinsics/always-gets-overridden.rs
@@ -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);
+    }
+}
diff --git a/tests/ui/intrinsics/not-overridden.rs b/tests/ui/intrinsics/not-overridden.rs
new file mode 100644
index 00000000000..d6655b51905
--- /dev/null
+++ b/tests/ui/intrinsics/not-overridden.rs
@@ -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
+}
diff --git a/tests/ui/intrinsics/not-overridden.stderr b/tests/ui/intrinsics/not-overridden.stderr
new file mode 100644
index 00000000000..9b8849cea1c
--- /dev/null
+++ b/tests/ui/intrinsics/not-overridden.stderr
@@ -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
+