mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #20253 from aschackmull/shared/basicblock-signature2
Shared: Add and use a signature for basic blocks
This commit is contained in:
@@ -12,7 +12,11 @@ private import codeql.util.Location
|
||||
|
||||
/** Provides the language-specific input specification. */
|
||||
signature module InputSig<LocationSig Location> {
|
||||
class SuccessorType;
|
||||
/** The type of a control flow successor. */
|
||||
class SuccessorType {
|
||||
/** Gets a textual representation of this successor type. */
|
||||
string toString();
|
||||
}
|
||||
|
||||
/** Hold if `t` represents a conditional successor type. */
|
||||
predicate successorTypeIsCondition(SuccessorType t);
|
||||
@@ -47,12 +51,137 @@ signature module InputSig<LocationSig Location> {
|
||||
predicate nodeIsPostDominanceExit(Node node);
|
||||
}
|
||||
|
||||
signature module CfgSig<LocationSig Location> {
|
||||
/** A control flow node. */
|
||||
class ControlFlowNode {
|
||||
/** Gets a textual representation of this control flow node. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this control flow node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/** The type of a control flow successor. */
|
||||
class SuccessorType {
|
||||
/** Gets a textual representation of this successor type. */
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
*/
|
||||
class BasicBlock {
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this basic block. */
|
||||
Location getLocation();
|
||||
|
||||
/** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
|
||||
ControlFlowNode getNode(int pos);
|
||||
|
||||
/** Gets the last control flow node in this basic block. */
|
||||
ControlFlowNode getLastNode();
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length();
|
||||
|
||||
/** Gets an immediate successor of this basic block, if any. */
|
||||
BasicBlock getASuccessor();
|
||||
|
||||
/** Gets an immediate successor of this basic block of a given type, if any. */
|
||||
BasicBlock getASuccessor(SuccessorType t);
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly dominates basic block `bb`.
|
||||
*
|
||||
* That is, all paths reaching `bb` from the entry point basic block must
|
||||
* go through this basic block and this basic block is different from `bb`.
|
||||
*/
|
||||
predicate strictlyDominates(BasicBlock bb);
|
||||
|
||||
/**
|
||||
* Holds if this basic block dominates basic block `bb`.
|
||||
*
|
||||
* That is, all paths reaching `bb` from the entry point basic block must
|
||||
* go through this basic block.
|
||||
*/
|
||||
predicate dominates(BasicBlock bb);
|
||||
|
||||
/**
|
||||
* Holds if `df` is in the dominance frontier of this basic block. That is,
|
||||
* this basic block dominates a predecessor of `df`, but does not dominate
|
||||
* `df` itself. I.e., it is equivaluent to:
|
||||
* ```
|
||||
* this.dominates(df.getAPredecessor()) and not this.strictlyDominates(df)
|
||||
* ```
|
||||
*/
|
||||
predicate inDominanceFrontier(BasicBlock df);
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates this basic block, if any.
|
||||
*
|
||||
* That is, the result is the unique basic block satisfying:
|
||||
* 1. The result strictly dominates this basic block.
|
||||
* 2. There exists no other basic block that is strictly dominated by the
|
||||
* result and which strictly dominates this basic block.
|
||||
*
|
||||
* All basic blocks, except entry basic blocks, have a unique immediate
|
||||
* dominator.
|
||||
*/
|
||||
BasicBlock getImmediateDominator();
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates basic block `bb`.
|
||||
*
|
||||
* That is, all paths reaching a normal exit point basic block from basic
|
||||
* block `bb` must go through this basic block and this basic block is
|
||||
* different from `bb`.
|
||||
*/
|
||||
predicate strictlyPostDominates(BasicBlock bb);
|
||||
|
||||
/**
|
||||
* Holds if this basic block post-dominates basic block `bb`.
|
||||
*
|
||||
* That is, all paths reaching a normal exit point basic block from basic
|
||||
* block `bb` must go through this basic block.
|
||||
*/
|
||||
predicate postDominates(BasicBlock bb);
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry basic block, that is, a basic block whose first node is
|
||||
* an entry node.
|
||||
*/
|
||||
class EntryBasicBlock extends BasicBlock;
|
||||
|
||||
/**
|
||||
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
|
||||
* and `bb2` is a dominating edge.
|
||||
*
|
||||
* An edge `(bb1, bb2)` is dominating if there exists a basic block that can
|
||||
* only be reached from the entry block by going through `(bb1, bb2)`. This
|
||||
* implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can
|
||||
* only be reached from the entry block by going via `(bb1, bb2)`.
|
||||
*
|
||||
* This is a necessary and sufficient condition for an edge to dominate some
|
||||
* block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)`
|
||||
* means that the edge `(bb1, bb2)` dominates `bb3`.
|
||||
*/
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a basic block construction on top of a control flow graph.
|
||||
*/
|
||||
module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
module Make<LocationSig Location, InputSig<Location> Input> implements CfgSig<Location> {
|
||||
private import Input
|
||||
|
||||
class ControlFlowNode = Input::Node;
|
||||
|
||||
class SuccessorType = Input::SuccessorType;
|
||||
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
@@ -239,6 +368,10 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
string toString() { result = this.getFirstNode().toString() }
|
||||
}
|
||||
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { nodeIsDominanceEntry(this.getFirstNode()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
|
||||
* and `bb2` is a dominating edge.
|
||||
@@ -343,14 +476,14 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
cached
|
||||
Node getNode(BasicBlock bb, int pos) { bbIndex(bb.getFirstNode(), result, pos) }
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
private predicate entryBB(BasicBlock bb) { nodeIsDominanceEntry(bb.getFirstNode()) }
|
||||
|
||||
cached
|
||||
predicate bbSuccessor(BasicBlock bb1, BasicBlock bb2, SuccessorType t) {
|
||||
bb2.getFirstNode() = nodeGetASuccessor(bb1.getLastNode(), t)
|
||||
}
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
private predicate entryBlock(EntryBasicBlock bb) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the first node of basic block `succ` is a control flow
|
||||
* successor of the last node of basic block `pred`.
|
||||
@@ -360,7 +493,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
/** Holds if `dom` is an immediate dominator of `bb`. */
|
||||
cached
|
||||
predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
|
||||
idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
idominance(entryBlock/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) }
|
||||
|
||||
@@ -1609,7 +1609,7 @@ module MakeWithSplitting<
|
||||
|
||||
private module BasicBlockImpl = BB::Make<Location, BasicBlockInputSig>;
|
||||
|
||||
final class BasicBlock = BasicBlockImpl::BasicBlock;
|
||||
class BasicBlock = BasicBlockImpl::BasicBlock;
|
||||
|
||||
predicate dominatingEdge = BasicBlockImpl::dominatingEdge/2;
|
||||
|
||||
|
||||
@@ -50,16 +50,14 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
private import codeql.util.Boolean
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Unit
|
||||
|
||||
signature module InputSig<LocationSig Location> {
|
||||
class SuccessorType {
|
||||
/** Gets a textual representation of this successor type. */
|
||||
string toString();
|
||||
}
|
||||
signature class TypSig;
|
||||
|
||||
signature module SuccessorTypesSig<TypSig SuccessorType> {
|
||||
class ExceptionSuccessor extends SuccessorType;
|
||||
|
||||
class ConditionalSuccessor extends SuccessorType {
|
||||
@@ -70,61 +68,12 @@ signature module InputSig<LocationSig Location> {
|
||||
class BooleanSuccessor extends ConditionalSuccessor;
|
||||
|
||||
class NullnessSuccessor extends ConditionalSuccessor;
|
||||
}
|
||||
|
||||
/** A control flow node. */
|
||||
class ControlFlowNode {
|
||||
/** Gets a textual representation of this control flow node. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this control flow node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
signature module InputSig<LocationSig Location, TypSig ControlFlowNode, TypSig BasicBlock> {
|
||||
/** A control flow node indicating normal termination of a callable. */
|
||||
class NormalExitNode extends ControlFlowNode;
|
||||
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
*/
|
||||
class BasicBlock {
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString();
|
||||
|
||||
/** Gets the `i`th node in this basic block. */
|
||||
ControlFlowNode getNode(int i);
|
||||
|
||||
/** Gets the last control flow node in this basic block. */
|
||||
ControlFlowNode getLastNode();
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length();
|
||||
|
||||
/** Gets the location of this basic block. */
|
||||
Location getLocation();
|
||||
|
||||
BasicBlock getASuccessor(SuccessorType t);
|
||||
|
||||
predicate dominates(BasicBlock bb);
|
||||
|
||||
predicate strictlyDominates(BasicBlock bb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb1` has `bb2` as a direct successor and the edge between `bb1`
|
||||
* and `bb2` is a dominating edge.
|
||||
*
|
||||
* An edge `(bb1, bb2)` is dominating if there exists a basic block that can
|
||||
* only be reached from the entry block by going through `(bb1, bb2)`. This
|
||||
* implies that `(bb1, bb2)` dominates its endpoint `bb2`. I.e., `bb2` can
|
||||
* only be reached from the entry block by going via `(bb1, bb2)`.
|
||||
*
|
||||
* This is a necessary and sufficient condition for an edge to dominate some
|
||||
* block, and therefore `dominatingEdge(bb1, bb2) and bb2.dominates(bb3)`
|
||||
* means that the edge `(bb1, bb2)` dominates `bb3`.
|
||||
*/
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2);
|
||||
|
||||
class AstNode {
|
||||
/** Gets a textual representation of this AST node. */
|
||||
string toString();
|
||||
@@ -254,7 +203,15 @@ signature module InputSig<LocationSig Location> {
|
||||
}
|
||||
|
||||
/** Provides guards-related predicates and classes. */
|
||||
module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
module Make<
|
||||
LocationSig Location, BB::CfgSig<Location> Cfg,
|
||||
SuccessorTypesSig<Cfg::SuccessorType> SuccessorTypes,
|
||||
InputSig<Location, Cfg::ControlFlowNode, Cfg::BasicBlock> Input>
|
||||
{
|
||||
private module Cfg_ = Cfg;
|
||||
|
||||
private import Cfg_
|
||||
private import SuccessorTypes
|
||||
private import Input
|
||||
|
||||
private newtype TAbstractSingleValue =
|
||||
|
||||
@@ -8,66 +8,14 @@ module;
|
||||
private import codeql.util.Boolean
|
||||
private import codeql.util.Unit
|
||||
private import codeql.util.Location
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
private import codeql.ssa.Ssa as Ssa
|
||||
|
||||
signature module InputSig<LocationSig Location> {
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
*/
|
||||
class BasicBlock {
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString();
|
||||
signature class BasicBlockSig;
|
||||
|
||||
/** Gets the `i`th node in this basic block. */
|
||||
ControlFlowNode getNode(int i);
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length();
|
||||
|
||||
/** Gets the enclosing callable. */
|
||||
Callable getEnclosingCallable();
|
||||
|
||||
/** Gets the location of this basic block. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/** A control flow node. */
|
||||
class ControlFlowNode {
|
||||
/** Gets a textual representation of this control flow node. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this control flow node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates basic block `bb`, if any.
|
||||
*
|
||||
* That is, all paths reaching `bb` from some entry point basic block must go
|
||||
* through the result.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```csharp
|
||||
* int M(string s) {
|
||||
* if (s == null)
|
||||
* throw new ArgumentNullException(nameof(s));
|
||||
* return s.Length;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The basic block starting on line 2 is an immediate dominator of
|
||||
* the basic block on line 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check.
|
||||
*/
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb);
|
||||
|
||||
/** Gets an immediate successor of basic block `bb`, if any. */
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb);
|
||||
|
||||
/** Holds if `bb` is a control-flow entry point. */
|
||||
default predicate entryBlock(BasicBlock bb) { not exists(getImmediateBasicBlockDominator(bb)) }
|
||||
signature module InputSig<LocationSig Location, BasicBlockSig BasicBlock> {
|
||||
/** Gets the enclosing callable of the basic block. */
|
||||
Callable basicBlockGetEnclosingCallable(BasicBlock bb);
|
||||
|
||||
/** A variable that is captured in a closure. */
|
||||
class CapturedVariable {
|
||||
@@ -153,7 +101,9 @@ signature module InputSig<LocationSig Location> {
|
||||
}
|
||||
}
|
||||
|
||||
signature module OutputSig<LocationSig Location, InputSig<Location> I> {
|
||||
signature module OutputSig<
|
||||
LocationSig Location, BasicBlockSig BasicBlock, InputSig<Location, BasicBlock> I>
|
||||
{
|
||||
/**
|
||||
* A data flow node that we need to reference in the step relations for
|
||||
* captured variables.
|
||||
@@ -255,9 +205,18 @@ signature module OutputSig<LocationSig Location, InputSig<Location> I> {
|
||||
* Constructs the type `ClosureNode` and associated step relations, which are
|
||||
* intended to be included in the data-flow node and step relations.
|
||||
*/
|
||||
module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig<Location, Input> {
|
||||
module Flow<
|
||||
LocationSig Location, BB::CfgSig<Location> Cfg, InputSig<Location, Cfg::BasicBlock> Input>
|
||||
implements OutputSig<Location, Cfg::BasicBlock, Input>
|
||||
{
|
||||
private import Input
|
||||
|
||||
final private class CfgBb = Cfg::BasicBlock;
|
||||
|
||||
private class BasicBlock extends CfgBb {
|
||||
Callable getEnclosingCallable() { result = basicBlockGetEnclosingCallable(this) }
|
||||
}
|
||||
|
||||
additional module ConsistencyChecks {
|
||||
final private class FinalExpr = Expr;
|
||||
|
||||
@@ -332,17 +291,17 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
|
||||
query predicate uniqueDominator(RelevantBasicBlock bb, string msg) {
|
||||
msg = "BasicBlock has multiple immediate dominators" and
|
||||
2 <= strictcount(getImmediateBasicBlockDominator(bb))
|
||||
2 <= strictcount(bb.getImmediateDominator())
|
||||
}
|
||||
|
||||
query predicate localDominator(RelevantBasicBlock bb, string msg) {
|
||||
msg = "BasicBlock has non-local dominator" and
|
||||
bb.getEnclosingCallable() != getImmediateBasicBlockDominator(bb).getEnclosingCallable()
|
||||
bb.getEnclosingCallable() != bb.getImmediateDominator().(BasicBlock).getEnclosingCallable()
|
||||
}
|
||||
|
||||
query predicate localSuccessor(RelevantBasicBlock bb, string msg) {
|
||||
msg = "BasicBlock has non-local successor" and
|
||||
bb.getEnclosingCallable() != getABasicBlockSuccessor(bb).getEnclosingCallable()
|
||||
bb.getEnclosingCallable() != bb.getASuccessor().(BasicBlock).getEnclosingCallable()
|
||||
}
|
||||
|
||||
query predicate uniqueDefiningScope(CapturedVariable v, string msg) {
|
||||
@@ -669,7 +628,7 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
/** Holds if `cc` needs a definition at the entry of its callable scope. */
|
||||
private predicate entryDef(CaptureContainer cc, BasicBlock bb, int i) {
|
||||
exists(Callable c |
|
||||
entryBlock(bb) and
|
||||
bb instanceof Cfg::EntryBasicBlock and
|
||||
pragma[only_bind_out](bb.getEnclosingCallable()) = c and
|
||||
i =
|
||||
min(int j |
|
||||
@@ -685,22 +644,10 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
)
|
||||
}
|
||||
|
||||
private module CaptureSsaInput implements Ssa::InputSig<Location> {
|
||||
final class BasicBlock = Input::BasicBlock;
|
||||
|
||||
final class ControlFlowNode = Input::ControlFlowNode;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
|
||||
result = Input::getImmediateBasicBlockDominator(bb)
|
||||
}
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) {
|
||||
result = Input::getABasicBlockSuccessor(bb)
|
||||
}
|
||||
|
||||
private module CaptureSsaInput implements Ssa::InputSig<Location, Cfg::BasicBlock> {
|
||||
class SourceVariable = CaptureContainer;
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable cc, boolean certain) {
|
||||
predicate variableWrite(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) {
|
||||
Cached::ref() and
|
||||
(
|
||||
exists(CapturedVariable v | cc = TVariable(v) and captureWrite(v, bb, i, true, _))
|
||||
@@ -710,9 +657,9 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
certain = true
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable cc, boolean certain) {
|
||||
predicate variableRead(Cfg::BasicBlock bb, int i, SourceVariable cc, boolean certain) {
|
||||
(
|
||||
synthThisQualifier(bb, i) and cc = TThis(bb.getEnclosingCallable())
|
||||
synthThisQualifier(bb, i) and cc = TThis(bb.(BasicBlock).getEnclosingCallable())
|
||||
or
|
||||
exists(CapturedVariable v | cc = TVariable(v) |
|
||||
captureRead(v, bb, i, true, _) or synthRead(v, bb, i, true, _)
|
||||
@@ -722,26 +669,30 @@ module Flow<LocationSig Location, InputSig<Location> Input> implements OutputSig
|
||||
}
|
||||
}
|
||||
|
||||
private module CaptureSsa = Ssa::Make<Location, CaptureSsaInput>;
|
||||
private module CaptureSsa = Ssa::Make<Location, Cfg, CaptureSsaInput>;
|
||||
|
||||
private module DataFlowIntegrationInput implements CaptureSsa::DataFlowIntegrationInputSig {
|
||||
private import codeql.util.Void
|
||||
|
||||
class Expr instanceof Input::ControlFlowNode {
|
||||
class Expr instanceof Cfg::ControlFlowNode {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
predicate hasCfgNode(BasicBlock bb, int i) { bb.getNode(i) = this }
|
||||
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { bb.getNode(i) = this }
|
||||
}
|
||||
|
||||
class GuardValue = Void;
|
||||
|
||||
class Guard extends Void {
|
||||
predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() }
|
||||
predicate hasValueBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) {
|
||||
none()
|
||||
}
|
||||
|
||||
predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() }
|
||||
predicate valueControlsBranchEdge(Cfg::BasicBlock bb1, Cfg::BasicBlock bb2, GuardValue val) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue val) { none() }
|
||||
predicate guardDirectlyControlsBlock(Guard guard, Cfg::BasicBlock bb, GuardValue val) { none() }
|
||||
|
||||
predicate includeWriteDefsInFlowStep() { none() }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ version: 2.0.14-dev
|
||||
groups: shared
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/controlflow: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/typetracking: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
|
||||
@@ -5,63 +5,14 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Unit
|
||||
|
||||
signature class BasicBlockSig;
|
||||
|
||||
/** Provides the input specification of the SSA implementation. */
|
||||
signature module InputSig<LocationSig Location> {
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
*/
|
||||
class BasicBlock {
|
||||
/** Gets a textual representation of this basic block. */
|
||||
string toString();
|
||||
|
||||
/** Gets the `i`th node in this basic block. */
|
||||
ControlFlowNode getNode(int i);
|
||||
|
||||
/** Gets the length of this basic block. */
|
||||
int length();
|
||||
|
||||
/** Gets the location of this basic block. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/** A control flow node. */
|
||||
class ControlFlowNode {
|
||||
/** Gets a textual representation of this control flow node. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this control flow node. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates basic block `bb`, if any.
|
||||
*
|
||||
* That is, all paths reaching `bb` from some entry point basic block must go
|
||||
* through the result.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```csharp
|
||||
* int M(string s) {
|
||||
* if (s == null)
|
||||
* throw new ArgumentNullException(nameof(s));
|
||||
* return s.Length;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The basic block starting on line 2 is an immediate dominator of
|
||||
* the basic block on line 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check.
|
||||
*/
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb);
|
||||
|
||||
/** Gets an immediate successor of basic block `bb`, if any. */
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb);
|
||||
|
||||
signature module InputSig<LocationSig Location, BasicBlockSig BasicBlock> {
|
||||
/** A variable that can be SSA converted. */
|
||||
class SourceVariable {
|
||||
/** Gets a textual representation of this variable. */
|
||||
@@ -108,12 +59,13 @@ signature module InputSig<LocationSig Location> {
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
* ```
|
||||
*/
|
||||
module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
module Make<
|
||||
LocationSig Location, BB::CfgSig<Location> Cfg, InputSig<Location, Cfg::BasicBlock> Input>
|
||||
{
|
||||
private import Cfg
|
||||
private import Input
|
||||
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) {
|
||||
getABasicBlockSuccessor(result) = bb
|
||||
}
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { result.getASuccessor() = bb }
|
||||
|
||||
/**
|
||||
* A classification of variable references into reads and
|
||||
@@ -237,9 +189,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
/**
|
||||
* Holds if source variable `v` is live at the end of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
|
||||
liveAtEntry(getABasicBlockSuccessor(bb), v)
|
||||
}
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) { liveAtEntry(bb.getASuccessor(), v) }
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at rank `rnk`.
|
||||
@@ -270,25 +220,6 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
|
||||
private import Liveness
|
||||
|
||||
/**
|
||||
* Holds if `df` is in the dominance frontier of `bb`.
|
||||
*
|
||||
* This is equivalent to:
|
||||
*
|
||||
* ```ql
|
||||
* bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and
|
||||
* not bb = getImmediateBasicBlockDominator+(df)
|
||||
* ```
|
||||
*/
|
||||
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
|
||||
bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df)
|
||||
or
|
||||
exists(BasicBlock prev | inDominanceFrontier(prev, df) |
|
||||
bb = getImmediateBasicBlockDominator(prev) and
|
||||
not bb = getImmediateBasicBlockDominator(df)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* definition of `v`.
|
||||
@@ -297,7 +228,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock defbb, Definition def |
|
||||
def.definesAt(v, defbb, _) and
|
||||
inDominanceFrontier(defbb, bb)
|
||||
defbb.inDominanceFrontier(bb)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -307,7 +238,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
|
||||
exists(BasicBlock readbb | readbb.inDominanceFrontier(bb) |
|
||||
ssaDefReachesRead(v, _, readbb, _) and
|
||||
variableRead(readbb, _, v, true) and
|
||||
not variableWrite(readbb, _, v, _)
|
||||
@@ -389,7 +320,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
|
||||
idom = getImmediateBasicBlockDominator(bb) and
|
||||
idom = bb.getImmediateDominator() and
|
||||
liveAtExit(bb, v) and
|
||||
not any(Definition def).definesAt(v, bb, _)
|
||||
}
|
||||
@@ -439,7 +370,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, Read()) and
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, v) and
|
||||
ssaDefReachesEndOfBlock(bb.getImmediateDominator(), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
@@ -483,7 +414,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
|
||||
idom = getImmediateBasicBlockDominator(bb) and
|
||||
idom = bb.getImmediateDominator() and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, _)
|
||||
}
|
||||
@@ -517,7 +448,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
bb1 = bb2 and
|
||||
refRank(bb1, i1, v, _) + 1 = refRank(bb2, i2, v, Read())
|
||||
or
|
||||
refReachesEndOfBlock(bb1, i1, getImmediateBasicBlockDominator(bb2), v) and
|
||||
refReachesEndOfBlock(bb1, i1, bb2.getImmediateDominator(), v) and
|
||||
1 = refRank(bb2, i2, v, Read())
|
||||
}
|
||||
|
||||
@@ -808,12 +739,12 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
DefinitionExt def, SourceVariable v, BasicBlock bb1, BasicBlock bb2
|
||||
) {
|
||||
defOccursInBlock(def, bb1, v, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
bb2 = bb1.getASuccessor()
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExt(def, v, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
bb2 = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -943,7 +874,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlockExt0(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
ssaDefReachesEndOfBlockExt0(bb.getImmediateDominator(), def, pragma[only_bind_into](v)) and
|
||||
liveThroughExt(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
@@ -1150,7 +1081,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
predicate uncertainWriteDefinitionInput = SsaDefReachesNew::uncertainWriteDefinitionInput/2;
|
||||
|
||||
/** Holds if `bb` is a control-flow exit point. */
|
||||
private predicate exitBlock(BasicBlock bb) { not exists(getABasicBlockSuccessor(bb)) }
|
||||
private predicate exitBlock(BasicBlock bb) { not exists(bb.getASuccessor()) }
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
@@ -1418,7 +1349,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not SsaDefReachesNew::ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
not def.definesAt(v, bb.getImmediateDominator*(), _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1667,7 +1598,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
DfInput::keepAllPhiInputBackEdges() and
|
||||
exists(getAPhiInputDef(phi, input)) and
|
||||
phi.getBasicBlock() = bbPhi and
|
||||
getImmediateBasicBlockDominator+(input) = bbPhi
|
||||
input.getImmediateDominator+() = bbPhi
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,5 +3,6 @@ version: 2.0.6-dev
|
||||
groups: shared
|
||||
library: true
|
||||
dependencies:
|
||||
codeql/controlflow: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
warnOnImplicitThis: true
|
||||
|
||||
Reference in New Issue
Block a user