mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
C#: Refactor SSA liveness logic
Simplify liveness analysis by avoiding the two extra copies of `liveAtRank()` (and other auxiliary predicates) for fields/captured variables analysis.
This commit is contained in:
@@ -319,59 +319,28 @@ module Ssa {
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `variableWrite()`, but extended to include implicit call definitions
|
||||
* for fields and properties.
|
||||
* Holds if source varible `v` is likely to be live at any node inside basic
|
||||
* block `bb`.
|
||||
*/
|
||||
private predicate variableWriteExt(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
variableWrite(bb, i, v, certain)
|
||||
predicate likelyLive(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v, _)
|
||||
or
|
||||
variableWriteExt(bb, i, v.(QualifiedFieldOrPropSourceVariable).getQualifier(), certain)
|
||||
or
|
||||
exists(Call c |
|
||||
bb.getNode(i) = c.getAControlFlowNode() |
|
||||
updatesNamedFieldOrProp(c, v, _) and
|
||||
certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ref()`, but extended to include implicit call definitions
|
||||
* for fields and properties.
|
||||
*/
|
||||
private predicate refExt(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(ReadKind rk |
|
||||
variableRead(bb, i, v, _, rk) |
|
||||
k = Read(rk)
|
||||
)
|
||||
or
|
||||
exists(boolean certain |
|
||||
variableWriteExt(bb, i, v, certain) |
|
||||
k = Write(certain)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `refRank()`, but extended to include implicit call definitions
|
||||
* for fields and properties.
|
||||
*/
|
||||
private int refRankExt(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
i = rank[result](int j | refExt(bb, j, v, _)) and
|
||||
refExt(bb, i, v, k)
|
||||
ref(bb, _, v, Read(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRankExt()`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk, ReadKind rk) {
|
||||
rnk = refRankExt(bb, i, v, _) and
|
||||
predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk, ReadKind rk) {
|
||||
rnk = refRank(bb, i, v, _) and
|
||||
(
|
||||
rnk = max(refRankExt(bb, _, v, _)) and
|
||||
rnk = max(refRank(bb, _, v, _)) and
|
||||
liveAtExit(bb, v, rk)
|
||||
or
|
||||
ref(bb, i, v, Read(rk))
|
||||
or
|
||||
exists(int j | liveAtRank(bb, j, v, rnk + 1, rk) | not refExt(bb, j, v, Write(true)))
|
||||
exists(int j | liveAtRank(bb, j, v, rnk + 1, rk) | not ref(bb, j, v, Write(true)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -382,7 +351,7 @@ module Ssa {
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v, ReadKind rk) {
|
||||
exists (int rnk |
|
||||
rnk = refRankExt(bb, i, v, Write(_)) |
|
||||
rnk = refRank(bb, i, v, Write(_)) |
|
||||
liveAtRank(bb, i, v, rnk, rk)
|
||||
)
|
||||
}
|
||||
@@ -1222,74 +1191,31 @@ module Ssa {
|
||||
setsOtherFieldOrProp(c, fp) and c = setter
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate callAt(BasicBlock bb, int i, Call call) {
|
||||
bb.getNode(i) = call.getAControlFlowNode() and
|
||||
getARuntimeTarget(call).hasBody()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` occurs in basic block `bb` at index `i`, `fp` has
|
||||
* an update somewhere, and `fp` is accessed somewhere inside the callable
|
||||
* to which `bb` belongs.
|
||||
* an update somewhere, and `fp` is likely to be live in `bb` at index
|
||||
* `i`.
|
||||
*/
|
||||
private predicate updateCandidate(BasicBlock bb, int i, TrackedFieldOrProp fp, Call call) {
|
||||
bb.getNode(i) = call.getAControlFlowNode() and
|
||||
call.getEnclosingCallable() = fp.getEnclosingCallable() and
|
||||
relevantDefinition(_, fp.getAssignable(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ref()`, but extended to include implicit call definitions
|
||||
* for fields and properties.
|
||||
*/
|
||||
private predicate refExt(BasicBlock bb, int i, TrackedFieldOrProp fp) {
|
||||
ref(bb, i, fp, _)
|
||||
or
|
||||
updateCandidate(bb, i, fp, _) and
|
||||
likelyLive(bb, fp) and
|
||||
callAt(bb, i, call) and
|
||||
relevantDefinition(_, fp.getAssignable(), _) and
|
||||
not ref(bb, i, fp, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `refRank()`, but extended to include implicit call definitions
|
||||
* for fields and properties, and restricted to basic blocks that have
|
||||
* a potential implicit call definition.
|
||||
*/
|
||||
private int refRankExt(BasicBlock bb, int i, TrackedFieldOrProp fp) {
|
||||
updateCandidate(bb, _, fp, _) and
|
||||
i = rank[result](int j | refExt(bb, j, fp)) and
|
||||
refExt(bb, i, fp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field or property `fp` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRankExt()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, TrackedFieldOrProp fp, int rnk) {
|
||||
rnk = refRankExt(bb, i, fp) and
|
||||
(
|
||||
rnk = max(refRankExt(bb, _, fp)) and
|
||||
liveAtExit(bb, fp, _)
|
||||
or
|
||||
ref(bb, i, fp, Read(_))
|
||||
or
|
||||
exists(int j | liveAtRank(bb, j, fp, rnk + 1) | not ref(bb, j, fp, Write(true)))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if field or property `fp` is live after the potential update at call `c`.
|
||||
*/
|
||||
private predicate liveAfterUpdateCandidate(Call c, TrackedFieldOrProp fp) {
|
||||
exists(BasicBlock bb, int i, int rnk |
|
||||
updateCandidate(bb, i, fp, c) |
|
||||
not ref(bb, i, fp, _) and
|
||||
rnk = refRankExt(bb, i, fp) and
|
||||
liveAtRank(bb, i, fp, rnk)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesNamedFieldOrPropPart1` based on following edges in forward direction.
|
||||
*/
|
||||
private predicate pruneFromLeft(Callable c) {
|
||||
exists(Call call, TrackedFieldOrProp f |
|
||||
liveAfterUpdateCandidate(call, f) and
|
||||
updateCandidate(_, _, f, call) and
|
||||
c = getARuntimeTarget(call) and
|
||||
generalSetter(_, f.getAssignable(), _)
|
||||
)
|
||||
@@ -1330,7 +1256,7 @@ module Ssa {
|
||||
|
||||
pragma [noinline]
|
||||
private predicate updatesNamedFieldOrPropPart1Prefix0(Call call, TrackedFieldOrProp tfp, Callable c1, FieldOrProp fp) {
|
||||
liveAfterUpdateCandidate(call, tfp) and
|
||||
updateCandidate(_, _, tfp, call) and
|
||||
fp = tfp.getAssignable() and
|
||||
generalSetter(_, fp, _) and
|
||||
c1 = getARuntimeTarget(call)
|
||||
@@ -1353,7 +1279,7 @@ module Ssa {
|
||||
* may not alias with `this`. The actual update occurs in `setter`.
|
||||
*/
|
||||
pragma [noopt]
|
||||
predicate updatesNamedFieldOrPropPart1(Call call, TrackedFieldOrProp tfp, Callable setter) {
|
||||
private predicate updatesNamedFieldOrPropPart1(Call call, TrackedFieldOrProp tfp, Callable setter) {
|
||||
exists(Callable c1, Callable c2, FieldOrProp fp |
|
||||
updatesNamedFieldOrPropPart1Prefix(call, tfp, c1, setter, fp) and
|
||||
generalSetter(c2, fp, setter) |
|
||||
@@ -1365,10 +1291,79 @@ module Ssa {
|
||||
* Holds if `call` may change the value of `tfp` on `this`. The actual update occurs
|
||||
* in `setter`.
|
||||
*/
|
||||
predicate updatesNamedFieldOrPropPart2(Call call, TrackedFieldOrProp tfp, Callable setter) {
|
||||
liveAfterUpdateCandidate(call, tfp) and
|
||||
private predicate updatesNamedFieldOrPropPart2(Call call, TrackedFieldOrProp tfp, Callable setter) {
|
||||
updateCandidate(_, _, tfp, call) and
|
||||
setsOwnFieldOrPropTransitive(getARuntimeTarget(call), tfp.getAssignable(), setter)
|
||||
}
|
||||
|
||||
private predicate updatesNamedFieldOrPropLikelyLive(BasicBlock bb, int i, TrackedFieldOrProp fp, Call call, Callable setter) {
|
||||
updateCandidate(bb, i, fp, call) and
|
||||
(
|
||||
updatesNamedFieldOrPropPart1(call, fp, setter)
|
||||
or
|
||||
updatesNamedFieldOrPropPart2(call, fp, setter)
|
||||
)
|
||||
}
|
||||
|
||||
private int firstRefAfterCall(BasicBlock bb, int i, TrackedFieldOrProp fp) {
|
||||
updatesNamedFieldOrPropLikelyLive(bb, i, fp, _, _) and
|
||||
result = min(int k | k > i and ref(bb, k, fp, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of field or property `fp`. The actual
|
||||
* update occurs in `setter`.
|
||||
*/
|
||||
cached
|
||||
predicate updatesNamedFieldOrProp(Call c, TrackedFieldOrProp fp, Callable setter) {
|
||||
forceCachingInSameStage() and
|
||||
exists(BasicBlock bb, int i |
|
||||
updatesNamedFieldOrPropLikelyLive(bb, i, fp, c, setter) |
|
||||
not exists(firstRefAfterCall(bb, i, fp)) and
|
||||
liveAtExit(bb, fp, _)
|
||||
or
|
||||
exists(int j |
|
||||
j = firstRefAfterCall(bb, i, fp) |
|
||||
liveAtRank(bb, j, fp, _, _) and
|
||||
not ref(bb, j, fp, Write(true))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `variableWrite()`, but extended to include implicit call definitions
|
||||
* for fields and properties.
|
||||
*/
|
||||
private predicate variableWriteExt(BasicBlock bb, int i, SourceVariable v) {
|
||||
ref(bb, i, v, Write(_))
|
||||
or
|
||||
variableWriteExt(bb, i, v.(QualifiedFieldOrPropSourceVariable).getQualifier())
|
||||
or
|
||||
exists(Call c | callAt(bb, i, c) | updatesNamedFieldOrProp(c, v, _))
|
||||
}
|
||||
|
||||
private int firstRefAfterQualifiedDef(BasicBlock bb, int i, QualifiedFieldOrPropSourceVariable q) {
|
||||
variableWriteExt(bb, i, q) and
|
||||
result = min(int k | k > i and ref(bb, k, q, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if qualified field or property `q` is live after the (certain or
|
||||
* uncertain) write at index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWriteQualified(BasicBlock bb, int i, QualifiedFieldOrPropSourceVariable q) {
|
||||
variableWriteExt(bb, i, q) and
|
||||
(
|
||||
not exists(firstRefAfterQualifiedDef(bb, i, q)) and
|
||||
liveAtExit(bb, q, _)
|
||||
or
|
||||
exists(int j |
|
||||
j = firstRefAfterQualifiedDef(bb, i, q) |
|
||||
liveAtRank(bb, j, q, _, _) and
|
||||
not ref(bb, j, q, Write(true))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import FieldOrPropsImpl
|
||||
@@ -1440,72 +1435,22 @@ module Ssa {
|
||||
|
||||
/**
|
||||
* Holds if `call` occurs in basic block `bb` at index `i`, captured variable
|
||||
* `v` has an update somewhere, and `v` is accessed somewhere inside the callable
|
||||
* to which `bb` belongs.
|
||||
* `v` has an update somewhere, and `v` is likely to be live in `bb` at index
|
||||
* `i`.
|
||||
*/
|
||||
private predicate updateCandidate(BasicBlock bb, int i, CapturedWrittenLocalScopeSourceVariable v, Call call) {
|
||||
bb.getNode(i) = call.getAControlFlowNode() and
|
||||
call.getEnclosingCallable() = v.getEnclosingCallable() and
|
||||
likelyLive(bb, v) and
|
||||
callAt(bb, i, call) and
|
||||
relevantDefinition(_, v.getAssignable(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ref()`, but extended to include implicit call definitions
|
||||
* for captured variables.
|
||||
*/
|
||||
private predicate refExt(BasicBlock bb, int i, CapturedWrittenLocalScopeSourceVariable v) {
|
||||
ref(bb, i, v, _)
|
||||
or
|
||||
updateCandidate(bb, i, v, _) and
|
||||
not ref(bb, i, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `refRank()`, but extended to include implicit call definitions
|
||||
* for captured variables, and restricted to basic blocks that have a
|
||||
* potential implicit call definition.
|
||||
*/
|
||||
private int refRankExt(BasicBlock bb, int i, CapturedWrittenLocalScopeSourceVariable v) {
|
||||
updateCandidate(bb, _, v, _) and
|
||||
i = rank[result](int j | refExt(bb, j, v)) and
|
||||
refExt(bb, i, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if captured source variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRankExt()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, CapturedWrittenLocalScopeSourceVariable v, int rnk) {
|
||||
rnk = refRankExt(bb, i, v) and
|
||||
(
|
||||
rnk = max(refRankExt(bb, _, v)) and
|
||||
liveAtExit(bb, v, _)
|
||||
or
|
||||
ref(bb, i, v, Read(_))
|
||||
or
|
||||
exists(int j | liveAtRank(bb, j, v, rnk + 1) | not ref(bb, j, v, Write(true)))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if captured source variable `v` is live after the potential update at call `c`.
|
||||
*/
|
||||
private predicate liveAfterUpdateCandidate(Call c, CapturedWrittenLocalScopeSourceVariable v) {
|
||||
exists(BasicBlock bb, int i, int rnk |
|
||||
updateCandidate(bb, i, v, c) |
|
||||
not ref(bb, i, v, _) and
|
||||
rnk = refRankExt(bb, i, v) and
|
||||
liveAtRank(bb, i, v, rnk)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesCapturedVariable` based on following edges in forward direction.
|
||||
*/
|
||||
private predicate pruneFromLeft(Callable c) {
|
||||
exists(Call call, CapturedWrittenLocalScopeSourceVariable v |
|
||||
liveAfterUpdateCandidate(call, v) and
|
||||
updateCandidate(_, _, v, call) and
|
||||
c = getARuntimeTarget(call) and
|
||||
relevantDefinition(_, v.getAssignable(), _)
|
||||
)
|
||||
@@ -1551,7 +1496,7 @@ module Ssa {
|
||||
|
||||
pragma [noinline]
|
||||
private predicate updatesCapturedVariablePrefix(Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable c, CapturedWrittenLocalScopeVariable captured) {
|
||||
liveAfterUpdateCandidate(call, v) and
|
||||
updateCandidate(_, _, v, call) and
|
||||
captured = v.getAssignable() and
|
||||
relevantDefinitionProj(_, captured) and
|
||||
c = getARuntimeTarget(call)
|
||||
@@ -1573,14 +1518,42 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
// A non-cached helper predicate that is cached in a cached module further down,
|
||||
// to make sure the predicate is evaluated in the same stage as other cached predicates
|
||||
predicate updatesCapturedVariableNonCached(Call call, CapturedWrittenLocalScopeSourceVariable v, AssignableDefinition def) {
|
||||
/**
|
||||
* Holds if `call` may change the value of captured variable `v`. The actual
|
||||
* update occurs in `def`.
|
||||
*/
|
||||
private predicate updatesCapturedVariableLikelyLive(BasicBlock bb, int i, Call call, LocalScopeSourceVariable v, AssignableDefinition def) {
|
||||
updateCandidate(bb, i, v, call) and
|
||||
exists(Callable writer |
|
||||
relevantDefinition(writer, v.getAssignable(), def) |
|
||||
updatesCapturedVariableWriter(call, v, writer)
|
||||
)
|
||||
}
|
||||
|
||||
private int firstRefAfter(BasicBlock bb, int i, CapturedWrittenLocalScopeSourceVariable v) {
|
||||
updatesCapturedVariableLikelyLive(bb, i, _, v, _) and
|
||||
result = min(int k | k > i and ref(bb, k, v, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of captured variable `v`. The actual
|
||||
* update occurs in `def`.
|
||||
*/
|
||||
cached
|
||||
predicate updatesCapturedVariable(Call call, LocalScopeSourceVariable v, AssignableDefinition def) {
|
||||
forceCachingInSameStage() and
|
||||
exists(BasicBlock bb, int i |
|
||||
updatesCapturedVariableLikelyLive(bb, i, call, v, def) |
|
||||
not exists(firstRefAfter(bb, i, v)) and
|
||||
liveAtExit(bb, v, _)
|
||||
or
|
||||
exists(int j |
|
||||
j = firstRefAfter(bb, i, v) |
|
||||
liveAtRank(bb, j, v, _, _) and
|
||||
not ref(bb, j, v, Write(true))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import CapturedVariableImpl
|
||||
@@ -1830,6 +1803,8 @@ module Ssa {
|
||||
private import CapturedVariableLivenessImpl
|
||||
|
||||
private cached module SsaImpl {
|
||||
cached predicate forceCachingInSameStage() { any() }
|
||||
|
||||
/**
|
||||
* A data type representing SSA definitions.
|
||||
*
|
||||
@@ -1905,7 +1880,7 @@ module Ssa {
|
||||
exists(BasicBlock bb, int i |
|
||||
qdef.getSourceVariable() = v.getQualifier() and
|
||||
qdef.definesAt(bb, i) and
|
||||
liveAfterWrite(bb, i, v, _) and
|
||||
liveAfterWriteQualified(bb, i, v) and
|
||||
// Eliminate corner case where a call definition can overlap with a
|
||||
// qualifier definition: if method `M` updates field `F`, then a call
|
||||
// to `M` is both an update of `x.M` and `x.M.M`, so the former call
|
||||
@@ -1952,23 +1927,6 @@ module Ssa {
|
||||
def = TPhiNode(v, bb) and i = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of field or property `fp`. The actual
|
||||
* update occurs in `setter`.
|
||||
*/
|
||||
cached predicate updatesNamedFieldOrProp(Call call, TrackedFieldOrProp fp, Callable setter) {
|
||||
updatesNamedFieldOrPropPart1(call, fp, setter) or
|
||||
updatesNamedFieldOrPropPart2(call, fp, setter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of captured variable `v`. The actual
|
||||
* update occurs in `def`.
|
||||
*/
|
||||
cached predicate updatesCapturedVariable(Call call, LocalScopeSourceVariable v, AssignableDefinition def) {
|
||||
updatesCapturedVariableNonCached(call, v, def)
|
||||
}
|
||||
|
||||
cached predicate isCapturedVariableDefinitionFlowIn(ExplicitDefinition def, ImplicitEntryDefinition edef, Call c) {
|
||||
exists(BasicBlock bb, int i, LocalScopeSourceVariable v |
|
||||
definesAt(def, bb, i, v) |
|
||||
|
||||
Reference in New Issue
Block a user