Shared: Add shared basic block library

This commit is contained in:
Simon Friis Vindum
2025-01-15 14:59:49 +01:00
parent ce5c886ad4
commit c051eecfb4
16 changed files with 1115 additions and 1010 deletions

View File

@@ -1,271 +1,96 @@
/** Provides classes representing basic blocks. */
private import swift
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl as Impl
private import internal.ControlFlowElements
private import CfgNodes
private import internal.ControlFlowGraphImpl as CfgImpl
private import SuccessorTypes
private import codeql.swift.generated.Raw
private import codeql.swift.generated.Synth
private import CfgImpl::BasicBlocks as BasicBlocksImpl
/**
* A basic block, that is, a maximal straight-line sequence of control flow nodes
* without branches or joins.
*/
class BasicBlock extends TBasicBlockStart {
/** Gets the scope of this basic block. */
CfgScope getScope() { result = this.getAPredecessor().getScope() }
final class BasicBlock extends BasicBlocksImpl::BasicBlock {
/** Gets an immediate successor of this basic block, if any. */
BasicBlock getASuccessor() { result = this.getASuccessor(_) }
BasicBlock getASuccessor() { result = super.getASuccessor() }
/** Gets an immediate successor of this basic block of a given type, if any. */
BasicBlock getASuccessor(SuccessorType t) {
result.getFirstNode() = this.getLastNode().getASuccessor(t)
}
BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
/** Gets an immediate predecessor of this basic block, if any. */
BasicBlock getAPredecessor() { result.getASuccessor() = this }
BasicBlock getAPredecessor() { result = super.getAPredecessor() }
/** Gets an immediate predecessor of this basic block of a given type, if any. */
BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this }
BasicBlock getAPredecessor(SuccessorType t) { result = super.getAPredecessor(t) }
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
ControlFlowNode getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) }
ControlFlowNode getNode(int pos) { result = super.getNode(pos) }
/** Gets a control flow node in this basic block. */
ControlFlowNode getANode() { result = this.getNode(_) }
ControlFlowNode getANode() { result = super.getANode() }
/** Gets the first control flow node in this basic block. */
ControlFlowNode getFirstNode() { this = TBasicBlockStart(result) }
ControlFlowNode getFirstNode() { result = super.getFirstNode() }
/** Gets the last control flow node in this basic block. */
ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) }
ControlFlowNode getLastNode() { result = super.getLastNode() }
/** Gets the length of this basic block. */
int length() { result = strictcount(this.getANode()) }
predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) }
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) }
predicate dominates(BasicBlock bb) { super.dominates(bb) }
predicate dominates(BasicBlock bb) {
bb = this or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
predicate inDominanceFrontier(BasicBlock df) {
this.dominatesPredecessor(df) and
not this.strictlyDominates(df)
}
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
/**
* Holds if this basic block dominates a predecessor of `df`.
*/
private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) }
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) }
predicate postDominates(BasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
/** Holds if this basic block is in a loop in the control flow graph. */
predicate inLoop() { this.getASuccessor+() = this }
/** Gets a textual representation of this basic block. */
string toString() { result = this.getFirstNode().toString() }
/** Gets the location of this basic block. */
Location getLocation() { result = this.getFirstNode().getLocation() }
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
}
cached
private module Cached {
/** Internal representation of basic blocks. */
cached
newtype TBasicBlock = TBasicBlockStart(ControlFlowNode cfn) { startsBB(cfn) }
/** Holds if `cfn` starts a new basic block. */
private predicate startsBB(ControlFlowNode cfn) {
not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor())
or
cfn.isJoin()
or
cfn.getAPredecessor().isBranch()
}
/**
* Holds if `succ` is a control flow successor of `pred` within
* the same basic block.
*/
private predicate intraBBSucc(ControlFlowNode pred, ControlFlowNode succ) {
succ = pred.getASuccessor() and
not startsBB(succ)
}
/**
* Holds if `cfn` 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 `cfn` along the `intraBBSucc` relation.
*/
cached
predicate bbIndex(ControlFlowNode bbStart, ControlFlowNode cfn, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i)
/**
* Holds if the first node of basic block `succ` is a control flow
* successor of the last node of basic block `pred`.
*/
private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() }
/** Holds if `dom` is an immediate dominator of `bb`. */
cached
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
/** Holds if `pred` is a basic block predecessor of `succ`. */
private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) }
/** Holds if `bb` is an exit basic block that represents normal exit. */
private predicate normalExitBB(BasicBlock bb) {
bb.getANode().(Impl::AnnotatedExitNode).isNormal()
}
/** Holds if `dom` is an immediate post-dominator of `bb`. */
cached
predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
idominance(normalExitBB/1, predBB/2)(_, dom, bb)
/**
* Gets the `i`th predecessor of join block `jb`, with respect to some
* arbitrary order.
*/
cached
JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) {
result =
rank[i + 1](JoinBlockPredecessor jbp |
jbp = jb.getAPredecessor()
|
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
)
}
}
private import Cached
/** Holds if `bb` is an entry basic block. */
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode }
/**
* An entry basic block, that is, a basic block whose first node is
* an entry node.
*/
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
override CfgScope getScope() {
this.getFirstNode() = any(EntryNode node | node.getScope() = result)
}
}
final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { }
/**
* An annotated exit basic block, that is, a basic block whose last node is
* an annotated exit node.
* An annotated exit basic block, that is, a basic block that contains an
* annotated exit node.
*/
class AnnotatedExitBasicBlock extends BasicBlock {
private boolean normal;
AnnotatedExitBasicBlock() {
exists(Impl::AnnotatedExitNode n |
n = this.getANode() and
if n.isNormal() then normal = true else normal = false
)
}
/** Holds if this block represent a normal exit. */
final predicate isNormal() { normal = true }
}
final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { }
/**
* An exit basic block, that is, a basic block whose last node is
* an exit node.
*/
class ExitBasicBlock extends BasicBlock {
ExitBasicBlock() { this.getLastNode() instanceof ExitNode }
}
private module JoinBlockPredecessors {
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
private predicate idOf(AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
private AstNode projectToAst(ControlFlowElement n) {
result = n.asAstNode()
or
isPropertyGetterElement(n, _, result)
or
isPropertySetterElement(n, _, result)
or
isPropertyObserverElement(n, _, result)
or
result = n.(KeyPathElement).getAst()
or
result = n.(FuncDeclElement).getAst()
}
int getId(JoinBlockPredecessor jbp) {
idOf(projectToAst(jbp.getFirstNode().(CfgNode).getNode()), result)
or
idOf(jbp.(EntryBasicBlock).getScope(), result)
}
string getSplitString(JoinBlockPredecessor jbp) {
result = jbp.getFirstNode().(CfgNode).getSplitsString()
or
not exists(jbp.getFirstNode().(CfgNode).getSplitsString()) and
result = ""
}
}
final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { }
/** A basic block with more than one predecessor. */
class JoinBlock extends BasicBlock {
final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock {
JoinBlock() { this.getFirstNode().isJoin() }
/**
* Gets the `i`th predecessor of this join block, with respect to some
* arbitrary order.
*/
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) }
JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
}
/** A basic block that is an immediate predecessor of a join block. */
class JoinBlockPredecessor extends BasicBlock {
JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock }
}
class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { }
/** A basic block that terminates in a condition, splitting the subsequent control flow. */
class ConditionBlock extends BasicBlock {
ConditionBlock() { this.getLastNode().isCondition() }
final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock {
/**
* Holds if basic block `succ` is immediately controlled by this basic
* block with conditional value `s`. That is, `succ` is an immediate
* successor of this block, and `succ` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
pragma[nomagic]
predicate immediatelyControls(BasicBlock succ, SuccessorType s) {
succ = this.getASuccessor(s) and
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
super.immediatelyControls(succ, s)
}
/**
@@ -273,7 +98,7 @@ class ConditionBlock extends BasicBlock {
* conditional value `s`. That is, `controlled` can only be reached from
* the callable entry point by going via the `s` edge out of this basic block.
*/
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
super.controls(controlled, s)
}
}

