about summary refs log tree commit diff
path: root/library/core
diff options
context:
space:
mode:
authorTomasz Miąsko <tomasz.miasko@gmail.com>2023-11-09 00:00:00 +0000
committerTomasz Miąsko <tomasz.miasko@gmail.com>2023-11-14 08:23:58 +0100
commit78da5776509f4e646a664de3ee7041f8008d5931 (patch)
tree003963619ebb79fe1f0eb86e2a4a601e8fedc48b /library/core
parent287ae4db75228ef8260a112bcc65245f05e18340 (diff)
downloadrust-78da5776509f4e646a664de3ee7041f8008d5931.tar.gz
rust-78da5776509f4e646a664de3ee7041f8008d5931.zip
Custom MIR: Support cleanup blocks
Cleanup blocks are declared with `bb (cleanup) = { ... }`.

`Call` and `Drop` terminators take an additional argument describing the
unwind action, which is one of the following:

* `UnwindContinue()`
* `UnwindUnreachable()`
* `UnwindTerminate(reason)`, where reason is `ReasonAbi` or `ReasonInCleanup`
* `UnwindCleanup(block)`

Also support unwind resume and unwind terminate terminators:

* `UnwindResume()`
* `UnwindTerminate(reason)`
Diffstat (limited to 'library/core')
-rw-r--r--library/core/src/intrinsics/mir.rs88
1 files changed, 73 insertions, 15 deletions
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index b26a17ec30e..0d8a306ace5 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -110,15 +110,15 @@
 //!         let popped;
 //!
 //!         {
-//!             Call(_unused = Vec::push(v, value), pop)
+//!             Call(_unused = Vec::push(v, value), pop, UnwindContinue())
 //!         }
 //!
 //!         pop = {
-//!             Call(popped = Vec::pop(v), drop)
+//!             Call(popped = Vec::pop(v), drop, UnwindContinue())
 //!         }
 //!
 //!         drop = {
-//!             Drop(popped, ret)
+//!             Drop(popped, ret, UnwindContinue())
 //!         }
 //!
 //!         ret = {
@@ -238,10 +238,6 @@
 //!
 //! #### Terminators
 //!
-//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
-//! are no resume and abort terminators, and terminators that might unwind do not have any way to
-//! indicate the unwind block.
-//!
 //!  - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions.
 //!  - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
 //!     - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
@@ -260,7 +256,26 @@
 /// Type representing basic blocks.
 ///
 /// All terminators will have this type as a return type. It helps achieve some type safety.
-pub struct BasicBlock;
+#[rustc_diagnostic_item = "mir_basic_block"]
+pub enum BasicBlock {
+    /// A non-cleanup basic block.
+    Normal,
+    /// A basic block that lies on an unwind path.
+    Cleanup,
+}
+
+/// The reason we are terminating the process during unwinding.
+#[rustc_diagnostic_item = "mir_unwind_terminate_reason"]
+pub enum UnwindTerminateReason {
+    /// Unwinding is just not possible given the ABI of this function.
+    Abi,
+    /// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was
+    /// triggered by the drop glue.
+    InCleanup,
+}
+
+pub use UnwindTerminateReason::Abi as ReasonAbi;
+pub use UnwindTerminateReason::InCleanup as ReasonInCleanup;
 
 macro_rules! define {
     ($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => {
@@ -271,11 +286,41 @@ macro_rules! define {
     }
 }
 
+// Unwind actions
+define!(
+    "mir_unwind_continue",
+    /// An unwind action that continues unwinding.
+    fn UnwindContinue()
+);
+define!(
+    "mir_unwind_unreachable",
+    /// An unwind action that triggers undefined behaviour.
+    fn UnwindUnreachable() -> BasicBlock
+);
+define!(
+    "mir_unwind_terminate",
+    /// An unwind action that terminates the execution.
+    ///
+    /// `UnwindTerminate` can also be used as a terminator.
+    fn UnwindTerminate(reason: UnwindTerminateReason)
+);
+define!(
+    "mir_unwind_cleanup",
+    /// An unwind action that continues execution in a given basic blok.
+    fn UnwindCleanup(goto: BasicBlock)
+);
+
+// Terminators
 define!("mir_return", fn Return() -> BasicBlock);
 define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
 define!("mir_unreachable", fn Unreachable() -> BasicBlock);
-define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
-define!("mir_call", fn Call(call: (), goto: BasicBlock));
+define!("mir_drop", fn Drop<T, U>(place: T, goto: BasicBlock, unwind_action: U));
+define!("mir_call", fn Call<U>(call: (), goto: BasicBlock, unwind_action: U));
+define!("mir_unwind_resume",
+    /// A terminator that resumes the unwinding.
+    fn UnwindResume()
+);
+
 define!("mir_storage_live", fn StorageLive<T>(local: T));
 define!("mir_storage_dead", fn StorageDead<T>(local: T));
 define!("mir_deinit", fn Deinit<T>(place: T));
@@ -382,16 +427,15 @@ pub macro mir {
         }
 
         $(
-            $block_name:ident = {
+            $block_name:ident $(($block_cleanup:ident))? = {
                 $($block:tt)*
             }
         )*
     ) => {{
         // First, we declare all basic blocks.
-        $(
-            let $block_name: ::core::intrinsics::mir::BasicBlock;
-        )*
-
+        __internal_declare_basic_blocks!($(
+            $block_name $(($block_cleanup))?
+        )*);
         {
             // Now all locals
             #[allow(non_snake_case)]
@@ -585,3 +629,17 @@ pub macro __internal_remove_let {
         }
     },
 }
+
+/// Helper macro that declares the basic blocks.
+#[doc(hidden)]
+pub macro __internal_declare_basic_blocks {
+    () => {},
+    ($name:ident (cleanup) $($rest:tt)*) => {
+        let $name = ::core::intrinsics::mir::BasicBlock::Cleanup;
+        __internal_declare_basic_blocks!($($rest)*)
+    },
+    ($name:ident $($rest:tt)*) => {
+        let $name = ::core::intrinsics::mir::BasicBlock::Normal;
+        __internal_declare_basic_blocks!($($rest)*)
+    },
+}