Skip to main content

solar_codegen/mir/
block.rs

1//! MIR basic blocks.
2
3use super::{BlockId, InstId, ValueId};
4use smallvec::SmallVec;
5use std::fmt;
6
7/// A basic block in the MIR.
8#[derive(Clone, Debug)]
9pub struct BasicBlock {
10    /// The instructions in this block (excluding the terminator).
11    pub instructions: Vec<InstId>,
12    /// The terminator instruction.
13    pub terminator: Option<Terminator>,
14    /// Predecessor blocks.
15    pub predecessors: SmallVec<[BlockId; 4]>,
16}
17
18impl BasicBlock {
19    /// Creates a new empty basic block.
20    #[must_use]
21    pub fn new() -> Self {
22        Self { instructions: Vec::new(), terminator: None, predecessors: SmallVec::new() }
23    }
24
25    /// Returns true if this block has a terminator.
26    #[must_use]
27    pub const fn is_terminated(&self) -> bool {
28        self.terminator.is_some()
29    }
30
31    /// Returns the terminator, if present.
32    #[must_use]
33    pub const fn terminator(&self) -> Option<&Terminator> {
34        self.terminator.as_ref()
35    }
36}
37
38impl Default for BasicBlock {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44/// A block terminator instruction.
45#[derive(Clone, Debug, PartialEq)]
46pub enum Terminator {
47    /// Unconditional jump to another block.
48    Jump(BlockId),
49    /// Conditional branch.
50    Branch {
51        /// The condition value (must be boolean).
52        condition: ValueId,
53        /// The block to jump to if true.
54        then_block: BlockId,
55        /// The block to jump to if false.
56        else_block: BlockId,
57    },
58    /// Multi-way switch.
59    Switch {
60        /// The value to switch on.
61        value: ValueId,
62        /// The default block.
63        default: BlockId,
64        /// The cases: (value, block).
65        cases: Vec<(ValueId, BlockId)>,
66    },
67    /// Return from function.
68    Return {
69        /// The return values.
70        values: SmallVec<[ValueId; 2]>,
71    },
72    /// Revert execution.
73    Revert {
74        /// Memory offset of revert data.
75        offset: ValueId,
76        /// Size of revert data.
77        size: ValueId,
78    },
79    /// Return raw, already-encoded data: `RETURN(offset, size)`. Used for
80    /// ABI-encoded external returns whose size is computed at runtime.
81    ReturnData {
82        /// Memory offset of the return data.
83        offset: ValueId,
84        /// Size of the return data in bytes.
85        size: ValueId,
86    },
87    /// Stop execution.
88    Stop,
89    /// Self-destruct the contract.
90    SelfDestruct {
91        /// The address to send remaining funds to.
92        recipient: ValueId,
93    },
94    /// Invalid operation (unreachable code).
95    Invalid,
96}
97
98impl Terminator {
99    /// Returns the successor blocks of this terminator.
100    #[must_use]
101    pub fn successors(&self) -> SmallVec<[BlockId; 2]> {
102        match self {
103            Self::Jump(target) => smallvec::smallvec![*target],
104            Self::Branch { then_block, else_block, .. } => {
105                smallvec::smallvec![*then_block, *else_block]
106            }
107            Self::Switch { default, cases, .. } => {
108                let mut succs = SmallVec::with_capacity(cases.len() + 1);
109                succs.push(*default);
110                for (_, block) in cases {
111                    succs.push(*block);
112                }
113                succs
114            }
115            Self::Return { .. }
116            | Self::Revert { .. }
117            | Self::ReturnData { .. }
118            | Self::Stop
119            | Self::SelfDestruct { .. }
120            | Self::Invalid => SmallVec::new(),
121        }
122    }
123
124    /// Returns the mnemonic for this terminator.
125    #[must_use]
126    pub const fn mnemonic(&self) -> &'static str {
127        match self {
128            Self::Jump(_) => "jump",
129            Self::Branch { .. } => "branch",
130            Self::Switch { .. } => "switch",
131            Self::Return { .. } => "return",
132            Self::Revert { .. } => "revert",
133            Self::ReturnData { .. } => "returndata",
134            Self::Stop => "stop",
135            Self::SelfDestruct { .. } => "selfdestruct",
136            Self::Invalid => "invalid",
137        }
138    }
139
140    /// Returns the [`ValueId`] operands of this terminator (the values it reads).
141    /// Block targets are NOT included; use [`Self::successors`] for those.
142    #[must_use]
143    pub fn operands(&self) -> SmallVec<[ValueId; 4]> {
144        let mut out = SmallVec::new();
145        match self {
146            Self::Jump(_) => {}
147            Self::Branch { condition, .. } => out.push(*condition),
148            Self::Switch { value, cases, .. } => {
149                out.push(*value);
150                for (case_val, _) in cases {
151                    out.push(*case_val);
152                }
153            }
154            Self::Return { values } => out.extend(values.iter().copied()),
155            Self::Revert { offset, size } | Self::ReturnData { offset, size } => {
156                out.push(*offset);
157                out.push(*size);
158            }
159            Self::Stop | Self::Invalid => {}
160            Self::SelfDestruct { recipient } => out.push(*recipient),
161        }
162        out
163    }
164}
165
166impl fmt::Display for Terminator {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        match self {
169            Self::Jump(target) => write!(f, "jump bb{}", target.index()),
170            Self::Branch { condition, then_block, else_block } => {
171                write!(
172                    f,
173                    "branch v{}, bb{}, bb{}",
174                    condition.index(),
175                    then_block.index(),
176                    else_block.index()
177                )
178            }
179            Self::Switch { value, default, cases } => {
180                write!(f, "switch v{}, default bb{}", value.index(), default.index())?;
181                for (val, block) in cases {
182                    write!(f, ", v{} => bb{}", val.index(), block.index())?;
183                }
184                Ok(())
185            }
186            Self::Return { values } => {
187                write!(f, "return")?;
188                for (i, v) in values.iter().enumerate() {
189                    if i > 0 {
190                        write!(f, ",")?;
191                    }
192                    write!(f, " v{}", v.index())?;
193                }
194                Ok(())
195            }
196            Self::Revert { offset, size } => {
197                write!(f, "revert v{}, v{}", offset.index(), size.index())
198            }
199            Self::ReturnData { offset, size } => {
200                write!(f, "returndata v{}, v{}", offset.index(), size.index())
201            }
202            Self::Stop => write!(f, "stop"),
203            Self::SelfDestruct { recipient } => {
204                write!(f, "selfdestruct v{}", recipient.index())
205            }
206            Self::Invalid => write!(f, "invalid"),
207        }
208    }
209}