|
| 1 | +/** Provides classes representing basic blocks. */ |
| 2 | + |
| 3 | +private import swift |
| 4 | +private import ControlFlowGraph |
| 5 | +private import internal.ControlFlowGraphImpl |
| 6 | +private import CfgNodes |
| 7 | +private import SuccessorTypes |
| 8 | + |
| 9 | +/** |
| 10 | + * A basic block, that is, a maximal straight-line sequence of control flow nodes |
| 11 | + * without branches or joins. |
| 12 | + */ |
| 13 | +class BasicBlock extends TBasicBlockStart { |
| 14 | + /** Gets the scope of this basic block. */ |
| 15 | + CfgScope getScope() { result = this.getAPredecessor().getScope() } |
| 16 | + |
| 17 | + /** Gets an immediate successor of this basic block, if any. */ |
| 18 | + BasicBlock getASuccessor() { result = this.getASuccessor(_) } |
| 19 | + |
| 20 | + /** Gets an immediate successor of this basic block of a given type, if any. */ |
| 21 | + BasicBlock getASuccessor(SuccessorType t) { |
| 22 | + result.getFirstNode() = this.getLastNode().getASuccessor(t) |
| 23 | + } |
| 24 | + |
| 25 | + /** Gets an immediate predecessor of this basic block, if any. */ |
| 26 | + BasicBlock getAPredecessor() { result.getASuccessor() = this } |
| 27 | + |
| 28 | + /** Gets an immediate predecessor of this basic block of a given type, if any. */ |
| 29 | + BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } |
| 30 | + |
| 31 | + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ |
| 32 | + ControlFlowNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } |
| 33 | + |
| 34 | + /** Gets a control flow node in this basic block. */ |
| 35 | + ControlFlowNode getANode() { result = this.getNode(_) } |
| 36 | + |
| 37 | + /** Gets the first control flow node in this basic block. */ |
| 38 | + ControlFlowNode getFirstNode() { this = TBasicBlockStart(result) } |
| 39 | + |
| 40 | + /** Gets the last control flow node in this basic block. */ |
| 41 | + ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) } |
| 42 | + |
| 43 | + /** Gets the length of this basic block. */ |
| 44 | + int length() { result = strictcount(this.getANode()) } |
| 45 | + |
| 46 | + predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } |
| 47 | + |
| 48 | + predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } |
| 49 | + |
| 50 | + predicate dominates(BasicBlock bb) { |
| 51 | + bb = this or |
| 52 | + this.strictlyDominates(bb) |
| 53 | + } |
| 54 | + |
| 55 | + predicate inDominanceFrontier(BasicBlock df) { |
| 56 | + this.dominatesPredecessor(df) and |
| 57 | + not this.strictlyDominates(df) |
| 58 | + } |
| 59 | + |
| 60 | + /** |
| 61 | + * Holds if this basic block dominates a predecessor of `df`. |
| 62 | + */ |
| 63 | + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } |
| 64 | + |
| 65 | + BasicBlock getImmediateDominator() { bbIDominates(result, this) } |
| 66 | + |
| 67 | + predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } |
| 68 | + |
| 69 | + predicate postDominates(BasicBlock bb) { |
| 70 | + this.strictlyPostDominates(bb) or |
| 71 | + this = bb |
| 72 | + } |
| 73 | + |
| 74 | + /** Holds if this basic block is in a loop in the control flow graph. */ |
| 75 | + predicate inLoop() { this.getASuccessor+() = this } |
| 76 | + |
| 77 | + /** Gets a textual representation of this basic block. */ |
| 78 | + string toString() { result = this.getFirstNode().toString() } |
| 79 | + |
| 80 | + /** Gets the location of this basic block. */ |
| 81 | + Location getLocation() { result = this.getFirstNode().getLocation() } |
| 82 | +} |
| 83 | + |
| 84 | +cached |
| 85 | +private module Cached { |
| 86 | + /** Internal representation of basic blocks. */ |
| 87 | + cached |
| 88 | + newtype TBasicBlock = TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) } |
| 89 | + |
| 90 | + /** Holds if `cfn` starts a new basic block. */ |
| 91 | + private predicate startsBB(ControlFlowNode cfn) { |
| 92 | + not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) |
| 93 | + or |
| 94 | + cfn.isJoin() |
| 95 | + or |
| 96 | + cfn.getAPredecessor().isBranch() |
| 97 | + } |
| 98 | + |
| 99 | + /** |
| 100 | + * Holds if `succ` is a control flow successor of `pred` within |
| 101 | + * the same basic block. |
| 102 | + */ |
| 103 | + private predicate intraBBSucc(ControlFlowNode pred, ControlFlowNode succ) { |
| 104 | + succ = pred.getASuccessor() and |
| 105 | + not startsBB(succ) |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * Holds if `cfn` is the `i`th node in basic block `bb`. |
| 110 | + * |
| 111 | + * In other words, `i` is the shortest distance from a node `bb` |
| 112 | + * that starts a basic block to `cfn` along the `intraBBSucc` relation. |
| 113 | + */ |
| 114 | + cached |
| 115 | + predicate bbIndex(ControlFlowNode bbStart, ControlFlowNode cfn, int i) = |
| 116 | + shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) |
| 117 | + |
| 118 | + /** |
| 119 | + * Holds if the first node of basic block `succ` is a control flow |
| 120 | + * successor of the last node of basic block `pred`. |
| 121 | + */ |
| 122 | + private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } |
| 123 | + |
| 124 | + /** Holds if `dom` is an immediate dominator of `bb`. */ |
| 125 | + cached |
| 126 | + predicate bbIDominates(BasicBlock dom, BasicBlock bb) = |
| 127 | + idominance(entryBB/1, succBB/2)(_, dom, bb) |
| 128 | + |
| 129 | + /** Holds if `pred` is a basic block predecessor of `succ`. */ |
| 130 | + private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } |
| 131 | + |
| 132 | + /** Holds if `bb` is an exit basic block that represents normal exit. */ |
| 133 | + private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() } |
| 134 | + |
| 135 | + /** Holds if `dom` is an immediate post-dominator of `bb`. */ |
| 136 | + cached |
| 137 | + predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = |
| 138 | + idominance(normalExitBB/1, predBB/2)(_, dom, bb) |
| 139 | + |
| 140 | + /** |
| 141 | + * Gets the `i`th predecessor of join block `jb`, with respect to some |
| 142 | + * arbitrary order. |
| 143 | + */ |
| 144 | + cached |
| 145 | + JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { |
| 146 | + result = |
| 147 | + rank[i + 1](JoinBlockPredecessor jbp | |
| 148 | + jbp = jb.getAPredecessor() |
| 149 | + | |
| 150 | + jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) |
| 151 | + ) |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +private import Cached |
| 156 | + |
| 157 | +/** Holds if `bb` is an entry basic block. */ |
| 158 | +private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode } |
| 159 | + |
| 160 | +/** |
| 161 | + * An entry basic block, that is, a basic block whose first node is |
| 162 | + * an entry node. |
| 163 | + */ |
| 164 | +class EntryBasicBlock extends BasicBlock { |
| 165 | + EntryBasicBlock() { entryBB(this) } |
| 166 | + |
| 167 | + override CfgScope getScope() { this.getFirstNode() = TEntryNode(result) } |
| 168 | +} |
| 169 | + |
| 170 | +/** |
| 171 | + * An annotated exit basic block, that is, a basic block whose last node is |
| 172 | + * an annotated exit node. |
| 173 | + */ |
| 174 | +class AnnotatedExitBasicBlock extends BasicBlock { |
| 175 | + private boolean normal; |
| 176 | + |
| 177 | + AnnotatedExitBasicBlock() { |
| 178 | + exists(AnnotatedExitNode n | |
| 179 | + n = this.getANode() and |
| 180 | + if n.isNormal() then normal = true else normal = false |
| 181 | + ) |
| 182 | + } |
| 183 | + |
| 184 | + /** Holds if this block represent a normal exit. */ |
| 185 | + final predicate isNormal() { normal = true } |
| 186 | +} |
| 187 | + |
| 188 | +/** |
| 189 | + * An exit basic block, that is, a basic block whose last node is |
| 190 | + * an exit node. |
| 191 | + */ |
| 192 | +class ExitBasicBlock extends BasicBlock { |
| 193 | + ExitBasicBlock() { this.getLastNode() instanceof ExitNode } |
| 194 | +} |
| 195 | + |
| 196 | +private module JoinBlockPredecessors { |
| 197 | + private predicate id(AstNode x, AstNode y) { x = y } |
| 198 | + |
| 199 | + private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y) |
| 200 | + |
| 201 | + int getId(JoinBlockPredecessor jbp) { |
| 202 | + idOf(jbp.getFirstNode().(AstCfgNode).getNode(), result) |
| 203 | + or |
| 204 | + idOf(jbp.(EntryBasicBlock).getScope(), result) |
| 205 | + } |
| 206 | + |
| 207 | + string getSplitString(JoinBlockPredecessor jbp) { |
| 208 | + result = jbp.getFirstNode().(AstCfgNode).getSplitsString() |
| 209 | + or |
| 210 | + not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and |
| 211 | + result = "" |
| 212 | + } |
| 213 | +} |
| 214 | + |
| 215 | +/** A basic block with more than one predecessor. */ |
| 216 | +class JoinBlock extends BasicBlock { |
| 217 | + JoinBlock() { this.getFirstNode().isJoin() } |
| 218 | + |
| 219 | + /** |
| 220 | + * Gets the `i`th predecessor of this join block, with respect to some |
| 221 | + * arbitrary order. |
| 222 | + */ |
| 223 | + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } |
| 224 | +} |
| 225 | + |
| 226 | +/** A basic block that is an immediate predecessor of a join block. */ |
| 227 | +class JoinBlockPredecessor extends BasicBlock { |
| 228 | + JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } |
| 229 | +} |
| 230 | + |
| 231 | +/** A basic block that terminates in a condition, splitting the subsequent control flow. */ |
| 232 | +class ConditionBlock extends BasicBlock { |
| 233 | + ConditionBlock() { this.getLastNode().isCondition() } |
| 234 | + |
| 235 | + /** |
| 236 | + * Holds if basic block `succ` is immediately controlled by this basic |
| 237 | + * block with conditional value `s`. That is, `succ` is an immediate |
| 238 | + * successor of this block, and `succ` can only be reached from |
| 239 | + * the callable entry point by going via the `s` edge out of this basic block. |
| 240 | + */ |
| 241 | + pragma[nomagic] |
| 242 | + predicate immediatelyControls(BasicBlock succ, SuccessorType s) { |
| 243 | + succ = this.getASuccessor(s) and |
| 244 | + forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred)) |
| 245 | + } |
| 246 | + |
| 247 | + /** |
| 248 | + * Holds if basic block `controlled` is controlled by this basic block with |
| 249 | + * conditional value `s`. That is, `controlled` can only be reached from |
| 250 | + * the callable entry point by going via the `s` edge out of this basic block. |
| 251 | + */ |
| 252 | + predicate controls(BasicBlock controlled, BooleanSuccessor s) { |
| 253 | + exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled)) |
| 254 | + } |
| 255 | +} |
0 commit comments