Skip to main content

ir_lang/
function.rs

1//! The [`Function`]: the unit of IR, and the textual form it prints in.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::fmt;
6
7use crate::entity::{Block, Value};
8use crate::inst::{Inst, Terminator};
9use crate::ty::Type;
10use crate::validate::ValidationError;
11
12/// How a [`Value`] comes to be: either a parameter of a block, or the result of an
13/// instruction in a block.
14#[derive(Clone, PartialEq, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub(crate) enum ValueDef {
17    /// A parameter of the named block.
18    Param(Block),
19    /// The result of an instruction located in the named block.
20    Inst(Block, Inst),
21}
22
23/// The type and origin of a single value.
24#[derive(Clone, PartialEq, Debug)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub(crate) struct ValueData {
27    pub(crate) ty: Type,
28    pub(crate) def: ValueDef,
29}
30
31/// The contents of a single basic block.
32#[derive(Clone, PartialEq, Debug)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub(crate) struct BlockData {
35    /// The parameter values of this block, in order.
36    pub(crate) params: Vec<Value>,
37    /// The values defined by this block's instructions, in program order.
38    pub(crate) insts: Vec<Value>,
39    /// The terminator that ends the block, or `None` if one was never set.
40    pub(crate) term: Option<Terminator>,
41}
42
43/// A function in SSA form: a control-flow graph of basic blocks over a single flat
44/// store of values.
45///
46/// A function is the unit ir-lang represents and the thing a front-end lowers into.
47/// It has a name, a parameter list, a return type, an entry block, and a set of
48/// blocks; each block is a run of value-producing [`Inst`]s ended by one
49/// [`Terminator`]. Values are named by [`Value`] handles and defined exactly once,
50/// either as a block parameter or as an instruction result.
51///
52/// You do not construct a `Function` field by field — a [`Builder`](crate::Builder)
53/// produces one. Once you hold it, the accessors here read it back, and
54/// [`validate`](Function::validate) checks it is well-formed. A function also prints
55/// as a readable textual IR through its [`Display`](core::fmt::Display)
56/// implementation.
57///
58/// # Examples
59///
60/// ```
61/// use ir_lang::{Builder, BinOp, Type};
62///
63/// // fn double(x: int) -> int { x + x }
64/// let mut b = Builder::new("double", &[Type::Int], Type::Int);
65/// let x = b.block_params(b.entry())[0];
66/// let sum = b.bin(BinOp::Add, x, x);
67/// b.ret(Some(sum));
68/// let func = b.finish();
69///
70/// assert_eq!(func.name(), "double");
71/// assert_eq!(func.params(), &[Type::Int]);
72/// assert_eq!(func.ret(), Type::Int);
73/// assert!(func.validate().is_ok());
74/// ```
75#[derive(Clone, PartialEq, Debug)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub struct Function {
78    pub(crate) name: String,
79    pub(crate) params: Vec<Type>,
80    pub(crate) ret: Type,
81    pub(crate) entry: Block,
82    pub(crate) blocks: Vec<BlockData>,
83    pub(crate) values: Vec<ValueData>,
84}
85
86impl Function {
87    pub(crate) fn from_parts(
88        name: String,
89        params: Vec<Type>,
90        ret: Type,
91        entry: Block,
92        blocks: Vec<BlockData>,
93        values: Vec<ValueData>,
94    ) -> Self {
95        Self {
96            name,
97            params,
98            ret,
99            entry,
100            blocks,
101            values,
102        }
103    }
104
105    /// Returns the function's name.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use ir_lang::{Builder, Type};
111    ///
112    /// let b = Builder::new("main", &[], Type::Unit);
113    /// assert_eq!(b.finish().name(), "main");
114    /// ```
115    #[must_use]
116    pub fn name(&self) -> &str {
117        &self.name
118    }
119
120    /// Returns the function's parameter types, in order. These are also the types
121    /// of the entry block's parameters.
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// use ir_lang::{Builder, Type};
127    ///
128    /// let b = Builder::new("f", &[Type::Int, Type::Bool], Type::Unit);
129    /// assert_eq!(b.finish().params(), &[Type::Int, Type::Bool]);
130    /// ```
131    #[must_use]
132    pub fn params(&self) -> &[Type] {
133        &self.params
134    }
135
136    /// Returns the function's return type.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use ir_lang::{Builder, Type};
142    ///
143    /// let b = Builder::new("f", &[], Type::Float);
144    /// assert_eq!(b.finish().ret(), Type::Float);
145    /// ```
146    #[must_use]
147    pub const fn ret(&self) -> Type {
148        self.ret
149    }
150
151    /// Returns the entry block — where execution begins. It is always block zero
152    /// and its parameters are the function's parameters.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use ir_lang::{Builder, Type, Block};
158    ///
159    /// let b = Builder::new("f", &[], Type::Unit);
160    /// assert_eq!(b.finish().entry().index(), 0);
161    /// ```
162    #[must_use]
163    pub const fn entry(&self) -> Block {
164        self.entry
165    }
166
167    /// Returns the number of blocks in the function.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use ir_lang::{Builder, Type};
173    ///
174    /// let mut b = Builder::new("f", &[], Type::Unit);
175    /// let _ = b.create_block(&[]);
176    /// b.ret(None);
177    /// assert_eq!(b.finish().block_count(), 2);
178    /// ```
179    #[must_use]
180    pub fn block_count(&self) -> usize {
181        self.blocks.len()
182    }
183
184    /// Returns the number of values defined in the function (block parameters and
185    /// instruction results together). Value handles run densely over `0..count`.
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// use ir_lang::{Builder, Type};
191    ///
192    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
193    /// let one = b.iconst(1);
194    /// b.ret(Some(one));
195    /// // one parameter value + one constant value
196    /// assert_eq!(b.finish().value_count(), 2);
197    /// ```
198    #[must_use]
199    pub fn value_count(&self) -> usize {
200        self.values.len()
201    }
202
203    /// Iterates over every block handle, entry first, in creation order.
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use ir_lang::{Builder, Type};
209    ///
210    /// let mut b = Builder::new("f", &[], Type::Unit);
211    /// let _ = b.create_block(&[]);
212    /// b.ret(None);
213    /// let func = b.finish();
214    /// assert_eq!(func.blocks().count(), 2);
215    /// ```
216    pub fn blocks(&self) -> impl Iterator<Item = Block> {
217        (0..self.blocks.len() as u32).map(Block::from_raw)
218    }
219
220    /// Returns a block's parameter values, in order, or an empty slice if the block
221    /// handle is out of range.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use ir_lang::{Builder, Type};
227    ///
228    /// let b = Builder::new("f", &[Type::Int, Type::Int], Type::Unit);
229    /// let func = b.finish();
230    /// assert_eq!(func.block_params(func.entry()).len(), 2);
231    /// ```
232    #[must_use]
233    pub fn block_params(&self, block: Block) -> &[Value] {
234        match self.blocks.get(block.index()) {
235            Some(data) => &data.params,
236            None => &[],
237        }
238    }
239
240    /// Returns the values defined by a block's instructions, in program order, or an
241    /// empty slice if the block handle is out of range.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use ir_lang::{Builder, BinOp, Type};
247    ///
248    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
249    /// let x = b.block_params(b.entry())[0];
250    /// let _ = b.bin(BinOp::Add, x, x);
251    /// b.ret(Some(x));
252    /// let func = b.finish();
253    /// assert_eq!(func.insts(func.entry()).len(), 1);
254    /// ```
255    #[must_use]
256    pub fn insts(&self, block: Block) -> &[Value] {
257        match self.blocks.get(block.index()) {
258            Some(data) => &data.insts,
259            None => &[],
260        }
261    }
262
263    /// Returns a block's terminator, or `None` if the block handle is out of range
264    /// or no terminator was set (an unterminated block — which
265    /// [`validate`](Function::validate) rejects).
266    ///
267    /// # Examples
268    ///
269    /// ```
270    /// use ir_lang::{Builder, Type, Terminator};
271    ///
272    /// let mut b = Builder::new("f", &[], Type::Unit);
273    /// b.ret(None);
274    /// let func = b.finish();
275    /// assert!(matches!(func.terminator(func.entry()), Some(Terminator::Return(None))));
276    /// ```
277    #[must_use]
278    pub fn terminator(&self, block: Block) -> Option<&Terminator> {
279        self.blocks.get(block.index())?.term.as_ref()
280    }
281
282    /// Returns the instruction that defined a value, or `None` if the value is a
283    /// block parameter or the handle is out of range.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use ir_lang::{Builder, Inst, Type};
289    ///
290    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
291    /// let param = b.block_params(b.entry())[0];
292    /// let five = b.iconst(5);
293    /// b.ret(Some(param));
294    /// let func = b.finish();
295    ///
296    /// assert!(matches!(func.inst(five), Some(Inst::Iconst(5))));
297    /// assert!(func.inst(param).is_none()); // a parameter has no defining instruction
298    /// ```
299    #[must_use]
300    pub fn inst(&self, value: Value) -> Option<&Inst> {
301        match &self.values.get(value.index())?.def {
302            ValueDef::Inst(_, inst) => Some(inst),
303            ValueDef::Param(_) => None,
304        }
305    }
306
307    /// Returns the type of a value, or `None` if the handle is out of range.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// use ir_lang::{Builder, BinOp, Type};
313    ///
314    /// let mut b = Builder::new("f", &[Type::Int], Type::Bool);
315    /// let x = b.block_params(b.entry())[0];
316    /// let cmp = b.bin(BinOp::Lt, x, x);
317    /// b.ret(Some(cmp));
318    /// let func = b.finish();
319    ///
320    /// assert_eq!(func.value_type(x), Some(Type::Int));
321    /// assert_eq!(func.value_type(cmp), Some(Type::Bool));
322    /// ```
323    #[must_use]
324    pub fn value_type(&self, value: Value) -> Option<Type> {
325        Some(self.values.get(value.index())?.ty)
326    }
327
328    /// Returns the block a value is defined in, or `None` if the handle is out of
329    /// range.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// use ir_lang::{Builder, Type};
335    ///
336    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
337    /// let x = b.block_params(b.entry())[0];
338    /// b.ret(Some(x));
339    /// let func = b.finish();
340    /// assert_eq!(func.value_block(x), Some(func.entry()));
341    /// ```
342    #[must_use]
343    pub fn value_block(&self, value: Value) -> Option<Block> {
344        match self.values.get(value.index())?.def {
345            ValueDef::Param(block) | ValueDef::Inst(block, _) => Some(block),
346        }
347    }
348
349    /// Checks that the function is well-formed, returning the first violation found.
350    ///
351    /// A function that validates satisfies the SSA invariants the rest of a
352    /// compiler relies on: every block ends in exactly one terminator; every branch
353    /// targets a real block with a matching number and type of arguments; every
354    /// value is referenced only where its single definition reaches it; operations
355    /// are applied to operands of the right type; and the entry block is never a
356    /// branch target. The [`Builder`](crate::Builder) does not check these as it
357    /// goes, so run this once construction is complete — and again on the output of
358    /// any pass that rewrites the IR.
359    ///
360    /// # Errors
361    ///
362    /// Returns the first [`ValidationError`] encountered. Each variant names the
363    /// offending block or value; see [`ValidationError`] for the meaning of each and
364    /// how to fix it.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use ir_lang::{Builder, BinOp, Type, ValidationError};
370    ///
371    /// // A well-formed function validates.
372    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
373    /// let x = b.block_params(b.entry())[0];
374    /// let two = b.iconst(2);
375    /// let doubled = b.bin(BinOp::Mul, x, two);
376    /// b.ret(Some(doubled));
377    /// assert!(b.finish().validate().is_ok());
378    ///
379    /// // A block with no terminator does not.
380    /// let unfinished = Builder::new("g", &[], Type::Unit).finish();
381    /// assert!(matches!(
382    ///     unfinished.validate(),
383    ///     Err(ValidationError::MissingTerminator { .. })
384    /// ));
385    /// ```
386    pub fn validate(&self) -> Result<(), ValidationError> {
387        crate::validate::validate(self)
388    }
389}
390
391impl fmt::Display for Function {
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        write!(f, "fn {}(", self.name)?;
394        for (i, ty) in self.params.iter().enumerate() {
395            if i != 0 {
396                f.write_str(", ")?;
397            }
398            write!(f, "{ty}")?;
399        }
400        writeln!(f, ") -> {} {{", self.ret)?;
401
402        for block in self.blocks() {
403            write_block(f, self, block)?;
404        }
405
406        f.write_str("}")
407    }
408}
409
410fn write_block(f: &mut fmt::Formatter<'_>, func: &Function, block: Block) -> fmt::Result {
411    write!(f, "  {block}(")?;
412    for (i, &param) in func.block_params(block).iter().enumerate() {
413        if i != 0 {
414            f.write_str(", ")?;
415        }
416        let ty = func.value_type(param).unwrap_or(Type::Unit);
417        write!(f, "{param}: {ty}")?;
418    }
419    writeln!(f, "):")?;
420
421    for &value in func.insts(block) {
422        let ty = func.value_type(value).unwrap_or(Type::Unit);
423        match func.inst(value) {
424            Some(inst) => writeln!(f, "    {value}: {ty} = {}", FmtInst(inst))?,
425            None => writeln!(f, "    {value}: {ty} = ?")?,
426        }
427    }
428
429    match func.terminator(block) {
430        Some(term) => writeln!(f, "    {}", FmtTerm(term))?,
431        None => writeln!(f, "    <missing terminator>")?,
432    }
433    Ok(())
434}
435
436/// Adapter that renders an [`Inst`] in the textual IR form.
437struct FmtInst<'a>(&'a Inst);
438
439impl fmt::Display for FmtInst<'_> {
440    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441        match self.0 {
442            Inst::Iconst(v) => write!(f, "iconst {v}"),
443            Inst::Fconst(v) => write!(f, "fconst {v}"),
444            Inst::Bconst(v) => write!(f, "bconst {v}"),
445            Inst::Bin(op, a, b) => write!(f, "{op} {a}, {b}"),
446            Inst::Un(op, a) => write!(f, "{op} {a}"),
447        }
448    }
449}
450
451/// Adapter that renders a [`Terminator`] in the textual IR form.
452struct FmtTerm<'a>(&'a Terminator);
453
454impl fmt::Display for FmtTerm<'_> {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        match self.0 {
457            Terminator::Return(None) => f.write_str("return"),
458            Terminator::Return(Some(v)) => write!(f, "return {v}"),
459            Terminator::Jump(target, args) => {
460                write!(f, "jump {target}")?;
461                write_args(f, args)
462            }
463            Terminator::Branch {
464                cond,
465                then_block,
466                then_args,
467                else_block,
468                else_args,
469            } => {
470                write!(f, "branch {cond}, {then_block}")?;
471                write_args(f, then_args)?;
472                write!(f, ", {else_block}")?;
473                write_args(f, else_args)
474            }
475        }
476    }
477}
478
479fn write_args(f: &mut fmt::Formatter<'_>, args: &[Value]) -> fmt::Result {
480    if args.is_empty() {
481        return Ok(());
482    }
483    f.write_str("(")?;
484    for (i, arg) in args.iter().enumerate() {
485        if i != 0 {
486            f.write_str(", ")?;
487        }
488        write!(f, "{arg}")?;
489    }
490    f.write_str(")")
491}
492
493#[cfg(test)]
494mod tests {
495    use crate::{BinOp, Builder, Type};
496
497    #[test]
498    fn test_display_renders_signature_block_and_terminator() {
499        let mut b = Builder::new("double", &[Type::Int], Type::Int);
500        let x = b.block_params(b.entry())[0];
501        let sum = b.bin(BinOp::Add, x, x);
502        b.ret(Some(sum));
503        let text = b.finish().to_string();
504
505        assert!(text.starts_with("fn double(int) -> int {"));
506        assert!(text.contains("b0(v0: int):"));
507        assert!(text.contains("v1: int = add v0, v0"));
508        assert!(text.contains("return v1"));
509    }
510
511    #[test]
512    fn test_accessors_report_value_origin() {
513        let mut b = Builder::new("f", &[Type::Int], Type::Int);
514        let p = b.block_params(b.entry())[0];
515        let five = b.iconst(5);
516        b.ret(Some(p));
517        let func = b.finish();
518
519        assert_eq!(func.value_block(p), Some(func.entry()));
520        assert_eq!(func.value_block(five), Some(func.entry()));
521        assert!(func.inst(p).is_none());
522        assert!(func.inst(five).is_some());
523        assert_eq!(func.value_type(five), Some(Type::Int));
524    }
525
526    #[test]
527    fn test_out_of_range_handles_return_none_not_panic() {
528        let func = Builder::new("f", &[], Type::Unit).finish();
529        let bogus_value = crate::Value::from_raw(99);
530        let bogus_block = crate::Block::from_raw(99);
531        assert_eq!(func.value_type(bogus_value), None);
532        assert_eq!(func.value_block(bogus_value), None);
533        assert!(func.inst(bogus_value).is_none());
534        assert!(func.terminator(bogus_block).is_none());
535        assert!(func.block_params(bogus_block).is_empty());
536        assert!(func.insts(bogus_block).is_empty());
537    }
538}