Skip to content

Commit 56bb9dc

Browse files
C++: Remove infeasible edges to reachable blocks
The existing unreachable IR removal code only retargeted an infeasible edge to an `Unreached` instruction if the successor of the edge was an unreachable block. This is too conservative, because it doesn't remove an infeasible edge that targets a block that is still reachable via other paths. The trivial example of this is `do { } while (false);`, where the back edge is infeasible, but the body block is still reachable from the loop entry. This change retargets all infeasible edges to `Unreached` instructions, regardless of the reachability of the successor block.
1 parent 3e04f53 commit 56bb9dc

File tree

15 files changed

+258
-42
lines changed

15 files changed

+258
-42
lines changed

config/identical-files.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,9 @@
9494
"C++ IR Dominance": [
9595
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
9696
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll"
97+
],
98+
"C++ IR PrintDominance": [
99+
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
100+
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
97101
]
98102
}

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,9 @@ cached private module Cached {
2626
hasChiNode(_, oldInstruction)
2727
} or
2828
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
29-
// We need an `Unreached` instruction for the destination of any edge whose predecessor
30-
// instruction is reachable, but whose successor block is not. This should occur only for
31-
// infeasible edges.
32-
exists(OldIR::Instruction succInstruction |
33-
succInstruction = oldInstruction.getSuccessor(kind) and
34-
not succInstruction instanceof OldInstruction
35-
)
29+
// We need an `Unreached` instruction for the destination of each infeasible edge whose
30+
// predecessor is reachable.
31+
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
3632
}
3733

