midenc_hir/ir/
block.rs

1use alloc::{format, vec::Vec};
2use core::{fmt, sync::atomic::AtomicBool};
3
4use smallvec::SmallVec;
5
6use super::{entity::EntityParent, *};
7use crate::traits::SingleRegion;
8
9/// A pointer to a [Block]
10pub type BlockRef = UnsafeIntrusiveEntityRef<Block>;
11/// An intrusive, doubly-linked list of [Block]
12pub type BlockList = EntityList<Block>;
13/// A cursor into a [BlockList]
14pub type BlockCursor<'a> = EntityCursor<'a, Block>;
15/// A mutable cursor into a [BlockList]
16pub type BlockCursorMut<'a> = EntityCursorMut<'a, Block>;
17/// An iterator over blocks produced by a depth-first, pre-order visit of the CFG
18pub type PreOrderBlockIter = cfg::PreOrderIter<BlockRef>;
19/// An iterator over blocks produced by a depth-first, post-order visit of the CFG
20pub type PostOrderBlockIter = cfg::PostOrderIter<BlockRef>;
21
22/// The unique identifier for a [Block]
23#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[repr(transparent)]
25pub struct BlockId(u32);
26impl BlockId {
27    pub const fn from_u32(id: u32) -> Self {
28        Self(id)
29    }
30
31    pub const fn as_u32(&self) -> u32 {
32        self.0
33    }
34}
35impl EntityId for BlockId {
36    #[inline(always)]
37    fn as_usize(&self) -> usize {
38        self.0 as usize
39    }
40}
41impl fmt::Debug for BlockId {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        write!(f, "^block{}", &self.0)
44    }
45}
46impl fmt::Display for BlockId {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "^block{}", &self.0)
49    }
50}
51
52/// Represents a basic block in the IR.
53///
54/// Basic blocks are used in SSA regions to provide the structure of the control-flow graph.
55/// Operations within a basic block appear in the order they will be executed.
56///
57/// A block must have a [traits::Terminator], an operation which transfers control to another block
58/// in the same region, or out of the containing operation (e.g. returning from a function).
59///
60/// Blocks have _predecessors_ and _successors_, representing the inbound and outbound edges
61/// (respectively) formed by operations that transfer control between blocks. A block can have
62/// zero or more predecessors and/or successors. A well-formed region will generally only have a
63/// single block (the entry block) with no predecessors (i.e. no unreachable blocks), and no blocks
64/// with both multiple predecessors _and_ multiple successors (i.e. no critical edges). It is valid
65/// to have both unreachable blocks and critical edges in the IR, but they must be removed during
66/// the course of compilation.
67pub struct Block {
68    /// The unique id of this block
69    id: BlockId,
70    /// Flag that indicates whether the ops in this block have a valid ordering
71    valid_op_ordering: AtomicBool,
72    /// The set of uses of this block
73    uses: BlockOperandList,
74    /// The list of [Operation]s that comprise this block
75    body: OpList,
76    /// The parameter list for this block
77    arguments: Vec<BlockArgumentRef>,
78}
79
80impl Eq for Block {}
81impl PartialEq for Block {
82    fn eq(&self, other: &Self) -> bool {
83        self.id == other.id
84    }
85}
86impl core::hash::Hash for Block {
87    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
88        self.id.hash(state);
89    }
90}
91
92impl fmt::Debug for Block {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        f.debug_struct("Block")
95            .field("id", &self.id)
96            .field_with("region", |f| match self.parent() {
97                None => f.write_str("None"),
98                Some(r) => write!(f, "Some({r:p})"),
99            })
100            .field_with("arguments", |f| {
101                let mut list = f.debug_list();
102                for arg in self.arguments.iter() {
103                    list.entry_with(|f| f.write_fmt(format_args!("{}", &arg.borrow())));
104                }
105                list.finish()
106            })
107            .finish_non_exhaustive()
108    }
109}
110
111impl fmt::Display for Block {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{}", self.id)
114    }
115}
116
117impl Block {
118    pub fn print(&self, flags: &OpPrintingFlags) -> crate::formatter::Document {
119        use crate::formatter::PrettyPrint;
120
121        let printer = BlockPrinter { block: self, flags };
122        printer.render()
123    }
124}
125
126struct BlockPrinter<'a> {
127    block: &'a Block,
128    flags: &'a OpPrintingFlags,
129}
130
131impl crate::formatter::PrettyPrint for BlockPrinter<'_> {
132    fn render(&self) -> crate::formatter::Document {
133        use crate::{formatter::*, traits::SingleBlock};
134
135        let is_entry_block = self.block.is_entry_block() && !self.flags.print_entry_block_headers;
136        let mut is_parent_op_single_block_single_region = false;
137        let mut is_parent_op_symbol = false;
138        if let Some(parent_op) = self.block.parent_op() {
139            let parent_op = parent_op.borrow();
140            is_parent_op_single_block_single_region = parent_op.implements::<dyn SingleBlock>()
141                && parent_op.implements::<dyn SingleRegion>();
142            is_parent_op_symbol = parent_op.implements::<dyn Symbol>();
143        }
144
145        let block = self.block.body.iter().fold(Document::Empty, |acc, op| {
146            let context = op.context();
147            let doc = op.print(self.flags, context);
148            if acc.is_empty() {
149                doc
150            } else if is_parent_op_single_block_single_region && is_parent_op_symbol {
151                // For blocks that serve as containers for symbol ops, e.g. module/component,
152                // add extra spacing between symbol ops to aid in readability
153                acc + nl() + nl() + doc
154            } else {
155                acc + nl() + doc
156            }
157        });
158
159        if is_parent_op_single_block_single_region {
160            block
161        } else if !is_entry_block || self.flags.print_entry_block_headers {
162            let args = self.block.arguments().iter().fold(Document::Empty, |acc, arg| {
163                let arg = *arg;
164                let doc = text(format!("{arg}: {}", arg.borrow().ty()));
165                if acc.is_empty() {
166                    doc
167                } else {
168                    acc + const_text(", ") + doc
169                }
170            });
171            let args = if self.block.has_arguments() {
172                const_text("(") + args + const_text(")")
173            } else {
174                args
175            };
176
177            let header = display(self.block.id) + args + const_text(":");
178            header + indent(4, nl() + block)
179        } else {
180            block
181        }
182    }
183}
184
185impl crate::formatter::PrettyPrint for Block {
186    fn render(&self) -> crate::formatter::Document {
187        let flags = OpPrintingFlags::default();
188
189        self.print(&flags)
190    }
191}
192
193impl Spanned for Block {
194    fn span(&self) -> SourceSpan {
195        self.body.front().get().map(|op| op.span()).unwrap_or_default()
196    }
197}
198
199impl Entity for Block {}
200
201impl EntityWithId for Block {
202    type Id = BlockId;
203
204    fn id(&self) -> Self::Id {
205        self.id
206    }
207}
208
209impl EntityWithParent for Block {
210    type Parent = Region;
211}
212
213impl EntityListItem for Block {}
214
215impl EntityParent<Operation> for Block {
216    fn offset() -> usize {
217        core::mem::offset_of!(Block, body)
218    }
219}
220
221impl EntityParent<BlockOperand> for Block {
222    fn offset() -> usize {
223        core::mem::offset_of!(Block, uses)
224    }
225}
226
227impl Usable for Block {
228    type Use = BlockOperand;
229
230    #[inline(always)]
231    fn uses(&self) -> &BlockOperandList {
232        &self.uses
233    }
234
235    #[inline(always)]
236    fn uses_mut(&mut self) -> &mut BlockOperandList {
237        &mut self.uses
238    }
239}
240
241impl cfg::Graph for Block {
242    type ChildEdgeIter = BlockSuccessorEdgesIter;
243    type ChildIter = BlockSuccessorIter;
244    type Edge = BlockOperandRef;
245    type Node = BlockRef;
246
247    fn size(&self) -> usize {
248        if let Some(term) = self.terminator() {
249            term.borrow().num_successors()
250        } else {
251            0
252        }
253    }
254
255    fn entry_node(&self) -> Self::Node {
256        self.as_block_ref()
257    }
258
259    fn children(parent: Self::Node) -> Self::ChildIter {
260        BlockSuccessorIter::new(parent)
261    }
262
263    fn children_edges(parent: Self::Node) -> Self::ChildEdgeIter {
264        BlockSuccessorEdgesIter::new(parent)
265    }
266
267    fn edge_dest(edge: Self::Edge) -> Self::Node {
268        edge.borrow().successor()
269    }
270}
271
272impl<'a> cfg::InvertibleGraph for &'a Block {
273    type Inverse = cfg::Inverse<&'a Block>;
274    type InvertibleChildEdgeIter = BlockPredecessorEdgesIter;
275    type InvertibleChildIter = BlockPredecessorIter;
276
277    fn inverse(self) -> Self::Inverse {
278        cfg::Inverse::new(self)
279    }
280
281    fn inverse_children(parent: Self::Node) -> Self::InvertibleChildIter {
282        BlockPredecessorIter::new(parent)
283    }
284
285    fn inverse_children_edges(parent: Self::Node) -> Self::InvertibleChildEdgeIter {
286        BlockPredecessorEdgesIter::new(parent)
287    }
288}
289
290impl cfg::Graph for BlockRef {
291    type ChildEdgeIter = BlockSuccessorEdgesIter;
292    type ChildIter = BlockSuccessorIter;
293    type Edge = BlockOperandRef;
294    type Node = BlockRef;
295
296    fn size(&self) -> usize {
297        if let Some(term) = self.borrow().terminator() {
298            term.borrow().num_successors()
299        } else {
300            0
301        }
302    }
303
304    fn entry_node(&self) -> Self::Node {
305        *self
306    }
307
308    fn children(parent: Self::Node) -> Self::ChildIter {
309        BlockSuccessorIter::new(parent)
310    }
311
312    fn children_edges(parent: Self::Node) -> Self::ChildEdgeIter {
313        BlockSuccessorEdgesIter::new(parent)
314    }
315
316    fn edge_dest(edge: Self::Edge) -> Self::Node {
317        edge.borrow().successor()
318    }
319}
320
321impl cfg::InvertibleGraph for BlockRef {
322    type Inverse = cfg::Inverse<Self>;
323    type InvertibleChildEdgeIter = BlockPredecessorEdgesIter;
324    type InvertibleChildIter = BlockPredecessorIter;
325
326    fn inverse(self) -> Self::Inverse {
327        cfg::Inverse::new(self)
328    }
329
330    fn inverse_children(parent: Self::Node) -> Self::InvertibleChildIter {
331        BlockPredecessorIter::new(parent)
332    }
333
334    fn inverse_children_edges(parent: Self::Node) -> Self::InvertibleChildEdgeIter {
335        BlockPredecessorEdgesIter::new(parent)
336    }
337}
338
339#[doc(hidden)]
340pub struct BlockSuccessorIter {
341    iter: BlockSuccessorEdgesIter,
342}
343impl BlockSuccessorIter {
344    pub fn new(parent: BlockRef) -> Self {
345        Self {
346            iter: BlockSuccessorEdgesIter::new(parent),
347        }
348    }
349}
350impl ExactSizeIterator for BlockSuccessorIter {
351    #[inline]
352    fn len(&self) -> usize {
353        self.iter.len()
354    }
355
356    #[inline]
357    fn is_empty(&self) -> bool {
358        self.iter.is_empty()
359    }
360}
361impl Iterator for BlockSuccessorIter {
362    type Item = BlockRef;
363
364    fn next(&mut self) -> Option<Self::Item> {
365        self.iter.next().map(|bo| bo.borrow().successor())
366    }
367
368    #[inline]
369    fn collect<B: FromIterator<Self::Item>>(self) -> B
370    where
371        Self: Sized,
372    {
373        let Some(terminator) = self.iter.terminator.as_ref() else {
374            return B::from_iter([]);
375        };
376        let terminator = terminator.borrow();
377        let successors = terminator.successors();
378        B::from_iter(
379            successors.all().as_slice()[self.iter.index..self.iter.num_successors]
380                .iter()
381                .map(|succ| succ.block.borrow().successor()),
382        )
383    }
384
385    fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
386    where
387        Self: Sized,
388    {
389        let Some(terminator) = self.iter.terminator.as_ref() else {
390            return collection;
391        };
392        let terminator = terminator.borrow();
393        let successors = terminator.successors();
394        collection.extend(
395            successors.all().as_slice()[self.iter.index..self.iter.num_successors]
396                .iter()
397                .map(|succ| succ.block.borrow().successor()),
398        );
399        collection
400    }
401}
402
403#[doc(hidden)]
404pub struct BlockSuccessorEdgesIter {
405    terminator: Option<OperationRef>,
406    num_successors: usize,
407    index: usize,
408}
409impl BlockSuccessorEdgesIter {
410    pub fn new(parent: BlockRef) -> Self {
411        let terminator = parent.borrow().terminator();
412        let num_successors = terminator.as_ref().map(|t| t.borrow().num_successors()).unwrap_or(0);
413        Self {
414            terminator,
415            num_successors,
416            index: 0,
417        }
418    }
419}
420impl ExactSizeIterator for BlockSuccessorEdgesIter {
421    #[inline]
422    fn len(&self) -> usize {
423        self.num_successors.saturating_sub(self.index)
424    }
425
426    #[inline]
427    fn is_empty(&self) -> bool {
428        self.index >= self.num_successors
429    }
430}
431impl Iterator for BlockSuccessorEdgesIter {
432    type Item = BlockOperandRef;
433
434    fn next(&mut self) -> Option<Self::Item> {
435        if self.index >= self.num_successors {
436            return None;
437        }
438
439        // SAFETY: We'll never have a none terminator if we have non-zero number of successors
440        let terminator = unsafe { self.terminator.as_ref().unwrap_unchecked() };
441        let index = self.index;
442        self.index += 1;
443        Some(terminator.borrow().successor(index).dest)
444    }
445
446    fn collect<B: FromIterator<Self::Item>>(self) -> B
447    where
448        Self: Sized,
449    {
450        let Some(terminator) = self.terminator.as_ref() else {
451            return B::from_iter([]);
452        };
453        let terminator = terminator.borrow();
454        let successors = terminator.successors();
455        B::from_iter(
456            successors.all().as_slice()[self.index..self.num_successors]
457                .iter()
458                .map(|succ| succ.block),
459        )
460    }
461
462    fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
463    where
464        Self: Sized,
465    {
466        let Some(terminator) = self.terminator.as_ref() else {
467            return collection;
468        };
469        let terminator = terminator.borrow();
470        let successors = terminator.successors();
471        collection.extend(
472            successors.all().as_slice()[self.index..self.num_successors]
473                .iter()
474                .map(|succ| succ.block),
475        );
476        collection
477    }
478}
479
480#[doc(hidden)]
481pub struct BlockPredecessorIter {
482    preds: SmallVec<[BlockRef; 4]>,
483    index: usize,
484}
485impl BlockPredecessorIter {
486    pub fn new(child: BlockRef) -> Self {
487        let preds = child.borrow().predecessors().map(|bo| bo.predecessor()).collect();
488        Self { preds, index: 0 }
489    }
490
491    #[inline(always)]
492    pub fn into_inner(self) -> SmallVec<[BlockRef; 4]> {
493        self.preds
494    }
495}
496impl ExactSizeIterator for BlockPredecessorIter {
497    #[inline]
498    fn len(&self) -> usize {
499        self.preds.len().saturating_sub(self.index)
500    }
501
502    #[inline]
503    fn is_empty(&self) -> bool {
504        self.index >= self.preds.len()
505    }
506}
507impl Iterator for BlockPredecessorIter {
508    type Item = BlockRef;
509
510    #[inline]
511    fn next(&mut self) -> Option<Self::Item> {
512        if self.is_empty() {
513            return None;
514        }
515        let index = self.index;
516        self.index += 1;
517        Some(self.preds[index])
518    }
519
520    fn collect<B: FromIterator<Self::Item>>(self) -> B
521    where
522        Self: Sized,
523    {
524        B::from_iter(self.preds)
525    }
526
527    fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
528    where
529        Self: Sized,
530    {
531        collection.extend(self.preds);
532        collection
533    }
534}
535
536#[doc(hidden)]
537pub struct BlockPredecessorEdgesIter {
538    preds: SmallVec<[BlockOperandRef; 4]>,
539    index: usize,
540}
541impl BlockPredecessorEdgesIter {
542    pub fn new(child: BlockRef) -> Self {
543        let preds = child
544            .borrow()
545            .predecessors()
546            .map(|bo| unsafe { BlockOperandRef::from_raw(&*bo) })
547            .collect();
548        Self { preds, index: 0 }
549    }
550}
551impl ExactSizeIterator for BlockPredecessorEdgesIter {
552    #[inline]
553    fn len(&self) -> usize {
554        self.preds.len().saturating_sub(self.index)
555    }
556
557    #[inline]
558    fn is_empty(&self) -> bool {
559        self.index >= self.preds.len()
560    }
561}
562impl Iterator for BlockPredecessorEdgesIter {
563    type Item = BlockOperandRef;
564
565    #[inline]
566    fn next(&mut self) -> Option<Self::Item> {
567        if self.is_empty() {
568            return None;
569        }
570        let index = self.index;
571        self.index += 1;
572        Some(self.preds[index])
573    }
574
575    fn collect<B: FromIterator<Self::Item>>(self) -> B
576    where
577        Self: Sized,
578    {
579        B::from_iter(self.preds)
580    }
581
582    fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
583    where
584        Self: Sized,
585    {
586        collection.extend(self.preds);
587        collection
588    }
589}
590
591impl Block {
592    pub fn new(id: BlockId) -> Self {
593        Self {
594            id,
595            valid_op_ordering: AtomicBool::new(true),
596            uses: Default::default(),
597            body: Default::default(),
598            arguments: Default::default(),
599        }
600    }
601
602    #[inline]
603    pub fn as_block_ref(&self) -> BlockRef {
604        unsafe { BlockRef::from_raw(self) }
605    }
606
607    /// Get a handle to the containing [Region] of this block, if it is attached to one
608    pub fn parent(&self) -> Option<RegionRef> {
609        self.as_block_ref().parent()
610    }
611
612    /// Get a handle to the containing [Operation] of this block, if it is attached to one
613    pub fn parent_op(&self) -> Option<OperationRef> {
614        self.parent().and_then(|region| region.parent())
615    }
616
617    /// Get a handle to the ancestor [Block] of this block, if one is present
618    pub fn parent_block(&self) -> Option<BlockRef> {
619        self.parent_op().and_then(|op| op.parent())
620    }
621
622    /// Returns true if this block is the entry block for its containing region
623    pub fn is_entry_block(&self) -> bool {
624        if let Some(parent) = self.parent().map(|r| r.borrow()) {
625            parent.entry_block_ref().is_some_and(|entry| entry == self.as_block_ref())
626        } else {
627            false
628        }
629    }
630
631    /// Get the first operation in the body of this block
632    #[inline]
633    pub fn front(&self) -> Option<OperationRef> {
634        self.body.front().as_pointer()
635    }
636
637    /// Get the last operation in the body of this block
638    #[inline]
639    pub fn back(&self) -> Option<OperationRef> {
640        self.body.back().as_pointer()
641    }
642
643    /// Get the list of [Operation] comprising the body of this block
644    #[inline(always)]
645    pub fn body(&self) -> &OpList {
646        &self.body
647    }
648
649    /// Get a mutable reference to the list of [Operation] comprising the body of this block
650    #[inline(always)]
651    pub fn body_mut(&mut self) -> &mut OpList {
652        &mut self.body
653    }
654}
655
656/// Arguments
657impl Block {
658    #[inline]
659    pub fn has_arguments(&self) -> bool {
660        !self.arguments.is_empty()
661    }
662
663    #[inline]
664    pub fn num_arguments(&self) -> usize {
665        self.arguments.len()
666    }
667
668    #[inline(always)]
669    pub fn arguments(&self) -> &[BlockArgumentRef] {
670        self.arguments.as_slice()
671    }
672
673    #[inline(always)]
674    pub fn arguments_mut(&mut self) -> &mut Vec<BlockArgumentRef> {
675        &mut self.arguments
676    }
677
678    pub fn argument_values(&self) -> impl ExactSizeIterator<Item = ValueRef> + '_ {
679        self.arguments.iter().copied().map(|arg| arg as ValueRef)
680    }
681
682    pub fn argument_types(&self) -> impl ExactSizeIterator<Item = Type> + '_ {
683        self.arguments.iter().copied().map(|arg| arg.borrow().ty().clone())
684    }
685
686    #[inline]
687    pub fn get_argument(&self, index: usize) -> BlockArgumentRef {
688        self.arguments[index]
689    }
690
691    /// Erase the block argument at `index`
692    ///
693    /// Panics if the argument still has uses.
694    pub fn erase_argument(&mut self, index: usize) {
695        assert!(
696            !self.arguments[index].borrow().is_used(),
697            "cannot erase block arguments with uses"
698        );
699        self.arguments.remove(index);
700    }
701
702    /// Erase every parameter of this block for which `should_erase` returns true.
703    ///
704    /// Panics if any argument to be erased still has uses.
705    pub fn erase_arguments<F>(&mut self, should_erase: F)
706    where
707        F: Fn(&BlockArgument) -> bool,
708    {
709        self.arguments.retain(|arg| {
710            let arg = arg.borrow();
711            let keep = !should_erase(&arg);
712            assert!(keep || !arg.is_used(), "cannot erase block arguments with uses");
713            keep
714        });
715    }
716
717    pub fn erase(&mut self) {
718        if let Some(mut region) = self.parent() {
719            let mut region = region.borrow_mut();
720            let body = region.body_mut();
721            let mut cursor = unsafe { body.cursor_mut_from_ptr(self.as_block_ref()) };
722            cursor.remove();
723        }
724    }
725}
726
727/// Placement
728impl Block {
729    /// Insert this block after `after` in its containing region.
730    ///
731    /// Panics if this block is already attached to a region, or if `after` is not attached.
732    #[track_caller]
733    pub fn insert_after(&mut self, after: BlockRef) {
734        assert!(
735            self.parent().is_none(),
736            "cannot insert block that is already attached to another region"
737        );
738        let mut region = after.parent().expect("'after' block is not attached to a region");
739        {
740            let mut region = region.borrow_mut();
741            let region_body = region.body_mut();
742            let mut cursor = unsafe { region_body.cursor_mut_from_ptr(after) };
743            cursor.insert_after(self.as_block_ref());
744        }
745    }
746
747    /// Insert this block before `before` in its containing region.
748    ///
749    /// Panics if this block is already attached to a region, or if `before` is not attached.
750    #[track_caller]
751    pub fn insert_before(&mut self, before: BlockRef) {
752        assert!(
753            self.parent().is_none(),
754            "cannot insert block that is already attached to another region"
755        );
756        let mut region = before.parent().expect("'before' block is not attached to a region");
757        {
758            let mut region = region.borrow_mut();
759            let region_body = region.body_mut();
760            let mut cursor = unsafe { region_body.cursor_mut_from_ptr(before) };
761            cursor.insert_before(self.as_block_ref());
762        }
763    }
764
765    /// Insert this block at the end of `region`.
766    ///
767    /// Panics if this block is already attached to a region.
768    #[track_caller]
769    pub fn insert_at_end(&mut self, mut region: RegionRef) {
770        assert!(
771            self.parent().is_none(),
772            "cannot insert block that is already attached to another region"
773        );
774        region.borrow_mut().body_mut().push_back(self.as_block_ref());
775    }
776
777    /// Unlink this block from its current region and insert it right before `before`
778    #[track_caller]
779    pub fn move_before(&mut self, before: BlockRef) {
780        self.unlink();
781        self.insert_before(before);
782    }
783
784    /// Remove this block from its containing region
785    fn unlink(&mut self) {
786        if let Some(mut region) = self.parent() {
787            let mut region = region.borrow_mut();
788            unsafe {
789                let mut cursor = region.body_mut().cursor_mut_from_ptr(self.as_block_ref());
790                cursor.remove();
791            }
792        }
793    }
794
795    /// Splice the body of `block` to the end of `self`, updating the parent of all spliced ops.
796    ///
797    /// It is up to the caller to ensure that this operation produces valid IR.
798    pub fn splice_block(&mut self, block: &mut Self) {
799        let ops = block.body_mut().take();
800        self.body.back_mut().splice_after(ops);
801    }
802
803    /// Splice the body of `block` to `self` before `ip`, updating the parent of all spliced ops.
804    ///
805    /// It is up to the caller to ensure that this operation produces valid IR.
806    pub fn splice_block_before(&mut self, block: &mut Self, ip: OperationRef) {
807        assert_eq!(ip.parent().unwrap(), block.as_block_ref());
808
809        let ops = block.body_mut().take();
810        let mut cursor = unsafe { self.body.cursor_mut_from_ptr(ip) };
811        cursor.splice_before(ops);
812    }
813
814    /// Splice the body of `block` to `self` after `ip`, updating the parent of all spliced ops.
815    ///
816    /// It is up to the caller to ensure that this operation produces valid IR.
817    pub fn splice_block_after(&mut self, block: &mut Self, ip: OperationRef) {
818        assert_eq!(ip.parent().unwrap(), block.as_block_ref());
819
820        let ops = block.body_mut().take();
821        let mut cursor = unsafe { self.body.cursor_mut_from_ptr(ip) };
822        cursor.splice_after(ops);
823    }
824
825    /// Split this block into two blocks before the specified operation
826    ///
827    /// Note that all operations in the block prior to `before` stay as part of the original block,
828    /// and the rest are moved to the new block, including the old terminator. The original block is
829    /// thus left without a terminator.
830    ///
831    /// Returns the newly created block.
832    pub fn split_block(&mut self, before: OperationRef) -> BlockRef {
833        let this = self.as_block_ref();
834        assert!(
835            BlockRef::ptr_eq(
836                &this,
837                &before.parent().expect("'before' op is not attached to a block")
838            ),
839            "cannot split block using an operation that does not belong to the block being split"
840        );
841
842        // We need the parent op so we can get access to the current Context, but this also tells us
843        // that this block is attached to a region and operation.
844        let mut region = self.parent().expect("block is not attached to a region");
845        let parent = region.parent().expect("parent region is not attached to an operation");
846        // Create a new empty block
847        let mut new_block = parent.borrow().context().create_block();
848        // Insert the block in the same region as `self`, immediately after `self`
849        {
850            let mut region_mut = region.borrow_mut();
851            let blocks = region_mut.body_mut();
852            let mut cursor = unsafe { blocks.cursor_mut_from_ptr(this) };
853            cursor.insert_after(new_block);
854        }
855        // Split the body of `self` at `before`, and splice everything after `before`, including
856        // `before` itself, into the new block we created.
857        let ops = {
858            let mut cursor = unsafe { self.body.cursor_mut_from_ptr(before) };
859            // Move the cursor before 'before' so that the split we get contains it
860            cursor.move_prev();
861            cursor.split_after()
862        };
863        new_block.borrow_mut().body_mut().back_mut().splice_after(ops);
864        new_block
865    }
866
867    pub fn clear(&mut self) {
868        // Drop all references from within this block
869        self.drop_all_references();
870
871        // Drop all operations within this block
872        self.body_mut().clear();
873    }
874}
875
876/// Ancestors
877impl Block {
878    pub fn is_legal_to_hoist_into(&self) -> bool {
879        use crate::traits::ReturnLike;
880
881        // No terminator means the block is under construction, and thus legal to hoist into
882        let Some(terminator) = self.terminator() else {
883            return true;
884        };
885
886        // If the block has no successors, it can never be legal to hoist into it, there is nothing
887        // to hoist!
888        if self.num_successors() == 0 {
889            return false;
890        }
891
892        // Instructions should not be hoisted across effectful or return-like terminators. This is
893        // typically only exception handling intrinsics, which HIR doesn't really have, but which
894        // we may nevertheless want to represent in the future.
895        //
896        // NOTE: Most return-like terminators would have no successors, but in LLVM, for example,
897        // there are instructions like `catch_ret`, which semantically are return-like, but which
898        // have a successor block (the landing pad).
899        let terminator = terminator.borrow();
900        !terminator.is_memory_effect_free() || terminator.implements::<dyn ReturnLike>()
901    }
902
903    pub fn has_ssa_dominance(&self) -> bool {
904        self.parent_op()
905            .and_then(|op| {
906                op.borrow()
907                    .as_trait::<dyn RegionKindInterface>()
908                    .map(|rki| rki.has_ssa_dominance())
909            })
910            .unwrap_or(true)
911    }
912
913    /// Walk up the ancestor blocks of `block`, until `f` returns `true` for a block.
914    ///
915    /// NOTE: `block` is visited before any of its ancestors.
916    pub fn traverse_ancestors<F>(block: BlockRef, mut f: F) -> Option<BlockRef>
917    where
918        F: FnMut(BlockRef) -> bool,
919    {
920        let mut block = Some(block);
921        while let Some(current) = block.take() {
922            if f(current) {
923                return Some(current);
924            }
925            block = current.borrow().parent_block();
926        }
927
928        None
929    }
930
931    /// Try to get a pair of blocks, starting with the given pair, which live in the same region,
932    /// by exploring the relationships of both blocks with respect to their regions.
933    ///
934    /// The returned block pair will either be the same input blocks, or some combination of those
935    /// blocks or their ancestors.
936    pub fn get_blocks_in_same_region(a: BlockRef, b: BlockRef) -> Option<(BlockRef, BlockRef)> {
937        // If both blocks do not live in the same region, we will have to check their parent
938        // operations.
939        let a_region = a.parent().unwrap();
940        let b_region = b.parent().unwrap();
941        if a_region == b_region {
942            return Some((a, b));
943        }
944
945        // Iterate over all ancestors of `a`, counting the depth of `a`.
946        //
947        // If one of `a`'s ancestors are in the same region as `b`, then we stop early because we
948        // found our nearest common ancestor.
949        let mut a_depth = 0;
950        let result = Self::traverse_ancestors(a, |block| {
951            a_depth += 1;
952            block.parent().is_some_and(|r| r == b_region)
953        });
954        if let Some(a) = result {
955            return Some((a, b));
956        }
957
958        // Iterate over all ancestors of `b`, counting the depth of `b`.
959        //
960        // If one of `b`'s ancestors are in the same region as `a`, then we stop early because we
961        // found our nearest common ancestor.
962        let mut b_depth = 0;
963        let result = Self::traverse_ancestors(b, |block| {
964            b_depth += 1;
965            block.parent().is_some_and(|r| r == a_region)
966        });
967        if let Some(b) = result {
968            return Some((a, b));
969        }
970
971        // Otherwise, we found two blocks that are siblings at some level. Walk the deepest one
972        // up until we reach the top or find a nearest common ancestor.
973        let mut a = Some(a);
974        let mut b = Some(b);
975        loop {
976            use core::cmp::Ordering;
977
978            match a_depth.cmp(&b_depth) {
979                Ordering::Greater => {
980                    a = a.and_then(|a| a.grandparent().and_then(|gp| gp.parent()));
981                    a_depth -= 1;
982                }
983                Ordering::Less => {
984                    b = b.and_then(|b| b.grandparent().and_then(|gp| gp.parent()));
985                    b_depth -= 1;
986                }
987                Ordering::Equal => break,
988            }
989        }
990
991        // If we found something with the same level, then we can march both up at the same time
992        // from here on out.
993        while let Some(next_a) = a.take() {
994            // If they are at the same level, and have the same parent region, then we succeeded.
995            let next_a_parent = next_a.parent();
996            let b_parent = b.as_ref().and_then(|b| b.parent());
997            if next_a_parent == b_parent {
998                return Some((next_a, b.unwrap()));
999            }
1000
1001            a = next_a_parent.and_then(|r| r.grandparent());
1002            b = b_parent.and_then(|r| r.grandparent());
1003        }
1004
1005        // They don't share a nearest common ancestor, perhaps they are in different modules or
1006        // something.
1007        None
1008    }
1009}
1010
1011/// Predecessors and Successors
1012impl Block {
1013    /// Returns true if this block has predecessors
1014    #[inline(always)]
1015    pub fn has_predecessors(&self) -> bool {
1016        self.is_used()
1017    }
1018
1019    /// Get an iterator over the predecessors of this block
1020    #[inline(always)]
1021    pub fn predecessors(&self) -> BlockOperandIter<'_> {
1022        self.iter_uses()
1023    }
1024
1025    /// If this block has exactly one predecessor, return it, otherwise `None`
1026    ///
1027    /// NOTE: A predecessor block with multiple edges, e.g. a conditional branch that has this block
1028    /// as the destination for both true/false branches is _not_ considered a single predecessor by
1029    /// this function.
1030    pub fn get_single_predecessor(&self) -> Option<BlockRef> {
1031        let front = self.uses.front().as_pointer()?;
1032        let back = self.uses.back().as_pointer().unwrap();
1033        if BlockOperandRef::ptr_eq(&front, &back) {
1034            Some(front.borrow().predecessor())
1035        } else {
1036            None
1037        }
1038    }
1039
1040    /// If this block has a unique predecessor, i.e. all incoming edges originate from one block,
1041    /// return it, otherwise `None`
1042    pub fn get_unique_predecessor(&self) -> Option<BlockRef> {
1043        let mut front = self.uses.front();
1044        let block_operand = front.get()?;
1045        let block = block_operand.predecessor();
1046        loop {
1047            front.move_next();
1048            if let Some(bo) = front.get() {
1049                if !BlockRef::ptr_eq(&block, &bo.predecessor()) {
1050                    break None;
1051                }
1052            } else {
1053                break Some(block);
1054            }
1055        }
1056    }
1057
1058    /// Returns true if this block has any successors
1059    #[inline]
1060    pub fn has_successors(&self) -> bool {
1061        self.num_successors() > 0
1062    }
1063
1064    /// Get the number of successors of this block in the CFG
1065    pub fn num_successors(&self) -> usize {
1066        self.terminator().map(|op| op.borrow().num_successors()).unwrap_or(0)
1067    }
1068
1069    /// Get the `index`th successor of this block's terminator operation
1070    #[track_caller]
1071    pub fn get_successor(&self, index: usize) -> BlockRef {
1072        let op = self.terminator().expect("this block has no terminator");
1073        op.borrow().successor(index).dest.borrow().successor()
1074    }
1075
1076    /// This drops all operand uses from operations within this block, which is an essential step in
1077    /// breaking cyclic dependences between references when they are to be deleted.
1078    pub fn drop_all_references(&mut self) {
1079        let mut cursor = self.body.front_mut();
1080        while let Some(mut op) = cursor.as_pointer() {
1081            op.borrow_mut().drop_all_references();
1082            cursor.move_next();
1083        }
1084    }
1085
1086    /// This drops all uses of values defined in this block or in the blocks of nested regions
1087    /// wherever the uses are located.
1088    pub fn drop_all_defined_value_uses(&mut self) {
1089        let mut cursor = self.body.back_mut();
1090        while let Some(mut op) = cursor.as_pointer() {
1091            op.borrow_mut().drop_all_defined_value_uses();
1092            cursor.move_prev();
1093        }
1094        for arg in self.arguments.iter_mut() {
1095            let mut arg = arg.borrow_mut();
1096            arg.uses_mut().clear();
1097        }
1098        self.drop_all_uses();
1099    }
1100
1101    /// Drop all uses of this block via [BlockOperand]
1102    #[inline]
1103    pub fn drop_all_uses(&mut self) {
1104        self.uses_mut().clear();
1105    }
1106
1107    pub fn replace_all_uses_with(&mut self, mut replacement: BlockRef) {
1108        if BlockRef::ptr_eq(&self.as_block_ref(), &replacement) {
1109            return;
1110        }
1111
1112        let mut replacement_block = replacement.borrow_mut();
1113
1114        let uses = self.uses_mut().take();
1115        replacement_block.uses_mut().back_mut().splice_after(uses);
1116    }
1117
1118    #[inline(always)]
1119    pub(super) fn is_op_order_valid(&self) -> bool {
1120        use core::sync::atomic::Ordering;
1121
1122        self.valid_op_ordering.load(Ordering::Acquire)
1123    }
1124
1125    #[inline(always)]
1126    pub(super) fn mark_op_order_valid(&self) {
1127        use core::sync::atomic::Ordering;
1128
1129        self.valid_op_ordering.store(true, Ordering::Release);
1130    }
1131
1132    pub(super) fn invalidate_op_order(&mut self) {
1133        use core::sync::atomic::Ordering;
1134
1135        // Validate the current ordering
1136        assert!(self.verify_op_order());
1137        self.valid_op_ordering.store(false, Ordering::Release);
1138    }
1139
1140    /// Returns true if the current operation ordering in this block is valid
1141    pub(super) fn verify_op_order(&self) -> bool {
1142        // The order is already known to be invalid
1143        if !self.is_op_order_valid() {
1144            return false;
1145        }
1146
1147        // The order is valid if there are less than 2 operations
1148        if self.body.is_empty()
1149            || OperationRef::ptr_eq(
1150                &self.body.front().as_pointer().unwrap(),
1151                &self.body.back().as_pointer().unwrap(),
1152            )
1153        {
1154            return true;
1155        }
1156
1157        let mut cursor = self.body.front();
1158        let mut prev = None;
1159        while let Some(op) = cursor.as_pointer() {
1160            cursor.move_next();
1161            if prev.is_none() {
1162                prev = Some(op);
1163                continue;
1164            }
1165
1166            // The previous operation must have a smaller order index than the next
1167            let prev_order = prev.take().unwrap().borrow().order();
1168            let current_order = op.borrow().order().unwrap_or(u32::MAX);
1169            if prev_order.is_some_and(|o| o >= current_order) {
1170                return false;
1171            }
1172            prev = Some(op);
1173        }
1174
1175        true
1176    }
1177
1178    /// Get the terminator operation of this block, or `None` if the block does not have one.
1179    pub fn terminator(&self) -> Option<OperationRef> {
1180        if !self.has_terminator() {
1181            None
1182        } else {
1183            self.body.back().as_pointer()
1184        }
1185    }
1186
1187    /// Returns true if this block has a terminator
1188    pub fn has_terminator(&self) -> bool {
1189        use crate::traits::Terminator;
1190        !self.body.is_empty()
1191            && self.body.back().get().is_some_and(|op| op.implements::<dyn Terminator>())
1192    }
1193}
1194
1195pub type BlockOperandRef = UnsafeIntrusiveEntityRef<BlockOperand>;
1196/// An intrusive, doubly-linked list of [BlockOperand]
1197pub type BlockOperandList = EntityList<BlockOperand>;
1198#[allow(unused)]
1199pub type BlockOperandCursor<'a> = EntityCursor<'a, BlockOperand>;
1200#[allow(unused)]
1201pub type BlockOperandCursorMut<'a> = EntityCursorMut<'a, BlockOperand>;
1202pub type BlockOperandIter<'a> = EntityIter<'a, BlockOperand>;
1203
1204/// A [BlockOperand] represents a use of a [Block] by an [Operation]
1205pub struct BlockOperand {
1206    /// The owner of this operand, i.e. the operation it is an operand of
1207    pub owner: OperationRef,
1208    /// The index of this operand in the set of block operands of the operation
1209    pub index: u8,
1210}
1211
1212impl Entity for BlockOperand {}
1213impl EntityWithParent for BlockOperand {
1214    type Parent = Block;
1215}
1216impl EntityListItem for BlockOperand {
1217    #[track_caller]
1218    fn on_inserted(this: UnsafeIntrusiveEntityRef<Self>, _cursor: &mut EntityCursorMut<'_, Self>) {
1219        assert!(this.parent().is_some());
1220    }
1221}
1222
1223impl BlockOperand {
1224    #[inline]
1225    pub fn new(owner: OperationRef, index: u8) -> Self {
1226        Self { owner, index }
1227    }
1228
1229    pub fn as_block_operand_ref(&self) -> BlockOperandRef {
1230        unsafe { BlockOperandRef::from_raw(self) }
1231    }
1232
1233    pub fn block_id(&self) -> BlockId {
1234        self.successor().borrow().id
1235    }
1236
1237    /// Get the block from which this block operand originates, i.e. the predecessor block
1238    pub fn predecessor(&self) -> BlockRef {
1239        self.owner.parent().expect("owning operation is not attached to a block")
1240    }
1241
1242    /// Get the block this operand references
1243    #[inline]
1244    pub fn successor(&self) -> BlockRef {
1245        self.as_block_operand_ref().parent().unwrap_or_else(|| {
1246            panic!(
1247                "block operand is dead at index {} in {}",
1248                self.index,
1249                &self.owner.borrow().name()
1250            )
1251        })
1252    }
1253
1254    /// Set the successor block to `block`, removing the block operand from the use list of the
1255    /// previous block, and adding it to the use list of `block`.
1256    ///
1257    /// NOTE: This requires a mutable borrow of `block` when mutating its use list.
1258    pub fn set(&mut self, mut block: BlockRef) {
1259        self.unlink();
1260        block.borrow_mut().insert_use(self.as_block_operand_ref());
1261    }
1262}
1263
1264impl fmt::Debug for BlockOperand {
1265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1266        f.debug_struct("BlockOperand")
1267            .field("block", &self.successor())
1268            .field_with("owner", |f| write!(f, "{:p}", &self.owner))
1269            .field("index", &self.index)
1270            .finish()
1271    }
1272}
1273impl fmt::Display for BlockOperand {
1274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1275        write!(f, "{}", &self.block_id())
1276    }
1277}
1278impl StorableEntity for BlockOperand {
1279    #[inline(always)]
1280    fn index(&self) -> usize {
1281        self.index as usize
1282    }
1283
1284    unsafe fn set_index(&mut self, index: usize) {
1285        self.index = index.try_into().expect("too many successors");
1286    }
1287
1288    /// Remove this use of `block`
1289    fn unlink(&mut self) {
1290        let this = self.as_block_operand_ref();
1291        if !this.is_linked() {
1292            return;
1293        }
1294        let Some(mut parent) = this.parent() else {
1295            return;
1296        };
1297
1298        let mut block = parent.borrow_mut();
1299        let uses = block.uses_mut();
1300        unsafe {
1301            let mut cursor = uses.cursor_mut_from_ptr(this);
1302            cursor.remove();
1303        }
1304    }
1305}