|
1 | 1 | private import IR
|
2 |
| -import InstructionSanity |
3 |
| -import IRTypeSanity |
| 2 | +import InstructionSanity // module is below |
| 3 | +import IRTypeSanity // module is in IRType.qll |
| 4 | + |
| 5 | +module InstructionSanity { |
| 6 | + private import internal.InstructionImports as Imports |
| 7 | + private import Imports::OperandTag |
| 8 | + private import internal.IRInternal |
| 9 | + |
| 10 | + /** |
| 11 | + * Holds if instruction `instr` is missing an expected operand with tag `tag`. |
| 12 | + */ |
| 13 | + query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) { |
| 14 | + exists(OperandTag tag | |
| 15 | + instr.getOpcode().hasOperand(tag) and |
| 16 | + not exists(NonPhiOperand operand | |
| 17 | + operand = instr.getAnOperand() and |
| 18 | + operand.getOperandTag() = tag |
| 19 | + ) and |
| 20 | + message = |
| 21 | + "Instruction '" + instr.getOpcode().toString() + |
| 22 | + "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and |
| 23 | + func = instr.getEnclosingIRFunction() and |
| 24 | + funcText = Language::getIdentityString(func.getFunction()) |
| 25 | + ) |
| 26 | + } |
| 27 | + |
| 28 | + /** |
| 29 | + * Holds if instruction `instr` has an unexpected operand with tag `tag`. |
| 30 | + */ |
| 31 | + query predicate unexpectedOperand(Instruction instr, OperandTag tag) { |
| 32 | + exists(NonPhiOperand operand | |
| 33 | + operand = instr.getAnOperand() and |
| 34 | + operand.getOperandTag() = tag |
| 35 | + ) and |
| 36 | + not instr.getOpcode().hasOperand(tag) and |
| 37 | + not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and |
| 38 | + not ( |
| 39 | + instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag |
| 40 | + ) and |
| 41 | + not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * Holds if instruction `instr` has multiple operands with tag `tag`. |
| 46 | + */ |
| 47 | + query predicate duplicateOperand( |
| 48 | + Instruction instr, string message, IRFunction func, string funcText |
| 49 | + ) { |
| 50 | + exists(OperandTag tag, int operandCount | |
| 51 | + operandCount = |
| 52 | + strictcount(NonPhiOperand operand | |
| 53 | + operand = instr.getAnOperand() and |
| 54 | + operand.getOperandTag() = tag |
| 55 | + ) and |
| 56 | + operandCount > 1 and |
| 57 | + not tag instanceof UnmodeledUseOperandTag and |
| 58 | + message = |
| 59 | + "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" + |
| 60 | + " in function '$@'." and |
| 61 | + func = instr.getEnclosingIRFunction() and |
| 62 | + funcText = Language::getIdentityString(func.getFunction()) |
| 63 | + ) |
| 64 | + } |
| 65 | + |
| 66 | + /** |
| 67 | + * Holds if `Phi` instruction `instr` is missing an operand corresponding to |
| 68 | + * the predecessor block `pred`. |
| 69 | + */ |
| 70 | + query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) { |
| 71 | + pred = instr.getBlock().getAPredecessor() and |
| 72 | + not exists(PhiInputOperand operand | |
| 73 | + operand = instr.getAnOperand() and |
| 74 | + operand.getPredecessorBlock() = pred |
| 75 | + ) |
| 76 | + } |
| 77 | + |
| 78 | + query predicate missingOperandType(Operand operand, string message) { |
| 79 | + exists(Language::Function func, Instruction use | |
| 80 | + not exists(operand.getType()) and |
| 81 | + use = operand.getUse() and |
| 82 | + func = use.getEnclosingFunction() and |
| 83 | + message = |
| 84 | + "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + |
| 85 | + "' missing type in function '" + Language::getIdentityString(func) + "'." |
| 86 | + ) |
| 87 | + } |
| 88 | + |
| 89 | + query predicate duplicateChiOperand( |
| 90 | + ChiInstruction chi, string message, IRFunction func, string funcText |
| 91 | + ) { |
| 92 | + chi.getTotal() = chi.getPartial() and |
| 93 | + message = |
| 94 | + "Chi instruction for " + chi.getPartial().toString() + |
| 95 | + " has duplicate operands in function $@" and |
| 96 | + func = chi.getEnclosingIRFunction() and |
| 97 | + funcText = Language::getIdentityString(func.getFunction()) |
| 98 | + } |
| 99 | + |
| 100 | + query predicate sideEffectWithoutPrimary( |
| 101 | + SideEffectInstruction instr, string message, IRFunction func, string funcText |
| 102 | + ) { |
| 103 | + not exists(instr.getPrimaryInstruction()) and |
| 104 | + message = "Side effect instruction missing primary instruction in function $@" and |
| 105 | + func = instr.getEnclosingIRFunction() and |
| 106 | + funcText = Language::getIdentityString(func.getFunction()) |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * Holds if an instruction, other than `ExitFunction`, has no successors. |
| 111 | + */ |
| 112 | + query predicate instructionWithoutSuccessor(Instruction instr) { |
| 113 | + not exists(instr.getASuccessor()) and |
| 114 | + not instr instanceof ExitFunctionInstruction and |
| 115 | + // Phi instructions aren't linked into the instruction-level flow graph. |
| 116 | + not instr instanceof PhiInstruction and |
| 117 | + not instr instanceof UnreachedInstruction |
| 118 | + } |
| 119 | + |
| 120 | + /** |
| 121 | + * Holds if there are multiple (`n`) edges of kind `kind` from `source`, |
| 122 | + * where `target` is among the targets of those edges. |
| 123 | + */ |
| 124 | + query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) { |
| 125 | + n = strictcount(Instruction t | source.getSuccessor(kind) = t) and |
| 126 | + n > 1 and |
| 127 | + source.getSuccessor(kind) = target |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Holds if `instr` in `f` is part of a loop even though the AST of `f` |
| 132 | + * contains no element that can cause loops. |
| 133 | + */ |
| 134 | + query predicate unexplainedLoop(Language::Function f, Instruction instr) { |
| 135 | + exists(IRBlock block | |
| 136 | + instr.getBlock() = block and |
| 137 | + block.getEnclosingFunction() = f and |
| 138 | + block.getASuccessor+() = block |
| 139 | + ) and |
| 140 | + not Language::hasPotentialLoop(f) |
| 141 | + } |
| 142 | + |
| 143 | + /** |
| 144 | + * Holds if a `Phi` instruction is present in a block with fewer than two |
| 145 | + * predecessors. |
| 146 | + */ |
| 147 | + query predicate unnecessaryPhiInstruction(PhiInstruction instr) { |
| 148 | + count(instr.getBlock().getAPredecessor()) < 2 |
| 149 | + } |
| 150 | + |
| 151 | + /** |
| 152 | + * Holds if operand `operand` consumes a value that was defined in |
| 153 | + * a different function. |
| 154 | + */ |
| 155 | + query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) { |
| 156 | + operand.getUse() = instr and |
| 157 | + operand.getAnyDef() = defInstr and |
| 158 | + instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction() |
| 159 | + } |
| 160 | + |
| 161 | + /** |
| 162 | + * Holds if instruction `instr` is not in exactly one block. |
| 163 | + */ |
| 164 | + query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) { |
| 165 | + blockCount = count(instr.getBlock()) and |
| 166 | + blockCount != 1 |
| 167 | + } |
| 168 | + |
| 169 | + private predicate forwardEdge(IRBlock b1, IRBlock b2) { |
| 170 | + b1.getASuccessor() = b2 and |
| 171 | + not b1.getBackEdgeSuccessor(_) = b2 |
| 172 | + } |
| 173 | + |
| 174 | + /** |
| 175 | + * Holds if `f` contains a loop in which no edge is a back edge. |
| 176 | + * |
| 177 | + * This check ensures we don't have too _few_ back edges. |
| 178 | + */ |
| 179 | + query predicate containsLoopOfForwardEdges(IRFunction f) { |
| 180 | + exists(IRBlock block | |
| 181 | + forwardEdge+(block, block) and |
| 182 | + block.getEnclosingIRFunction() = f |
| 183 | + ) |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * Holds if `block` is reachable from its function entry point but would not |
| 188 | + * be reachable by traversing only forward edges. This check is skipped for |
| 189 | + * functions containing `goto` statements as the property does not generally |
| 190 | + * hold there. |
| 191 | + * |
| 192 | + * This check ensures we don't have too _many_ back edges. |
| 193 | + */ |
| 194 | + query predicate lostReachability(IRBlock block) { |
| 195 | + exists(IRFunction f, IRBlock entry | |
| 196 | + entry = f.getEntryBlock() and |
| 197 | + entry.getASuccessor+() = block and |
| 198 | + not forwardEdge+(entry, block) and |
| 199 | + not Language::hasGoto(f.getFunction()) |
| 200 | + ) |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Holds if the number of back edges differs between the `Instruction` graph |
| 205 | + * and the `IRBlock` graph. |
| 206 | + */ |
| 207 | + query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) { |
| 208 | + fromInstr = |
| 209 | + count(Instruction i1, Instruction i2 | |
| 210 | + i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2 |
| 211 | + ) and |
| 212 | + fromBlock = |
| 213 | + count(IRBlock b1, IRBlock b2 | |
| 214 | + b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2 |
| 215 | + ) and |
| 216 | + fromInstr != fromBlock |
| 217 | + } |
| 218 | + |
| 219 | + /** |
| 220 | + * Gets the point in the function at which the specified operand is evaluated. For most operands, |
| 221 | + * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point |
| 222 | + * of evaluation is at the end of the corresponding predecessor block. |
| 223 | + */ |
| 224 | + private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) { |
| 225 | + block = operand.(PhiInputOperand).getPredecessorBlock() and |
| 226 | + index = block.getInstructionCount() |
| 227 | + or |
| 228 | + exists(Instruction use | |
| 229 | + use = operand.(NonPhiOperand).getUse() and |
| 230 | + block.getInstruction(index) = use |
| 231 | + ) |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * Holds if `useOperand` has a definition that does not dominate the use. |
| 236 | + */ |
| 237 | + query predicate useNotDominatedByDefinition( |
| 238 | + Operand useOperand, string message, IRFunction func, string funcText |
| 239 | + ) { |
| 240 | + exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | |
| 241 | + not useOperand.getUse() instanceof UnmodeledUseInstruction and |
| 242 | + not defInstr instanceof UnmodeledDefinitionInstruction and |
| 243 | + pointOfEvaluation(useOperand, useBlock, useIndex) and |
| 244 | + defInstr = useOperand.getAnyDef() and |
| 245 | + ( |
| 246 | + defInstr instanceof PhiInstruction and |
| 247 | + defBlock = defInstr.getBlock() and |
| 248 | + defIndex = -1 |
| 249 | + or |
| 250 | + defBlock.getInstruction(defIndex) = defInstr |
| 251 | + ) and |
| 252 | + not ( |
| 253 | + defBlock.strictlyDominates(useBlock) |
| 254 | + or |
| 255 | + defBlock = useBlock and |
| 256 | + defIndex < useIndex |
| 257 | + ) and |
| 258 | + message = |
| 259 | + "Operand '" + useOperand.toString() + |
| 260 | + "' is not dominated by its definition in function '$@'." and |
| 261 | + func = useOperand.getEnclosingIRFunction() and |
| 262 | + funcText = Language::getIdentityString(func.getFunction()) |
| 263 | + ) |
| 264 | + } |
| 265 | + |
| 266 | + query predicate switchInstructionWithoutDefaultEdge( |
| 267 | + SwitchInstruction switchInstr, string message, IRFunction func, string funcText |
| 268 | + ) { |
| 269 | + not exists(switchInstr.getDefaultSuccessor()) and |
| 270 | + message = |
| 271 | + "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and |
| 272 | + func = switchInstr.getEnclosingIRFunction() and |
| 273 | + funcText = Language::getIdentityString(func.getFunction()) |
| 274 | + } |
| 275 | +} |
0 commit comments