Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class IRBlock extends IRBlockBase {
blockSuccessor(this, result, kind)
}

final IRBlock getBackEdgeSuccessor(EdgeKind kind) {
backEdgeSuccessor(this, result, kind)
}

final predicate immediatelyDominates(IRBlock block) {
blockImmediatelyDominates(this, block)
}
Expand Down Expand Up @@ -132,7 +136,10 @@ private predicate startsBasicBlock(Instruction instr) {
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
) or // Incoming edge is not a GotoEdge
exists(Instruction predecessor |
instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
) // A back edge enters this instruction
)
}

Expand Down Expand Up @@ -184,6 +191,41 @@ private cached module Cached {
)
}

cached predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
backEdgeSuccessorRaw(pred, succ, kind)
or
forwardEdgeRaw+(pred, pred) and
blockSuccessor(pred, succ, kind)
}

/**
* Holds if there is an edge from `pred` to `succ` that is not a back edge.
*/
private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) {
exists(EdgeKind kind |
blockSuccessor(pred, succ, kind) and
not backEdgeSuccessorRaw(pred, succ, kind)
)
}

/**
* Holds if the `kind`-edge from `pred` to `succ` is a back edge according to
* `Construction`.
*
* There could be loops of non-back-edges if there is a flaw in the IR
* construction or back-edge detection, and this could cause non-termination
* of subsequent analysis. To prevent that, a subsequent predicate further
* classifies all edges as back edges if they are involved in a loop of
* non-back-edges.
*/
private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and
succ = MkIRBlock(succFirst)
)
}

cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,54 @@ module InstructionSanity {
blockCount = count(instr.getBlock()) and
blockCount != 1
}

private predicate forwardEdge(IRBlock b1, IRBlock b2) {
b1.getASuccessor() = b2 and
not b1.getBackEdgeSuccessor(_) = b2
}

/**
* Holds if `f` contains a loop in which no edge is a back edge.
*
* This check ensures we don't have too _few_ back edges.
*/
query predicate containsLoopOfForwardEdges(FunctionIR f) {
exists(IRBlock block |
forwardEdge+(block, block) and
block.getFunctionIR() = f
)
}

/**
* Holds if `block` is reachable from its function entry point but would not
* be reachable by traversing only forward edges. This check is skipped for
* functions containing `goto` statements as the property does not generally
* hold there.
*
* This check ensures we don't have too _many_ back edges.
*/
query predicate lostReachability(IRBlock block) {
exists(FunctionIR f, IRBlock entry |
entry = f.getEntryBlock() and
entry.getASuccessor+() = block and
not forwardEdge+(entry, block) and
not exists(GotoStmt s | s.getEnclosingFunction() = f.getFunction())
)
}

/**
* Holds if the number of back edges differs between the `Instruction` graph
* and the `IRBlock` graph.
*/
query predicate backEdgeCountMismatch(Function f, int fromInstr, int fromBlock) {
fromInstr = count(Instruction i1, Instruction i2 |
i1.getFunction() = f and i1.getBackEdgeSuccessor(_) = i2
) and
fromBlock = count(IRBlock b1, IRBlock b2 |
b1.getFunction() = f and b1.getBackEdgeSuccessor(_) = b2
) and
fromInstr != fromBlock
}
}

/**
Expand Down Expand Up @@ -463,6 +511,24 @@ class Instruction extends Construction::TInstruction {
result = Construction::getInstructionSuccessor(this, kind)
}

/**
* Gets the a _back-edge successor_ of this instruction along the control
* flow edge specified by `kind`. A back edge in the control-flow graph is
* intuitively the edge that goes back around a loop. If all back edges are
* removed from the control-flow graph, it becomes acyclic.
*/
final Instruction getBackEdgeSuccessor(EdgeKind kind) {
// We don't take these edges from
// `Construction::getInstructionBackEdgeSuccessor` since that relation has
// not been treated to remove any loops that might be left over due to
// flaws in the IR construction or back-edge detection.
exists(IRBlock block |
block = this.getBlock() and
this = block.getLastInstruction() and
result = block.getBackEdgeSuccessor(kind).getFirstInstruction()
)
}

