custom_mir)Expand description
Rustc internal tooling for hand-writing MIR.
If for some reasons you are not writing rustc tests and have found yourself considering using this feature, turn back. This is exceptionally unstable. There is no attempt at all to make anything work besides those things which the rustc test suite happened to need. If you make a typo you’ll probably ICE. Really, this is not the solution to your problems. Consider instead supporting the stable MIR project group.
The documentation for this module describes how to use this feature. If you are interested in
hacking on the implementation, most of that documentation lives at
rustc_mir_build/src/build/custom/mod.rs.
Typical usage will look like this:
#![feature(core_intrinsics, custom_mir)]
#![allow(internal_features)]
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
pub fn simple(x: i32) -> i32 {
    mir! {
        let temp2: i32;
        {
            let temp1 = x;
            Goto(my_second_block)
        }
        my_second_block = {
            temp2 = Move(temp1);
            RET = temp2;
            Return()
        }
    }
}The custom_mir attribute tells the compiler to treat the function as being custom MIR. This
attribute only works on functions - there is no way to insert custom MIR into the middle of
another function. The dialect and phase parameters indicate which version of MIR you are inserting here. Generally you’ll want to use #![custom_mir(dialect = "built")]
if you want your MIR to be modified by the full MIR pipeline, or #![custom_mir(dialect = "runtime", phase = "optimized")] if you don’t.
The input to the mir! macro is:
- An optional return type annotation in the form of type RET = ...;. This may be required if the compiler cannot infer the type of RET.
- A possibly empty list of local declarations. Locals can also be declared inline on
assignments via let. Type inference generally works. Shadowing does not.
- A list of basic blocks. The first of these is the start block and is where execution begins.
All blocks other than the start block need to be given a name, so that they can be referred
to later.
- Each block is a list of semicolon terminated statements, followed by a terminator. The syntax for the various statements and terminators is designed to be as similar as possible to the syntax for analogous concepts in native Rust. See below for a list.
 
§Examples
#![feature(core_intrinsics, custom_mir)]
#![allow(internal_features)]
#![allow(unused_assignments)]
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 {
    mir! {
        {
            match c {
                true => t,
                _ => f,
            }
        }
        t = {
            let temp = a;
            Goto(load_and_exit)
        }
        f = {
            temp = b;
            Goto(load_and_exit)
        }
        load_and_exit = {
            RET = *temp;
            Return()
        }
    }
}
#[custom_mir(dialect = "built")]
fn unwrap_unchecked<T>(opt: Option<T>) -> T {
    mir! {
        {
            RET = Move(Field(Variant(opt, 1), 0));
            Return()
        }
    }
}
#[custom_mir(dialect = "runtime", phase = "optimized")]
fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
    mir! {
        let _unused;
        let popped;
        {
            Call(_unused = Vec::push(v, value), ReturnTo(pop), UnwindContinue())
        }
        pop = {
            Call(popped = Vec::pop(v), ReturnTo(drop), UnwindContinue())
        }
        drop = {
            Drop(popped, ReturnTo(ret), UnwindContinue())
        }
        ret = {
            Return()
        }
    }
}
#[custom_mir(dialect = "runtime", phase = "optimized")]
fn annotated_return_type() -> (i32, bool) {
    mir! {
        type RET = (i32, bool);
        {
            RET.0 = 1;
            RET.1 = true;
            Return()
        }
    }
}We can also set off compilation failures that happen in sufficiently late stages of the compiler:
#![feature(core_intrinsics, custom_mir)]
extern crate core;
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
fn borrow_error(should_init: bool) -> i32 {
    mir! {
        let temp: i32;
        {
            match should_init {
                true => init,
                _ => use_temp,
            }
        }
        init = {
            temp = 0;
            Goto(use_temp)
        }
        use_temp = {
            RET = temp;
            Return()
        }
    }
}error[E0381]: used binding is possibly-uninitialized
  --> test.rs:24:13
   |
