Skip to content

Commit 99d33f9

Browse files
C++: Remove unreachable IR
This change removes any IR instructions that can be statically proven unreachable. To detect unreachable IR, we first run a simple constant value analysis on the IR. Then, any `ConditionalBranch` with a constant condition has the appropriate edge marked as "infeasible". We define a class `ReachableBlock` as any `IRBlock` with a path from the entry block of the function. SSA construction has been modified to operate only on `ReachableBlock` and `ReachableInstruction`, which ensures that only reachable IR gets translated into SSA form. For any infeasible edge where its predecessor block is reachable, we replace the original target of the branch with an `Unreached` instruction, which lets us preserve the invariant that all `ConditionalBranch` instructions have both a true and a false edge, and allows guard inference to still work. The changes to `SSAConstruction.qll` are not as scary as they look. They are almost entirely a mechanical replacement of `OldIR::IRBlock` with `OldBlock`, which is just an alias for `ReachableBlock`. Note that the `constant_func.ql` test can determine that the two new test functions always return 0. Removing unreachable code helps get rid of some common FPs in IR-based dataflow analysis, especially for constructs like `while(true)`.
1 parent 59fc77f commit 99d33f9

31 files changed

Lines changed: 811 additions & 498 deletions

config/identical-files.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,20 @@
8282
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
8383
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
8484
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
85+
],
86+
"C++ IR ReachableBlock": [
87+
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
88+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll",
89+
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/ReachableBlock.qll"
90+
],
91+
"C++ IR PrintReachableBlock": [
92+
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
93+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll",
94+
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/PrintReachableBlock.qll"
95+
],
96+
"C++ IR Dominance": [
97+
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
98+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll",
99+
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/Dominance.qll"
85100
]
86101
}

cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ private newtype TOpcode =
6868
TBufferReadSideEffect() or
6969
TBufferWriteSideEffect() or
7070
TBufferMayWriteSideEffect() or
71-
TChi()
71+
TChi() or
72+
TUnreached()
7273

7374
class Opcode extends TOpcode {
7475
string toString() {
@@ -195,5 +196,6 @@ module Opcode {
195196
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
196197
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
197198
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
198-
class Chi extends Opcode, TChi {override final string toString() { result = "Chi" } }
199+
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
200+
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
199201
}

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
private import internal.IRInternal
22
import Instruction
33
import semmle.code.cpp.ir.implementation.EdgeKind
4-
import Cached
4+
private import Cached
55

66
class IRBlock extends TIRBlock {
77
final string toString() {

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ module InstructionSanity {
102102
not exists(instr.getASuccessor()) and
103103
not instr instanceof ExitFunctionInstruction and
104104
// Phi instructions aren't linked into the instruction-level flow graph.
105-
not instr instanceof PhiInstruction
105+
not instr instanceof PhiInstruction and
106+
not instr instanceof UnreachedInstruction
106107
}
107108

108109
/**
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
451452
final predicate isResultModeled() {
452453
// Register results are always in SSA form.
453454
not hasMemoryResult() or
454-
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
455-
not (getAUse() instanceof UnmodeledUseOperand)
455+
Construction::hasModeledMemoryResult(this)
456456
}
457457

458458
/**
@@ -1469,6 +1469,17 @@ class ChiInstruction extends Instruction {
14691469
}
14701470
}
14711471

1472+
/**
1473+
* An instruction representing unreachable code. Inserted in place of the original target
1474+
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
1475+
* infeasible.
1476+
*/
1477+
class UnreachedInstruction extends Instruction {
1478+
UnreachedInstruction() {
1479+
opcode instanceof Opcode::Unreached
1480+
}
1481+
}
1482+
14721483
/**
14731484
* An instruction representing a built-in operation. This is used to represent
14741485
* operations such as access to variable argument lists.

0 commit comments

Comments
 (0)