/**
* Gets all direct successors of this instruction.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key,
(
(
key = "semmle.label" and
value = kind.toString()
if predBlock.getBackEdgeSuccessor(kind) = succBlock
then value = kind.toString() + " (back edge)"
else value = kind.toString()
) or
(
key = "semmle.order" and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,23 @@ cached private module Cached {
)
}

cached Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
exists(OldInstruction oldInstruction |
not Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind) and
// There is only one case for the translation into `result` because the
// SSA construction never inserts extra instructions _before_ an existing
// instruction.
getOldInstruction(result) = oldInstruction.getBackEdgeSuccessor(kind) and
// There are two cases for the translation into `instruction` because the
// SSA construction might have inserted a chi node _after_
// `oldInstruction`, in which case the back edge should come out of the
// chi node instead.
if hasChiNode(_, oldInstruction)
then instruction = getChiInstruction(oldInstruction)
else instruction = getNewInstruction(oldInstruction)
)
}

cached IRVariable getInstructionVariable(Instruction instruction) {
result = getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getVariable())
}
Expand Down
44 changes: 43 additions & 1 deletion cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class IRBlock extends IRBlockBase {
blockSuccessor(this, result, kind)
}

final IRBlock getBackEdgeSuccessor(EdgeKind kind) {
backEdgeSuccessor(this, result, kind)
}

final predicate immediatelyDominates(IRBlock block) {
blockImmediatelyDominates(this, block)
}
Expand Down Expand Up @@ -132,7 +136,10 @@ private predicate startsBasicBlock(Instruction instr) {
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
) or // Incoming edge is not a GotoEdge
exists(Instruction predecessor |
instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
) // A back edge enters this instruction
)
}

Expand Down Expand Up @@ -184,6 +191,41 @@ private cached module Cached {
)
}

cached predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
backEdgeSuccessorRaw(pred, succ, kind)
or
forwardEdgeRaw+(pred, pred) and
blockSuccessor(pred, succ, kind)
}

/**
* Holds if there is an edge from `pred` to `succ` that is not a back edge.
*/
private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) {
exists(EdgeKind kind |
blockSuccessor(pred, succ, kind) and
not backEdgeSuccessorRaw(pred, succ, kind)
)
}

/**
* Holds if the `kind`-edge from `pred` to `succ` is a back edge according to
* `Construction`.
*
* There could be loops of non-back-edges if there is a flaw in the IR
* construction or back-edge detection, and this could cause non-termination
* of subsequent analysis. To prevent that, a subsequent predicate further
* classifies all edges as back edges if they are involved in a loop of
* non-back-edges.
*/
private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and
succ = MkIRBlock(succFirst)
)
}

cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
Expand Down
66 changes: 66 additions & 0 deletions cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,54 @@ module InstructionSanity {
blockCount = count(instr.getBlock()) and
blockCount != 1
}

private predicate forwardEdge(IRBlock b1, IRBlock b2) {
b1.getASuccessor() = b2 and
not b1.getBackEdgeSuccessor(_) = b2
}

/**
* Holds if `f` contains a loop in which no edge is a back edge.
*
* This check ensures we don't have too _few_ back edges.
*/
query predicate containsLoopOfForwardEdges(FunctionIR f) {
exists(IRBlock block |
forwardEdge+(block, block) and
block.getFunctionIR() = f
)
}

/**
* Holds if `block` is reachable from its function entry point but would not
* be reachable by traversing only forward edges. This check is skipped for
* functions containing `goto` statements as the property does not generally
* hold there.
*
* This check ensures we don't have too _many_ back edges.
*/
query predicate lostReachability(IRBlock block) {
exists(FunctionIR f, IRBlock entry |
entry = f.getEntryBlock() and
entry.getASuccessor+() = block and
not forwardEdge+(entry, block) and
not exists(GotoStmt s | s.getEnclosingFunction() = f.getFunction())
)
}

/**
* Holds if the number of back edges differs between the `Instruction` graph
* and the `IRBlock` graph.
*/
query predicate backEdgeCountMismatch(Function f, int fromInstr, int fromBlock) {
fromInstr = count(Instruction i1, Instruction i2 |
i1.getFunction() = f and i1.getBackEdgeSuccessor(_) = i2
) and
fromBlock = count(IRBlock b1, IRBlock b2 |
b1.getFunction() = f and b1.getBackEdgeSuccessor(_) = b2
) and
fromInstr != fromBlock
}
}

/**
Expand Down Expand Up @@ -463,6 +511,24 @@ class Instruction extends Construction::TInstruction {
result = Construction::getInstructionSuccessor(this, kind)
}

/**
* Gets the a _back-edge successor_ of this instruction along the control
* flow edge specified by `kind`. A back edge in the control-flow graph is
* intuitively the edge that goes back around a loop. If all back edges are
* removed from the control-flow graph, it becomes acyclic.
*/
final Instruction getBackEdgeSuccessor(EdgeKind kind) {
// We don't take these edges from
// `Construction::getInstructionBackEdgeSuccessor` since that relation has
// not been treated to remove any loops that might be left over due to
// flaws in the IR construction or back-edge detection.
exists(IRBlock block |
block = this.getBlock() and
this = block.getLastInstruction() and
result = block.getBackEdgeSuccessor(kind).getFirstInstruction()
)
}

/**
* Gets all direct successors of this instruction.
*/
Expand Down
4 changes: 3 additions & 1 deletion cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key,
(
(
key = "semmle.label" and
value = kind.toString()
if predBlock.getBackEdgeSuccessor(kind) = succBlock
then value = kind.toString() + " (back edge)"
else value = kind.toString()
) or
(
key = "semmle.order" and
Expand Down
Loading