8  | /     mir! {
9  | |         let temp: i32;
10 | |
11 | |         {
...  |
19 | |             temp = 0;
   | |             -------- binding initialized here in some conditions
...  |
24 | |             RET = temp;
   | |             ^^^^^^^^^^ value used here but it is possibly-uninitialized
25 | |             Return()
26 | |         }
27 | |     }
   | |_____- binding declared here but left uninitialized
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0381`.§Syntax
The lists below are an exhaustive description of how various MIR constructs can be created. Anything missing from the list should be assumed to not be supported, PRs welcome.
§Locals
- The _0return local can always be accessed viaRET.
- Arguments can be accessed via their regular name.
- All other locals need to be declared with letsomewhere and then can be accessed by name.
§Places
- Locals implicitly convert to places.
- Field accesses, derefs, and indexing work normally.
- Fields in variants can be accessed via the VariantandFieldassociated functions, see their documentation for details.
§Operands
- Places implicitly convert to Copyoperands.
- Moveoperands can be created via- Move.
- Const blocks, literals, named constants, and const params all just work.
- Staticand- StaticMutcan be used to create- &Tand- *mut Ts to statics. These are constants in MIR and the only way to access statics.
§Statements
- Assign statements work via normal Rust assignment.
- Retag,- StorageLive,- StorageDead,- Deinitstatements have an associated function.
§Rvalues
- Operands implicitly convert to Uservalues.
- &,- &mut,- addr_of!, and- addr_of_mut!all work to create their associated rvalue.
- Discriminant,- Len, and- CopyForDerefhave associated functions.
- Unary and binary operations use their normal Rust syntax - a * b,!c, etc.
- The binary operation Offsetcan be created viaOffset.
- Checked binary operations are represented by wrapping the associated binop in Checked.
- Array repetition syntax ([foo; 10]) creates the associated rvalue.
§Terminators
- Goto,- Return,- Unreachableand- Drophave associated functions.
- match some_int_operandbecomes a- SwitchInt. Each arm should be- literal => basic_block- The exception is the last arm, which must be _ => basic_blockand corresponds to the otherwise branch.
 
- The exception is the last arm, which must be 
- Callhas an associated function as well, with special syntax:- Call(ret_val = function(arg1, arg2, ...), ReturnTo(next_block), UnwindContinue()).
- TailCalldoes not have a return destination or next block, so its syntax is just- TailCall(function(arg1, arg2, ...)).
§Debuginfo
Debuginfo associates source code variable names (of variables that may not exist any more) with MIR expressions that indicate where the value of that variable is stored. The syntax to do so is:
debug source_var_name => expression;Both places and constants are supported in the expression.
#![allow(internal_features)]
#![feature(core_intrinsics, custom_mir)]
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
fn debuginfo(arg: Option<&i32>) {
    mir!(
        // Debuginfo for a source variable `plain_local` that just duplicates `arg`.
        debug plain_local => arg;
        // Debuginfo for a source variable `projection` that can be computed by dereferencing
        // a field of `arg`.
        debug projection => *Field::<&i32>(Variant(arg, 1), 0);
        // Debuginfo for a source variable `constant` that always holds the value `5`.
        debug constant => 5_usize;
        {
            Return()
        }
    )
}Re-exports§
- pub use UnwindTerminateReason::Abi as ReasonAbi;- Experimental 
- pub use UnwindTerminateReason::InCleanup as ReasonInCleanup;- Experimental 
Macros§
- mirExperimental Macro for generating custom MIR.
- placeExperimental Helper macro that allows you to treat a value expression like a place expression.
Structs§
- ReturnToArg Experimental 
- UnwindAction Arg Experimental 
Enums§
- BasicBlock Experimental Type representing basic blocks.
- UnwindTerminate Reason Experimental The reason we are terminating the process during unwinding.
Functions§
- AssumeExperimental 
- CallExperimental Call a function.
- CastPtrTo Ptr Experimental Emits aCastKind::PtrToPtrcast.
- CastTransmute Experimental Emits aCastKind::Transmutecast.
- CheckedExperimental 
- CopyForDeref Experimental 
- DeinitExperimental 
- DiscriminantExperimental Gets the discriminant of a place.
- DropExperimental Drop the contents of a place.
- FieldExperimental Access the field with the given index of some place.
- GotoExperimental 
- LenExperimental 
- MoveExperimental 
- OffsetExperimental 
- PtrMetadataExperimental 
- RetagExperimental 
- ReturnExperimental 
- ReturnTo Experimental 
- SetDiscriminantExperimental 
- StaticExperimental 
- StaticMut Experimental 
- StorageDead Experimental 
- StorageLive Experimental 
- TailCall Experimental Call a function.
- UnreachableExperimental 
- UnwindCleanup Experimental An unwind action that continues execution in a given basic block.
- UnwindContinue Experimental An unwind action that continues unwinding.
- UnwindResume Experimental A terminator that resumes the unwinding.
- UnwindTerminate Experimental An unwind action that terminates the execution.
- UnwindUnreachable Experimental An unwind action that triggers undefined behavior.
- VariantExperimental Adds a variant projection with the given index to the place.