ir_lang/function.rs
1//! The body of a function: blocks, instructions, values, and stack slots.
2
3use alloc::vec::Vec;
4
5use crate::entity::{Block, Inst, NONE, StackSlot, Value, ValueRange};
6use crate::inst::{InstData, Terminator};
7use crate::types::Type;
8
9/// Where a value comes from.
10///
11/// # Examples
12///
13/// ```
14/// use ir_lang::{Linkage, Module, Signature, Type, ValueDef};
15///
16/// let mut m = Module::new("demo");
17/// let f = m.declare_function("f", &Signature::new(&[Type::I32], &[]), Linkage::Export)?;
18/// let b = m.build(f)?;
19/// let x = b.param(0)?;
20/// assert_eq!(b.func().value_def(x), Some(ValueDef::Param { block: b.func().entry(), index: 0 }));
21/// # Ok::<(), Box<dyn std::error::Error>>(())
22/// ```
23#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
24#[non_exhaustive]
25pub enum ValueDef {
26 /// The `index`-th parameter of `block`.
27 Param {
28 /// The block.
29 block: Block,
30 /// The parameter position.
31 index: u32,
32 },
33 /// The `index`-th result of `inst`.
34 Result {
35 /// The instruction.
36 inst: Inst,
37 /// The result position.
38 index: u32,
39 },
40 /// No longer defined: its instruction or block was removed, or its block
41 /// parameter was removed. Using a detached value is a validation error.
42 Detached,
43}
44
45/// The size and alignment of a stack slot, in bytes.
46///
47/// # Examples
48///
49/// ```
50/// use ir_lang::SlotData;
51///
52/// let s = SlotData { size: 16, align: 8 };
53/// assert_eq!(s.size, 16);
54/// ```
55#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
56pub struct SlotData {
57 /// Size in bytes.
58 pub size: u32,
59 /// Alignment in bytes (a power of two, at most 2^16).
60 pub align: u32,
61}
62
63impl SlotData {
64 /// The largest alignment a stack slot may request.
65 pub const MAX_ALIGN: u32 = 1 << 16;
66}
67
68#[derive(Clone, Debug)]
69pub(crate) struct BlockNode {
70 pub(crate) params: Vec<Value>,
71 pub(crate) first: u32,
72 pub(crate) last: u32,
73 pub(crate) term: Option<Terminator>,
74 pub(crate) live: bool,
75}
76
77impl BlockNode {
78 pub(crate) fn new(params: Vec<Value>) -> BlockNode {
79 BlockNode {
80 params,
81 first: NONE,
82 last: NONE,
83 term: None,
84 live: true,
85 }
86 }
87}
88
89#[derive(Clone, Debug)]
90pub(crate) struct InstNode {
91 pub(crate) data: InstData,
92 pub(crate) results: ValueRange,
93 pub(crate) prev: u32,
94 pub(crate) next: u32,
95 /// The containing block, or `NONE` once removed.
96 pub(crate) block: u32,
97}
98
99#[derive(Clone, Debug)]
100pub(crate) struct ValueNode {
101 pub(crate) ty: Type,
102 pub(crate) def: ValueDef,
103 /// The value this one forwards to after `replace_all_uses`, or `NONE`.
104 pub(crate) alias: u32,
105}
106
107/// The largest number of entities of one kind (values, blocks, instructions, slots)
108/// a function may hold.
109pub const MAX_ENTITIES: u32 = u32::MAX - 1;
110
111/// A function body in SSA form: a control-flow graph of basic blocks.
112///
113/// Each block has parameters (the SSA values that flow in along its incoming edges,
114/// in place of phi nodes), a sequence of instructions, and one [`Terminator`]. Every
115/// value is defined once, by a block parameter or an instruction result, and has
116/// one [`Type`]. The entry block's parameters are the function's parameters.
117///
118/// A `Function` lives in a [`Module`](crate::Module), which holds its declaration
119/// (name, [`Signature`](crate::Signature), linkage, attributes). It is created and
120/// changed through a [`Builder`](crate::Builder) (`Module::build`, `Module::edit`)
121/// and read through the accessors here.
122///
123/// Instructions of a block form a linked list over a dense arena, so inserting or
124/// removing an instruction is O(1) and handles never move. Removed instructions and
125/// blocks leave tombstones; [`Builder::compact`](crate::Builder::compact) renumbers densely and
126/// reports the mapping.
127///
128/// # Examples
129///
130/// ```
131/// use ir_lang::{Linkage, Module, Overflow, Signature, Type};
132///
133/// let mut m = Module::new("demo");
134/// let f = m.declare_function("double", &Signature::new(&[Type::I64], &[Type::I64]), Linkage::Export)?;
135/// let mut b = m.build(f)?;
136/// let x = b.param(0)?;
137/// let y = b.add(x, x, Overflow::Wrap)?;
138/// b.ret(&[y])?;
139///
140/// let func = m.function(f).unwrap();
141/// let entry = func.entry();
142/// assert_eq!(func.block_params(entry), &[x]);
143/// assert_eq!(func.insts(entry).count(), 1);
144/// assert_eq!(func.value_type(y), Some(Type::I64));
145/// m.validate()?;
146/// # Ok::<(), Box<dyn std::error::Error>>(())
147/// ```
148#[derive(Clone, Debug)]
149pub struct Function {
150 pub(crate) blocks: Vec<BlockNode>,
151 pub(crate) insts: Vec<InstNode>,
152 pub(crate) values: Vec<ValueNode>,
153 pub(crate) slots: Vec<SlotData>,
154 pub(crate) entry: Block,
155}
156
157/// An iterator over the instructions of a block, in order.
158#[derive(Clone, Debug)]
159pub struct Insts<'f> {
160 func: &'f Function,
161 next: u32,
162 budget: usize,
163}
164
165impl Iterator for Insts<'_> {
166 type Item = Inst;
167
168 fn next(&mut self) -> Option<Inst> {
169 // The budget bounds the walk even if the list were corrupt (it never is
170 // through the public API), so iteration always terminates.
171 if self.next == NONE || self.budget == 0 {
172 return None;
173 }
174 self.budget -= 1;
175 let inst = Inst::from_u32(self.next);
176 self.next = self
177 .func
178 .insts
179 .get(self.next as usize)
180 .map_or(NONE, |n| n.next);
181 Some(inst)
182 }
183}
184
185impl Function {
186 /// A function whose entry block has parameters of the given types.
187 pub(crate) fn new(params: &[Type]) -> Function {
188 let mut f = Function {
189 blocks: Vec::new(),
190 insts: Vec::new(),
191 values: Vec::new(),
192 slots: Vec::new(),
193 entry: Block::from_u32(0),
194 };
195 let mut values = Vec::with_capacity(params.len());
196 for (i, &ty) in params.iter().enumerate() {
197 let v = Value::from_u32(f.values.len() as u32);
198 f.values.push(ValueNode {
199 ty,
200 def: ValueDef::Param {
201 block: Block::from_u32(0),
202 index: i as u32,
203 },
204 alias: NONE,
205 });
206 values.push(v);
207 }
208 f.blocks.push(BlockNode::new(values));
209 f
210 }
211
212 /// An empty function with no blocks (the parser fills it in).
213 pub(crate) fn empty() -> Function {
214 Function {
215 blocks: Vec::new(),
216 insts: Vec::new(),
217 values: Vec::new(),
218 slots: Vec::new(),
219 entry: Block::from_u32(0),
220 }
221 }
222
223 /// The entry block, where execution begins. Its parameters are the function's
224 /// parameters, and no edge may target it.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// use ir_lang::{Linkage, Module, Signature};
230 ///
231 /// let mut m = Module::new("m");
232 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
233 /// let b = m.build(f)?;
234 /// assert_eq!(b.func().entry().index(), 0);
235 /// # Ok::<(), Box<dyn std::error::Error>>(())
236 /// ```
237 #[inline]
238 #[must_use]
239 pub const fn entry(&self) -> Block {
240 self.entry
241 }
242
243 /// The number of block handles ever created (live and removed): block handles
244 /// run over `0..block_count()`.
245 ///
246 /// # Examples
247 ///
248 /// ```
249 /// use ir_lang::{Linkage, Module, Signature};
250 ///
251 /// let mut m = Module::new("m");
252 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
253 /// let mut b = m.build(f)?;
254 /// let _ = b.create_block(&[])?;
255 /// assert_eq!(b.func().block_count(), 2);
256 /// # Ok::<(), Box<dyn std::error::Error>>(())
257 /// ```
258 #[inline]
259 #[must_use]
260 pub fn block_count(&self) -> usize {
261 self.blocks.len()
262 }
263
264 /// Iterates over the live blocks in handle order (the entry is block 0 for a
265 /// function from the builder).
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// use ir_lang::{Linkage, Module, Signature};
271 ///
272 /// let mut m = Module::new("m");
273 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
274 /// let mut b = m.build(f)?;
275 /// let extra = b.create_block(&[])?;
276 /// b.remove_block(extra)?;
277 /// assert_eq!(b.func().blocks().count(), 1);
278 /// # Ok::<(), Box<dyn std::error::Error>>(())
279 /// ```
280 pub fn blocks(&self) -> impl Iterator<Item = Block> + '_ {
281 self.blocks
282 .iter()
283 .enumerate()
284 .filter(|(_, b)| b.live)
285 .map(|(i, _)| Block::from_u32(i as u32))
286 }
287
288 /// Whether `block` exists and has not been removed.
289 ///
290 /// # Examples
291 ///
292 /// ```
293 /// use ir_lang::{Block, Linkage, Module, Signature};
294 ///
295 /// let mut m = Module::new("m");
296 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
297 /// let b = m.build(f)?;
298 /// assert!(b.func().is_live_block(Block::from_u32(0)));
299 /// assert!(!b.func().is_live_block(Block::from_u32(9)));
300 /// # Ok::<(), Box<dyn std::error::Error>>(())
301 /// ```
302 #[must_use]
303 pub fn is_live_block(&self, block: Block) -> bool {
304 self.blocks.get(block.index()).is_some_and(|b| b.live)
305 }
306
307 /// The parameters of `block`, or an empty slice for an unknown block.
308 ///
309 /// # Examples
310 ///
311 /// ```
312 /// use ir_lang::{Linkage, Module, Signature, Type};
313 ///
314 /// let mut m = Module::new("m");
315 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
316 /// let mut b = m.build(f)?;
317 /// let blk = b.create_block(&[Type::Bool, Type::F64])?;
318 /// assert_eq!(b.func().block_params(blk).len(), 2);
319 /// # Ok::<(), Box<dyn std::error::Error>>(())
320 /// ```
321 #[must_use]
322 pub fn block_params(&self, block: Block) -> &[Value] {
323 self.blocks
324 .get(block.index())
325 .map_or(&[], |b| b.params.as_slice())
326 }
327
328 /// Iterates over the instructions of `block` in order (empty for an unknown
329 /// block). The terminator is not an instruction; see
330 /// [`terminator`](Function::terminator).
331 ///
332 /// # Examples
333 ///
334 /// ```
335 /// use ir_lang::{Linkage, Module, Signature, Type};
336 ///
337 /// let mut m = Module::new("m");
338 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
339 /// let mut b = m.build(f)?;
340 /// let _ = b.iconst(Type::I32, 1)?;
341 /// let _ = b.iconst(Type::I32, 2)?;
342 /// let entry = b.func().entry();
343 /// assert_eq!(b.func().insts(entry).count(), 2);
344 /// # Ok::<(), Box<dyn std::error::Error>>(())
345 /// ```
346 #[must_use]
347 pub fn insts(&self, block: Block) -> Insts<'_> {
348 let first = self.blocks.get(block.index()).map_or(NONE, |b| b.first);
349 Insts {
350 func: self,
351 next: first,
352 budget: self.insts.len(),
353 }
354 }
355
356 /// The first instruction of `block`, if any.
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// use ir_lang::{Linkage, Module, Signature};
362 ///
363 /// let mut m = Module::new("m");
364 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
365 /// let b = m.build(f)?;
366 /// assert_eq!(b.func().first_inst(b.func().entry()), None);
367 /// # Ok::<(), Box<dyn std::error::Error>>(())
368 /// ```
369 #[must_use]
370 pub fn first_inst(&self, block: Block) -> Option<Inst> {
371 self.blocks
372 .get(block.index())
373 .and_then(|b| (b.first != NONE).then(|| Inst::from_u32(b.first)))
374 }
375
376 /// The last instruction of `block` (before its terminator), if any.
377 ///
378 /// # Examples
379 ///
380 /// ```
381 /// use ir_lang::{Linkage, Module, Signature, Type};
382 ///
383 /// let mut m = Module::new("m");
384 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
385 /// let mut b = m.build(f)?;
386 /// let _ = b.iconst(Type::U8, 1)?;
387 /// let entry = b.func().entry();
388 /// assert!(b.func().last_inst(entry).is_some());
389 /// # Ok::<(), Box<dyn std::error::Error>>(())
390 /// ```
391 #[must_use]
392 pub fn last_inst(&self, block: Block) -> Option<Inst> {
393 self.blocks
394 .get(block.index())
395 .and_then(|b| (b.last != NONE).then(|| Inst::from_u32(b.last)))
396 }
397
398 /// The instruction after `inst` in its block, if any.
399 ///
400 /// # Examples
401 ///
402 /// ```
403 /// use ir_lang::{Linkage, Module, Signature, Type};
404 ///
405 /// let mut m = Module::new("m");
406 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
407 /// let mut b = m.build(f)?;
408 /// let a = b.iconst(Type::U8, 1)?;
409 /// let c = b.iconst(Type::U8, 2)?;
410 /// let (ia, ic) = (b.func().defining_inst(a).unwrap(), b.func().defining_inst(c).unwrap());
411 /// assert_eq!(b.func().next_inst(ia), Some(ic));
412 /// assert_eq!(b.func().prev_inst(ic), Some(ia));
413 /// # Ok::<(), Box<dyn std::error::Error>>(())
414 /// ```
415 #[must_use]
416 pub fn next_inst(&self, inst: Inst) -> Option<Inst> {
417 self.insts
418 .get(inst.index())
419 .filter(|n| n.block != NONE && n.next != NONE)
420 .map(|n| Inst::from_u32(n.next))
421 }
422
423 /// The instruction before `inst` in its block, if any.
424 ///
425 /// See [`next_inst`](Function::next_inst) for an example.
426 #[must_use]
427 pub fn prev_inst(&self, inst: Inst) -> Option<Inst> {
428 self.insts
429 .get(inst.index())
430 .filter(|n| n.block != NONE && n.prev != NONE)
431 .map(|n| Inst::from_u32(n.prev))
432 }
433
434 /// The number of instruction handles ever created (live and removed).
435 ///
436 /// # Examples
437 ///
438 /// ```
439 /// use ir_lang::{Linkage, Module, Signature, Type};
440 ///
441 /// let mut m = Module::new("m");
442 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
443 /// let mut b = m.build(f)?;
444 /// let _ = b.iconst(Type::U8, 1)?;
445 /// assert_eq!(b.func().inst_count(), 1);
446 /// # Ok::<(), Box<dyn std::error::Error>>(())
447 /// ```
448 #[inline]
449 #[must_use]
450 pub fn inst_count(&self) -> usize {
451 self.insts.len()
452 }
453
454 /// The data of `inst` (also for a removed instruction), or `None` for an
455 /// unknown handle.
456 ///
457 /// # Examples
458 ///
459 /// ```
460 /// use ir_lang::{InstData, Linkage, Module, Signature, Type};
461 ///
462 /// let mut m = Module::new("m");
463 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
464 /// let mut b = m.build(f)?;
465 /// let v = b.iconst(Type::I8, -1)?;
466 /// let inst = b.func().defining_inst(v).unwrap();
467 /// assert_eq!(b.func().inst(inst), Some(&InstData::Const { ty: Type::I8, bits: 0xff }));
468 /// # Ok::<(), Box<dyn std::error::Error>>(())
469 /// ```
470 #[must_use]
471 pub fn inst(&self, inst: Inst) -> Option<&InstData> {
472 self.insts.get(inst.index()).map(|n| &n.data)
473 }
474
475 /// The block containing `inst`, or `None` if it was removed or is unknown.
476 ///
477 /// # Examples
478 ///
479 /// ```
480 /// use ir_lang::{Linkage, Module, Signature, Type};
481 ///
482 /// let mut m = Module::new("m");
483 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
484 /// let mut b = m.build(f)?;
485 /// let v = b.iconst(Type::I8, 0)?;
486 /// let inst = b.func().defining_inst(v).unwrap();
487 /// assert_eq!(b.func().inst_block(inst), Some(b.func().entry()));
488 /// # Ok::<(), Box<dyn std::error::Error>>(())
489 /// ```
490 #[must_use]
491 pub fn inst_block(&self, inst: Inst) -> Option<Block> {
492 self.insts
493 .get(inst.index())
494 .filter(|n| n.block != NONE)
495 .map(|n| Block::from_u32(n.block))
496 }
497
498 /// The values `inst` defines (empty for an unknown handle).
499 ///
500 /// # Examples
501 ///
502 /// ```
503 /// use ir_lang::{Linkage, Module, Signature, Type};
504 ///
505 /// let mut m = Module::new("m");
506 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
507 /// let mut b = m.build(f)?;
508 /// let v = b.iconst(Type::I8, 0)?;
509 /// let inst = b.func().defining_inst(v).unwrap();
510 /// assert_eq!(b.func().results(inst).first(), Some(v));
511 /// # Ok::<(), Box<dyn std::error::Error>>(())
512 /// ```
513 #[must_use]
514 pub fn results(&self, inst: Inst) -> ValueRange {
515 self.insts
516 .get(inst.index())
517 .map_or(ValueRange::default(), |n| n.results)
518 }
519
520 /// The terminator of `block`, or `None` if it has none yet (or is unknown).
521 ///
522 /// # Examples
523 ///
524 /// ```
525 /// use ir_lang::{Linkage, Module, Signature, Terminator};
526 ///
527 /// let mut m = Module::new("m");
528 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
529 /// let mut b = m.build(f)?;
530 /// b.unreachable()?;
531 /// let entry = b.func().entry();
532 /// assert_eq!(b.func().terminator(entry), Some(&Terminator::Unreachable));
533 /// # Ok::<(), Box<dyn std::error::Error>>(())
534 /// ```
535 #[must_use]
536 pub fn terminator(&self, block: Block) -> Option<&Terminator> {
537 self.blocks.get(block.index()).and_then(|b| b.term.as_ref())
538 }
539
540 /// The number of value handles ever created: value handles run over
541 /// `0..value_count()`.
542 ///
543 /// # Examples
544 ///
545 /// ```
546 /// use ir_lang::{Linkage, Module, Signature, Type};
547 ///
548 /// let mut m = Module::new("m");
549 /// let f = m.declare_function("f", &Signature::new(&[Type::I32], &[]), Linkage::Local)?;
550 /// let b = m.build(f)?;
551 /// assert_eq!(b.func().value_count(), 1);
552 /// # Ok::<(), Box<dyn std::error::Error>>(())
553 /// ```
554 #[inline]
555 #[must_use]
556 pub fn value_count(&self) -> usize {
557 self.values.len()
558 }
559
560 /// The type of `value`, or `None` for an unknown handle.
561 ///
562 /// # Examples
563 ///
564 /// ```
565 /// use ir_lang::{Linkage, Module, Signature, Type};
566 ///
567 /// let mut m = Module::new("m");
568 /// let f = m.declare_function("f", &Signature::new(&[Type::F32], &[]), Linkage::Local)?;
569 /// let b = m.build(f)?;
570 /// assert_eq!(b.func().value_type(b.param(0)?), Some(Type::F32));
571 /// # Ok::<(), Box<dyn std::error::Error>>(())
572 /// ```
573 #[must_use]
574 pub fn value_type(&self, value: Value) -> Option<Type> {
575 self.values.get(value.index()).map(|v| v.ty)
576 }
577
578 /// Where `value` is defined, or `None` for an unknown handle.
579 ///
580 /// See [`ValueDef`] for an example.
581 #[must_use]
582 pub fn value_def(&self, value: Value) -> Option<ValueDef> {
583 self.values.get(value.index()).map(|v| v.def)
584 }
585
586 /// The instruction defining `value`, if it is an instruction result.
587 ///
588 /// # Examples
589 ///
590 /// ```
591 /// use ir_lang::{Linkage, Module, Signature, Type};
592 ///
593 /// let mut m = Module::new("m");
594 /// let f = m.declare_function("f", &Signature::new(&[Type::F32], &[]), Linkage::Local)?;
595 /// let mut b = m.build(f)?;
596 /// let p = b.param(0)?;
597 /// let c = b.iconst(Type::U8, 3)?;
598 /// assert!(b.func().defining_inst(c).is_some());
599 /// assert!(b.func().defining_inst(p).is_none());
600 /// # Ok::<(), Box<dyn std::error::Error>>(())
601 /// ```
602 #[must_use]
603 pub fn defining_inst(&self, value: Value) -> Option<Inst> {
604 match self.value_def(value) {
605 Some(ValueDef::Result { inst, .. }) => Some(inst),
606 _ => None,
607 }
608 }
609
610 /// Follows `replace_all_uses` forwarding: the value every use of `value` now
611 /// reads. A value that was never replaced resolves to itself.
612 ///
613 /// Operands stored in instructions are not rewritten eagerly by
614 /// [`Builder::replace_all_uses`](crate::Builder::replace_all_uses) (that keeps it
615 /// O(1)), so a pass reading operands should resolve them, or call
616 /// [`Builder::resolve_aliases`](crate::Builder::resolve_aliases) once first.
617 ///
618 /// # Examples
619 ///
620 /// ```
621 /// use ir_lang::{Linkage, Module, Signature, Type};
622 ///
623 /// let mut m = Module::new("m");
624 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
625 /// let mut b = m.build(f)?;
626 /// let one = b.iconst(Type::I32, 1)?;
627 /// let two = b.iconst(Type::I32, 2)?;
628 /// b.replace_all_uses(one, two)?;
629 /// assert_eq!(b.func().resolve(one), two);
630 /// assert_eq!(b.func().resolve(two), two);
631 /// # Ok::<(), Box<dyn std::error::Error>>(())
632 /// ```
633 #[must_use]
634 pub fn resolve(&self, value: Value) -> Value {
635 let mut v = value.as_u32();
636 // Alias chains are acyclic by construction (replace_all_uses refuses a
637 // cycle); the step bound keeps resolution total on any input regardless.
638 for _ in 0..=self.values.len() {
639 match self.values.get(v as usize) {
640 Some(node) if node.alias != NONE => v = node.alias,
641 _ => return Value::from_u32(v),
642 }
643 }
644 Value::from_u32(v)
645 }
646
647 /// The number of stack slots.
648 ///
649 /// # Examples
650 ///
651 /// ```
652 /// use ir_lang::{Linkage, Module, Signature};
653 ///
654 /// let mut m = Module::new("m");
655 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
656 /// let mut b = m.build(f)?;
657 /// let _ = b.stack_slot(8, 8)?;
658 /// assert_eq!(b.func().slot_count(), 1);
659 /// # Ok::<(), Box<dyn std::error::Error>>(())
660 /// ```
661 #[inline]
662 #[must_use]
663 pub fn slot_count(&self) -> usize {
664 self.slots.len()
665 }
666
667 /// The size and alignment of a stack slot.
668 ///
669 /// # Examples
670 ///
671 /// ```
672 /// use ir_lang::{Linkage, Module, Signature, SlotData};
673 ///
674 /// let mut m = Module::new("m");
675 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
676 /// let mut b = m.build(f)?;
677 /// let ss = b.stack_slot(24, 8)?;
678 /// assert_eq!(b.func().slot(ss), Some(SlotData { size: 24, align: 8 }));
679 /// # Ok::<(), Box<dyn std::error::Error>>(())
680 /// ```
681 #[must_use]
682 pub fn slot(&self, slot: StackSlot) -> Option<SlotData> {
683 self.slots.get(slot.index()).copied()
684 }
685
686 /// The predecessor blocks of every block, indexed by block handle, listing a
687 /// predecessor once per edge (a branch with both edges to one block lists it
688 /// twice). Only live blocks contribute edges.
689 ///
690 /// # Examples
691 ///
692 /// ```
693 /// use ir_lang::{Linkage, Module, Signature};
694 ///
695 /// let mut m = Module::new("m");
696 /// let f = m.declare_function("f", &Signature::new(&[], &[]), Linkage::Local)?;
697 /// let mut b = m.build(f)?;
698 /// let exit = b.create_block(&[])?;
699 /// b.jump(exit, &[])?;
700 /// b.switch_to(exit)?;
701 /// b.ret(&[])?;
702 /// let preds = b.func().predecessors();
703 /// assert_eq!(preds[exit.index()], [b.func().entry()]);
704 /// # Ok::<(), Box<dyn std::error::Error>>(())
705 /// ```
706 #[must_use]
707 pub fn predecessors(&self) -> Vec<Vec<Block>> {
708 let mut preds = alloc::vec![Vec::new(); self.blocks.len()];
709 for (i, node) in self.blocks.iter().enumerate() {
710 if !node.live {
711 continue;
712 }
713 if let Some(term) = &node.term {
714 for dest in term.successors() {
715 if let Some(p) = preds.get_mut(dest.block.index()) {
716 p.push(Block::from_u32(i as u32));
717 }
718 }
719 }
720 }
721 preds
722 }
723
724 // ----- internal construction helpers (used by the builder and the parser) -----
725
726 pub(crate) fn push_value(&mut self, ty: Type, def: ValueDef) -> Option<Value> {
727 let index = u32::try_from(self.values.len())
728 .ok()
729 .filter(|&i| i < MAX_ENTITIES)?;
730 self.values.push(ValueNode {
731 ty,
732 def,
733 alias: NONE,
734 });
735 Some(Value::from_u32(index))
736 }
737
738 pub(crate) fn push_block(&mut self) -> Option<Block> {
739 let index = u32::try_from(self.blocks.len())
740 .ok()
741 .filter(|&i| i < MAX_ENTITIES)?;
742 self.blocks.push(BlockNode::new(Vec::new()));
743 Some(Block::from_u32(index))
744 }
745
746 /// Allocates an unlinked instruction node.
747 pub(crate) fn push_inst(&mut self, data: InstData) -> Option<Inst> {
748 let index = u32::try_from(self.insts.len())
749 .ok()
750 .filter(|&i| i < MAX_ENTITIES)?;
751 self.insts.push(InstNode {
752 data,
753 results: ValueRange::default(),
754 prev: NONE,
755 next: NONE,
756 block: NONE,
757 });
758 Some(Inst::from_u32(index))
759 }
760
761 /// Links an unlinked instruction at the end of `block`.
762 pub(crate) fn link_at_end(&mut self, inst: Inst, block: Block) {
763 let last = match self.blocks.get(block.index()) {
764 Some(b) => b.last,
765 None => return,
766 };
767 self.link_between(inst, block, last, NONE);
768 }
769
770 /// Links an unlinked instruction immediately before `before`.
771 pub(crate) fn link_before(&mut self, inst: Inst, before: Inst) {
772 let Some(node) = self.insts.get(before.index()) else {
773 return;
774 };
775 let (block, prev) = (node.block, node.prev);
776 if block == NONE {
777 return;
778 }
779 self.link_between(inst, Block::from_u32(block), prev, before.as_u32());
780 }
781
782 /// Links an unlinked instruction immediately after `after`.
783 pub(crate) fn link_after(&mut self, inst: Inst, after: Inst) {
784 let Some(node) = self.insts.get(after.index()) else {
785 return;
786 };
787 let (block, next) = (node.block, node.next);
788 if block == NONE {
789 return;
790 }
791 self.link_between(inst, Block::from_u32(block), after.as_u32(), next);
792 }
793
794 fn link_between(&mut self, inst: Inst, block: Block, prev: u32, next: u32) {
795 let i = inst.as_u32();
796 if let Some(n) = self.insts.get_mut(inst.index()) {
797 n.prev = prev;
798 n.next = next;
799 n.block = block.as_u32();
800 }
801 if prev == NONE {
802 if let Some(b) = self.blocks.get_mut(block.index()) {
803 b.first = i;
804 }
805 } else if let Some(p) = self.insts.get_mut(prev as usize) {
806 p.next = i;
807 }
808 if next == NONE {
809 if let Some(b) = self.blocks.get_mut(block.index()) {
810 b.last = i;
811 }
812 } else if let Some(n) = self.insts.get_mut(next as usize) {
813 n.prev = i;
814 }
815 }
816
817 /// Unlinks a linked instruction from its block (its node stays in the arena).
818 pub(crate) fn unlink(&mut self, inst: Inst) {
819 let Some(node) = self.insts.get(inst.index()) else {
820 return;
821 };
822 let (block, prev, next) = (node.block, node.prev, node.next);
823 if block == NONE {
824 return;
825 }
826 if prev == NONE {
827 if let Some(b) = self.blocks.get_mut(block as usize) {
828 b.first = next;
829 }
830 } else if let Some(p) = self.insts.get_mut(prev as usize) {
831 p.next = next;
832 }
833 if next == NONE {
834 if let Some(b) = self.blocks.get_mut(block as usize) {
835 b.last = prev;
836 }
837 } else if let Some(n) = self.insts.get_mut(next as usize) {
838 n.prev = prev;
839 }
840 if let Some(n) = self.insts.get_mut(inst.index()) {
841 n.prev = NONE;
842 n.next = NONE;
843 n.block = NONE;
844 }
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851
852 #[test]
853 fn test_link_and_unlink_keep_list_consistent() {
854 let mut f = Function::new(&[]);
855 let entry = f.entry();
856 let mut made = Vec::new();
857 for _ in 0..3 {
858 let i = f
859 .push_inst(InstData::Fence {
860 order: crate::inst::MemOrder::SeqCst,
861 })
862 .unwrap_or(Inst::from_u32(NONE));
863 f.link_at_end(i, entry);
864 made.push(i);
865 }
866 assert_eq!(f.insts(entry).collect::<Vec<_>>(), made);
867 f.unlink(made[1]);
868 assert_eq!(f.insts(entry).collect::<Vec<_>>(), [made[0], made[2]]);
869 f.link_before(made[1], made[0]);
870 assert_eq!(
871 f.insts(entry).collect::<Vec<_>>(),
872 [made[1], made[0], made[2]]
873 );
874 f.unlink(made[2]);
875 f.link_after(made[2], made[1]);
876 assert_eq!(
877 f.insts(entry).collect::<Vec<_>>(),
878 [made[1], made[2], made[0]]
879 );
880 assert_eq!(f.first_inst(entry), Some(made[1]));
881 assert_eq!(f.last_inst(entry), Some(made[0]));
882 }
883
884 #[test]
885 fn test_resolve_terminates_on_a_corrupt_cycle() {
886 let mut f = Function::new(&[Type::I32, Type::I32]);
887 f.values[0].alias = 1;
888 f.values[1].alias = 0;
889 // A cycle cannot be built through the API; resolution still terminates.
890 let _ = f.resolve(Value::from_u32(0));
891 }
892
893 #[test]
894 fn test_unknown_handles_read_as_empty() {
895 let f = Function::new(&[]);
896 assert!(f.block_params(Block::from_u32(5)).is_empty());
897 assert_eq!(f.insts(Block::from_u32(5)).count(), 0);
898 assert_eq!(f.value_type(Value::from_u32(3)), None);
899 assert_eq!(f.inst(Inst::from_u32(0)), None);
900 assert!(f.results(Inst::from_u32(0)).is_empty());
901 }
902}