@@ -80,7 +80,6 @@ module Make<LocationSig Location, InputSig<Location> Input> {
8080 BasicBlock getAPredecessor ( SuccessorType t ) { result .getASuccessor ( t ) = this }
8181
8282 /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
83- cached
8483 Node getNode ( int pos ) { bbIndex ( this .getFirstNode ( ) , result , pos ) }
8584
8685 /** Gets a control flow node in this basic block. */
@@ -152,6 +151,60 @@ module Make<LocationSig Location, InputSig<Location> Input> {
152151 */
153152 BasicBlock getImmediateDominator ( ) { bbIDominates ( result , this ) }
154153
154+ /**
155+ * Holds if basic block `succ` is immediately controlled by this basic
156+ * block with conditional value `s`. That is, `succ` is an immediate
157+ * successor of this block, and `succ` can only be reached from
158+ * the callable entry point by going via the `s` edge out of this basic block.
159+ */
160+ pragma [ nomagic]
161+ predicate immediatelyControls ( BasicBlock succ , SuccessorType s ) {
162+ succ = this .getASuccessor ( s ) and
163+ forall ( BasicBlock pred | pred = succ .getAPredecessor ( ) and pred != this |
164+ succ .dominates ( pred )
165+ )
166+ }
167+
168+ /**
169+ * Holds if basic block `controlled` is controlled by this basic block with
170+ * conditional value `s`. That is, `controlled` can only be reached from
171+ * the callable entry point by going via the `s` edge out of this basic block.
172+ */
173+ predicate controls ( BasicBlock controlled , SuccessorType s ) {
174+ // For this block to control the block `controlled` with `s` the following must be true:
175+ // 1/ Execution must have passed through the test i.e. `this` must strictly dominate `controlled`.
176+ // 2/ Execution must have passed through the `s` edge leaving `this`.
177+ //
178+ // Although "passed through the `s` edge" implies that `this.getASuccessor(s)` dominates `controlled`,
179+ // the reverse is not true, as flow may have passed through another edge to get to `this.getASuccessor(s)`
180+ // so we need to assert that `this.getASuccessor(s)` dominates `controlled` *and* that
181+ // all predecessors of `this.getASuccessor(s)` are either `this` or dominated by `this.getASuccessor(s)`.
182+ //
183+ // For example, in the following C# snippet:
184+ // ```csharp
185+ // if (x)
186+ // controlled;
187+ // false_successor;
188+ // uncontrolled;
189+ // ```
190+ // `false_successor` dominates `uncontrolled`, but not all of its predecessors are `this` (`if (x)`)
191+ // or dominated by itself. Whereas in the following code:
192+ // ```csharp
193+ // if (x)
194+ // while (controlled)
195+ // also_controlled;
196+ // false_successor;
197+ // uncontrolled;
198+ // ```
199+ // the block `while controlled` is controlled because all of its predecessors are `this` (`if (x)`)
200+ // or (in the case of `also_controlled`) dominated by itself.
201+ //
202+ // The additional constraint on the predecessors of the test successor implies
203+ // that `this` strictly dominates `controlled` so that isn't necessary to check
204+ // directly.
205+ exists ( BasicBlock succ | this .immediatelyControls ( succ , s ) | succ .dominates ( controlled ) )
206+ }
207+
155208 /**
156209 * Holds if this basic block strictly post-dominates basic block `bb`.
157210 *
@@ -188,6 +241,52 @@ module Make<LocationSig Location, InputSig<Location> Input> {
188241 cached
189242 newtype TBasicBlock = TBasicBlockStart ( Node cfn ) { startsBB ( cfn ) }
190243
244+ /** Holds if `cfn` starts a new basic block. */
245+ private predicate startsBB ( Node cfn ) {
246+ not exists ( nodeGetAPredecessor ( cfn , _) ) and exists ( nodeGetASuccessor ( cfn , _) )
247+ or
248+ nodeIsJoin ( cfn )
249+ or
250+ nodeIsBranch ( nodeGetAPredecessor ( cfn , _) )
251+ or
252+ // In cases such as
253+ //
254+ // ```rb
255+ // if x and y
256+ // foo
257+ // else
258+ // bar
259+ // ```
260+ //
261+ // we have a CFG that looks like
262+ //
263+ // x --false--> [false] x and y --false--> bar
264+ // \ |
265+ // --true--> y --false--
266+ // \
267+ // --true--> [true] x and y --true--> foo
268+ //
269+ // and we want to ensure that both `foo` and `bar` start a new basic block.
270+ exists ( nodeGetAPredecessor ( cfn , any ( SuccessorType s | successorTypeIsCondition ( s ) ) ) )
271+ }
272+
273+ /**
274+ * Holds if `succ` is a control flow successor of `pred` within
275+ * the same basic block.
276+ */
277+ private predicate intraBBSucc ( Node pred , Node succ ) {
278+ succ = nodeGetASuccessor ( pred , _) and
279+ not startsBB ( succ )
280+ }
281+
282+ /**
283+ * Holds if `bbStart` is the first node in a basic block and `cfn` is the
284+ * `i`th node in the same basic block.
285+ */
286+ cached
287+ predicate bbIndex ( Node bbStart , Node cfn , int i ) =
288+ shortestDistances( startsBB / 1 , intraBBSucc / 2 ) ( bbStart , cfn , i )
289+
191290 /**
192291 * Holds if the first node of basic block `succ` is a control flow
193292 * successor of the last node of basic block `pred`.
@@ -213,51 +312,6 @@ module Make<LocationSig Location, InputSig<Location> Input> {
213312
214313 private import Cached
215314
216- /** Holds if `cfn` starts a new basic block. */
217- private predicate startsBB ( Node cfn ) {
218- not exists ( nodeGetAPredecessor ( cfn , _) ) and exists ( nodeGetASuccessor ( cfn , _) )
219- or
220- nodeIsJoin ( cfn )
221- or
222- nodeIsBranch ( nodeGetAPredecessor ( cfn , _) )
223- or
224- // In cases such as
225- //
226- // ```rb
227- // if x and y
228- // foo
229- // else
230- // bar
231- // ```
232- //
233- // we have a CFG that looks like
234- //
235- // x --false--> [false] x or y --false--> bar
236- // \ |
237- // --true--> y --false--
238- // \
239- // --true--> [true] x or y --true--> foo
240- //
241- // and we want to ensure that both `foo` and `bar` start a new basic block.
242- exists ( nodeGetAPredecessor ( cfn , any ( SuccessorType s | successorTypeIsCondition ( s ) ) ) )
243- }
244-
245- /**
246- * Holds if `succ` is a control flow successor of `pred` within
247- * the same basic block.
248- */
249- predicate intraBBSucc ( Node pred , Node succ ) {
250- succ = nodeGetASuccessor ( pred , _) and
251- not startsBB ( succ )
252- }
253-
254- /**
255- * Holds if `bbStart` is the first node in a basic block and `cfn` is the
256- * `i`th node in the same basic block.
257- */
258- private predicate bbIndex ( Node bbStart , Node cfn , int i ) =
259- shortestDistances( startsBB / 1 , intraBBSucc / 2 ) ( bbStart , cfn , i )
260-
261315 /** Holds if `bb` is an entry basic block. */
262316 private predicate entryBB ( BasicBlock bb ) { nodeIsDominanceEntry ( bb .getFirstNode ( ) ) }
263317}
0 commit comments