Use shared basic blocks library

This commit is contained in:
Owen Mansel-Chan
2026-01-28 14:52:57 +00:00
parent c316d51d41
commit 3dd6b3fb69
3 changed files with 39 additions and 164 deletions

View File

@@ -4,140 +4,62 @@
import go
private import ControlFlowGraphImpl
private import codeql.controlflow.BasicBlock as BB
private import codeql.controlflow.SuccessorType
/**
* Holds if `nd` starts a new basic block.
*/
private predicate startsBB(ControlFlow::Node nd) {
count(nd.getAPredecessor()) != 1
or
nd.getAPredecessor().isBranch()
}
private module Input implements BB::InputSig<Location> {
/** A delineated part of the AST with its own CFG. */
class CfgScope = ControlFlow::Root;
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `bb`.
*/
private predicate succBB(BasicBlock bb, BasicBlock succ) { succ = bb.getLastNode().getASuccessor() }
/** The class of control flow nodes. */
class Node = ControlFlowNode;
/**
* Holds if the first node of basic block `bb` is a control flow
* successor of the last node of basic block `pre`.
*/
private predicate predBB(BasicBlock bb, BasicBlock pre) { succBB(pre, bb) }
/** Gets the CFG scope in which this node occurs. */
CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result }
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode().isEntryNode() }
/** Holds if `bb` is an exit basic block. */
private predicate exitBB(BasicBlock bb) { bb.getLastNode().isExitNode() }
cached
private module Internal {
/**
* Holds if `succ` is a control flow successor of `nd` within the same basic block.
*/
private predicate intraBBSucc(ControlFlow::Node nd, ControlFlow::Node succ) {
succ = nd.getASuccessor() and
not startsBB(succ)
/** Gets an immediate successor of this node. */
Node nodeGetASuccessor(Node node, SuccessorType t) {
result = node.getASuccessor() and
(
not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor
or
t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome()
)
}
/**
* Holds if `nd` is the `i`th node in basic block `bb`.
*
* In other words, `i` is the shortest distance from a node `bb`
* that starts a basic block to `nd` along the `intraBBSucc` relation.
* Holds if `node` represents an entry node to be used when calculating
* dominance.
*/
cached
predicate bbIndex(BasicBlock bb, ControlFlow::Node nd, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bb, nd, i)
predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode }
cached
int bbLength(BasicBlock bb) { result = strictcount(ControlFlow::Node nd | bbIndex(bb, nd, _)) }
cached
predicate reachableBB(BasicBlock bb) {
entryBB(bb)
or
exists(BasicBlock predBB | succBB(predBB, bb) | reachableBB(predBB))
}
/**
* Holds if `node` represents an exit node to be used when calculating
* post dominance.
*/
predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode }
}
private import Internal
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
private predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
private predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(exitBB/1, predBB/2)(_, dom, bb)
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*
* At the database level, a basic block is represented by its first control flow node.
*/
class BasicBlock extends TControlFlowNode {
BasicBlock() { startsBB(this) }
private module BbImpl = BB::Make<Location, Input>;
class BasicBlock extends BbImpl::BasicBlock {
/** Gets a basic block succeeding this one. */
BasicBlock getASuccessor() { succBB(this, result) }
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
/** Gets a basic block preceding this one. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
/** Gets a node in this block. */
ControlFlow::Node getANode() { result = this.getNode(_) }
/** Gets the node at the given position in this block. */
ControlFlow::Node getNode(int pos) { bbIndex(this, result, pos) }
/** Gets the first node in this block. */
ControlFlow::Node getFirstNode() { result = this }
/** Gets the last node in this block. */
ControlFlow::Node getLastNode() { result = this.getNode(this.length() - 1) }
/** Gets the length of this block. */
int length() { result = bbLength(this) }
/** Gets the basic block that immediately dominates this basic block. */
ReachableBasicBlock getImmediateDominator() { bbIDominates(result, this) }
/** Gets the innermost function or file to which this basic block belongs. */
ControlFlow::Root getRoot() { result = this.getFirstNode().getRoot() }
/** Gets a textual representation of this basic block. */
string toString() { result = "basic block" }
/** Gets the source location for this element. */
Location getLocation() { result = this.getFirstNode().getLocation() }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Holds if this basic block is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
ControlFlow::Root getRoot() { result = this.getScope() }
}
/**
* An entry basic block, that is, a basic block whose first node is an entry node.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
class EntryBasicBlock = BbImpl::EntryBasicBlock;
cached
private predicate reachableBB(BasicBlock bb) {
bb instanceof EntryBasicBlock
or
exists(BasicBlock predBB | predBB.getASuccessor() = bb | reachableBB(predBB))
}
/**
@@ -145,38 +67,6 @@ class EntryBasicBlock extends BasicBlock {
*/
class ReachableBasicBlock extends BasicBlock {
ReachableBasicBlock() { reachableBB(this) }
/**
* Holds if this basic block strictly dominates `bb`.
*/
cached
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
/**
* Holds if this basic block dominates `bb`.
*
* This predicate is reflexive: each reachable basic block dominates itself.
*/
predicate dominates(ReachableBasicBlock bb) {
bb = this or
this.strictlyDominates(bb)
}
/**
* Holds if this basic block strictly post-dominates `bb`.
*/
cached
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
/**
* Holds if this basic block post-dominates `bb`.
*
* This predicate is reflexive: each reachable basic block post-dominates itself.
*/
predicate postDominates(ReachableBasicBlock bb) {
bb = this or
this.strictlyPostDominates(bb)
}
}
/**
@@ -184,21 +74,4 @@ class ReachableBasicBlock extends BasicBlock {
*/
class ReachableJoinBlock extends ReachableBasicBlock {
ReachableJoinBlock() { this.getFirstNode().isJoin() }
/**
* Holds if this basic block belongs to the dominance frontier of `b`, that is
* `b` dominates a predecessor of this block, but not this block itself.
*
* Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5),
* who in turn attribute it to Ferrante et al., "The program dependence graph and
* its use in optimization".
*/
predicate inDominanceFrontierOf(ReachableBasicBlock b) {
b = this.getAPredecessor() and not b = this.getImmediateDominator()
or
exists(ReachableBasicBlock prev | this.inDominanceFrontierOf(prev) |
b = prev.getImmediateDominator() and
not b = this.getImmediateDominator()
)
}
}

View File

@@ -353,4 +353,6 @@ module ControlFlow {
}
}
class ControlFlowNode = ControlFlow::Node;
class Write = ControlFlow::WriteNode;

View File

@@ -71,7 +71,7 @@ private module Internal {
private predicate inDefDominanceFrontier(ReachableJoinBlock bb, SsaSourceVariable v) {
exists(ReachableBasicBlock defbb, SsaDefinition def |
def.definesAt(defbb, _, v) and
bb.inDominanceFrontierOf(defbb)
defbb.inDominanceFrontier(bb)
)
}