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)`.
This commit is contained in:
Dave Bartolomeo
2018-12-10 00:53:36 -08:00
parent 59fc77f066
commit 99d33f9623
31 changed files with 805 additions and 492 deletions

View File

@@ -82,5 +82,20 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/PrintConstantAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/PrintConstantAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/PrintConstantAnalysis.qll"
],
"C++ IR ReachableBlock": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/ReachableBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/ReachableBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/ReachableBlock.qll"
],
"C++ IR PrintReachableBlock": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintReachableBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintReachableBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/PrintReachableBlock.qll"
],
"C++ IR Dominance": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/Dominance.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/Dominance.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/reachability/Dominance.qll"
]
}

View File

@@ -68,7 +68,8 @@ private newtype TOpcode =
TBufferReadSideEffect() or
TBufferWriteSideEffect() or
TBufferMayWriteSideEffect() or
TChi()
TChi() or
TUnreached()
class Opcode extends TOpcode {
string toString() {
@@ -195,5 +196,6 @@ module Opcode {
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
class Chi extends Opcode, TChi {override final string toString() { result = "Chi" } }
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
}

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
import Cached
private import Cached
class IRBlock extends TIRBlock {
final string toString() {

View File

@@ -102,7 +102,8 @@ module InstructionSanity {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
not instr instanceof PhiInstruction and
not instr instanceof UnreachedInstruction
}
/**
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
final predicate isResultModeled() {
// Register results are always in SSA form.
not hasMemoryResult() or
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
not (getAUse() instanceof UnmodeledUseOperand)
Construction::hasModeledMemoryResult(this)
}
/**
@@ -1469,6 +1469,17 @@ class ChiInstruction extends Instruction {
}
}
/**
* An instruction representing unreachable code. Inserted in place of the original target
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
* infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() {
opcode instanceof Opcode::Unreached
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -4,23 +4,35 @@ private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
private class OldInstruction = Reachability::ReachableInstruction;
import Cached
cached private module Cached {
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
private IRBlock getNewBlock(OldBlock oldBlock) {
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
cached newtype TInstructionTag =
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
WrappedInstructionTag(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction
} or
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
hasPhiNode(vvar, block)
} or
ChiTag(OldIR::Instruction oldInstruction) {
ChiTag(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
} or
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
// We need an `Unreached` instruction for the destination of any edge whose predecessor
// instruction is reachable, but whose successor block is not. This should occur only for
// infeasible edges.
exists(OldIR::Instruction succInstruction |
succInstruction = oldInstruction.getSuccessor(kind) and
not succInstruction instanceof OldInstruction
)
}
cached class InstructionTagType extends TInstructionTag {
@@ -35,11 +47,11 @@ cached private module Cached {
)
}
cached OldIR::Instruction getOldInstruction(Instruction instr) {
cached OldInstruction getOldInstruction(Instruction instr) {
instr.getTag() = WrappedInstructionTag(result)
}
private Instruction getNewInstruction(OldIR::Instruction instr) {
private Instruction getNewInstruction(OldInstruction instr) {
getOldInstruction(result) = instr
}
@@ -47,21 +59,21 @@ cached private module Cached {
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
* corresponding to `instr` if there is no `Chi` node.
*/
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
private Instruction getNewFinalInstruction(OldInstruction instr) {
result = getChiInstruction(instr)
or
not exists(getChiInstruction(instr)) and
result = getNewInstruction(instr)
}
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
Alias::VirtualVariable vvar) {
result.getFunction() = func and
result.getAST() = oldBlock.getFirstInstruction().getAST() and
result.getTag() = PhiTag(vvar, oldBlock)
}
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
private ChiInstruction getChiInstruction (OldInstruction instr) {
hasChiNode(_, instr) and
result.getTag() = ChiTag(instr)
}
@@ -91,8 +103,8 @@ cached private module Cached {
}
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
InstructionTag tag, Type resultType, boolean isGLValue) {
exists(OldIR::Instruction instr |
InstructionTag tag, Type resultType, boolean isGLValue) {
exists(OldInstruction instr |
instr.getFunction() = func and
instr.getOpcode() = opcode and
instr.getAST() = ast and
@@ -103,7 +115,7 @@ cached private module Cached {
else
isGLValue = false
) or
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
exists(OldBlock block, Alias::VirtualVariable vvar |
hasPhiNode(vvar, block) and
block.getFunction() = func and
opcode instanceof Opcode::Phi and
@@ -112,7 +124,7 @@ cached private module Cached {
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
exists(OldInstruction instr, Alias::VirtualVariable vvar |
hasChiNode(vvar, instr) and
instr.getFunction() = func and
opcode instanceof Opcode::Chi and
@@ -120,11 +132,19 @@ cached private module Cached {
tag = ChiTag(instr) and
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldInstruction oldInstruction, EdgeKind kind |
oldInstruction.getFunction() = func and
tag = UnreachedTag(oldInstruction, kind) and
opcode instanceof Opcode::Unreached and
ast = oldInstruction.getSuccessor(kind).getAST() and
resultType instanceof VoidType and
isGLValue = false
)
}
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
Type type) {
Type type) {
exists(OldIR::IRTempVariable var |
var.getFunction() = func and
var.getAST() = ast and
@@ -135,19 +155,20 @@ cached private module Cached {
cached predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
instruction instanceof PhiInstruction // Phis always have modeled results
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
if oldOperand instanceof OldIR::MemoryOperand then (
(
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, int defIndex |
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldBlock defBlock, int defRank, int defIndex |
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
@@ -164,7 +185,7 @@ cached private module Cached {
) or
// Connect any definitions that are not being modeled in SSA to the
// `UnmodeledUse` instruction.
exists(OldIR::Instruction oldDefinition |
exists(OldInstruction oldDefinition |
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
oldDefinition = oldOperand.getDefinitionInstruction() and
@@ -189,8 +210,8 @@ cached private module Cached {
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock) {
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
hasPhiNode(vvar, phiBlock) and
predBlock = phiBlock.getAPredecessor() and
instr.getTag() = PhiTag(vvar, phiBlock) and
@@ -205,8 +226,8 @@ cached private module Cached {
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
int defRank, int defIndex, OldBlock useBlock, int useRank |
ChiTag(oldInstr) = chiInstr.getTag() and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
@@ -220,7 +241,7 @@ cached private module Cached {
}
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldIR::IRBlock oldBlock |
exists(OldBlock oldBlock |
instr.getTag() = PhiTag(_, oldBlock) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
@@ -247,10 +268,11 @@ cached private module Cached {
else (
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
or
exists(OldIR::Instruction oldInstruction |
exists(OldInstruction oldInstruction |
instruction = getChiInstruction(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
) or
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
)
}
@@ -310,29 +332,29 @@ cached private module Cached {
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
OldInstruction instr, OldBlock block, int index) {
block.getInstruction(index) = instr and
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
}
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
(
hasPhiNode(vvar, block) and
index = -1
) or
exists(Alias::MemoryAccess access, OldIR::Instruction def |
exists(Alias::MemoryAccess access, OldInstruction def |
access = Alias::getResultMemoryAccess(def) and
block.getInstruction(index) = def and
vvar = access.getVirtualVariable()
)
}
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
}
private predicate hasUse(Alias::VirtualVariable vvar,
OldIR::Instruction use, OldIR::IRBlock block, int index) {
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
int index) {
exists(Alias::MemoryAccess access |
(
access = Alias::getOperandMemoryAccess(use.getAnOperand())
@@ -349,7 +371,7 @@ cached private module Cached {
)
}
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
exists (int index | hasUse(vvar, _, block, index) |
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
) or
@@ -357,7 +379,7 @@ cached private module Cached {
}
pragma[noinline]
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
}
@@ -367,18 +389,18 @@ cached private module Cached {
* end of the block, even if the definition is the last instruction in the
* block.
*/
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
}
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
int instructionIndex) {
hasDefinition(vvar, block, instructionIndex) and
defUseRank(vvar, block, rankIndex, instructionIndex)
}
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
int rankIndex, OldIR::Instruction use) {
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
OldInstruction use) {
exists(int index |
hasUse(vvar, use, block, index) and
defUseRank(vvar, block, rankIndex, index)
@@ -389,8 +411,8 @@ cached private module Cached {
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
* index `reachesRank` in block `block`.
*/
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
OldIR::IRBlock block, int defRank, int reachesRank) {
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
int reachesRank) {
hasDefinitionAtRank(vvar, block, defRank, _) and
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
(
@@ -410,8 +432,8 @@ cached private module Cached {
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
* block `block`.
*/
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock block) {
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
(
(
@@ -421,7 +443,7 @@ cached private module Cached {
variableLiveOnExitFromBlock(vvar, defBlock) and
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
) or
exists(OldIR::IRBlock idom |
exists(OldBlock idom |
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(vvar, idom, block)
)
@@ -429,24 +451,23 @@ cached private module Cached {
}
pragma[noinline]
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
OldIR::IRBlock idom, OldIR::IRBlock block) {
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
OldBlock block) {
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
variableLiveOnExitFromBlock(vvar, block) and
not hasDefinition(vvar, block, _)
}
private predicate definitionReachesUseWithinBlock(
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
OldIR::IRBlock useBlock, int useRank) {
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasUseAtRank(vvar, useBlock, useRank, _) and
definitionReachesRank(vvar, defBlock, defRank, useRank)
}
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(vvar, useBlock, useRank, _) and
(
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
@@ -459,24 +480,21 @@ cached private module Cached {
)
}
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
OldIR::IRBlock phiBlock) {
exists(OldIR::IRBlock defBlock |
phiBlock = defBlock.dominanceFrontier() and
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
hasDefinition(vvar, defBlock, _) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
variableLiveOnEntryToBlock(vvar, phiBlock)
)
}
private predicate hasPhiNode(Alias::VirtualVariable vvar,
OldIR::IRBlock phiBlock) {
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
}
private predicate hasChiNode(Alias::VirtualVariable vvar,
OldIR::Instruction def) {
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
@@ -492,13 +510,17 @@ cached private module CachedForDebugging {
}
cached string getInstructionUniqueId(Instruction instr) {
exists(OldIR::Instruction oldInstr |
exists(OldInstruction oldInstr |
oldInstr = getOldInstruction(instr) and
result = "NonSSA: " + oldInstr.getUniqueId()
) or
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
instr.getTag() = PhiTag(vvar, phiBlock) and
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
) or
exists(OldInstruction oldInstr, EdgeKind kind |
instr.getTag() = UnreachedTag(oldInstr, kind) and
result = "Unreached(" + oldInstr.getUniqueId() + ":" + kind.toString() + ")"
)
}

View File

@@ -1,3 +1,5 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
import AliasedSSA as Alias

View File

@@ -0,0 +1,21 @@
private import DominanceInternal
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
blockImmediatelyDominates+(dominator, block)
}
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
blockStrictlyDominates(dominator, block) or dominator = block
}
pragma[noinline]
Graph::Block getDominanceFrontier(Graph::Block dominator) {
exists(Graph::Block pred |
Graph::blockSuccessor(pred, result) and
blockDominates(dominator, pred) and
not blockStrictlyDominates(dominator, result)
)
}

View File

@@ -0,0 +1,20 @@
private import ReachableBlockInternal
private import ReachableBlock
import IR
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
override string getBlockProperty(IRBlock block, string key) {
(
not block instanceof ReachableBlock and
key = "Unreachable" and
result = "true"
) or
(
exists(EdgeKind kind |
isInfeasibleEdge(block, kind) and
key = "Infeasible(" + kind.toString() + ")" and
result = "true"
)
)
}
}

View File

@@ -0,0 +1,52 @@
private import ReachableBlockInternal
private import semmle.code.cpp.ir.internal.IntegerConstant
private import IR
private import ConstantAnalysis
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
exists(ConditionalBranchInstruction instr, int conditionValue |
instr = block.getLastInstruction() and
conditionValue = getValue(getConstantValue(instr.getCondition())) and
if conditionValue = 0 then
kind instanceof TrueEdge
else
kind instanceof FalseEdge
)
}
IRBlock getAFeasiblePredecessor(IRBlock successor) {
exists(EdgeKind kind |
result.getSuccessor(kind) = successor and
not isInfeasibleEdge(result, kind)
)
}
predicate isBlockReachable(IRBlock block) {
getAFeasiblePredecessor*(block) = block.getFunctionIR().getEntryBlock()
}
predicate isInstructionReachable(Instruction instr) {
isBlockReachable(instr.getBlock())
}
class ReachableBlock extends IRBlock {
ReachableBlock() {
isBlockReachable(this)
}
}
class ReachableInstruction extends Instruction {
ReachableInstruction() {
this.getBlock() instanceof ReachableBlock
}
}
module Graph {
predicate isEntryBlock(ReachableBlock block) {
block = block.getFunctionIR().getEntryBlock()
}
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
succ = pred.getASuccessor()
}
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
import semmle.code.cpp.ir.implementation.aliased_ssa.constant.ConstantAnalysis as ConstantAnalysis

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
import Cached
private import Cached
class IRBlock extends TIRBlock {
final string toString() {

View File

@@ -102,7 +102,8 @@ module InstructionSanity {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
not instr instanceof PhiInstruction and
not instr instanceof UnreachedInstruction
}
/**
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
final predicate isResultModeled() {
// Register results are always in SSA form.
not hasMemoryResult() or
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
not (getAUse() instanceof UnmodeledUseOperand)
Construction::hasModeledMemoryResult(this)
}
/**
@@ -1469,6 +1469,17 @@ class ChiInstruction extends Instruction {
}
}
/**
* An instruction representing unreachable code. Inserted in place of the original target
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
* infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() {
opcode instanceof Opcode::Unreached
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -0,0 +1,21 @@
private import DominanceInternal
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
blockImmediatelyDominates+(dominator, block)
}
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
blockStrictlyDominates(dominator, block) or dominator = block
}
pragma[noinline]
Graph::Block getDominanceFrontier(Graph::Block dominator) {
exists(Graph::Block pred |
Graph::blockSuccessor(pred, result) and
blockDominates(dominator, pred) and
not blockStrictlyDominates(dominator, result)
)
}

View File

@@ -0,0 +1,7 @@
private import ReachableBlock as Reachability
private module ReachabilityGraph = Reachability::Graph;
module Graph {
import Reachability::Graph
class Block = Reachability::ReachableBlock;
}

View File

@@ -0,0 +1,20 @@
private import ReachableBlockInternal
private import ReachableBlock
import IR
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
override string getBlockProperty(IRBlock block, string key) {
(
not block instanceof ReachableBlock and
key = "Unreachable" and
result = "true"
) or
(
exists(EdgeKind kind |
isInfeasibleEdge(block, kind) and
key = "Infeasible(" + kind.toString() + ")" and
result = "true"
)
)
}
}

View File

@@ -0,0 +1,52 @@
private import ReachableBlockInternal
private import semmle.code.cpp.ir.internal.IntegerConstant
private import IR
private import ConstantAnalysis
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
exists(ConditionalBranchInstruction instr, int conditionValue |
instr = block.getLastInstruction() and
conditionValue = getValue(getConstantValue(instr.getCondition())) and
if conditionValue = 0 then
kind instanceof TrueEdge
else
kind instanceof FalseEdge
)
}
IRBlock getAFeasiblePredecessor(IRBlock successor) {
exists(EdgeKind kind |
result.getSuccessor(kind) = successor and
not isInfeasibleEdge(result, kind)
)
}
predicate isBlockReachable(IRBlock block) {
getAFeasiblePredecessor*(block) = block.getFunctionIR().getEntryBlock()
}
predicate isInstructionReachable(Instruction instr) {
isBlockReachable(instr.getBlock())
}
class ReachableBlock extends IRBlock {
ReachableBlock() {
isBlockReachable(this)
}
}
class ReachableInstruction extends Instruction {
ReachableInstruction() {
this.getBlock() instanceof ReachableBlock
}
}
module Graph {
predicate isEntryBlock(ReachableBlock block) {
block = block.getFunctionIR().getEntryBlock()
}
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
succ = pred.getASuccessor()
}
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.implementation.raw.IR as IR
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
import Cached
private import Cached
class IRBlock extends TIRBlock {
final string toString() {

View File

@@ -102,7 +102,8 @@ module InstructionSanity {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction
not instr instanceof PhiInstruction and
not instr instanceof UnreachedInstruction
}
/**
@@ -451,8 +452,7 @@ class Instruction extends Construction::TInstruction {
final predicate isResultModeled() {
// Register results are always in SSA form.
not hasMemoryResult() or
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
not (getAUse() instanceof UnmodeledUseOperand)
Construction::hasModeledMemoryResult(this)
}
/**
@@ -1469,6 +1469,17 @@ class ChiInstruction extends Instruction {
}
}
/**
* An instruction representing unreachable code. Inserted in place of the original target
* instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
* infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() {
opcode instanceof Opcode::Unreached
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -4,23 +4,35 @@ private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
private class OldInstruction = Reachability::ReachableInstruction;
import Cached
cached private module Cached {
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
private IRBlock getNewBlock(OldBlock oldBlock) {
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
cached newtype TInstructionTag =
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
WrappedInstructionTag(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction
} or
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
PhiTag(Alias::VirtualVariable vvar, OldBlock block) {
hasPhiNode(vvar, block)
} or
ChiTag(OldIR::Instruction oldInstruction) {
ChiTag(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
} or
UnreachedTag(OldInstruction oldInstruction, EdgeKind kind) {
// We need an `Unreached` instruction for the destination of any edge whose predecessor
// instruction is reachable, but whose successor block is not. This should occur only for
// infeasible edges.
exists(OldIR::Instruction succInstruction |
succInstruction = oldInstruction.getSuccessor(kind) and
not succInstruction instanceof OldInstruction
)
}
cached class InstructionTagType extends TInstructionTag {
@@ -35,11 +47,11 @@ cached private module Cached {
)
}
cached OldIR::Instruction getOldInstruction(Instruction instr) {
cached OldInstruction getOldInstruction(Instruction instr) {
instr.getTag() = WrappedInstructionTag(result)
}
private Instruction getNewInstruction(OldIR::Instruction instr) {
private Instruction getNewInstruction(OldInstruction instr) {
getOldInstruction(result) = instr
}
@@ -47,21 +59,21 @@ cached private module Cached {
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
* corresponding to `instr` if there is no `Chi` node.
*/
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
private Instruction getNewFinalInstruction(OldInstruction instr) {
result = getChiInstruction(instr)
or
not exists(getChiInstruction(instr)) and
result = getNewInstruction(instr)
}
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
private PhiInstruction getPhiInstruction(Function func, OldBlock oldBlock,
Alias::VirtualVariable vvar) {
result.getFunction() = func and
result.getAST() = oldBlock.getFirstInstruction().getAST() and
result.getTag() = PhiTag(vvar, oldBlock)
}
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
private ChiInstruction getChiInstruction (OldInstruction instr) {
hasChiNode(_, instr) and
result.getTag() = ChiTag(instr)
}
@@ -91,8 +103,8 @@ cached private module Cached {
}
private predicate hasInstruction(Function func, Opcode opcode, Locatable ast,
InstructionTag tag, Type resultType, boolean isGLValue) {
exists(OldIR::Instruction instr |
InstructionTag tag, Type resultType, boolean isGLValue) {
exists(OldInstruction instr |
instr.getFunction() = func and
instr.getOpcode() = opcode and
instr.getAST() = ast and
@@ -103,7 +115,7 @@ cached private module Cached {
else
isGLValue = false
) or
exists(OldIR::IRBlock block, Alias::VirtualVariable vvar |
exists(OldBlock block, Alias::VirtualVariable vvar |
hasPhiNode(vvar, block) and
block.getFunction() = func and
opcode instanceof Opcode::Phi and
@@ -112,7 +124,7 @@ cached private module Cached {
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
exists(OldInstruction instr, Alias::VirtualVariable vvar |
hasChiNode(vvar, instr) and
instr.getFunction() = func and
opcode instanceof Opcode::Chi and
@@ -120,11 +132,19 @@ cached private module Cached {
tag = ChiTag(instr) and
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldInstruction oldInstruction, EdgeKind kind |
oldInstruction.getFunction() = func and
tag = UnreachedTag(oldInstruction, kind) and
opcode instanceof Opcode::Unreached and
ast = oldInstruction.getSuccessor(kind).getAST() and
resultType instanceof VoidType and
isGLValue = false
)
}
cached predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag,
Type type) {
Type type) {
exists(OldIR::IRTempVariable var |
var.getFunction() = func and
var.getAST() = ast and
@@ -135,19 +155,20 @@ cached private module Cached {
cached predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
instruction instanceof PhiInstruction // Phis always have modeled results
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
if oldOperand instanceof OldIR::MemoryOperand then (
(
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, int defIndex |
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldBlock defBlock, int defRank, int defIndex |
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
@@ -164,7 +185,7 @@ cached private module Cached {
) or
// Connect any definitions that are not being modeled in SSA to the
// `UnmodeledUse` instruction.
exists(OldIR::Instruction oldDefinition |
exists(OldInstruction oldDefinition |
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
oldDefinition = oldOperand.getDefinitionInstruction() and
@@ -189,8 +210,8 @@ cached private module Cached {
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock) {
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
hasPhiNode(vvar, phiBlock) and
predBlock = phiBlock.getAPredecessor() and
instr.getTag() = PhiTag(vvar, phiBlock) and
@@ -205,8 +226,8 @@ cached private module Cached {
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
int defRank, int defIndex, OldBlock useBlock, int useRank |
ChiTag(oldInstr) = chiInstr.getTag() and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
@@ -220,7 +241,7 @@ cached private module Cached {
}
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldIR::IRBlock oldBlock |
exists(OldBlock oldBlock |
instr.getTag() = PhiTag(_, oldBlock) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
@@ -247,10 +268,11 @@ cached private module Cached {
else (
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
or
exists(OldIR::Instruction oldInstruction |
exists(OldInstruction oldInstruction |
instruction = getChiInstruction(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
) or
result.getTag() = UnreachedTag(getOldInstruction(instruction), kind)
)
}
@@ -310,29 +332,29 @@ cached private module Cached {
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
OldInstruction instr, OldBlock block, int index) {
block.getInstruction(index) = instr and
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
}
private predicate hasDefinition(Alias::VirtualVariable vvar, OldIR::IRBlock block, int index) {
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
(
hasPhiNode(vvar, block) and
index = -1
) or
exists(Alias::MemoryAccess access, OldIR::Instruction def |
exists(Alias::MemoryAccess access, OldInstruction def |
access = Alias::getResultMemoryAccess(def) and
block.getInstruction(index) = def and
vvar = access.getVirtualVariable()
)
}
private predicate defUseRank(Alias::VirtualVariable vvar, OldIR::IRBlock block, int rankIndex, int index) {
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, _, block, j))
}
private predicate hasUse(Alias::VirtualVariable vvar,
OldIR::Instruction use, OldIR::IRBlock block, int index) {
private predicate hasUse(Alias::VirtualVariable vvar, OldInstruction use, OldBlock block,
int index) {
exists(Alias::MemoryAccess access |
(
access = Alias::getOperandMemoryAccess(use.getAnOperand())
@@ -349,7 +371,7 @@ cached private module Cached {
)
}
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
exists (int index | hasUse(vvar, _, block, index) |
not exists (int j | ssa_variableUpdate(vvar, _, block, j) | j < index)
) or
@@ -357,7 +379,7 @@ cached private module Cached {
}
pragma[noinline]
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
variableLiveOnEntryToBlock(vvar, block.getASuccessor())
}
@@ -367,18 +389,18 @@ cached private module Cached {
* end of the block, even if the definition is the last instruction in the
* block.
*/
private int exitRank(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
}
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar,
OldIR::IRBlock block, int rankIndex, int instructionIndex) {
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
int instructionIndex) {
hasDefinition(vvar, block, instructionIndex) and
defUseRank(vvar, block, rankIndex, instructionIndex)
}
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldIR::IRBlock block,
int rankIndex, OldIR::Instruction use) {
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
OldInstruction use) {
exists(int index |
hasUse(vvar, use, block, index) and
defUseRank(vvar, block, rankIndex, index)
@@ -389,8 +411,8 @@ cached private module Cached {
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
* index `reachesRank` in block `block`.
*/
private predicate definitionReachesRank(Alias::VirtualVariable vvar,
OldIR::IRBlock block, int defRank, int reachesRank) {
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
int reachesRank) {
hasDefinitionAtRank(vvar, block, defRank, _) and
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
(
@@ -410,8 +432,8 @@ cached private module Cached {
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
* block `block`.
*/
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock block) {
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock block) {
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
(
(
@@ -421,7 +443,7 @@ cached private module Cached {
variableLiveOnExitFromBlock(vvar, defBlock) and
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
) or
exists(OldIR::IRBlock idom |
exists(OldBlock idom |
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(vvar, idom, block)
)
@@ -429,24 +451,23 @@ cached private module Cached {
}
pragma[noinline]
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar,
OldIR::IRBlock idom, OldIR::IRBlock block) {
idom.immediatelyDominates(block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
OldBlock block) {
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
variableLiveOnExitFromBlock(vvar, block) and
not hasDefinition(vvar, block, _)
}
private predicate definitionReachesUseWithinBlock(
Alias::VirtualVariable vvar, OldIR::IRBlock defBlock, int defRank,
OldIR::IRBlock useBlock, int useRank) {
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasUseAtRank(vvar, useBlock, useRank, _) and
definitionReachesRank(vvar, defBlock, defRank, useRank)
}
private predicate definitionReachesUse(Alias::VirtualVariable vvar,
OldIR::IRBlock defBlock, int defRank, OldIR::IRBlock useBlock, int useRank) {
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(vvar, useBlock, useRank, _) and
(
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
@@ -459,24 +480,21 @@ cached private module Cached {
)
}
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar,
OldIR::IRBlock phiBlock) {
exists(OldIR::IRBlock defBlock |
phiBlock = defBlock.dominanceFrontier() and
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
hasDefinition(vvar, defBlock, _) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
variableLiveOnEntryToBlock(vvar, phiBlock)
)
}
private predicate hasPhiNode(Alias::VirtualVariable vvar,
OldIR::IRBlock phiBlock) {
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
}
private predicate hasChiNode(Alias::VirtualVariable vvar,
OldIR::Instruction def) {
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
@@ -492,13 +510,17 @@ cached private module CachedForDebugging {
}
cached string getInstructionUniqueId(Instruction instr) {
exists(OldIR::Instruction oldInstr |
exists(OldInstruction oldInstr |
oldInstr = getOldInstruction(instr) and
result = "NonSSA: " + oldInstr.getUniqueId()
) or
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock |
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
instr.getTag() = PhiTag(vvar, phiBlock) and
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
) or
exists(OldInstruction oldInstr, EdgeKind kind |
instr.getTag() = UnreachedTag(oldInstr, kind) and
result = "Unreached(" + oldInstr.getUniqueId() + ":" + kind.toString() + ")"
)
}

View File

@@ -1,3 +1,5 @@
import semmle.code.cpp.ir.implementation.raw.IR as OldIR
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
import SimpleSSA as Alias

View File

@@ -0,0 +1,21 @@
private import DominanceInternal
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
blockImmediatelyDominates+(dominator, block)
}
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
blockStrictlyDominates(dominator, block) or dominator = block
}
pragma[noinline]
Graph::Block getDominanceFrontier(Graph::Block dominator) {
exists(Graph::Block pred |
Graph::blockSuccessor(pred, result) and
blockDominates(dominator, pred) and
not blockStrictlyDominates(dominator, result)
)
}

View File

@@ -0,0 +1,7 @@
private import ReachableBlock as Reachability
private module ReachabilityGraph = Reachability::Graph;
module Graph {
import Reachability::Graph
class Block = Reachability::ReachableBlock;
}

View File

@@ -0,0 +1,20 @@
private import ReachableBlockInternal
private import ReachableBlock
import IR
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
override string getBlockProperty(IRBlock block, string key) {
(
not block instanceof ReachableBlock and
key = "Unreachable" and
result = "true"
) or
(
exists(EdgeKind kind |
isInfeasibleEdge(block, kind) and
key = "Infeasible(" + kind.toString() + ")" and
result = "true"
)
)
}
}

View File

@@ -0,0 +1,52 @@
private import ReachableBlockInternal
private import semmle.code.cpp.ir.internal.IntegerConstant
private import IR
private import ConstantAnalysis
predicate isInfeasibleEdge(IRBlock block, EdgeKind kind) {
exists(ConditionalBranchInstruction instr, int conditionValue |
instr = block.getLastInstruction() and
conditionValue = getValue(getConstantValue(instr.getCondition())) and
if conditionValue = 0 then
kind instanceof TrueEdge
else
kind instanceof FalseEdge
)
}
IRBlock getAFeasiblePredecessor(IRBlock successor) {
exists(EdgeKind kind |
result.getSuccessor(kind) = successor and
not isInfeasibleEdge(result, kind)
)
}
predicate isBlockReachable(IRBlock block) {
getAFeasiblePredecessor*(block) = block.getFunctionIR().getEntryBlock()
}
predicate isInstructionReachable(Instruction instr) {
isBlockReachable(instr.getBlock())
}
class ReachableBlock extends IRBlock {
ReachableBlock() {
isBlockReachable(this)
}
}
class ReachableInstruction extends Instruction {
ReachableInstruction() {
this.getBlock() instanceof ReachableBlock
}
}
module Graph {
predicate isEntryBlock(ReachableBlock block) {
block = block.getFunctionIR().getEntryBlock()
}
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
succ = pred.getASuccessor()
}
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis

View File

@@ -30,3 +30,29 @@ int ReturnConstantPhiLoop(int x) {
}
return y;
}
int UnreachableViaGoto() {
goto skip;
return 1;
skip:
return 0;
}
int UnreachableIf(bool b) {
if (b) {
if (false) {
return 1;
}
else {
return 0;
}
}
else {
if (true) {
return 0;
}
else {
return 1;
}
}
}

View File

@@ -1,3 +1,5 @@
| constant_func.cpp:1:5:1:18 | IR: ReturnConstant | 7 |
| constant_func.cpp:5:5:5:21 | IR: ReturnConstantPhi | 7 |
| constant_func.cpp:25:5:25:25 | IR: ReturnConstantPhiLoop | 7 |
| constant_func.cpp:34:5:34:22 | IR: UnreachableViaGoto | 0 |
| constant_func.cpp:41:5:41:17 | IR: UnreachableIf | 0 |

View File

@@ -1171,16 +1171,11 @@ ir.cpp:
# 265| mu0_2(unknown) = UnmodeledDefinition :
# 266| r0_3(glval<int>) = VariableAddress[j] :
# 266| m0_4(int) = Uninitialized[j] : r0_3
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 265| Block 1
# 265| v1_0(void) = ReturnVoid :
# 265| v1_1(void) = UnmodeledUse : mu*
# 265| v1_2(void) = ExitFunction :
# 268| Block 2
# 268| v2_0(void) = NoOp :
#-----| Goto -> Block 2
# 268| Block 1
# 268| v1_0(void) = NoOp :
#-----| Goto -> Block 1
# 272| For_Init() -> void
# 272| Block 0
@@ -1190,16 +1185,11 @@ ir.cpp:
# 273| r0_3(glval<int>) = VariableAddress[i] :
# 273| r0_4(int) = Constant[0] :
# 273| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 272| Block 1
# 272| v1_0(void) = ReturnVoid :
# 272| v1_1(void) = UnmodeledUse : mu*
# 272| v1_2(void) = ExitFunction :
# 274| Block 2
# 274| v2_0(void) = NoOp :
#-----| Goto -> Block 2
# 274| Block 1
# 274| v1_0(void) = NoOp :
#-----| Goto -> Block 1
# 278| For_Condition() -> void
# 278| Block 0
@@ -1238,22 +1228,17 @@ ir.cpp:
# 286| r0_3(glval<int>) = VariableAddress[i] :
# 286| r0_4(int) = Constant[0] :
# 286| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 285| Block 1
# 285| v1_0(void) = ReturnVoid :
# 285| v1_1(void) = UnmodeledUse : mu*
# 285| v1_2(void) = ExitFunction :
# 288| Block 2
# 288| m2_0(int) = Phi : from 0:m0_5, from 2:m2_6
# 288| v2_1(void) = NoOp :
# 287| r2_2(int) = Constant[1] :
# 287| r2_3(glval<int>) = VariableAddress[i] :
# 287| r2_4(int) = Load : r2_3, m2_0
# 287| r2_5(int) = Add : r2_4, r2_2
# 287| m2_6(int) = Store : r2_3, r2_5
#-----| Goto -> Block 2
# 288| Block 1
# 288| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6
# 288| v1_1(void) = NoOp :
# 287| r1_2(int) = Constant[1] :
# 287| r1_3(glval<int>) = VariableAddress[i] :
# 287| r1_4(int) = Load : r1_3, m1_0
# 287| r1_5(int) = Add : r1_4, r1_2
# 287| m1_6(int) = Store : r1_3, r1_5
#-----| Goto -> Block 1
# 292| For_InitCondition() -> void
# 292| Block 0
@@ -1292,22 +1277,17 @@ ir.cpp:
# 299| r0_3(glval<int>) = VariableAddress[i] :
# 299| r0_4(int) = Constant[0] :
# 299| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 298| Block 1
# 298| v1_0(void) = ReturnVoid :
# 298| v1_1(void) = UnmodeledUse : mu*
# 298| v1_2(void) = ExitFunction :
# 300| Block 2
# 300| m2_0(int) = Phi : from 0:m0_5, from 2:m2_6
# 300| v2_1(void) = NoOp :
# 299| r2_2(int) = Constant[1] :
# 299| r2_3(glval<int>) = VariableAddress[i] :
# 299| r2_4(int) = Load : r2_3, m2_0
# 299| r2_5(int) = Add : r2_4, r2_2
# 299| m2_6(int) = Store : r2_3, r2_5
#-----| Goto -> Block 2
# 300| Block 1
# 300| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6
# 300| v1_1(void) = NoOp :
# 299| r1_2(int) = Constant[1] :
# 299| r1_3(glval<int>) = VariableAddress[i] :
# 299| r1_4(int) = Load : r1_3, m1_0
# 299| r1_5(int) = Add : r1_4, r1_2
# 299| m1_6(int) = Store : r1_3, r1_5
#-----| Goto -> Block 1
# 304| For_ConditionUpdate() -> void
# 304| Block 0
@@ -1725,74 +1705,62 @@ ir.cpp:
# 386| r0_7(glval<int>) = VariableAddress[x] :
# 386| r0_8(int) = Load : r0_7, m0_4
# 386| v0_9(void) = Switch : r0_8
#-----| Case[-1] -> Block 2
#-----| Case[1] -> Block 3
#-----| Case[2] -> Block 4
#-----| Case[3] -> Block 5
#-----| Case[4] -> Block 6
#-----| Default -> Block 7
#-----| Case[-1] -> Block 1
#-----| Case[1] -> Block 2
#-----| Case[2] -> Block 3
#-----| Case[3] -> Block 4
#-----| Case[4] -> Block 5
#-----| Default -> Block 6
# 387| Block 1
# 387| r1_0(int) = Constant[1234] :
# 387| r1_1(glval<int>) = VariableAddress[y] :
# 387| m1_2(int) = Store : r1_1, r1_0
#-----| Goto -> Block 2
# 389| Block 1
# 389| v1_0(void) = NoOp :
# 390| r1_1(int) = Constant[-1] :
# 390| r1_2(glval<int>) = VariableAddress[y] :
# 390| m1_3(int) = Store : r1_2, r1_1
# 391| v1_4(void) = NoOp :
#-----| Goto -> Block 7
# 389| Block 2
# 389| v2_0(void) = NoOp :
# 390| r2_1(int) = Constant[-1] :
# 390| r2_2(glval<int>) = VariableAddress[y] :
# 390| m2_3(int) = Store : r2_2, r2_1
# 391| v2_4(void) = NoOp :
#-----| Goto -> Block 9
# 393| Block 2
# 393| v2_0(void) = NoOp :
#-----| Goto -> Block 3
# 393| Block 3
# 393| v3_0(void) = NoOp :
#-----| Goto -> Block 4
# 394| Block 3
# 394| v3_0(void) = NoOp :
# 395| r3_1(int) = Constant[1] :
# 395| r3_2(glval<int>) = VariableAddress[y] :
# 395| m3_3(int) = Store : r3_2, r3_1
# 396| v3_4(void) = NoOp :
#-----| Goto -> Block 7
# 394| Block 4
# 394| v4_0(void) = NoOp :
# 395| r4_1(int) = Constant[1] :
# 395| r4_2(glval<int>) = VariableAddress[y] :
# 395| m4_3(int) = Store : r4_2, r4_1
# 396| v4_4(void) = NoOp :
#-----| Goto -> Block 9
# 398| Block 4
# 398| v4_0(void) = NoOp :
# 399| r4_1(int) = Constant[3] :
# 399| r4_2(glval<int>) = VariableAddress[y] :
# 399| m4_3(int) = Store : r4_2, r4_1
#-----| Goto -> Block 5
# 398| Block 5
# 398| v5_0(void) = NoOp :
# 399| r5_1(int) = Constant[3] :
# 399| r5_2(glval<int>) = VariableAddress[y] :
# 399| m5_3(int) = Store : r5_2, r5_1
#-----| Goto -> Block 6
# 400| Block 5
# 400| v5_0(void) = NoOp :
# 401| r5_1(int) = Constant[4] :
# 401| r5_2(glval<int>) = VariableAddress[y] :
# 401| m5_3(int) = Store : r5_2, r5_1
# 402| v5_4(void) = NoOp :
#-----| Goto -> Block 7
# 400| Block 6
# 400| v6_0(void) = NoOp :
# 401| r6_1(int) = Constant[4] :
# 401| r6_2(glval<int>) = VariableAddress[y] :
# 401| m6_3(int) = Store : r6_2, r6_1
# 402| v6_4(void) = NoOp :
#-----| Goto -> Block 9
# 404| Block 6
# 404| v6_0(void) = NoOp :
# 405| r6_1(int) = Constant[0] :
# 405| r6_2(glval<int>) = VariableAddress[y] :
# 405| m6_3(int) = Store : r6_2, r6_1
# 406| v6_4(void) = NoOp :
#-----| Goto -> Block 7
# 404| Block 7
# 404| v7_0(void) = NoOp :
# 405| r7_1(int) = Constant[0] :
# 405| r7_2(glval<int>) = VariableAddress[y] :
# 405| m7_3(int) = Store : r7_2, r7_1
# 406| v7_4(void) = NoOp :
#-----| Goto -> Block 9
# 408| Block 8
# 408| r8_0(int) = Constant[5678] :
# 408| r8_1(glval<int>) = VariableAddress[y] :
# 408| m8_2(int) = Store : r8_1, r8_0
#-----| Goto -> Block 9
# 409| Block 9
# 409| v9_0(void) = NoOp :
# 410| v9_1(void) = NoOp :
# 384| v9_2(void) = ReturnVoid :
# 384| v9_3(void) = UnmodeledUse : mu*
# 384| v9_4(void) = ExitFunction :
# 409| Block 7
# 409| v7_0(void) = NoOp :
# 410| v7_1(void) = NoOp :
# 384| v7_2(void) = ReturnVoid :
# 384| v7_3(void) = UnmodeledUse : mu*
# 384| v7_4(void) = ExitFunction :
# 422| ReturnStruct(Point) -> Point
# 422| Block 0
@@ -2592,7 +2560,7 @@ ir.cpp:
# 560| m0_1(unknown) = AliasedDefinition :
# 560| mu0_2(unknown) = UnmodeledDefinition :
# 560| r0_3(glval<E>) = VariableAddress[e] :
# 560| m0_4(E) = InitializeParameter[e] : r0_3
# 560| mu0_4(E) = InitializeParameter[e] : r0_3
# 561| r0_5(glval<E>) = VariableAddress[e] :
# 561| r0_6(E) = Load : r0_5, mu0_2
# 561| r0_7(int) = Convert : r0_6
@@ -4153,32 +4121,24 @@ ir.cpp:
# 906| r0_8(glval<int>) = VariableAddress[b] :
# 906| r0_9(bool) = Constant[1] :
# 906| v0_10(void) = ConditionalBranch : r0_9
#-----| False -> Block 3
#-----| True -> Block 2
#-----| False -> Block 2
#-----| True -> Block 1
# 906| Block 1
# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3
# 906| r1_1(glval<int>) = VariableAddress[#temp906:11] :
# 906| r1_2(int) = Load : r1_1, m1_0
# 906| m1_3(int) = Store : r0_8, r1_2
# 907| v1_4(void) = NoOp :
# 904| v1_5(void) = ReturnVoid :
# 904| v1_6(void) = UnmodeledUse : mu*
# 904| v1_7(void) = ExitFunction :
# 906| r1_0(glval<int>) = VariableAddress[x] :
# 906| r1_1(int) = Load : r1_0, m0_4
# 906| r1_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m1_3(int) = Store : r1_2, r1_1
# 906| r1_4(glval<int>) = VariableAddress[#temp906:11] :
# 906| r1_5(int) = Load : r1_4, m1_3
# 906| m1_6(int) = Store : r0_8, r1_5
# 907| v1_7(void) = NoOp :
# 904| v1_8(void) = ReturnVoid :
# 904| v1_9(void) = UnmodeledUse : mu*
# 904| v1_10(void) = ExitFunction :
# 906| Block 2
# 906| r2_0(glval<int>) = VariableAddress[x] :
# 906| r2_1(int) = Load : r2_0, m0_4
# 906| r2_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m2_3(int) = Store : r2_2, r2_1
#-----| Goto -> Block 1
# 906| Block 3
# 906| r3_0(glval<int>) = VariableAddress[x] :
# 906| r3_1(int) = Load : r3_0, m0_4
# 906| r3_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m3_3(int) = Store : r3_2, r3_1
#-----| Goto -> Block 1
# 906| v2_0(void) = Unreached :
# 940| OperatorNew() -> void
# 940| Block 0
@@ -4629,20 +4589,10 @@ ir.cpp:
# 1025| r0_5(glval<int>) = VariableAddress[#return] :
# 1025| r0_6(int) = Constant[0] :
# 1025| m0_7(int) = Store : r0_5, r0_6
#-----| Goto -> Block 1
# 1021| Block 1
# 1021| m1_0(int) = Phi : from 0:m0_7, from 2:m2_2
# 1021| r1_1(glval<int>) = VariableAddress[#return] :
# 1021| v1_2(void) = ReturnValue : r1_1, m1_0
# 1021| v1_3(void) = UnmodeledUse : mu*
# 1021| v1_4(void) = ExitFunction :
# 1023| Block 2
# 1023| r2_0(glval<int>) = VariableAddress[#return] :
# 1023| r2_1(int) = Constant[1] :
# 1023| m2_2(int) = Store : r2_0, r2_1
#-----| Goto -> Block 1
# 1021| r0_8(glval<int>) = VariableAddress[#return] :
# 1021| v0_9(void) = ReturnValue : r0_8, m0_7
# 1021| v0_10(void) = UnmodeledUse : mu*
# 1021| v0_11(void) = ExitFunction :
# 1028| UnreachableIf(bool) -> int
# 1028| Block 0
@@ -4654,11 +4604,11 @@ ir.cpp:
# 1029| r0_5(glval<bool>) = VariableAddress[b] :
# 1029| r0_6(bool) = Load : r0_5, m0_4
# 1029| v0_7(void) = ConditionalBranch : r0_6
#-----| False -> Block 5
#-----| False -> Block 4
#-----| True -> Block 2
# 1028| Block 1
# 1028| m1_0(int) = Phi : from 3:m3_2, from 4:m4_2, from 6:m6_2, from 7:m7_2
# 1028| m1_0(int) = Phi : from 3:m3_2, from 5:m5_2
# 1028| r1_1(glval<int>) = VariableAddress[#return] :
# 1028| v1_2(void) = ReturnValue : r1_1, m1_0
# 1028| v1_3(void) = UnmodeledUse : mu*
@@ -4667,35 +4617,29 @@ ir.cpp:
# 1030| Block 2
# 1030| r2_0(bool) = Constant[0] :
# 1030| v2_1(void) = ConditionalBranch : r2_0
#-----| False -> Block 4
#-----| True -> Block 3
#-----| False -> Block 3
#-----| True -> Block 7
# 1031| Block 3
# 1031| r3_0(glval<int>) = VariableAddress[#return] :
# 1031| r3_1(int) = Constant[1] :
# 1031| m3_2(int) = Store : r3_0, r3_1
# 1034| Block 3
# 1034| r3_0(glval<int>) = VariableAddress[#return] :
# 1034| r3_1(int) = Constant[0] :
# 1034| m3_2(int) = Store : r3_0, r3_1
#-----| Goto -> Block 1
# 1034| Block 4
# 1034| r4_0(glval<int>) = VariableAddress[#return] :
# 1034| r4_1(int) = Constant[0] :
# 1034| m4_2(int) = Store : r4_0, r4_1
# 1038| Block 4
# 1038| r4_0(bool) = Constant[1] :
# 1038| v4_1(void) = ConditionalBranch : r4_0
#-----| False -> Block 6
#-----| True -> Block 5
# 1039| Block 5
# 1039| r5_0(glval<int>) = VariableAddress[#return] :
# 1039| r5_1(int) = Constant[0] :
# 1039| m5_2(int) = Store : r5_0, r5_1
#-----| Goto -> Block 1
# 1038| Block 5
# 1038| r5_0(bool) = Constant[1] :
# 1038| v5_1(void) = ConditionalBranch : r5_0
#-----| False -> Block 7
#-----| True -> Block 6
# 1042| Block 6
# 1042| v6_0(void) = Unreached :
# 1039| Block 6
# 1039| r6_0(glval<int>) = VariableAddress[#return] :
# 1039| r6_1(int) = Constant[0] :
# 1039| m6_2(int) = Store : r6_0, r6_1
#-----| Goto -> Block 1
# 1042| Block 7
# 1042| r7_0(glval<int>) = VariableAddress[#return] :
# 1042| r7_1(int) = Constant[1] :
# 1042| m7_2(int) = Store : r7_0, r7_1
#-----| Goto -> Block 1
# 1031| Block 7
# 1031| v7_0(void) = Unreached :

View File

@@ -22,7 +22,7 @@
| IR: Conditional | 4 |
| IR: Conditional_LValue | 4 |
| IR: Conditional_Void | 4 |
| IR: ConstantConditions | 4 |
| IR: ConstantConditions | 3 |
| IR: Constants | 1 |
| IR: Continue | 6 |
| IR: DeclareObject | 1 |
@@ -45,12 +45,12 @@
| IR: For_ConditionUpdate | 4 |
| IR: For_Continue_NoUpdate | 6 |
| IR: For_Continue_Update | 6 |
| IR: For_Empty | 3 |
| IR: For_Init | 3 |
| IR: For_Empty | 2 |
| IR: For_Init | 2 |
| IR: For_InitCondition | 4 |
| IR: For_InitConditionUpdate | 4 |
| IR: For_InitUpdate | 3 |
| IR: For_Update | 3 |
| IR: For_InitUpdate | 2 |
| IR: For_Update | 2 |
| IR: Func | 1 |
| IR: FuncPtrConversions | 1 |
| IR: FunctionReferences | 1 |
@@ -88,13 +88,13 @@
| IR: StaticMemberFunction | 1 |
| IR: String | 1 |
| IR: StringLiteral | 1 |
| IR: Switch | 10 |
| IR: Switch | 8 |
| IR: TakeReference | 1 |
| IR: TryCatch | 15 |
| IR: UninitializedVariables | 1 |
| IR: UnionInit | 1 |
| IR: UnreachableIf | 8 |
| IR: UnreachableViaGoto | 3 |
| IR: UnreachableViaGoto | 1 |
| IR: VarArgUsage | 1 |
| IR: VarArgs | 1 |
| IR: VirtualMemberFunction | 1 |

View File

@@ -1164,16 +1164,11 @@ ir.cpp:
# 265| mu0_2(unknown) = UnmodeledDefinition :
# 266| r0_3(glval<int>) = VariableAddress[j] :
# 266| m0_4(int) = Uninitialized[j] : r0_3
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 265| Block 1
# 265| v1_0(void) = ReturnVoid :
# 265| v1_1(void) = UnmodeledUse : mu*
# 265| v1_2(void) = ExitFunction :
# 268| Block 2
# 268| v2_0(void) = NoOp :
#-----| Goto -> Block 2
# 268| Block 1
# 268| v1_0(void) = NoOp :
#-----| Goto -> Block 1
# 272| For_Init() -> void
# 272| Block 0
@@ -1183,16 +1178,11 @@ ir.cpp:
# 273| r0_3(glval<int>) = VariableAddress[i] :
# 273| r0_4(int) = Constant[0] :
# 273| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 272| Block 1
# 272| v1_0(void) = ReturnVoid :
# 272| v1_1(void) = UnmodeledUse : mu*
# 272| v1_2(void) = ExitFunction :
# 274| Block 2
# 274| v2_0(void) = NoOp :
#-----| Goto -> Block 2
# 274| Block 1
# 274| v1_0(void) = NoOp :
#-----| Goto -> Block 1
# 278| For_Condition() -> void
# 278| Block 0
@@ -1231,22 +1221,17 @@ ir.cpp:
# 286| r0_3(glval<int>) = VariableAddress[i] :
# 286| r0_4(int) = Constant[0] :
# 286| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 285| Block 1
# 285| v1_0(void) = ReturnVoid :
# 285| v1_1(void) = UnmodeledUse : mu*
# 285| v1_2(void) = ExitFunction :
# 288| Block 2
# 288| m2_0(int) = Phi : from 0:m0_5, from 2:m2_6
# 288| v2_1(void) = NoOp :
# 287| r2_2(int) = Constant[1] :
# 287| r2_3(glval<int>) = VariableAddress[i] :
# 287| r2_4(int) = Load : r2_3, m2_0
# 287| r2_5(int) = Add : r2_4, r2_2
# 287| m2_6(int) = Store : r2_3, r2_5
#-----| Goto -> Block 2
# 288| Block 1
# 288| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6
# 288| v1_1(void) = NoOp :
# 287| r1_2(int) = Constant[1] :
# 287| r1_3(glval<int>) = VariableAddress[i] :
# 287| r1_4(int) = Load : r1_3, m1_0
# 287| r1_5(int) = Add : r1_4, r1_2
# 287| m1_6(int) = Store : r1_3, r1_5
#-----| Goto -> Block 1
# 292| For_InitCondition() -> void
# 292| Block 0
@@ -1285,22 +1270,17 @@ ir.cpp:
# 299| r0_3(glval<int>) = VariableAddress[i] :
# 299| r0_4(int) = Constant[0] :
# 299| m0_5(int) = Store : r0_3, r0_4
#-----| Goto -> Block 2
#-----| Goto -> Block 1
# 298| Block 1
# 298| v1_0(void) = ReturnVoid :
# 298| v1_1(void) = UnmodeledUse : mu*
# 298| v1_2(void) = ExitFunction :
# 300| Block 2
# 300| m2_0(int) = Phi : from 0:m0_5, from 2:m2_6
# 300| v2_1(void) = NoOp :
# 299| r2_2(int) = Constant[1] :
# 299| r2_3(glval<int>) = VariableAddress[i] :
# 299| r2_4(int) = Load : r2_3, m2_0
# 299| r2_5(int) = Add : r2_4, r2_2
# 299| m2_6(int) = Store : r2_3, r2_5
#-----| Goto -> Block 2
# 300| Block 1
# 300| m1_0(int) = Phi : from 0:m0_5, from 1:m1_6
# 300| v1_1(void) = NoOp :
# 299| r1_2(int) = Constant[1] :
# 299| r1_3(glval<int>) = VariableAddress[i] :
# 299| r1_4(int) = Load : r1_3, m1_0
# 299| r1_5(int) = Add : r1_4, r1_2
# 299| m1_6(int) = Store : r1_3, r1_5
#-----| Goto -> Block 1
# 304| For_ConditionUpdate() -> void
# 304| Block 0
@@ -1713,74 +1693,62 @@ ir.cpp:
# 386| r0_7(glval<int>) = VariableAddress[x] :
# 386| r0_8(int) = Load : r0_7, m0_4
# 386| v0_9(void) = Switch : r0_8
#-----| Case[-1] -> Block 2
#-----| Case[1] -> Block 3
#-----| Case[2] -> Block 4
#-----| Case[3] -> Block 5
#-----| Case[4] -> Block 6
#-----| Default -> Block 7
#-----| Case[-1] -> Block 1
#-----| Case[1] -> Block 2
#-----| Case[2] -> Block 3
#-----| Case[3] -> Block 4
#-----| Case[4] -> Block 5
#-----| Default -> Block 6
# 387| Block 1
# 387| r1_0(int) = Constant[1234] :
# 387| r1_1(glval<int>) = VariableAddress[y] :
# 387| m1_2(int) = Store : r1_1, r1_0
#-----| Goto -> Block 2
# 389| Block 1
# 389| v1_0(void) = NoOp :
# 390| r1_1(int) = Constant[-1] :
# 390| r1_2(glval<int>) = VariableAddress[y] :
# 390| m1_3(int) = Store : r1_2, r1_1
# 391| v1_4(void) = NoOp :
#-----| Goto -> Block 7
# 389| Block 2
# 389| v2_0(void) = NoOp :
# 390| r2_1(int) = Constant[-1] :
# 390| r2_2(glval<int>) = VariableAddress[y] :
# 390| m2_3(int) = Store : r2_2, r2_1
# 391| v2_4(void) = NoOp :
#-----| Goto -> Block 9
# 393| Block 2
# 393| v2_0(void) = NoOp :
#-----| Goto -> Block 3
# 393| Block 3
# 393| v3_0(void) = NoOp :
#-----| Goto -> Block 4
# 394| Block 3
# 394| v3_0(void) = NoOp :
# 395| r3_1(int) = Constant[1] :
# 395| r3_2(glval<int>) = VariableAddress[y] :
# 395| m3_3(int) = Store : r3_2, r3_1
# 396| v3_4(void) = NoOp :
#-----| Goto -> Block 7
# 394| Block 4
# 394| v4_0(void) = NoOp :
# 395| r4_1(int) = Constant[1] :
# 395| r4_2(glval<int>) = VariableAddress[y] :
# 395| m4_3(int) = Store : r4_2, r4_1
# 396| v4_4(void) = NoOp :
#-----| Goto -> Block 9
# 398| Block 4
# 398| v4_0(void) = NoOp :
# 399| r4_1(int) = Constant[3] :
# 399| r4_2(glval<int>) = VariableAddress[y] :
# 399| m4_3(int) = Store : r4_2, r4_1
#-----| Goto -> Block 5
# 398| Block 5
# 398| v5_0(void) = NoOp :
# 399| r5_1(int) = Constant[3] :
# 399| r5_2(glval<int>) = VariableAddress[y] :
# 399| m5_3(int) = Store : r5_2, r5_1
#-----| Goto -> Block 6
# 400| Block 5
# 400| v5_0(void) = NoOp :
# 401| r5_1(int) = Constant[4] :
# 401| r5_2(glval<int>) = VariableAddress[y] :
# 401| m5_3(int) = Store : r5_2, r5_1
# 402| v5_4(void) = NoOp :
#-----| Goto -> Block 7
# 400| Block 6
# 400| v6_0(void) = NoOp :
# 401| r6_1(int) = Constant[4] :
# 401| r6_2(glval<int>) = VariableAddress[y] :
# 401| m6_3(int) = Store : r6_2, r6_1
# 402| v6_4(void) = NoOp :
#-----| Goto -> Block 9
# 404| Block 6
# 404| v6_0(void) = NoOp :
# 405| r6_1(int) = Constant[0] :
# 405| r6_2(glval<int>) = VariableAddress[y] :
# 405| m6_3(int) = Store : r6_2, r6_1
# 406| v6_4(void) = NoOp :
#-----| Goto -> Block 7
# 404| Block 7
# 404| v7_0(void) = NoOp :
# 405| r7_1(int) = Constant[0] :
# 405| r7_2(glval<int>) = VariableAddress[y] :
# 405| m7_3(int) = Store : r7_2, r7_1
# 406| v7_4(void) = NoOp :
#-----| Goto -> Block 9
# 408| Block 8
# 408| r8_0(int) = Constant[5678] :
# 408| r8_1(glval<int>) = VariableAddress[y] :
# 408| m8_2(int) = Store : r8_1, r8_0
#-----| Goto -> Block 9
# 409| Block 9
# 409| v9_0(void) = NoOp :
# 410| v9_1(void) = NoOp :
# 384| v9_2(void) = ReturnVoid :
# 384| v9_3(void) = UnmodeledUse : mu*
# 384| v9_4(void) = ExitFunction :
# 409| Block 7
# 409| v7_0(void) = NoOp :
# 410| v7_1(void) = NoOp :
# 384| v7_2(void) = ReturnVoid :
# 384| v7_3(void) = UnmodeledUse : mu*
# 384| v7_4(void) = ExitFunction :
# 422| ReturnStruct(Point) -> Point
# 422| Block 0
@@ -4007,32 +3975,24 @@ ir.cpp:
# 906| r0_8(glval<int>) = VariableAddress[b] :
# 906| r0_9(bool) = Constant[1] :
# 906| v0_10(void) = ConditionalBranch : r0_9
#-----| False -> Block 3
#-----| True -> Block 2
#-----| False -> Block 2
#-----| True -> Block 1
# 906| Block 1
# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3
# 906| r1_1(glval<int>) = VariableAddress[#temp906:11] :
# 906| r1_2(int) = Load : r1_1, m1_0
# 906| m1_3(int) = Store : r0_8, r1_2
# 907| v1_4(void) = NoOp :
# 904| v1_5(void) = ReturnVoid :
# 904| v1_6(void) = UnmodeledUse : mu*
# 904| v1_7(void) = ExitFunction :
# 906| r1_0(glval<int>) = VariableAddress[x] :
# 906| r1_1(int) = Load : r1_0, m0_4
# 906| r1_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m1_3(int) = Store : r1_2, r1_1
# 906| r1_4(glval<int>) = VariableAddress[#temp906:11] :
# 906| r1_5(int) = Load : r1_4, m1_3
# 906| m1_6(int) = Store : r0_8, r1_5
# 907| v1_7(void) = NoOp :
# 904| v1_8(void) = ReturnVoid :
# 904| v1_9(void) = UnmodeledUse : mu*
# 904| v1_10(void) = ExitFunction :
# 906| Block 2
# 906| r2_0(glval<int>) = VariableAddress[x] :
# 906| r2_1(int) = Load : r2_0, m0_4
# 906| r2_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m2_3(int) = Store : r2_2, r2_1
#-----| Goto -> Block 1
# 906| Block 3
# 906| r3_0(glval<int>) = VariableAddress[x] :
# 906| r3_1(int) = Load : r3_0, m0_4
# 906| r3_2(glval<int>) = VariableAddress[#temp906:11] :
# 906| m3_3(int) = Store : r3_2, r3_1
#-----| Goto -> Block 1
# 906| v2_0(void) = Unreached :
# 940| OperatorNew() -> void
# 940| Block 0
@@ -4452,20 +4412,10 @@ ir.cpp:
# 1025| r0_5(glval<int>) = VariableAddress[#return] :
# 1025| r0_6(int) = Constant[0] :
# 1025| m0_7(int) = Store : r0_5, r0_6
#-----| Goto -> Block 1
# 1021| Block 1
# 1021| m1_0(int) = Phi : from 0:m0_7, from 2:m2_2
# 1021| r1_1(glval<int>) = VariableAddress[#return] :
# 1021| v1_2(void) = ReturnValue : r1_1, m1_0
# 1021| v1_3(void) = UnmodeledUse : mu*
# 1021| v1_4(void) = ExitFunction :
# 1023| Block 2
# 1023| r2_0(glval<int>) = VariableAddress[#return] :
# 1023| r2_1(int) = Constant[1] :
# 1023| m2_2(int) = Store : r2_0, r2_1
#-----| Goto -> Block 1
# 1021| r0_8(glval<int>) = VariableAddress[#return] :
# 1021| v0_9(void) = ReturnValue : r0_8, m0_7
# 1021| v0_10(void) = UnmodeledUse : mu*
# 1021| v0_11(void) = ExitFunction :
# 1028| UnreachableIf(bool) -> int
# 1028| Block 0
@@ -4477,11 +4427,11 @@ ir.cpp:
# 1029| r0_5(glval<bool>) = VariableAddress[b] :
# 1029| r0_6(bool) = Load : r0_5, m0_4
# 1029| v0_7(void) = ConditionalBranch : r0_6
#-----| False -> Block 5
#-----| False -> Block 4
#-----| True -> Block 2
# 1028| Block 1
# 1028| m1_0(int) = Phi : from 3:m3_2, from 4:m4_2, from 6:m6_2, from 7:m7_2
# 1028| m1_0(int) = Phi : from 3:m3_2, from 5:m5_2
# 1028| r1_1(glval<int>) = VariableAddress[#return] :
# 1028| v1_2(void) = ReturnValue : r1_1, m1_0
# 1028| v1_3(void) = UnmodeledUse : mu*
@@ -4490,35 +4440,29 @@ ir.cpp:
# 1030| Block 2
# 1030| r2_0(bool) = Constant[0] :
# 1030| v2_1(void) = ConditionalBranch : r2_0
#-----| False -> Block 4
#-----| True -> Block 3
#-----| False -> Block 3
#-----| True -> Block 7
# 1031| Block 3
# 1031| r3_0(glval<int>) = VariableAddress[#return] :
# 1031| r3_1(int) = Constant[1] :
# 1031| m3_2(int) = Store : r3_0, r3_1
# 1034| Block 3
# 1034| r3_0(glval<int>) = VariableAddress[#return] :
# 1034| r3_1(int) = Constant[0] :
# 1034| m3_2(int) = Store : r3_0, r3_1
#-----| Goto -> Block 1
# 1034| Block 4
# 1034| r4_0(glval<int>) = VariableAddress[#return] :
# 1034| r4_1(int) = Constant[0] :
# 1034| m4_2(int) = Store : r4_0, r4_1
# 1038| Block 4
# 1038| r4_0(bool) = Constant[1] :
# 1038| v4_1(void) = ConditionalBranch : r4_0
#-----| False -> Block 6
#-----| True -> Block 5
# 1039| Block 5
# 1039| r5_0(glval<int>) = VariableAddress[#return] :
# 1039| r5_1(int) = Constant[0] :
# 1039| m5_2(int) = Store : r5_0, r5_1
#-----| Goto -> Block 1
# 1038| Block 5
# 1038| r5_0(bool) = Constant[1] :
# 1038| v5_1(void) = ConditionalBranch : r5_0
#-----| False -> Block 7
#-----| True -> Block 6
# 1042| Block 6
# 1042| v6_0(void) = Unreached :
# 1039| Block 6
# 1039| r6_0(glval<int>) = VariableAddress[#return] :
# 1039| r6_1(int) = Constant[0] :
# 1039| m6_2(int) = Store : r6_0, r6_1
#-----| Goto -> Block 1
# 1042| Block 7
# 1042| r7_0(glval<int>) = VariableAddress[#return] :
# 1042| r7_1(int) = Constant[1] :
# 1042| m7_2(int) = Store : r7_0, r7_1
#-----| Goto -> Block 1
# 1031| Block 7
# 1031| v7_0(void) = Unreached :