Java/C++: Move SsaReadPosition to shared qlpack.

This commit is contained in:
Anders Schack-Mulligen
2023-11-03 15:43:44 +01:00
parent ab64d9a9d6
commit 1f4cd74a1c
11 changed files with 203 additions and 257 deletions

View File

@@ -21,7 +21,7 @@ module ModulusAnalysis<
bindingset[pos, v]
pragma[inline_late]
private predicate hasReadOfVarInlineLate(Sem::SsaReadPosition pos, Sem::SsaVariable v) {
private predicate hasReadOfVarInlineLate(SsaReadPosition pos, Sem::SsaVariable v) {
pos.hasReadOfVar(v)
}
@@ -29,9 +29,7 @@ module ModulusAnalysis<
* Holds if `e + delta` equals `v` at `pos`.
*/
pragma[nomagic]
private predicate valueFlowStepSsa(
Sem::SsaVariable v, Sem::SsaReadPosition pos, Sem::Expr e, int delta
) {
private predicate valueFlowStepSsa(Sem::SsaVariable v, SsaReadPosition pos, Sem::Expr e, int delta) {
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
or
exists(Sem::Guard guard, boolean testIsTrue |
@@ -105,7 +103,7 @@ module ModulusAnalysis<
/**
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
*/
private predicate moduloGuardedRead(Sem::SsaVariable v, Sem::SsaReadPosition pos, int val, int mod) {
private predicate moduloGuardedRead(Sem::SsaVariable v, SsaReadPosition pos, int val, int mod) {
exists(Sem::Guard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = moduloCheck(v, val, mod, testIsTrue) and
@@ -148,7 +146,7 @@ module ModulusAnalysis<
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
*/
private predicate phiSelfModulus(
Sem::SsaPhiNode phi, Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, int mod
Sem::SsaPhiNode phi, Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge, int mod
) {
exists(Bounds::SemSsaBound phibound, int v, int m |
edge.phiInput(phi, inp) and
@@ -163,7 +161,7 @@ module ModulusAnalysis<
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
*/
private predicate phiModulusInit(Sem::SsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
exists(Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge |
exists(Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge |
edge.phiInput(phi, inp) and
ssaModulus(inp, edge, b, val, mod)
)
@@ -181,7 +179,7 @@ module ModulusAnalysis<
rix = 0 and
phiModulusInit(phi, b, val, mod)
or
exists(Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, int v1, int m1 |
exists(Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge, int v1, int m1 |
mod != 1 and
val = remainder(v1, mod)
|
@@ -192,7 +190,7 @@ module ModulusAnalysis<
// equals `v2` modulo `mod`. The largest value of `mod` that satisfies
// this is the greatest common divisor of `m1`, `m2`, and `v1 - v2`.
exists(int v2, int m2 |
U::rankedPhiInput(phi, inp, edge, rix) and
rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
ssaModulus(inp, edge, b, v2, m2) and
mod = m1.gcd(m2).gcd(v1 - v2)
@@ -202,7 +200,7 @@ module ModulusAnalysis<
// preceding potential congruence class `b + v1` mod `m1`. The result will be
// the congruence class modulo the greatest common divisor of `m1` and `m2`.
exists(int m2 |
U::rankedPhiInput(phi, inp, edge, rix) and
rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
phiSelfModulus(phi, inp, edge, m2) and
mod = m1.gcd(m2)
@@ -215,7 +213,7 @@ module ModulusAnalysis<
*/
private predicate phiModulus(Sem::SsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
exists(int r |
U::maxPhiInputRank(phi, r) and
maxPhiInputRank(phi, r) and
phiModulusRankStep(phi, b, val, mod, r)
)
}
@@ -224,7 +222,7 @@ module ModulusAnalysis<
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
*/
private predicate ssaModulus(
Sem::SsaVariable v, Sem::SsaReadPosition pos, Bounds::SemBound b, int val, int mod
Sem::SsaVariable v, SsaReadPosition pos, Bounds::SemBound b, int val, int mod
) {
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
or
@@ -254,7 +252,7 @@ module ModulusAnalysis<
val = 0 and
b instanceof Bounds::SemZeroBound
or
exists(Sem::SsaVariable v, Sem::SsaReadPositionBlock bb |
exists(Sem::SsaVariable v, SsaReadPositionBlock bb |
ssaModulus(v, bb, b, val, mod) and
e = v.getAUse() and
bb.getBlock() = e.getBasicBlock()

View File

@@ -150,6 +150,16 @@ signature module Semantic {
/** Gets an immediate successor of basic block `bb`, if any. */
BasicBlock getABasicBlockSuccessor(BasicBlock bb);
/**
* Gets an ideally unique integer for `bb`. If it is undesirable to make this
* unique, then `getBlock2` must provide a tiebreaker, such that the pair
* `(getBlockId1(bb),getBlockId2(bb))` becomes unique.
*/
int getBlockId1(BasicBlock bb);
/** Gets a tiebreaker id in case `getBlockId1` is not unique. */
default string getBlockId2(BasicBlock bb) { result = "" }
class Guard {
string toString();
@@ -184,28 +194,15 @@ signature module Semantic {
BasicBlock getBasicBlock();
}
class SsaPhiNode extends SsaVariable;
class SsaPhiNode extends SsaVariable {
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(SsaVariable inp, BasicBlock bb);
}
class SsaExplicitUpdate extends SsaVariable {
Expr getDefiningExpr();
}
class SsaReadPosition {
predicate hasReadOfVar(SsaVariable v);
}
class SsaReadPositionPhiInputEdge extends SsaReadPosition {
BasicBlock getOrigBlock();
BasicBlock getPhiBlock();
predicate phiInput(SsaPhiNode phi, SsaVariable inp);
}
class SsaReadPositionBlock extends SsaReadPosition {
BasicBlock getBlock();
}
predicate conversionCannotOverflow(Type fromType, Type toType);
}
@@ -330,19 +327,6 @@ signature module UtilSig<Semantic Sem, DeltaSig DeltaParam> {
* primitive types as the underlying primitive type.
*/
Sem::Type getTrackedType(Sem::Expr e);
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
predicate rankedPhiInput(
Sem::SsaPhiNode phi, Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, int r
);
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
predicate maxPhiInputRank(Sem::SsaPhiNode phi, int rix);
}
signature module BoundSig<LocationSig Location, Semantic Sem, DeltaSig D> {
@@ -688,7 +672,7 @@ module RangeStage<
* - `upper = false` : `v >= e + delta`
*/
private predicate boundFlowStepSsa(
Sem::SsaVariable v, Sem::SsaReadPosition pos, Sem::Expr e, D::Delta delta, boolean upper,
Sem::SsaVariable v, SsaReadPosition pos, Sem::Expr e, D::Delta delta, boolean upper,
SemReason reason
) {
semSsaUpdateStep(v, e, delta) and
@@ -706,7 +690,7 @@ module RangeStage<
/** Holds if `v != e + delta` at `pos` and `v` is of integral type. */
private predicate unequalFlowStepIntegralSsa(
Sem::SsaVariable v, Sem::SsaReadPosition pos, Sem::Expr e, D::Delta delta, SemReason reason
Sem::SsaVariable v, SsaReadPosition pos, Sem::Expr e, D::Delta delta, SemReason reason
) {
getTrackedTypeForSsaVariable(v) instanceof Sem::IntegerType and
exists(Sem::Guard guard, boolean testIsTrue |
@@ -836,7 +820,7 @@ module RangeStage<
* - `upper = false` : `v >= b + delta`
*/
private predicate boundedSsa(
Sem::SsaVariable v, SemBound b, D::Delta delta, Sem::SsaReadPosition pos, boolean upper,
Sem::SsaVariable v, SemBound b, D::Delta delta, SsaReadPosition pos, boolean upper,
boolean fromBackEdge, D::Delta origdelta, SemReason reason
) {
exists(Sem::Expr mid, D::Delta d1, D::Delta d2, SemReason r1, SemReason r2 |
@@ -873,7 +857,7 @@ module RangeStage<
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
*/
private predicate unequalIntegralSsa(
Sem::SsaVariable v, SemBound b, D::Delta delta, Sem::SsaReadPosition pos, SemReason reason
Sem::SsaVariable v, SemBound b, D::Delta delta, SsaReadPosition pos, SemReason reason
) {
exists(Sem::Expr e, D::Delta d1, D::Delta d2 |
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
@@ -920,7 +904,7 @@ module RangeStage<
* - `upper = false` : `inp >= b + delta`
*/
private predicate boundedPhiInp(
Sem::SsaPhiNode phi, Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, SemBound b,
Sem::SsaPhiNode phi, Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge, SemBound b,
D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta, SemReason reason
) {
edge.phiInput(phi, inp) and
@@ -964,7 +948,7 @@ module RangeStage<
pragma[noinline]
private predicate boundedPhiInp1(
Sem::SsaPhiNode phi, SemBound b, boolean upper, Sem::SsaVariable inp,
Sem::SsaReadPositionPhiInputEdge edge, D::Delta delta
SsaReadPositionPhiInputEdge edge, D::Delta delta
) {
boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)
}
@@ -976,7 +960,7 @@ module RangeStage<
* - `upper = false` : `inp >= phi`
*/
private predicate selfBoundedPhiInp(
Sem::SsaPhiNode phi, Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge, boolean upper
Sem::SsaPhiNode phi, Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge, boolean upper
) {
exists(D::Delta d, SemSsaBound phibound |
phibound.getVariable() = phi and
@@ -1009,8 +993,7 @@ module RangeStage<
*/
private predicate boundedPhiCandValidForEdge(
Sem::SsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,
D::Delta origdelta, SemReason reason, Sem::SsaVariable inp,
Sem::SsaReadPositionPhiInputEdge edge
D::Delta origdelta, SemReason reason, Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge
) {
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
(
@@ -1036,7 +1019,7 @@ module RangeStage<
Sem::SsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,
D::Delta origdelta, SemReason reason, int rix
) {
exists(Sem::SsaVariable inp, Sem::SsaReadPositionPhiInputEdge edge |
exists(Sem::SsaVariable inp, SsaReadPositionPhiInputEdge edge |
rankedPhiInput(phi, inp, edge, rix) and
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
|
@@ -1208,7 +1191,7 @@ module RangeStage<
origdelta = delta and
reason = TSemNoReason()
or
exists(Sem::SsaVariable v, Sem::SsaReadPositionBlock bb |
exists(Sem::SsaVariable v, SsaReadPositionBlock bb |
boundedSsa(v, b, delta, bb, upper, fromBackEdge, origdelta, reason) and
e = v.getAUse() and
bb.getBlock() = e.getBasicBlock()

View File

@@ -1,38 +1,99 @@
private import codeql.rangeanalysis.RangeAnalysis
module MakeUtils<Semantic Lang, DeltaSig D> {
private import Lang
/**
* Gets an expression that equals `v - d`.
*/
Lang::Expr ssaRead(Lang::SsaVariable v, D::Delta delta) {
Expr ssaRead(SsaVariable v, D::Delta delta) {
result = v.getAUse() and delta = D::fromInt(0)
or
exists(D::Delta d1, Lang::ConstantIntegerExpr c |
result.(Lang::AddExpr).hasOperands(ssaRead(v, d1), c) and
exists(D::Delta d1, ConstantIntegerExpr c |
result.(AddExpr).hasOperands(ssaRead(v, d1), c) and
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue()) and
// In the scope of `x += ..`, which is SSA translated as `x2 = x1 + ..`,
// the variable `x1` is shadowed by `x2`, so there's no need to view this
// as a read of `x1`.
not Lang::isAssignOp(result)
not isAssignOp(result)
)
or
exists(Lang::SubExpr sub, D::Delta d1, Lang::ConstantIntegerExpr c |
exists(SubExpr sub, D::Delta d1, ConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = ssaRead(v, d1) and
sub.getRightOperand() = c and
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
not Lang::isAssignOp(result)
not isAssignOp(result)
)
or
result = v.(Lang::SsaExplicitUpdate).getDefiningExpr() and
if result instanceof Lang::PostIncExpr
result = v.(SsaExplicitUpdate).getDefiningExpr() and
if result instanceof PostIncExpr
then delta = D::fromFloat(1) // x++ === ++x - 1
else
if result instanceof Lang::PostDecExpr
if result instanceof PostDecExpr
then delta = D::fromFloat(-1) // x-- === --x + 1
else delta = D::fromFloat(0)
or
result.(Lang::CopyValueExpr).getOperand() = ssaRead(v, delta)
result.(CopyValueExpr).getOperand() = ssaRead(v, delta)
}
private newtype TSsaReadPosition =
TSsaReadPositionBlock(BasicBlock bb) {
exists(SsaVariable v | v.getAUse().getBasicBlock() = bb)
} or
TSsaReadPositionPhiInputEdge(BasicBlock bbOrig, BasicBlock bbPhi) {
exists(SsaPhiNode phi | phi.hasInputFromBlock(_, bbOrig) and bbPhi = phi.getBasicBlock())
}
/**
* A position at which an SSA variable is read. This includes both ordinary
* reads occurring in basic blocks and input to phi nodes occurring along an
* edge between two basic blocks.
*/
class SsaReadPosition extends TSsaReadPosition {
/** Holds if `v` is read at this position. */
abstract predicate hasReadOfVar(SsaVariable v);
/** Gets a textual representation of this SSA read position. */
abstract string toString();
}
/** A basic block in which an SSA variable is read. */
class SsaReadPositionBlock extends SsaReadPosition, TSsaReadPositionBlock {
/** Gets the basic block corresponding to this position. */
BasicBlock getBlock() { this = TSsaReadPositionBlock(result) }
override predicate hasReadOfVar(SsaVariable v) { exists(this.getAnSsaRead(v)) }
pragma[nomagic]
Expr getAnSsaRead(SsaVariable v) {
v.getAUse() = result and result.getBasicBlock() = this.getBlock()
}
override string toString() { result = "block" }
}
/**
* An edge between two basic blocks where the latter block has an SSA phi
* definition. The edge therefore has a read of an SSA variable serving as the
* input to the phi node.
*/
class SsaReadPositionPhiInputEdge extends SsaReadPosition, TSsaReadPositionPhiInputEdge {
/** Gets the source of the edge. */
BasicBlock getOrigBlock() { this = TSsaReadPositionPhiInputEdge(result, _) }
/** Gets the target of the edge. */
BasicBlock getPhiBlock() { this = TSsaReadPositionPhiInputEdge(_, result) }
override predicate hasReadOfVar(SsaVariable v) { this.phiInput(_, v) }
/** Holds if `inp` is an input to `phi` along this edge. */
predicate phiInput(SsaPhiNode phi, SsaVariable inp) {
phi.hasInputFromBlock(inp, this.getOrigBlock()) and
this.getPhiBlock() = phi.getBasicBlock()
}
override string toString() { result = "edge" }
}
/**
@@ -40,10 +101,10 @@ module MakeUtils<Semantic Lang, DeltaSig D> {
* value `testIsTrue`.
*/
pragma[nomagic]
predicate guardDirectlyControlsSsaRead(Lang::Guard guard, Lang::SsaReadPosition controlled, boolean testIsTrue) {
guard.directlyControls(controlled.(Lang::SsaReadPositionBlock).getBlock(), testIsTrue)
predicate guardDirectlyControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
guard.directlyControls(controlled.(SsaReadPositionBlock).getBlock(), testIsTrue)
or
exists(Lang::SsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
exists(SsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
)
@@ -52,11 +113,11 @@ module MakeUtils<Semantic Lang, DeltaSig D> {
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate guardControlsSsaRead(Lang::Guard guard, Lang::SsaReadPosition controlled, boolean testIsTrue) {
predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
guardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
or
exists(Lang::Guard guard0, boolean testIsTrue0 |
Lang::implies_v2(guard0, testIsTrue0, guard, testIsTrue) and
exists(Guard guard0, boolean testIsTrue0 |
implies_v2(guard0, testIsTrue0, guard, testIsTrue) and
guardControlsSsaRead(guard0, controlled, testIsTrue0)
)
}
@@ -64,9 +125,7 @@ module MakeUtils<Semantic Lang, DeltaSig D> {
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
predicate backEdge(
Lang::SsaPhiNode phi, Lang::SsaVariable inp, Lang::SsaReadPositionPhiInputEdge edge
) {
predicate backEdge(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge) {
edge.phiInput(phi, inp) and
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
@@ -85,12 +144,33 @@ module MakeUtils<Semantic Lang, DeltaSig D> {
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
* edges.
*/
private predicate irreducibleSccEdge(Lang::BasicBlock b1, Lang::BasicBlock b2) {
private predicate irreducibleSccEdge(BasicBlock b1, BasicBlock b2) {
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
}
private predicate trimmedEdge(Lang::BasicBlock pred, Lang::BasicBlock succ) {
Lang::getABasicBlockSuccessor(pred) = succ and
private predicate trimmedEdge(BasicBlock pred, BasicBlock succ) {
getABasicBlockSuccessor(pred) = succ and
not succ.bbDominates(pred)
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
predicate rankedPhiInput(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int r) {
edge.phiInput(phi, inp) and
edge =
rank[r](SsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by getBlockId1(e.getOrigBlock()), getBlockId2(e.getOrigBlock())
)
}
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
predicate maxPhiInputRank(SsaPhiNode phi, int rix) {
rix = max(int r | rankedPhiInput(phi, _, _, r))
}
}