midenc_hir/ir/dominance/
traits.rs

1use super::{DominanceInfo, PostDominanceInfo};
2use crate::{Block, BlockRef, Operation, Value};
3
4/// This trait is implemented on a type which has a dominance relationship with `Rhs`.
5pub trait Dominates<Rhs = Self> {
6    /// Returns true if `self` dominates `other`.
7    ///
8    /// In cases where `Rhs = Self`, implementations should return true when `self == other`.
9    ///
10    /// For a stricter form of dominance, use [Dominates::properly_dominates].
11    fn dominates(&self, other: &Rhs, dom_info: &DominanceInfo) -> bool;
12    /// Returns true if `self` properly dominates `other`.
13    ///
14    /// In cases where `Rhs = Self`, implementations should return false when `self == other`.
15    fn properly_dominates(&self, other: &Rhs, dom_info: &DominanceInfo) -> bool;
16}
17
18/// This trait is implemented on a type which has a post-dominance relationship with `Rhs`.
19pub trait PostDominates<Rhs = Self> {
20    /// Returns true if `self` post-dominates `other`.
21    ///
22    /// In cases where `Rhs = Self`, implementations should return true when `self == other`.
23    ///
24    /// For a stricter form of dominance, use [PostDominates::properly_dominates].
25    fn post_dominates(&self, other: &Rhs, dom_info: &PostDominanceInfo) -> bool;
26    /// Returns true if `self` properly post-dominates `other`.
27    ///
28    /// In cases where `Rhs = Self`, implementations should return false when `self == other`.
29    fn properly_post_dominates(&self, other: &Rhs, dom_info: &PostDominanceInfo) -> bool;
30}
31
32/// The dominance relationship between two blocks.
33impl Dominates for Block {
34    /// Returns true if `a == b` or `a` properly dominates `b`.
35    fn dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
36        core::ptr::addr_eq(self, other) || self.properly_dominates(other, dom_info)
37    }
38
39    /// Returns true if `a != b` and:
40    ///
41    /// * `a` is an ancestor of `b`
42    /// * The region containing `a` also contains `b` or some ancestor of `b`, and `a` dominates
43    ///   that block in that kind of region.
44    /// * In SSA regions, `a` properly dominates `b` if all control flow paths from the entry
45    ///   block to `b`, flow through `a`.
46    /// * In graph regions, all blocks dominate all other blocks.
47    fn properly_dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
48        dom_info.info().properly_dominates(self.as_block_ref(), other.as_block_ref())
49    }
50}
51
52/// The dominance relationship between two blocks.
53impl Dominates for BlockRef {
54    /// Returns true if `a == b` or `a` properly dominates `b`.
55    fn dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
56        BlockRef::ptr_eq(self, other) || self.properly_dominates(other, dom_info)
57    }
58
59    /// Returns true if `a != b` and:
60    ///
61    /// * `a` is an ancestor of `b`
62    /// * The region containing `a` also contains `b` or some ancestor of `b`, and `a` dominates
63    ///   that block in that kind of region.
64    /// * In SSA regions, `a` properly dominates `b` if all control flow paths from the entry
65    ///   block to `b`, flow through `a`.
66    /// * In graph regions, all blocks dominate all other blocks.
67    fn properly_dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
68        dom_info.info().properly_dominates(*self, *other)
69    }
70}
71
72/// The post-dominance relationship between two blocks.
73impl PostDominates for Block {
74    fn post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
75        core::ptr::addr_eq(self, other) || self.properly_post_dominates(other, dom_info)
76    }
77
78    /// Returns true if `a != b` and:
79    ///
80    /// * `a` is an ancestor of `b`
81    /// * The region containing `a` also contains `b` or some ancestor of `b`, and `a` dominates
82    ///   that block in that kind of region.
83    /// * In SSA regions, `a` properly post-dominates `b` if all control flow paths from `b` to
84    ///   an exit node, flow through `a`.
85    /// * In graph regions, all blocks post-dominate all other blocks.
86    fn properly_post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
87        dom_info.info().properly_dominates(self.as_block_ref(), other.as_block_ref())
88    }
89}
90
91/// The post-dominance relationship between two blocks.
92impl PostDominates for BlockRef {
93    fn post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
94        BlockRef::ptr_eq(self, other) || self.properly_post_dominates(other, dom_info)
95    }
96
97    /// Returns true if `a != b` and:
98    ///
99    /// * `a` is an ancestor of `b`
100    /// * The region containing `a` also contains `b` or some ancestor of `b`, and `a` dominates
101    ///   that block in that kind of region.
102    /// * In SSA regions, `a` properly post-dominates `b` if all control flow paths from `b` to
103    ///   an exit node, flow through `a`.
104    /// * In graph regions, all blocks post-dominate all other blocks.
105    fn properly_post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
106        dom_info.info().properly_dominates(*self, *other)
107    }
108}
109
110/// The dominance relationship for operations
111impl Dominates for Operation {
112    fn dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
113        core::ptr::addr_eq(self, other) || self.properly_dominates(other, dom_info)
114    }
115
116    /// Returns true if `a != b`, and:
117    ///
118    /// * `a` and `b` are in the same block, and `a` properly dominates `b` within the block, or
119    /// * the block that contains `a` properly dominates the block that contains `b`.
120    /// * `b` is enclosed in a region of `a`
121    ///
122    /// In any SSA region, `a` dominates `b` in the same block if `a` precedes `b`. In a graph
123    /// region all operations in a block dominate all other operations in the same block.
124    fn properly_dominates(&self, other: &Self, dom_info: &DominanceInfo) -> bool {
125        let a = self.as_operation_ref();
126        let b = other.as_operation_ref();
127        dom_info.properly_dominates_with_options(a, b, /*enclosing_op_ok= */ true)
128    }
129}
130
131/// The post-dominance relationship for operations
132impl PostDominates for Operation {
133    fn post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
134        core::ptr::addr_eq(self, other) || self.properly_post_dominates(other, dom_info)
135    }
136
137    /// Returns true if `a != b`, and:
138    ///
139    /// * `a` and `b` are in the same block, and `a` properly post-dominates `b` within the block
140    /// * the block that contains `a` properly post-dominates the block that contains `b`.
141    /// * `b` is enclosed in a region of `a`
142    ///
143    /// In any SSA region, `a` post-dominates `b` in the same block if `b` precedes `a`. In a graph
144    /// region all operations in a block post-dominate all other operations in the same block.
145    fn properly_post_dominates(&self, other: &Self, dom_info: &PostDominanceInfo) -> bool {
146        let a_block = self.parent().expect("`self` must be in a block");
147        let mut b_block = other.parent().expect("`other` must be in a block");
148
149        // An instruction post dominates, but does not properly post-dominate itself unless this is
150        // a graph region.
151        if core::ptr::addr_eq(self, other) {
152            return !a_block.borrow().has_ssa_dominance();
153        }
154
155        // If these ops are in different regions, then normalize one into the other.
156        let a_region = a_block.parent();
157        let b_region = b_block.parent();
158        let a = self.as_operation_ref();
159        let mut b = other.as_operation_ref();
160        if a_region != b_region {
161            // Walk up `b`'s region tree until we find an operation in `a`'s region that encloses
162            // it. If this fails, then we know there is no post-dominance relation.
163            let Some(found) = a_region.as_ref().and_then(|r| r.borrow().find_ancestor_op(b)) else {
164                return false;
165            };
166            b = found;
167            b_block = b.parent().unwrap();
168            assert!(b_block.parent() == a_region);
169
170            // If `a` encloses `b`, then we consider it to post-dominate.
171            if a == b {
172                return true;
173            }
174        }
175
176        // Ok, they are in the same region. If they are in the same block, check if `b` is before
177        // `a` in the block.
178        if a_block == b_block {
179            // Dominance changes based on the region type
180            return if a_block.borrow().has_ssa_dominance() {
181                // If the blocks are the same, then check if `b` is before `a` in the block.
182                b.borrow().is_before_in_block(&a)
183            } else {
184                true
185            };
186        }
187
188        // If the blocks are different, check if `a`'s block post-dominates `b`'s
189        dom_info
190            .info()
191            .dominance(a_region.unwrap())
192            .properly_dominates(Some(a_block), Some(b_block))
193    }
194}
195
196/// The dominance relationship between a value and an operation, e.g. between a definition of a
197/// value and a user of that same value.
198impl Dominates<Operation> for dyn Value {
199    /// Return true if the definition of `self` dominates a use by operation `other`.
200    fn dominates(&self, other: &Operation, dom_info: &DominanceInfo) -> bool {
201        self.get_defining_op().is_some_and(|op| op == other.as_operation_ref())
202            || self.properly_dominates(other, dom_info)
203    }
204
205    /// Returns true if the definition of `self` properly dominates `other`.
206    ///
207    /// This requires the value to either be a block argument, where the block containing `other`
208    /// is dominated by the block defining `self`, OR that the value is an operation result, and
209    /// the defining op of `self` properly dominates `other`.
210    ///
211    /// If the defining op of `self` encloses `b` in one of its regions, `a` does not dominate `b`.
212    fn properly_dominates(&self, other: &Operation, dom_info: &DominanceInfo) -> bool {
213        // Block arguments properly dominate all operations in their own block, so we use a
214        // dominates check here, not a properly_dominates check.
215        if let Some(block_arg) = self.downcast_ref::<crate::BlockArgument>() {
216            return block_arg
217                .owner()
218                .borrow()
219                .dominates(&other.parent().unwrap().borrow(), dom_info);
220        }
221
222        // `a` properly dominates `b` if the operation defining `a` properly dominates `b`, but `a`
223        // does not itself enclose `b` in one of its regions.
224        let defining_op = self.get_defining_op().unwrap();
225        dom_info.properly_dominates_with_options(
226            defining_op,
227            other.as_operation_ref(),
228            /*enclosing_op_ok= */ false,
229        )
230    }
231}