mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Merge pull request #18819 from aschackmull/ssa/refactor-phiread3
Ssa: Refactor shared SSA in preparation for eliminating phi-read definitions
This commit is contained in:
@@ -114,43 +114,41 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
* A classification of variable references into reads and
|
||||
* (certain or uncertain) writes / definitions.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* A classification of variable references into reads (of a given kind) and
|
||||
* (certain or uncertain) writes.
|
||||
*/
|
||||
private newtype TRefKind =
|
||||
Read(boolean certain) { certain in [false, true] } or
|
||||
Write(boolean certain) { certain in [false, true] }
|
||||
private newtype TRefKind =
|
||||
Read() or
|
||||
Write(boolean certain) { certain in [false, true] } or
|
||||
Def()
|
||||
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
|
||||
or
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read(_) and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
this = Read() and result = "read"
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
or
|
||||
this = Def() and result = "def"
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read() and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
or
|
||||
this = Def() and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
private signature predicate refSig(BasicBlock bb, int i, SourceVariable v, RefKind k);
|
||||
|
||||
private module RankRefs<refSig/4 ref> {
|
||||
private newtype OrderedRefIndex =
|
||||
MkOrderedRefIndex(int i, int tag) {
|
||||
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
|
||||
@@ -168,7 +166,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
refOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedRefIndex res |
|
||||
res = refOrd(bb, j, v, _, ord)
|
||||
@@ -177,18 +175,39 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
)
|
||||
}
|
||||
|
||||
private int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = refRank(bb, _, v, _) and
|
||||
not result + 1 = refRank(bb, _, v, _)
|
||||
}
|
||||
}
|
||||
|
||||
predicate lastRefIsRead(BasicBlock bb, SourceVariable v) {
|
||||
maxRefRank(bb, v) = refRank(bb, _, v, Read(_))
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
predicate varRef(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
variableRead(bb, i, v, _) and k = Read()
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
}
|
||||
|
||||
private import RankRefs<varRef/4>
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
|
||||
* that is either a read or a certain write.
|
||||
*
|
||||
* Note that uncertain writes have no impact on liveness: a variable is
|
||||
* live before an uncertain write if and only if it is live after.
|
||||
* The reference identified here therefore determines liveness at the
|
||||
* beginning of `bb`: if it is a read then the variable is live and if it
|
||||
* is a write then it is not. For basic blocks without reads or certain
|
||||
* writes, liveness at the beginning of the block is equivalent to liveness
|
||||
* at the end of the block.
|
||||
*/
|
||||
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
|
||||
result =
|
||||
@@ -205,7 +224,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*/
|
||||
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
|
||||
// The first read or certain write to `v` inside `bb` is a read
|
||||
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
|
||||
refRank(bb, _, v, Read()) = firstReadOrCertainWrite(bb, v)
|
||||
or
|
||||
// There is no certain write to `v` inside `bb`, but `v` is live at entry
|
||||
// to a successor basic block of `bb`
|
||||
@@ -221,19 +240,17 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
* Holds if variable `v` is live in basic block `bb` at rank `rnk`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
|
||||
private predicate liveAtRank(BasicBlock bb, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, _, v, kind) |
|
||||
rnk = maxRefRank(bb, v) and
|
||||
liveAtExit(bb, v)
|
||||
or
|
||||
ref(bb, i, v, kind) and
|
||||
kind = Read(_)
|
||||
kind = Read()
|
||||
or
|
||||
exists(RefKind nextKind |
|
||||
liveAtRank(bb, _, v, rnk + 1) and
|
||||
liveAtRank(bb, v, rnk + 1) and
|
||||
rnk + 1 = refRank(bb, _, v, nextKind) and
|
||||
nextKind != Write(true)
|
||||
)
|
||||
@@ -245,7 +262,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
* index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, v, rnk))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,13 +306,29 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
pragma[nomagic]
|
||||
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
|
||||
lastRefIsRead(readbb, v)
|
||||
ssaDefReachesRead(v, _, readbb, _) and
|
||||
not variableWrite(readbb, _, v, _)
|
||||
or
|
||||
exists(TPhiReadNode(v, readbb)) and
|
||||
not ref(readbb, _, v, _)
|
||||
synthPhiRead(readbb, v) and
|
||||
not varRef(readbb, _, v, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we should synthesize a pseudo-read of `v` at the beginning of `bb`.
|
||||
*
|
||||
* These reads are named phi-reads, since they are constructed in the same
|
||||
* way as ordinary phi nodes except all reads are treated as potential
|
||||
* "definitions". This ensures that use-use flow has the same dominance
|
||||
* properties as def-use flow.
|
||||
*/
|
||||
private predicate synthPhiRead(BasicBlock bb, SourceVariable v) {
|
||||
inReadDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v) and
|
||||
// no need to create a phi-read if there is already a normal phi
|
||||
not any(PhiNode def).definesAt(v, bb, _)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDefinitionExt =
|
||||
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
@@ -306,15 +339,273 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
inDefDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v)
|
||||
} or
|
||||
TPhiReadNode(SourceVariable v, BasicBlock bb) {
|
||||
inReadDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v) and
|
||||
// no need to create a phi-read if there is already a normal phi
|
||||
not any(PhiNode def).definesAt(v, bb, _)
|
||||
}
|
||||
TPhiReadNode(SourceVariable v, BasicBlock bb) { synthPhiRead(bb, v) }
|
||||
|
||||
private class TDefinition = TWriteDef or TPhiNode;
|
||||
|
||||
private module SsaDefReachesNew {
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `Read()`) or an SSA definition (when
|
||||
* `k` is `Def()`).
|
||||
*
|
||||
* Unlike `Liveness::varRef`, this includes `phi` nodes and pseudo-reads
|
||||
* associated with uncertain writes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = Read()
|
||||
or
|
||||
variableWrite(bb, i, v, false) and
|
||||
k = Read()
|
||||
or
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = Def()
|
||||
}
|
||||
|
||||
private import RankRefs<ssaRef/4>
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
|
||||
* basic block `bb`.
|
||||
*/
|
||||
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
|
||||
exists(int i |
|
||||
rnk = refRank(bb, i, v, Def()) and
|
||||
def.definesAt(v, bb, i)
|
||||
)
|
||||
or
|
||||
ssaDefReachesRank(bb, def, rnk - 1, v) and
|
||||
rnk = refRank(bb, _, v, Read())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is live at the end of basic block `bb` with the same value as at
|
||||
* the end of the immediate dominator, `idom`, of `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
|
||||
idom = getImmediateBasicBlockDominator(bb) and
|
||||
liveAtExit(bb, v) and
|
||||
not any(Definition def).definesAt(v, bb, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of basic
|
||||
* block `bb`, at which point it is still live, without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(int last |
|
||||
last = maxRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// 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.
|
||||
ssaDefReachesEndOfBlock(idom, def, v) and
|
||||
liveThrough(idom, bb, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches index `i` in its own
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
exists(int rnk |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = refRank(bb, i, v, Read())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, Read()) and
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
exists(SourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
ssaDefReachesRead(v, inp, bb, i)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module AdjacentSsaRefs {
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `Read()`) or an SSA definition (when
|
||||
* `k` is `Def()`).
|
||||
*
|
||||
* Unlike `Liveness::varRef`, this includes phi nodes, phi reads, and
|
||||
* pseudo-reads associated with uncertain writes, but excludes uncertain
|
||||
* reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
variableRead(bb, i, v, true) and
|
||||
k = Read()
|
||||
or
|
||||
variableWrite(bb, i, v, false) and
|
||||
k = Read()
|
||||
or
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = Def()
|
||||
or
|
||||
synthPhiRead(bb, v) and i = -1 and k = Def()
|
||||
}
|
||||
|
||||
private import RankRefs<ssaRef/4>
|
||||
|
||||
/**
|
||||
* Holds if `v` is live at the end of basic block `bb`, which contains no
|
||||
* reference to `v`, and `idom` is the immediate dominator of `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate liveThrough(BasicBlock idom, BasicBlock bb, SourceVariable v) {
|
||||
idom = getImmediateBasicBlockDominator(bb) and
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate refReachesEndOfBlock(BasicBlock bbRef, int i, BasicBlock bb, SourceVariable v) {
|
||||
maxRefRank(bb, v) = refRank(bb, i, v, _) and
|
||||
liveAtExit(bb, v) and
|
||||
bbRef = bb
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
refReachesEndOfBlock(bbRef, i, idom, v) and
|
||||
liveThrough(idom, bb, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` has adjacent references at index `i1` in basic block `bb1`
|
||||
* and index `i2` in basic block `bb2`, that is, there is a path between
|
||||
* the first reference to the second without any other reference to `v` in
|
||||
* between. References include certain reads, SSA definitions, and
|
||||
* pseudo-reads in the form of phi-reads. The first reference can be any of
|
||||
* these kinds while the second is restricted to certain reads and
|
||||
* uncertain writes.
|
||||
*
|
||||
* Note that the placement of phi-reads ensures that the first reference is
|
||||
* uniquely determined by the second and that the first reference dominates
|
||||
* the second.
|
||||
*/
|
||||
predicate adjacentRefRead(BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v) {
|
||||
bb1 = bb2 and
|
||||
refRank(bb1, i1, v, _) + 1 = refRank(bb2, i2, v, Read())
|
||||
or
|
||||
refReachesEndOfBlock(bb1, i1, getImmediateBasicBlockDominator(bb2), v) and
|
||||
1 = refRank(bb2, i2, v, Read())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the phi node or phi-read for `v` in basic block `bbPhi` takes
|
||||
* input from basic block `input`, and that the reference to `v` at index
|
||||
* `i` in basic block `bb` reaches the end of `input` without going through
|
||||
* any other reference to `v`.
|
||||
*/
|
||||
predicate adjacentRefPhi(
|
||||
BasicBlock bb, int i, BasicBlock input, BasicBlock bbPhi, SourceVariable v
|
||||
) {
|
||||
refReachesEndOfBlock(bb, i, input, v) and
|
||||
input = getABasicBlockPredecessor(bbPhi) and
|
||||
1 = refRank(bbPhi, -1, v, _)
|
||||
}
|
||||
|
||||
private predicate adjacentRefs(BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v) {
|
||||
adjacentRefRead(bb1, i1, bb2, i2, v)
|
||||
or
|
||||
adjacentRefPhi(bb1, i1, _, bb2, v) and i2 = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `v` at index `i1` in basic block `bb1` reaches
|
||||
* the certain read at index `i2` in basic block `bb2` without going
|
||||
* through any other certain read. The boolean `samevar` indicates whether
|
||||
* the two references are to the same SSA variable.
|
||||
*
|
||||
* Note that since this relation skips over phi nodes and phi reads, it may
|
||||
* be quadratic in the number of variable references for certain access
|
||||
* patterns.
|
||||
*/
|
||||
predicate firstUseAfterRef(
|
||||
BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v, boolean samevar
|
||||
) {
|
||||
adjacentRefs(bb1, i1, bb2, i2, v) and
|
||||
variableRead(bb2, i2, v, _) and
|
||||
samevar = true
|
||||
or
|
||||
exists(BasicBlock bb0, int i0, boolean samevar0 |
|
||||
firstUseAfterRef(bb0, i0, bb2, i2, v, samevar0) and
|
||||
adjacentRefs(bb1, i1, bb0, i0, v) and
|
||||
not variableWrite(bb0, i0, v, true) and
|
||||
if any(Definition def).definesAt(v, bb0, i0)
|
||||
then samevar = false
|
||||
else (
|
||||
samevar = samevar0 and synthPhiRead(bb0, v) and i0 = -1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` reaches the certain read at index `i` in basic block `bb`
|
||||
* without going through any other certain read. The boolean `samevar`
|
||||
* indicates whether the read is a use of `def` or whether some number of phi
|
||||
* nodes and/or uncertain reads occur between `def` and the read.
|
||||
*
|
||||
* Note that since this relation skips over phi nodes and phi reads, it may
|
||||
* be quadratic in the number of variable references for certain access
|
||||
* patterns.
|
||||
*/
|
||||
predicate firstUse(Definition def, BasicBlock bb, int i, boolean samevar) {
|
||||
exists(BasicBlock bb1, int i1, SourceVariable v |
|
||||
def.definesAt(v, bb1, i1) and
|
||||
AdjacentSsaRefs::firstUseAfterRef(bb1, i1, bb, i, v, samevar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the certain read at index `i1` in basic block `bb1` reaches the
|
||||
* certain read at index `i2` in basic block `bb2` without going through any
|
||||
* other certain read. The boolean `samevar` indicates whether the two reads
|
||||
* are of the same SSA variable.
|
||||
*
|
||||
* Note that since this relation skips over phi nodes and phi reads, it may
|
||||
* be quadratic in the number of variable references for certain access
|
||||
* patterns.
|
||||
*/
|
||||
predicate adjacentUseUse(
|
||||
BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v, boolean samevar
|
||||
) {
|
||||
exists(boolean samevar0 |
|
||||
variableRead(bb1, i1, v, true) and
|
||||
not variableWrite(bb1, i1, v, true) and
|
||||
AdjacentSsaRefs::firstUseAfterRef(bb1, i1, bb2, i2, v, samevar0) and
|
||||
if any(Definition def).definesAt(v, bb1, i1) then samevar = false else samevar = samevar0
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaDefReaches {
|
||||
newtype TSsaRefKind =
|
||||
SsaActualRead() or
|
||||
@@ -356,14 +647,17 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
* either a read (when `k` is `SsaActualRead()`), an SSA definition (when `k`
|
||||
* is `SsaDef()`), or a phi-read (when `k` is `SsaPhiRead()`).
|
||||
*
|
||||
* Unlike `Liveness::ref`, this includes `phi` (read) nodes.
|
||||
* Unlike `Liveness::varRef`, this includes `phi` (read) nodes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = SsaActualRead()
|
||||
or
|
||||
any(DefinitionExt def).definesAt(v, bb, i, k)
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = SsaDef()
|
||||
or
|
||||
synthPhiRead(bb, v) and i = -1 and k = SsaPhiRead()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,7 +779,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
pragma[noinline]
|
||||
predicate ssaDefReachesThroughBlock(DefinitionExt def, BasicBlock bb) {
|
||||
exists(SourceVariable v |
|
||||
ssaDefReachesEndOfBlockExt(bb, def, v) and
|
||||
ssaDefReachesEndOfBlockExt0(bb, def, v) and
|
||||
not defOccursInBlock(_, bb, v, _)
|
||||
)
|
||||
}
|
||||
@@ -513,7 +807,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
exists(SourceVariable v |
|
||||
varBlockReachesExt(pragma[only_bind_into](def), v, bb1, pragma[only_bind_into](bb2)) and
|
||||
phi.definesAt(v, bb2, _, _) and
|
||||
not ref(bb2, _, v, _)
|
||||
not varRef(bb2, _, v, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -569,7 +863,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
predicate varBlockReachesExitExt(DefinitionExt def, BasicBlock bb) {
|
||||
exists(BasicBlock bb2 | varBlockReachesExt(def, _, bb, bb2) |
|
||||
not defOccursInBlock(def, bb2, _, _) and
|
||||
not ssaDefReachesEndOfBlockExt(bb2, def, _)
|
||||
not ssaDefReachesEndOfBlockExt0(bb2, def, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -578,7 +872,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
exists(BasicBlock bb2, SourceVariable v |
|
||||
varBlockReachesExt(def, v, bb, bb2) and
|
||||
not defOccursInBlock(def, bb2, _, _) and
|
||||
not ssaDefReachesEndOfBlockExt(bb2, def, _) and
|
||||
not ssaDefReachesEndOfBlockExt0(bb2, def, _) and
|
||||
not any(PhiReadNode phi).definesAt(v, bb2, _, _)
|
||||
)
|
||||
or
|
||||
@@ -613,7 +907,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlockExt(BasicBlock bb, DefinitionExt def, SourceVariable v) {
|
||||
private predicate ssaDefReachesEndOfBlockExt0(BasicBlock bb, DefinitionExt def, SourceVariable v) {
|
||||
exists(int last |
|
||||
last = maxSsaRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
@@ -626,32 +920,18 @@ 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.
|
||||
ssaDefReachesEndOfBlockExt(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
ssaDefReachesEndOfBlockExt0(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThroughExt(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate phiReadReachesEndOfBlock(BasicBlock pred, BasicBlock bb, SourceVariable v) {
|
||||
exists(PhiReadNode phi |
|
||||
ssaDefReachesEndOfBlockExt(bb, phi, v) and
|
||||
pred = getImmediateBasicBlockDominator(phi.getBasicBlock())
|
||||
)
|
||||
}
|
||||
deprecated predicate ssaDefReachesEndOfBlockExt = ssaDefReachesEndOfBlockExt0/3;
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `ssaDefReachesEndOfBlockExt`, but ignores phi-reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
ssaDefReachesEndOfBlockExt(bb, def, v)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
ssaDefReachesEndOfBlock(mid, def, v) and
|
||||
phiReadReachesEndOfBlock(mid, bb, v)
|
||||
)
|
||||
}
|
||||
predicate ssaDefReachesEndOfBlock = SsaDefReachesNew::ssaDefReachesEndOfBlock/3;
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
@@ -677,7 +957,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
exists(SourceVariable v, BasicBlock bbDef |
|
||||
phi.definesAt(v, bbDef, _, _) and
|
||||
getABasicBlockPredecessor(bbDef) = bb and
|
||||
ssaDefReachesEndOfBlockExt(bb, inp, v)
|
||||
ssaDefReachesEndOfBlockExt0(bb, inp, v)
|
||||
|
|
||||
phi instanceof PhiNode or
|
||||
phi instanceof PhiReadNode
|
||||
@@ -695,7 +975,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, SsaActualRead()) and
|
||||
ssaDefReachesEndOfBlockExt(getABasicBlockPredecessor(bb), def, v) and
|
||||
ssaDefReachesEndOfBlockExt0(getABasicBlockPredecessor(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
@@ -704,13 +984,9 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
*
|
||||
* Same as `ssaDefReachesReadExt`, but ignores phi-reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, SsaActualRead()) and
|
||||
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
|
||||
not exists(Definition other | ssaDefReachesReadWithinBlock(v, other, bb, i))
|
||||
SsaDefReachesNew::ssaDefReachesRead(v, def, bb, i) and
|
||||
variableRead(bb, i, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -844,10 +1120,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
|
||||
* `def`. Since `def` is uncertain, the value from the preceding definition might
|
||||
* still be valid.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
lastRefRedef(inp, _, _, def)
|
||||
}
|
||||
predicate uncertainWriteDefinitionInput = SsaDefReachesNew::uncertainWriteDefinitionInput/2;
|
||||
|
||||
/** Holds if `bb` is a control-flow exit point. */
|
||||
private predicate exitBlock(BasicBlock bb) { not exists(getABasicBlockSuccessor(bb)) }
|
||||
|
||||
Reference in New Issue
Block a user