|
| 1 | +private import internal.IRInternal |
| 2 | +import Instruction |
| 3 | +private import internal.IRBlockImports as Imports |
| 4 | +import Imports::EdgeKind |
| 5 | +private import Cached |
| 6 | + |
| 7 | +/** |
| 8 | + * A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only |
| 9 | + * incoming edges at the beginning of the sequence and the only outgoing edges at the end of the |
| 10 | + * sequence. |
| 11 | + * |
| 12 | + * This class does not contain any members that query the predecessor or successor edges of the |
| 13 | + * block. This allows different classes that extend `IRBlockBase` to expose different subsets of |
| 14 | + * edges (e.g. ignoring unreachable edges). |
| 15 | + * |
| 16 | + * Most consumers should use the class `IRBlock`. |
| 17 | + */ |
| 18 | +class IRBlockBase extends TIRBlock { |
| 19 | + final string toString() { result = getFirstInstruction(this).toString() } |
| 20 | + |
| 21 | + final Language::Location getLocation() { result = getFirstInstruction().getLocation() } |
| 22 | + |
| 23 | + final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() } |
| 24 | + |
| 25 | + /** |
| 26 | + * Gets the zero-based index of the block within its function. This is used |
| 27 | + * by debugging and printing code only. |
| 28 | + */ |
| 29 | + int getDisplayIndex() { |
| 30 | + this = rank[result + 1](IRBlock funcBlock | |
| 31 | + funcBlock.getEnclosingFunction() = getEnclosingFunction() |
| 32 | + | |
| 33 | + funcBlock order by funcBlock.getUniqueId() |
| 34 | + ) |
| 35 | + } |
| 36 | + |
| 37 | + final Instruction getInstruction(int index) { result = getInstruction(this, index) } |
| 38 | + |
| 39 | + final PhiInstruction getAPhiInstruction() { |
| 40 | + Construction::getPhiInstructionBlockStart(result) = getFirstInstruction() |
| 41 | + } |
| 42 | + |
| 43 | + final Instruction getAnInstruction() { |
| 44 | + result = getInstruction(_) or |
| 45 | + result = getAPhiInstruction() |
| 46 | + } |
| 47 | + |
| 48 | + final Instruction getFirstInstruction() { result = getFirstInstruction(this) } |
| 49 | + |
| 50 | + final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) } |
| 51 | + |
| 52 | + final int getInstructionCount() { result = getInstructionCount(this) } |
| 53 | + |
| 54 | + final IRFunction getEnclosingIRFunction() { |
| 55 | + result = getFirstInstruction(this).getEnclosingIRFunction() |
| 56 | + } |
| 57 | + |
| 58 | + final Language::Function getEnclosingFunction() { |
| 59 | + result = getFirstInstruction(this).getEnclosingFunction() |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +/** |
| 64 | + * A basic block with additional information about its predecessor and successor edges. Each edge |
| 65 | + * corresponds to the control flow between the last instruction of one block and the first |
| 66 | + * instruction of another block. |
| 67 | + */ |
| 68 | +class IRBlock extends IRBlockBase { |
| 69 | + final IRBlock getASuccessor() { blockSuccessor(this, result) } |
| 70 | + |
| 71 | + final IRBlock getAPredecessor() { blockSuccessor(result, this) } |
| 72 | + |
| 73 | + final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) } |
| 74 | + |
| 75 | + final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) } |
| 76 | + |
| 77 | + final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) } |
| 78 | + |
| 79 | + final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) } |
| 80 | + |
| 81 | + final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block } |
| 82 | + |
| 83 | + pragma[noinline] |
| 84 | + final IRBlock dominanceFrontier() { |
| 85 | + dominates(result.getAPredecessor()) and |
| 86 | + not strictlyDominates(result) |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Holds if this block is reachable from the entry point of its function |
| 91 | + */ |
| 92 | + final predicate isReachableFromFunctionEntry() { |
| 93 | + this = getEnclosingIRFunction().getEntryBlock() or |
| 94 | + getAPredecessor().isReachableFromFunctionEntry() |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +private predicate startsBasicBlock(Instruction instr) { |
| 99 | + not instr instanceof PhiInstruction and |
| 100 | + ( |
| 101 | + count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor |
| 102 | + or |
| 103 | + exists(Instruction predecessor | |
| 104 | + instr = predecessor.getASuccessor() and |
| 105 | + strictcount(Instruction other | other = predecessor.getASuccessor()) > 1 |
| 106 | + ) // Predecessor has multiple successors |
| 107 | + or |
| 108 | + exists(Instruction predecessor, EdgeKind kind | |
| 109 | + instr = predecessor.getSuccessor(kind) and |
| 110 | + not kind instanceof GotoEdge |
| 111 | + ) // Incoming edge is not a GotoEdge |
| 112 | + or |
| 113 | + exists(Instruction predecessor | |
| 114 | + instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _) |
| 115 | + ) // A back edge enters this instruction |
| 116 | + ) |
| 117 | +} |
| 118 | + |
| 119 | +private predicate isEntryBlock(TIRBlock block) { |
| 120 | + block = MkIRBlock(any(EnterFunctionInstruction enter)) |
| 121 | +} |
| 122 | + |
| 123 | +cached |
| 124 | +private module Cached { |
| 125 | + cached |
| 126 | + newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } |
| 127 | + |
| 128 | + /** Holds if `i2` follows `i1` in a `IRBlock`. */ |
| 129 | + private predicate adjacentInBlock(Instruction i1, Instruction i2) { |
| 130 | + exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and |
| 131 | + not startsBasicBlock(i2) |
| 132 | + } |
| 133 | + |
| 134 | + /** Holds if `i` is the `index`th instruction the block starting with `first`. */ |
| 135 | + private Instruction getInstructionFromFirst(Instruction first, int index) = |
| 136 | + shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) |
| 137 | + |
| 138 | + /** Holds if `i` is the `index`th instruction in `block`. */ |
| 139 | + cached |
| 140 | + Instruction getInstruction(TIRBlock block, int index) { |
| 141 | + result = getInstructionFromFirst(getFirstInstruction(block), index) |
| 142 | + } |
| 143 | + |
| 144 | + cached |
| 145 | + int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) } |
| 146 | + |
| 147 | + cached |
| 148 | + predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { |
| 149 | + exists(Instruction predLast, Instruction succFirst | |
| 150 | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and |
| 151 | + succFirst = predLast.getSuccessor(kind) and |
| 152 | + succ = MkIRBlock(succFirst) |
| 153 | + ) |
| 154 | + } |
| 155 | + |
| 156 | + pragma[noinline] |
| 157 | + private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 } |
| 158 | + |
| 159 | + pragma[noopt] |
| 160 | + cached |
| 161 | + predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) { |
| 162 | + backEdgeSuccessorRaw(pred, succ, kind) |
| 163 | + or |
| 164 | + // See the QLDoc on `backEdgeSuccessorRaw`. |
| 165 | + exists(TIRBlock pred2 | |
| 166 | + // Joining with `blockIdentity` is a performance trick to get |
| 167 | + // `forwardEdgeRaw` on the RHS of a join, where it's fast. |
| 168 | + blockIdentity(pred, pred2) and |
| 169 | + forwardEdgeRaw+(pred, pred2) |
| 170 | + ) and |
| 171 | + blockSuccessor(pred, succ, kind) |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * Holds if there is an edge from `pred` to `succ` that is not a back edge. |
| 176 | + */ |
| 177 | + private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) { |
| 178 | + exists(EdgeKind kind | |
| 179 | + blockSuccessor(pred, succ, kind) and |
| 180 | + not backEdgeSuccessorRaw(pred, succ, kind) |
| 181 | + ) |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * Holds if the `kind`-edge from `pred` to `succ` is a back edge according to |
| 186 | + * `Construction`. |
| 187 | + * |
| 188 | + * There could be loops of non-back-edges if there is a flaw in the IR |
| 189 | + * construction or back-edge detection, and this could cause non-termination |
| 190 | + * of subsequent analysis. To prevent that, a subsequent predicate further |
| 191 | + * classifies all edges as back edges if they are involved in a loop of |
| 192 | + * non-back-edges. |
| 193 | + */ |
| 194 | + private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) { |
| 195 | + exists(Instruction predLast, Instruction succFirst | |
| 196 | + predLast = getInstruction(pred, getInstructionCount(pred) - 1) and |
| 197 | + succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and |
| 198 | + succ = MkIRBlock(succFirst) |
| 199 | + ) |
| 200 | + } |
| 201 | + |
| 202 | + cached |
| 203 | + predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) } |
| 204 | + |
| 205 | + cached |
| 206 | + predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) = |
| 207 | + idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block) |
| 208 | +} |
| 209 | + |
| 210 | +Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) } |
0 commit comments