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}