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 check also gates the value table itself — handle ranges,
357    /// definition sites, and recorded result types — so it is a complete check for a
358    /// function assembled by hand or deserialized through `serde`, not only one the
359    /// [`Builder`](crate::Builder) produced. The builder does not check any of this
360    /// as it goes, so run this once construction is complete — and again on the
361    /// output of any pass that rewrites the IR.
362    ///
363    /// # Errors
364    ///
365    /// Returns the first [`ValidationError`] encountered. Each variant names the
366    /// offending block or value; see [`ValidationError`] for the meaning of each and
367    /// how to fix it.
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// use ir_lang::{Builder, BinOp, Type, ValidationError};
373    ///
374    /// // A well-formed function validates.
375    /// let mut b = Builder::new("f", &[Type::Int], Type::Int);
376    /// let x = b.block_params(b.entry())[0];
377    /// let two = b.iconst(2);
378    /// let doubled = b.bin(BinOp::Mul, x, two);
379    /// b.ret(Some(doubled));
380    /// assert!(b.finish().validate().is_ok());
381    ///
382    /// // A block with no terminator does not.
383    /// let unfinished = Builder::new("g", &[], Type::Unit).finish();
384    /// assert!(matches!(
385    ///     unfinished.validate(),
386    ///     Err(ValidationError::MissingTerminator { .. })
387    /// ));
388    /// ```
389    pub fn validate(&self) -> Result<(), ValidationError> {
390        crate::validate::validate(self)
391    }
392}
393
394impl fmt::Display for Function {
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        write!(f, "fn {}(", self.name)?;
397        for (i, ty) in self.params.iter().enumerate() {
398            if i != 0 {
399                f.write_str(", ")?;
400            }
401            write!(f, "{ty}")?;
402        }
403        writeln!(f, ") -> {} {{", self.ret)?;
404
405        for block in self.blocks() {
406            write_block(f, self, block)?;
407        }
408
409        f.write_str("}")
410    }
411}
412
413fn write_block(f: &mut fmt::Formatter<'_>, func: &Function, block: Block) -> fmt::Result {
414    write!(f, "  {block}(")?;
415    for (i, &param) in func.block_params(block).iter().enumerate() {
416        if i != 0 {
417            f.write_str(", ")?;
418        }
419        let ty = func.value_type(param).unwrap_or(Type::Unit);
420        write!(f, "{param}: {ty}")?;
421    }
422    writeln!(f, "):")?;
423
424    for &value in func.insts(block) {
425        let ty = func.value_type(value).unwrap_or(Type::Unit);
426        match func.inst(value) {
427            Some(inst) => writeln!(f, "    {value}: {ty} = {}", FmtInst(inst))?,
428            None => writeln!(f, "    {value}: {ty} = ?")?,
429        }
430    }
431
432    match func.terminator(block) {
433        Some(term) => writeln!(f, "    {}", FmtTerm(term))?,
434        None => writeln!(f, "    <missing terminator>")?,
435    }
436    Ok(())
437}
438
439/// Adapter that renders an [`Inst`] in the textual IR form.
440struct FmtInst<'a>(&'a Inst);
441
442impl fmt::Display for FmtInst<'_> {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        match self.0 {
445            Inst::Iconst(v) => write!(f, "iconst {v}"),
446            Inst::Fconst(v) => write!(f, "fconst {v}"),
447            Inst::Bconst(v) => write!(f, "bconst {v}"),
448            Inst::Bin(op, a, b) => write!(f, "{op} {a}, {b}"),
449            Inst::Un(op, a) => write!(f, "{op} {a}"),
450        }
451    }
452}
453
454/// Adapter that renders a [`Terminator`] in the textual IR form.
455struct FmtTerm<'a>(&'a Terminator);
456
457impl fmt::Display for FmtTerm<'_> {
458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459        match self.0 {
460            Terminator::Return(None) => f.write_str("return"),
461            Terminator::Return(Some(v)) => write!(f, "return {v}"),
462            Terminator::Jump(target, args) => {
463                write!(f, "jump {target}")?;
464                write_args(f, args)
465            }
466            Terminator::Branch {
467                cond,
468                then_block,
469                then_args,
470                else_block,
471                else_args,
472            } => {
473                write!(f, "branch {cond}, {then_block}")?;
474                write_args(f, then_args)?;
475                write!(f, ", {else_block}")?;
476                write_args(f, else_args)
477            }
478        }
479    }
480}
481
482fn write_args(f: &mut fmt::Formatter<'_>, args: &[Value]) -> fmt::Result {
483    if args.is_empty() {
484        return Ok(());
485    }
486    f.write_str("(")?;
487    for (i, arg) in args.iter().enumerate() {
488        if i != 0 {
489            f.write_str(", ")?;
490        }
491        write!(f, "{arg}")?;
492    }
493    f.write_str(")")
494}
495
496#[cfg(test)]
497mod tests {
498    use crate::{BinOp, Builder, Type};
499
500    #[test]
501    fn test_display_renders_signature_block_and_terminator() {
502        let mut b = Builder::new("double", &[Type::Int], Type::Int);
503        let x = b.block_params(b.entry())[0];
504        let sum = b.bin(BinOp::Add, x, x);
505        b.ret(Some(sum));
506        let text = b.finish().to_string();
507
508        assert!(text.starts_with("fn double(int) -> int {"));
509        assert!(text.contains("b0(v0: int):"));
510        assert!(text.contains("v1: int = add v0, v0"));
511        assert!(text.contains("return v1"));
512    }
513
514    #[test]
515    fn test_accessors_report_value_origin() {
516        let mut b = Builder::new("f", &[Type::Int], Type::Int);
517        let p = b.block_params(b.entry())[0];
518        let five = b.iconst(5);
519        b.ret(Some(p));
520        let func = b.finish();
521
522        assert_eq!(func.value_block(p), Some(func.entry()));
523        assert_eq!(func.value_block(five), Some(func.entry()));
524        assert!(func.inst(p).is_none());
525        assert!(func.inst(five).is_some());
526        assert_eq!(func.value_type(five), Some(Type::Int));
527    }
528
529    #[test]
530    fn test_out_of_range_handles_return_none_not_panic() {
531        let func = Builder::new("f", &[], Type::Unit).finish();
532        let bogus_value = crate::Value::from_raw(99);
533        let bogus_block = crate::Block::from_raw(99);
534        assert_eq!(func.value_type(bogus_value), None);
535        assert_eq!(func.value_block(bogus_value), None);
536        assert!(func.inst(bogus_value).is_none());
537        assert!(func.terminator(bogus_block).is_none());
538        assert!(func.block_params(bogus_block).is_empty());
539        assert!(func.insts(bogus_block).is_empty());
540    }
541}