3834
cached class InstructionTagType extends TInstructionTag {
@@ -213,7 +209,7 @@ cached private module Cached {
213209
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
214210
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
215211
hasPhiNode(vvar, phiBlock) and
216-
predBlock = phiBlock.getAPredecessor() and
212+
predBlock = phiBlock.getAFeasiblePredecessor() and
217213
instr.getTag() = PhiTag(vvar, phiBlock) and
218214
newPredecessorBlock = getNewBlock(predBlock) and
219215
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
@@ -266,13 +262,20 @@ cached private module Cached {
266262
result = getChiInstruction(getOldInstruction(instruction)) and
267263
kind instanceof GotoEdge
268264
else (
269-
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
270-
or
265+
exists(OldInstruction oldInstruction |
266+
oldInstruction = getOldInstruction(instruction) and
267+
(
268+
result.getTag() = UnreachedTag(oldInstruction, kind) or
269+
(
270+
result = getNewInstruction(oldInstruction.getSuccessor(kind)) and
271+
not exists(UnreachedTag(oldInstruction, kind))
272+
)
273+
)
274+
) or
271275
exists(OldInstruction oldInstruction |
272276
instruction = getChiInstruction(oldInstruction) and
273277
result = getNewInstruction(oldInstruction.getSuccessor(kind))
274-
) or
275-
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
278+
)
276279
)
277280
}
278281

@@ -380,7 +383,7 @@ cached private module Cached {
380383

381384
pragma[noinline]
382385
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
383-
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
386+
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
384387
}
385388

386389
/**
@@ -474,7 +477,7 @@ cached private module Cached {
474477
useRank) or
475478
(
476479
definitionReachesEndOfBlock(vvar, defBlock, defRank,
477-
useBlock.getAPredecessor()) and
480+
useBlock.getAFeasiblePredecessor()) and
478481
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
479482
)
480483
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
private import DominanceInternal
2+
private import ReachableBlockInternal
3+
private import Dominance
4+
import IR
5+
6+
private class DominancePropertyProvider extends IRPropertyProvider {
7+
override string getBlockProperty(IRBlock block, string key) {
8+
exists(IRBlock dominator |
9+
blockImmediatelyDominates(dominator, block) and
10+
key = "ImmediateDominator" and
11+
result = "Block " + dominator.getDisplayIndex().toString()
12+
) or
13+
(
14+
key = "DominanceFrontier" and
15+
result = strictconcat(IRBlock frontierBlock |
16+
frontierBlock = getDominanceFrontier(block) |
17+
frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex()
18+
)
19+
)
20+
}
21+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ private import semmle.code.cpp.ir.internal.IntegerConstant
33
private import IR
44
private import ConstantAnalysis
55

6-
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
7-
exists(ConditionalBranchInstruction instr, int conditionValue |
8-
instr = block.getLastInstruction() and
9-
conditionValue = getValue(getConstantValue(instr.getCondition())) and
6+
predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
7+
exists(int conditionValue |
8+
conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and
109
if conditionValue = 0 then
1110
kind instanceof TrueEdge
1211
else
1312
kind instanceof FalseEdge
1413
)
1514
}
1615

17-
IRBlock getAFeasiblePredecessor(IRBlock successor) {
16+
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
17+
isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind)
18+
}
19+
20+
IRBlock getAFeasiblePredecessorBlock(IRBlock successor) {
1821
exists(EdgeKind kind |
1922
result.getSuccessor(kind) = successor and
2023
not isInfeasibleEdge(result, kind)
@@ -23,7 +26,7 @@ IRBlock getAFeasiblePredecessor(IRBlock successor) {
2326

2427
predicate isBlockReachable(IRBlock block) {
2528
exists(FunctionIR f |
26-
getAFeasiblePredecessor*(block) = f.getEntryBlock()
29+
getAFeasiblePredecessorBlock*(block) = f.getEntryBlock()
2730
)
2831
}
2932

@@ -35,6 +38,14 @@ class ReachableBlock extends IRBlock {
3538
ReachableBlock() {
3639
isBlockReachable(this)
3740
}
41+
42+
final ReachableBlock getAFeasiblePredecessor() {
43+
result = getAFeasiblePredecessorBlock(this)
44+
}
45+
46+
final ReachableBlock getAFeasibleSuccessor() {
47+
this = getAFeasiblePredecessorBlock(result)
48+
}
3849
}
3950

4051
class ReachableInstruction extends Instruction {
@@ -51,6 +62,6 @@ module Graph {
5162
}
5263

5364
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
54-
succ = pred.getASuccessor()
65+
succ = pred.getAFeasibleSuccessor()
5566
}
5667
}

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,9 @@ cached private module Cached {
2626
hasChiNode(_, oldInstruction)
2727
} or
2828
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
29-
// We need an `Unreached` instruction for the destination of any edge whose predecessor
30-
// instruction is reachable, but whose successor block is not. This should occur only for
31-
// infeasible edges.
32-
exists(OldIR::Instruction succInstruction |
33-
succInstruction = oldInstruction.getSuccessor(kind) and
34-
not succInstruction instanceof OldInstruction
35-
)
29+
// We need an `Unreached` instruction for the destination of each infeasible edge whose
30+
// predecessor is reachable.
31+
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
3632
}
3733

3834
cached class InstructionTagType extends TInstructionTag {
@@ -213,7 +209,7 @@ cached private module Cached {
213209
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
214210
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
215211
hasPhiNode(vvar, phiBlock) and
216-
predBlock = phiBlock.getAPredecessor() and
212+
predBlock = phiBlock.getAFeasiblePredecessor() and
217213
instr.getTag() = PhiTag(vvar, phiBlock) and
218214
newPredecessorBlock = getNewBlock(predBlock) and
219215
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
@@ -266,13 +262,20 @@ cached private module Cached {
266262
result = getChiInstruction(getOldInstruction(instruction)) and
267263
kind instanceof GotoEdge
268264
else (
269-
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
270-
or
265+
exists(OldInstruction oldInstruction |
266+
oldInstruction = getOldInstruction(instruction) and
267+
(
268+
result.getTag() = UnreachedTag(oldInstruction, kind) or
269+
(
270+
result = getNewInstruction(oldInstruction.getSuccessor(kind)) and
271+
not exists(UnreachedTag(oldInstruction, kind))
272+
)
273+
)
274+
) or
271275
exists(OldInstruction oldInstruction |
272276
instruction = getChiInstruction(oldInstruction) and
273277
result = getNewInstruction(oldInstruction.getSuccessor(kind))
274-
) or
275-
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
278+
)
276279
)
277280
}
278281

@@ -380,7 +383,7 @@ cached private module Cached {
380383

381384
pragma[noinline]
382385
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
383-
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
386+
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
384387
}
385388

386389
/**
@@ -474,7 +477,7 @@ cached private module Cached {
474477
useRank) or
475478
(
476479
definitionReachesEndOfBlock(vvar, defBlock, defRank,
477-
useBlock.getAPredecessor()) and
480+
useBlock.getAFeasiblePredecessor()) and
478481
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
479482
)
480483
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
private import DominanceInternal
2+
private import ReachableBlockInternal
3+
private import Dominance
4+
import IR
5+
6+
private class DominancePropertyProvider extends IRPropertyProvider {
7+
override string getBlockProperty(IRBlock block, string key) {
8+
exists(IRBlock dominator |
9+
blockImmediatelyDominates(dominator, block) and
10+
key = "ImmediateDominator" and
11+
result = "Block " + dominator.getDisplayIndex().toString()
12+
) or
13+
(
14+
key = "DominanceFrontier" and
15+
result = strictconcat(IRBlock frontierBlock |
16+
frontierBlock = getDominanceFrontier(block) |
17+
frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex()
18+
)
19+
)
20+
}
21+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ private import semmle.code.cpp.ir.internal.IntegerConstant
33
private import IR
44
private import ConstantAnalysis
55

6-
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
7-
exists(ConditionalBranchInstruction instr, int conditionValue |
8-
instr = block.getLastInstruction() and
9-
conditionValue = getValue(getConstantValue(instr.getCondition())) and
6+
predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
7+
exists(int conditionValue |
8+
conditionValue = getValue(getConstantValue(instr.(ConditionalBranchInstruction).getCondition())) and
109
if conditionValue = 0 then
1110
kind instanceof TrueEdge
1211
else
1312
kind instanceof FalseEdge
1413
)
1514
}
1615

17-
IRBlock getAFeasiblePredecessor(IRBlock successor) {
16+
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
17+
isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind)
18+
}
19+
20+
IRBlock getAFeasiblePredecessorBlock(IRBlock successor) {
1821
exists(EdgeKind kind |
1922
result.getSuccessor(kind) = successor and
2023
not isInfeasibleEdge(result, kind)
@@ -23,7 +26,7 @@ IRBlock getAFeasiblePredecessor(IRBlock successor) {
2326

2427
predicate isBlockReachable(IRBlock block) {
2528
exists(FunctionIR f |
26-
getAFeasiblePredecessor*(block) = f.getEntryBlock()
29+
getAFeasiblePredecessorBlock*(block) = f.getEntryBlock()
2730
)
2831
}
2932

@@ -35,6 +38,14 @@ class ReachableBlock extends IRBlock {
3538
ReachableBlock() {
3639
isBlockReachable(this)
3740
}
41+
42+
final ReachableBlock getAFeasiblePredecessor() {
43+
result = getAFeasiblePredecessorBlock(this)
44+
}
45+
46+
final ReachableBlock getAFeasibleSuccessor() {
47+
this = getAFeasiblePredecessorBlock(result)
48+
}
3849
}
3950

4051
class ReachableInstruction extends Instruction {
@@ -51,6 +62,6 @@ module Graph {
5162
}
5263

5364
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
54-
succ = pred.getASuccessor()
65+
succ = pred.getAFeasibleSuccessor()
5566
}
5667
}

cpp/ql/test/library-tests/ir/constant_func/constant_func.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,13 @@ int UnreachableIf(bool b) {
5858
}
5959
}
6060
}
61+
62+
int DoWhileFalse() {
63+
int i = 0;
64+
do {
65+
i++;
66+
} while (false);
67+
68+
return i;
69+
}
70+

cpp/ql/test/library-tests/ir/constant_func/constant_func.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
| constant_func.cpp:25:5:25:25 | IR: ReturnConstantPhiLoop | 7 |
44
| constant_func.cpp:34:5:34:22 | IR: UnreachableViaGoto | 0 |
55
| constant_func.cpp:41:5:41:17 | IR: UnreachableIf | 0 |
6+
| constant_func.cpp:62:5:62:16 | IR: DoWhileFalse | 1 |

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6754,3 +6754,31 @@ ir.cpp:
67546754
# 1044| Type = int
67556755
# 1044| Value = 1
67566756
# 1044| ValueCategory = prvalue
6757+
# 1049| DoWhileFalse() -> int
6758+
# 1049| params:
6759+
# 1049| body: { ... }
6760+
# 1050| 0: declaration
6761+
# 1050| 0: definition of i
6762+
# 1050| Type = int
6763+
# 1050| init: initializer for i
6764+
# 1050| expr: 0
6765+
# 1050| Type = int
6766+
# 1050| Value = 0
6767+
# 1050| ValueCategory = prvalue
6768+
# 1051| 1: do (...) ...
6769+
# 1053| 0: 0
6770+
# 1053| Type = bool
6771+
# 1053| Value = 0
6772+
# 1053| ValueCategory = prvalue
6773+
# 1051| 1: { ... }
6774+
# 1052| 0: ExprStmt
6775+
# 1052| 0: ... ++
6776+
# 1052| Type = int
6777+
# 1052| ValueCategory = prvalue
6778+
# 1052| 0: i
6779+
# 1052| Type = int
6780+
# 1052| ValueCategory = lvalue
6781+
# 1055| 2: return ...
6782+
# 1055| 0: i
6783+
# 1055| Type = int
6784+
# 1055| ValueCategory = prvalue(load)

0 commit comments

Comments
 (0)