Skip to content

Commit 65ea01e

Browse files
authored
Merge pull request #4999 from hvitved/csharp/dataflow/phi-input
C#: Adjust flow into phi nodes
2 parents 0addce5 + 7c9a606 commit 65ea01e

File tree

8 files changed

+156
-136
lines changed

8 files changed

+156
-136
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ module Ssa {
235235
final AssignableRead getAReadAtNode(ControlFlow::Node cfn) {
236236
exists(SourceVariable v, ControlFlow::BasicBlock bb, int i |
237237
SsaImpl::ssaDefReachesRead(v, this, bb, i) and
238+
SsaImpl::variableReadActual(bb, i, v) and
238239
cfn = bb.getNode(i) and
239-
cfn = result.getAControlFlowNode() and
240-
result = v.getAnAccess().(AssignableRead)
240+
result.getAControlFlowNode() = cfn
241241
)
242242
}
243243

csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,15 @@ module LocalFlow {
266266
}
267267

268268
/**
269-
* Holds if `nodeFrom` is a last node referencing SSA definition `def`.
270-
* Either an SSA definition node for `def` when there is no read of `def`,
271-
* or a last read of `def`.
269+
* Holds if `nodeFrom` is a last node referencing SSA definition `def`, which
270+
* can reach `next`.
272271
*/
273-
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def) {
274-
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
275-
not exists(def.getARead())
276-
or
277-
exists(AssignableRead read, ControlFlow::Node cfn | read = nodeFrom.asExprAtNode(cfn) |
278-
def.getALastReadAtNode(cfn) = read
272+
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
273+
exists(ControlFlow::BasicBlock bb, int i | SsaImpl::lastRefBeforeRedef(def, bb, i, next) |
274+
def = nodeFrom.(SsaDefinitionNode).getDefinition() and
275+
def.definesAt(_, bb, i)
276+
or
277+
nodeFrom.asExprAtNode(bb.getNode(i)) instanceof AssignableRead
279278
)
280279
}
281280

@@ -302,14 +301,14 @@ module LocalFlow {
302301
or
303302
// Flow into phi node
304303
exists(Ssa::PhiNode phi |
305-
localFlowSsaInput(nodeFrom, def) and
304+
localFlowSsaInput(nodeFrom, def, phi) and
306305
phi = nodeTo.(SsaDefinitionNode).getDefinition() and
307306
def = phi.getAnInput()
308307
)
309308
or
310309
// Flow into uncertain SSA definition
311310
exists(LocalFlow::UncertainExplicitSsaDefinition uncertain |
312-
localFlowSsaInput(nodeFrom, def) and
311+
localFlowSsaInput(nodeFrom, def, uncertain) and
313312
uncertain = nodeTo.(SsaDefinitionNode).getDefinition() and
314313
def = uncertain.getPriorDefinition()
315314
)

csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll

Lines changed: 76 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,11 @@
55
import csharp
66
import SsaImplCommon
77

8-
/** A classification of variable reads. */
9-
newtype TReadKind =
10-
/** An actual read. */
11-
ActualRead() or
12-
/**
13-
* A pseudo read for a `ref` or `out` variable at the end of the variable's enclosing
14-
* callable. A pseudo read is inserted to make assignments to `out`/`ref` variables
15-
* live, for example line 1 in
16-
*
17-
* ```csharp
18-
* void M(out int i) {
19-
* i = 0;
20-
* }
21-
* ```
22-
*/
23-
OutRefExitRead() or
24-
/**
25-
* A pseudo read for a captured variable at the end of the capturing
26-
* callable. A write to a captured variable needs to be live for the same reasons
27-
* as a write to a `ref` or `out` variable (see above).
28-
*/
29-
CapturedVarExitRead() or
30-
/**
31-
* A pseudo read for a captured variable via a call.
32-
*/
33-
CapturedVarCallRead() or
34-
/**
35-
* A pseudo read for a `ref` variable, just prior to an update of the referenced value.
36-
* A pseudo read is inserted to make assignments to the `ref` variable live, for example
37-
* line 2 in
38-
*
39-
* ```csharp
40-
* void M() {
41-
* ref int i = ref GetRef();
42-
* i = 0;
43-
* }
44-
* ```
45-
*
46-
* The pseudo read is inserted at the CFG node `i` on the left-hand side of the
47-
* assignment on line 3.
48-
*/
49-
RefReadBeforeWrite()
50-
51-
/** A classification of variable reads. */
52-
class ReadKind extends TReadKind {
53-
string toString() {
54-
this = ActualRead() and
55-
result = "ActualRead"
56-
or
57-
this = OutRefExitRead() and
58-
result = "OutRefExitRead"
59-
or
60-
this = CapturedVarExitRead() and
61-
result = "CapturedVarExitRead"
62-
or
63-
this = CapturedVarCallRead() and
64-
result = "CapturedVarCallRead"
65-
or
66-
this = RefReadBeforeWrite() and
67-
result = "RefReadBeforeWrite"
68-
}
69-
70-
/** Holds if this kind represents a pseudo read. */
71-
predicate isPseudo() { this != ActualRead() }
8+
/**
9+
* Holds if the `i`th node of basic block `bb` reads source variable `v`.
10+
*/
11+
predicate variableReadActual(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
12+
v.getAnAccess().(AssignableRead) = bb.getNode(i).getElement()
7213
}
7314

7415
private module SourceVariableImpl {
@@ -183,23 +124,17 @@ private module SourceVariableImpl {
183124
}
184125

185126
/**
186-
* Holds if the `i`th node of basic block `bb` reads source variable `v`.
187-
* The read is of kind `rk`.
127+
* Holds if a pseudo read for `ref` or `out` variable `v` happens at index `i`
128+
* in basic block `bb`. A pseudo read is inserted to make assignments to
129+
* `out`/`ref` variables live, for example line 1 in
188130
*
189-
* This excludes implicit reads via calls.
131+
* ```csharp
132+
* void M(out int i) {
133+
* i = 0;
134+
* }
135+
* ```
190136
*/
191-
predicate variableReadDirect(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, ReadKind rk) {
192-
v.getAnAccess().(AssignableRead) = bb.getNode(i).getElement() and
193-
rk = ActualRead()
194-
or
195-
outRefExitRead(bb, i, v) and
196-
rk = OutRefExitRead()
197-
or
198-
refReadBeforeWrite(bb, i, v) and
199-
rk = RefReadBeforeWrite()
200-
}
201-
202-
private predicate outRefExitRead(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
137+
predicate outRefExitRead(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
203138
exists(ControlFlow::Nodes::AnnotatedExitNode exit |
204139
exit.isNormal() and
205140
exists(LocalScopeVariable lsv |
@@ -215,7 +150,23 @@ private module SourceVariableImpl {
215150
)
216151
}
217152

218-
private predicate refReadBeforeWrite(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
153+
/**
154+
* Holds if a pseudo read for `ref` variable `v` happens at index `i` in basic
155+
* block `bb`, just prior to an update of the referenced value. A pseudo read
156+
* is inserted to make assignments to the `ref` variable live, for example
157+
* line 2 in
158+
*
159+
* ```csharp
160+
* void M() {
161+
* ref int i = ref GetRef();
162+
* i = 0;
163+
* }
164+
* ```
165+
*
166+
* The pseudo read is inserted at the CFG node `i` on the left-hand side of the
167+
* assignment on line 3.
168+
*/
169+
predicate refReadBeforeWrite(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
219170
exists(AssignableDefinitions::AssignmentDefinition def, LocalVariable lv |
220171
def.getTarget() = lv and
221172
lv.isRef() and
@@ -812,10 +763,13 @@ private module CapturedVariableLivenessImpl {
812763
*/
813764
private predicate capturerReads(Callable c, LocalScopeVariable v) {
814765
exists(LocalScopeSourceVariable sv |
815-
variableReadDirect(_, _, sv, _) and
816766
c = sv.getEnclosingCallable() and
817767
v = sv.getAssignable() and
818768
v.getCallable() != c
769+
|
770+
variableReadActual(_, _, sv)
771+
or
772+
refReadBeforeWrite(_, _, sv)
819773
)
820774
}
821775

@@ -985,20 +939,25 @@ private module CapturedVariableLivenessImpl {
985939

986940
private import CapturedVariableLivenessImpl
987941

942+
private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
943+
outRefExitRead(bb, i, v)
944+
or
945+
refReadBeforeWrite(bb, i, v)
946+
or
947+
capturedReadOut(bb, i, v, _, _, _)
948+
or
949+
capturedReadIn(bb, i, v, _, _, _)
950+
}
951+
988952
/**
989953
* Holds if the `i`th of basic block `bb` reads source variable `v`.
990-
* The read is of kind `rk`.
991954
*
992955
* This includes implicit reads via calls.
993956
*/
994-
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, ReadKind rk) {
995-
variableReadDirect(bb, i, v, rk)
957+
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
958+
variableReadActual(bb, i, v)
996959
or
997-
capturedReadOut(bb, i, v, _, _, _) and
998-
rk = CapturedVarExitRead()
999-
or
1000-
capturedReadIn(bb, i, v, _, _, _) and
1001-
rk = CapturedVarCallRead()
960+
variableReadPseudo(bb, i, v)
1002961
}
1003962

1004963
cached
@@ -1119,7 +1078,7 @@ private module Cached {
11191078
capturedReadIn(_, _, v, edef.getSourceVariable(), c, additionalCalls) and
11201079
def = def0.getAnUltimateDefinition() and
11211080
ssaDefReachesRead(_, def0, bb, i) and
1122-
variableRead(bb, i, v, CapturedVarCallRead()) and
1081+
capturedReadIn(bb, i, v, _, _, _) and
11231082
c = bb.getNode(i)
11241083
)
11251084
}
@@ -1130,7 +1089,6 @@ private module Cached {
11301089
) {
11311090
exists(Ssa::Definition def0, ControlFlow::BasicBlock bb, int i |
11321091
def = def0.getAnUltimateDefinition() and
1133-
lastRef(def0, bb, i) and
11341092
capturedReadOut(bb, i, def0.getSourceVariable(), cdef.getSourceVariable(), cdef.getCall(),
11351093
additionalCalls)
11361094
)
@@ -1151,7 +1109,7 @@ private module Cached {
11511109
or
11521110
exists(ControlFlow::BasicBlock bb3, int i3 |
11531111
adjacentDefReaches(def, bb1, i1, bb3, i3) and
1154-
variableRead(bb3, i3, _, any(ReadKind rk | rk.isPseudo())) and
1112+
variableReadPseudo(bb3, i3, _) and
11551113
adjacentDefRead(def, bb3, i3, bb2, i2)
11561114
)
11571115
}
@@ -1161,7 +1119,7 @@ private module Cached {
11611119
Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
11621120
) {
11631121
adjacentDefReaches(def, bb1, i1, bb2, i2) and
1164-
variableRead(bb2, i2, _, ActualRead())
1122+
variableReadActual(bb2, i2, _)
11651123
}
11661124

11671125
/**
@@ -1186,7 +1144,7 @@ private module Cached {
11861144
predicate adjacentReadPairSameVar(Definition def, ControlFlow::Node cfn1, ControlFlow::Node cfn2) {
11871145
exists(ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 |
11881146
cfn1 = bb1.getNode(i1) and
1189-
variableRead(bb1, i1, _, ActualRead()) and
1147+
variableReadActual(bb1, i1, _) and
11901148
adjacentDefActualRead(def, bb1, i1, bb2, i2) and
11911149
cfn2 = bb2.getNode(i2)
11921150
)
@@ -1196,15 +1154,31 @@ private module Cached {
11961154
Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
11971155
) {
11981156
adjacentDefReaches(def, bb1, i1, bb2, i2) and
1199-
variableRead(bb2, i2, _, any(ReadKind rk | rk.isPseudo()))
1157+
variableReadPseudo(bb2, i2, _)
1158+
}
1159+
1160+
private predicate reachesLastRefRedef(
1161+
Definition def, ControlFlow::BasicBlock bb, int i, Definition next
1162+
) {
1163+
lastRefRedef(def, bb, i, next)
1164+
or
1165+
exists(ControlFlow::BasicBlock bb0, int i0 |
1166+
reachesLastRefRedef(def, bb0, i0, next) and
1167+
adjacentDefPseudoRead(def, bb, i, bb0, i0)
1168+
)
1169+
}
1170+
1171+
cached
1172+
predicate lastRefBeforeRedef(Definition def, ControlFlow::BasicBlock bb, int i, Definition next) {
1173+
reachesLastRefRedef(def, bb, i, next) and
1174+
not variableReadPseudo(bb, i, def.getSourceVariable())
12001175
}
12011176

12021177
private predicate reachesLastRef(Definition def, ControlFlow::BasicBlock bb, int i) {
12031178
lastRef(def, bb, i)
12041179
or
12051180
exists(ControlFlow::BasicBlock bb0, int i0 |
12061181
reachesLastRef(def, bb0, i0) and
1207-
variableRead(bb0, i0, _, any(ReadKind rk | rk.isPseudo())) and
12081182
adjacentDefPseudoRead(def, bb, i, bb0, i0)
12091183
)
12101184
}
@@ -1213,19 +1187,20 @@ private module Cached {
12131187
predicate lastReadSameVar(Definition def, ControlFlow::Node cfn) {
12141188
exists(ControlFlow::BasicBlock bb, int i |
12151189
reachesLastRef(def, bb, i) and
1216-
variableRead(bb, i, _, ActualRead()) and
1190+
variableReadActual(bb, i, _) and
12171191
cfn = bb.getNode(i)
12181192
)
12191193
}
12201194

12211195
cached
12221196
predicate isLiveOutRefParameterDefinition(Ssa::Definition def, Parameter p) {
12231197
p.isOutOrRef() and
1224-
exists(Ssa::Definition def0, ControlFlow::BasicBlock bb, int i |
1198+
exists(Ssa::SourceVariable v, Ssa::Definition def0, ControlFlow::BasicBlock bb, int i |
1199+
v = def.getSourceVariable() and
1200+
p = v.getAssignable() and
12251201
def = def0.getAnUltimateDefinition() and
1226-
reachesLastRef(def0, bb, i) and
1227-
variableRead(bb, i, def0.getSourceVariable(), OutRefExitRead()) and
1228-
p = def0.getSourceVariable().getAssignable()
1202+
ssaDefReachesRead(_, def0, bb, i) and
1203+
outRefExitRead(bb, i, v)
12291204
)
12301205
}
12311206
}

0 commit comments

Comments
 (0)