rune_ssa/
block.rs

1use crate::global::Global;
2use crate::internal::commas;
3use crate::{Assign, BlockId, Constant, Error, Phi, StaticId, Term, Value, Var};
4use std::cell::{Cell, RefCell};
5use std::collections::{BTreeMap, VecDeque};
6use std::fmt;
7use std::rc::Rc;
8
9/// Macro to help build a unary op.
10macro_rules! block_unary_op {
11    ($new:ident, $assign:ident, $variant:ident, $doc:literal) => {
12        #[doc = $doc]
13        pub fn $new(&self, a: Var) -> Result<Var, Error> {
14            let a = self.read(a)?;
15            self.assign_new(Value::$variant(a))
16        }
17
18        #[doc = $doc]
19        pub fn $assign(&self, id: Var, a: Var) -> Result<(), Error> {
20            let a = self.read(a)?;
21            self.assign_value(id, Value::$variant(a))?;
22            Ok(())
23        }
24    };
25}
26
27/// Macro to help build a binary op.
28macro_rules! block_binary_op {
29    ($new:ident, $assign:ident, $variant:ident, $doc:literal) => {
30        #[doc = $doc]
31        pub fn $new(&self, a: Var, b: Var) -> Result<Var, Error> {
32            let a = self.read(a)?;
33            let b = self.read(b)?;
34            self.assign_new(Value::$variant(a, b))
35        }
36
37        #[doc = $doc]
38        pub fn $assign(&self, id: Var, a: Var, b: Var) -> Result<(), Error> {
39            let a = self.read(a)?;
40            let b = self.read(b)?;
41            self.assign_value(id, Value::$variant(a, b))?;
42            Ok(())
43        }
44    };
45}
46
47/// A block containing a sequence of assignments.
48///
49/// A block carries a definition of its entry.
50/// The entry is the sequence of input variables the block expects.
51#[derive(Clone)]
52pub struct Block {
53    inner: Rc<BlockInner>,
54}
55
56impl Block {
57    /// Construct a new empty block.
58    pub(crate) fn new(id: BlockId, global: Global, name: Option<Box<str>>) -> Self {
59        Self {
60            inner: Rc::new(BlockInner {
61                id,
62                inputs: Cell::new(0),
63                name,
64                global,
65                open: Cell::new(true),
66                vars: RefCell::new(BTreeMap::new()),
67                incomplete: RefCell::new(Vec::new()),
68                term: RefCell::new(Term::Panic),
69                ancestors: RefCell::new(Vec::new()),
70            }),
71        }
72    }
73
74    /// The name of the block.
75    pub fn name(&self) -> Option<&str> {
76        self.inner.name.as_deref()
77    }
78
79    /// Read the given variable, looking it up recursively in ancestor blocks
80    /// and memoizing as needed.
81    pub fn read(&self, var: Var) -> Result<Assign, Error> {
82        // Local assignment that is already present.
83        if let Some(assign) = self.inner.vars.borrow().get(&var) {
84            return Ok(assign.clone());
85        }
86
87        let id = self.inner.global.static_id();
88        let assign = Assign::new(id, self.id());
89
90        self.inner
91            .global
92            .values_mut()
93            .insert(id, Value::Phi(Phi::new()));
94
95        self.inner.vars.borrow_mut().insert(var, assign.clone());
96
97        if self.inner.open.get() {
98            self.inner
99                .incomplete
100                .borrow_mut()
101                .push((assign.clone(), var));
102        } else {
103            self.add_phi(id, var)?;
104        }
105
106        Ok(assign)
107    }
108
109    /// Read the given dependencies recursively.
110    fn read_dependencies(&self, var: Var) -> Result<Vec<Assign>, Error> {
111        let blocks = self.inner.global.blocks();
112        let mut deps = Vec::new();
113
114        let mut queue = VecDeque::new();
115
116        for ancestor in &*self.inner.ancestors.borrow() {
117            queue.push_back(blocks.get(*ancestor)?);
118        }
119
120        while let Some(block) = queue.pop_front() {
121            if let Some(assign) = block.inner.vars.borrow().get(&var) {
122                deps.push(assign.clone());
123                continue;
124            }
125
126            for ancestor in &*block.inner.ancestors.borrow() {
127                queue.push_back(blocks.get(*ancestor)?);
128            }
129        }
130
131        Ok(deps)
132    }
133
134    /// Assign an instruction to a new vvar.
135    fn assign_new(&self, value: Value) -> Result<Var, Error> {
136        let var = self.inner.global.var();
137        self.assign_value(var, value)?;
138        Ok(var)
139    }
140
141    /// Assign an instruction to an existing var.
142    fn assign_value(&self, id: Var, value: Value) -> Result<(), Error> {
143        self.inner.assign(id, value)?;
144        Ok(())
145    }
146
147    /// Assign a variable.
148    pub fn assign(&self, var: Var, to_read: Var) -> Result<(), Error> {
149        let assign = self.read(to_read)?;
150        self.inner.vars.borrow_mut().insert(var, assign);
151        Ok(())
152    }
153
154    /// Define an input into the block.
155    pub fn input(&self) -> Result<Var, Error> {
156        let id = self.inner.global.var();
157        let input = self.inner.inputs.get();
158        self.inner.inputs.set(input + 1);
159        self.assign_value(id, Value::Input(input))?;
160        Ok(id)
161    }
162
163    /// Finalize the block.
164    pub(crate) fn seal(&self) -> Result<(), Error> {
165        let open = self.inner.open.take();
166
167        if open {
168            let incomplete = std::mem::take(&mut *self.inner.incomplete.borrow_mut());
169
170            for (assign, var_to_read) in incomplete {
171                self.add_phi(assign.id(), var_to_read)?;
172            }
173        }
174
175        Ok(())
176    }
177
178    /// Populate phi operands.
179    fn add_phi(&self, id: StaticId, var_to_read: Var) -> Result<(), Error> {
180        let deps = self.read_dependencies(var_to_read)?;
181
182        if deps.len() <= 1 {
183            if let Some(assign) = deps.into_iter().next() {
184                if let Some(Value::Phi(..)) = self.inner.global.values_mut().remove(id) {
185                    let old = self
186                        .inner
187                        .vars
188                        .borrow_mut()
189                        .insert(var_to_read, assign.clone());
190
191                    if let Some(existing) = old {
192                        existing.replace(&assign);
193                    }
194
195                    return Ok(());
196                }
197            }
198        } else {
199            if let Some(Value::Phi(phi)) = self.inner.global.values_mut().get_mut(id) {
200                phi.extend(deps);
201                return Ok(());
202            }
203        }
204
205        Err(Error::MissingPhiNode(id))
206    }
207
208    /// Get the identifier of the block.
209    #[inline]
210    pub fn id(&self) -> BlockId {
211        self.inner.id
212    }
213
214    /// Perform a diagnostical dump of a block.
215    #[inline]
216    pub fn dump(&self) -> BlockDump<'_> {
217        BlockDump(self)
218    }
219
220    /// Define a unit.
221    pub fn unit(&self) -> Result<Var, Error> {
222        self.constant(Constant::Unit)
223    }
224
225    /// Assign a unit.
226    pub fn assign_unit(&self, id: Var) -> Result<(), Error> {
227        self.assign_constant(id, Constant::Unit)?;
228        Ok(())
229    }
230
231    /// Define a constant.
232    pub fn constant(&self, constant: Constant) -> Result<Var, Error> {
233        let const_id = self.inner.global.constant(constant);
234        self.assign_new(Value::Const(const_id))
235    }
236
237    /// Assign a constant.
238    pub fn assign_constant(&self, id: Var, constant: Constant) -> Result<(), Error> {
239        let const_id = self.inner.global.constant(constant);
240        self.assign_value(id, Value::Const(const_id))?;
241        Ok(())
242    }
243
244    block_unary_op!(not, assign_not, Not, "Compute `!arg`.");
245    block_binary_op!(add, assign_add, Add, "Compute `lhs + rhs`.");
246    block_binary_op!(sub, assign_sub, Sub, "Compute `lhs - rhs`.");
247    block_binary_op!(div, assign_div, Div, "Compute `lhs / rhs`.");
248    block_binary_op!(mul, assign_mul, Mul, "Compute `lhs * rhs`.");
249    block_binary_op!(cmp_lt, assign_cmp_lt, CmpLt, "Compare if `lhs < rhs`.");
250    block_binary_op!(cmp_lte, assign_cmp_lte, CmpLte, "Compare if `lhs <= rhs`.");
251    block_binary_op!(cmp_eq, assign_cmp_eq, CmpEq, "Compare if `lhs == rhs`.");
252    block_binary_op!(cmp_gt, assign_cmp_gt, CmpGt, "Compare if `lhs > rhs`.");
253    block_binary_op!(cmp_gte, assign_cmp_gte, CmpGte, "Compare if `lhs >= rhs`.");
254
255    /// Perform an unconditional jump to the given block with the specified
256    /// inputs.
257    pub fn jump(&self, block: &Block) -> Result<(), Error> {
258        self.mark_control(block)?;
259
260        *self.inner.term.borrow_mut() = Term::Jump { block: block.id() };
261        Ok(())
262    }
263
264    /// Perform a conditional jump to the given block with the specified inputs
265    /// if the given condition is true.
266    pub fn jump_if(
267        &self,
268        condition: Var,
269        then_block: &Block,
270        else_block: &Block,
271    ) -> Result<(), Error> {
272        let condition = self.read(condition)?;
273
274        self.mark_control(then_block)?;
275        self.mark_control(else_block)?;
276
277        *self.inner.term.borrow_mut() = Term::JumpIf {
278            condition,
279            then_block: then_block.id(),
280            else_block: else_block.id(),
281        };
282
283        Ok(())
284    }
285
286    /// Return from this the procedure this block belongs to.
287    pub fn return_unit(&self) -> Result<(), Error> {
288        let var = self.unit()?;
289        let var = self.read(var)?;
290
291        *self.inner.term.borrow_mut() = Term::Return { var };
292        self.inner.global.mark_return(self.id());
293        Ok(())
294    }
295
296    /// Return from this the procedure this block belongs to.
297    pub fn return_(&self, var: Var) -> Result<(), Error> {
298        let var = self.read(var)?;
299
300        *self.inner.term.borrow_mut() = Term::Return { var };
301        self.inner.global.mark_return(self.id());
302        Ok(())
303    }
304
305    fn mark_control(&self, other: &Self) -> Result<(), Error> {
306        if !other.inner.open.get() {
307            return Err(Error::SealedBlockJump(self.id(), other.inner.id));
308        }
309
310        other.inner.ancestors.borrow_mut().push(self.id());
311        Ok(())
312    }
313}
314
315pub struct BlockDump<'a>(&'a Block);
316
317impl fmt::Display for BlockDump<'_> {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        let ancestors = self.0.inner.ancestors.borrow();
320
321        write!(f, "{}", self.0.id())?;
322
323        if self.0.inner.open.get() {
324            write!(f, " (open)")?;
325        }
326
327        if ancestors.is_empty() {
328            write!(f, ":")?;
329        } else {
330            write!(f, ": {}", commas(&ancestors[..]))?;
331        }
332
333        if let Some(name) = &self.0.inner.name {
334            write!(f, " // {}", name)?;
335        }
336
337        writeln!(f)?;
338
339        for (var, assign) in self.0.inner.vars.borrow().iter() {
340            if let Some(value) = self.0.inner.global.values().get(assign.id()) {
341                writeln!(f, "  {}: {} <- {}", var, assign, value.dump())?;
342            } else {
343                writeln!(f, "  {}: {} <- ?", var, assign)?;
344            }
345        }
346
347        writeln!(f, "  {}", self.0.inner.term.borrow().dump())?;
348        Ok(())
349    }
350}
351
352struct BlockInner {
353    /// The identifier of the block.
354    id: BlockId,
355    /// The number of inputs in the block.
356    inputs: Cell<usize>,
357    /// If the block is finalized or not.
358    ///
359    /// Control flows can only be added to non-finalized blocks.
360    open: Cell<bool>,
361    /// The (optional) name of the block for debugging and symbolic purposes.
362    name: Option<Box<str>>,
363    /// Global shared stack machine state.
364    global: Global,
365    /// Instructions being built.
366    vars: RefCell<BTreeMap<Var, Assign>>,
367    /// Collection of locally incomplete phi nodes.
368    incomplete: RefCell<Vec<(Assign, Var)>>,
369    /// Instructions that do not produce a value.
370    term: RefCell<Term>,
371    /// Ancestor blocks.
372    ancestors: RefCell<Vec<BlockId>>,
373}
374
375impl BlockInner {
376    /// Reassign the given variable.
377    fn assign(&self, var: Var, value: Value) -> Result<(), Error> {
378        let id = self.global.static_id();
379        self.vars.borrow_mut().insert(var, Assign::new(id, self.id));
380        self.global.values_mut().insert(id, value);
381        Ok(())
382    }
383}