View File

@@ -3,18 +3,18 @@
private import swift
private import BasicBlocks
private import SuccessorTypes
private import internal.ControlFlowGraphImpl
private import internal.ControlFlowGraphImpl as CfgImpl
private import internal.Completion
private import internal.Scope
private import internal.ControlFlowElements
/** An AST node with an associated control-flow graph. */
class CfgScope extends Scope instanceof CfgScope::Range_ {
class CfgScope extends Scope instanceof CfgImpl::CfgScope::Range_ {
/** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() {
exists(ControlFlowElement parent |
parent.asAstNode() = getParentOfAst(this) and
result = getCfgScope(parent)
result = CfgImpl::getCfgScope(parent)
)
}
}
@@ -27,7 +27,7 @@ class CfgScope extends Scope instanceof CfgScope::Range_ {
*
* Only nodes that can be reached from an entry point are included in the CFG.
*/
class ControlFlowNode extends Node {
class ControlFlowNode extends CfgImpl::Node {
/** Gets the AST node that this node corresponds to, if any. */
ControlFlowElement getNode() { none() }
@@ -63,7 +63,7 @@ class ControlFlowNode extends Node {
}
/** The type of a control flow successor. */
class SuccessorType extends TSuccessorType {
class SuccessorType extends CfgImpl::TSuccessorType {
/** Gets a textual representation of successor type. */
string toString() { none() }
}
@@ -71,7 +71,7 @@ class SuccessorType extends TSuccessorType {
/** Provides different types of control flow successor types. */
module SuccessorTypes {
/** A normal control flow successor. */
class NormalSuccessor extends SuccessorType, TSuccessorSuccessor {
class NormalSuccessor extends SuccessorType, CfgImpl::TSuccessorSuccessor {
final override string toString() { result = "successor" }
}
@@ -89,24 +89,24 @@ module SuccessorTypes {
}
/** A Boolean control flow successor. */
class BooleanSuccessor extends ConditionalSuccessor, TBooleanSuccessor {
BooleanSuccessor() { this = TBooleanSuccessor(value) }
class BooleanSuccessor extends ConditionalSuccessor, CfgImpl::TBooleanSuccessor {
BooleanSuccessor() { this = CfgImpl::TBooleanSuccessor(value) }
}
class BreakSuccessor extends SuccessorType, TBreakSuccessor {
class BreakSuccessor extends SuccessorType, CfgImpl::TBreakSuccessor {
final override string toString() { result = "break" }
}
class ContinueSuccessor extends SuccessorType, TContinueSuccessor {
class ContinueSuccessor extends SuccessorType, CfgImpl::TContinueSuccessor {
final override string toString() { result = "continue" }
}
class ReturnSuccessor extends SuccessorType, TReturnSuccessor {
class ReturnSuccessor extends SuccessorType, CfgImpl::TReturnSuccessor {
final override string toString() { result = "return" }
}
class MatchingSuccessor extends ConditionalSuccessor, TMatchingSuccessor {
MatchingSuccessor() { this = TMatchingSuccessor(value) }
class MatchingSuccessor extends ConditionalSuccessor, CfgImpl::TMatchingSuccessor {
MatchingSuccessor() { this = CfgImpl::TMatchingSuccessor(value) }
/** Holds if this is a match successor. */
predicate isMatch() { value = true }
@@ -114,19 +114,19 @@ module SuccessorTypes {
override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
}
class FallthroughSuccessor extends SuccessorType, TFallthroughSuccessor {
class FallthroughSuccessor extends SuccessorType, CfgImpl::TFallthroughSuccessor {
final override string toString() { result = "fallthrough" }
}
class EmptinessSuccessor extends ConditionalSuccessor, TEmptinessSuccessor {
EmptinessSuccessor() { this = TEmptinessSuccessor(value) }
class EmptinessSuccessor extends ConditionalSuccessor, CfgImpl::TEmptinessSuccessor {
EmptinessSuccessor() { this = CfgImpl::TEmptinessSuccessor(value) }
predicate isEmpty() { value = true }
override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
}
class ExceptionSuccessor extends SuccessorType, TExceptionSuccessor {
class ExceptionSuccessor extends SuccessorType, CfgImpl::TExceptionSuccessor {
override string toString() { result = "exception" }
}
}

View File

@@ -15,6 +15,8 @@ import Completion
private import codeql.swift.controlflow.ControlFlowGraph as Cfg
private import Splitting as Splitting
private import Scope
private import codeql.swift.generated.Raw
private import codeql.swift.generated.Synth
import ControlFlowElements
import AstControlFlowTrees
@@ -82,6 +84,31 @@ module CfgInput implements InputSig<Location> {
predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
scope.(Impl::CfgScope::Range_).exit(last, c)
}
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
// TODO: does not work if fresh ipa entities (`ipa: on:`) turn out to be first of the block
private predicate idOf(S::AstNode x, int y) { idOfDbAstNode(Synth::convertAstNodeToRaw(x), y) }
private S::AstNode projectToAst(ControlFlowElement n) {
result = n.asAstNode()
or
isPropertyGetterElement(n, _, result)
or
isPropertySetterElement(n, _, result)
or
isPropertyObserverElement(n, _, result)
or
result = n.(KeyPathElement).getAst()
or
result = n.(FuncDeclElement).getAst()
}
predicate idOfAstNode(AstNode node, int id) { idOf(projectToAst(node), id) }
predicate idOfCfgScope(CfgScope node, int id) { idOf(node, id) }
}
private module CfgSplittingInput implements SplittingInputSig<Location, CfgInput> {

View File

@@ -21,7 +21,7 @@ module Ssa {
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class ExitBasicBlock extends BasicBlock, BasicBlocks::ExitBasicBlock { }
class ExitBasicBlock = BasicBlocks::ExitBasicBlock;
private newtype TSourceVariable =
TNormalSourceVariable(VarDecl v) or