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, ¶m) 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}