Skip to content

Commit 74fd2c1

Browse files
committed
C#: Move uncertain-read logic into shared SSA implementation
1 parent 8abc37f commit 74fd2c1

File tree

3 files changed

+90
-68
lines changed

3 files changed

+90
-68
lines changed

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

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -954,10 +954,12 @@ private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::Sou
954954
*
955955
* This includes implicit reads via calls.
956956
*/
957-
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
958-
variableReadActual(bb, i, v)
957+
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
958+
variableReadActual(bb, i, v) and
959+
certain = true
959960
or
960-
variableReadPseudo(bb, i, v)
961+
variableReadPseudo(bb, i, v) and
962+
certain = false
961963
}
962964

963965
cached
@@ -1107,26 +1109,6 @@ private module Cached {
11071109
ssaDefReachesEndOfBlock(bb, def, _)
11081110
}
11091111

1110-
private predicate adjacentDefReaches(
1111-
Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
1112-
) {
1113-
adjacentDefRead(def, bb1, i1, bb2, i2)
1114-
or
1115-
exists(ControlFlow::BasicBlock bb3, int i3 |
1116-
adjacentDefReaches(def, bb1, i1, bb3, i3) and
1117-
variableReadPseudo(bb3, i3, _) and
1118-
adjacentDefRead(def, bb3, i3, bb2, i2)
1119-
)
1120-
}
1121-
1122-
pragma[noinline]
1123-
private predicate adjacentDefActualRead(
1124-
Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
1125-
) {
1126-
adjacentDefReaches(def, bb1, i1, bb2, i2) and
1127-
variableReadActual(bb2, i2, _)
1128-
}
1129-
11301112
cached
11311113
AssignableRead getAReadAtNode(Definition def, ControlFlow::Node cfn) {
11321114
exists(Ssa::SourceVariable v, ControlFlow::BasicBlock bb, int i |
@@ -1145,7 +1127,7 @@ private module Cached {
11451127
predicate firstReadSameVar(Definition def, ControlFlow::Node cfn) {
11461128
exists(ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 |
11471129
def.definesAt(_, bb1, i1) and
1148-
adjacentDefActualRead(def, bb1, i1, bb2, i2) and
1130+
adjacentDefNoUncertainReads(def, bb1, i1, bb2, i2) and
11491131
cfn = bb2.getNode(i2)
11501132
)
11511133
}
@@ -1160,48 +1142,20 @@ private module Cached {
11601142
exists(ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2 |
11611143
cfn1 = bb1.getNode(i1) and
11621144
variableReadActual(bb1, i1, _) and
1163-
adjacentDefActualRead(def, bb1, i1, bb2, i2) and
1145+
adjacentDefNoUncertainReads(def, bb1, i1, bb2, i2) and
11641146
cfn2 = bb2.getNode(i2)
11651147
)
11661148
}
11671149

1168-
private predicate adjacentDefPseudoRead(
1169-
Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
1170-
) {
1171-
adjacentDefReaches(def, bb1, i1, bb2, i2) and
1172-
variableReadPseudo(bb2, i2, _)
1173-
}
1174-
1175-
private predicate reachesLastRefRedef(
1176-
Definition def, ControlFlow::BasicBlock bb, int i, Definition next
1177-
) {
1178-
lastRefRedef(def, bb, i, next)
1179-
or
1180-
exists(ControlFlow::BasicBlock bb0, int i0 |
1181-
reachesLastRefRedef(def, bb0, i0, next) and
1182-
adjacentDefPseudoRead(def, bb, i, bb0, i0)
1183-
)
1184-
}
1185-
11861150
cached
11871151
predicate lastRefBeforeRedef(Definition def, ControlFlow::BasicBlock bb, int i, Definition next) {
1188-
reachesLastRefRedef(def, bb, i, next) and
1189-
not variableReadPseudo(bb, i, def.getSourceVariable())
1190-
}
1191-
1192-
private predicate reachesLastRef(Definition def, ControlFlow::BasicBlock bb, int i) {
1193-
lastRef(def, bb, i)
1194-
or
1195-
exists(ControlFlow::BasicBlock bb0, int i0 |
1196-
reachesLastRef(def, bb0, i0) and
1197-
adjacentDefPseudoRead(def, bb, i, bb0, i0)
1198-
)
1152+
lastRefRedefNoUncertainReads(def, bb, i, next)
11991153
}
12001154

12011155
cached
12021156
predicate lastReadSameVar(Definition def, ControlFlow::Node cfn) {
12031157
exists(ControlFlow::BasicBlock bb, int i |
1204-
reachesLastRef(def, bb, i) and
1158+
lastRefNoUncertainReads(def, bb, i) and
12051159
variableReadActual(bb, i, _) and
12061160
cfn = bb.getNode(i)
12071161
)

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

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ private module Liveness {
1717
* (certain or uncertain) writes.
1818
*/
1919
private newtype TRefKind =
20-
Read() or
21-
Write(boolean certain) { certain = true or certain = false }
20+
Read(boolean certain) { certain in [false, true] } or
21+
Write(boolean certain) { certain in [false, true] }
2222

2323
private class RefKind extends TRefKind {
2424
string toString() {
25-
this = Read() and result = "read"
25+
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
2626
or
2727
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
2828
}
2929

3030
int getOrder() {
31-
this = Read() and
31+
this = Read(_) and
3232
result = 0
3333
or
3434
this = Write(_) and
@@ -40,7 +40,7 @@ private module Liveness {
4040
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
4141
*/
4242
private predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
43-
variableRead(bb, i, v) and k = Read()
43+
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
4444
or
4545
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
4646
}
@@ -95,7 +95,7 @@ private module Liveness {
9595
*/
9696
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
9797
// The first read or certain write to `v` inside `bb` is a read
98-
refRank(bb, _, v, Read()) = firstReadOrCertainWrite(bb, v)
98+
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
9999
or
100100
// There is no certain write to `v` inside `bb`, but `v` is live at entry
101101
// to a successor basic block of `bb`
@@ -120,7 +120,7 @@ private module Liveness {
120120
liveAtExit(bb, v)
121121
or
122122
ref(bb, i, v, kind) and
123-
kind = Read()
123+
kind = Read(_)
124124
or
125125
exists(RefKind nextKind |
126126
liveAtRank(bb, _, v, rnk + 1) and
@@ -237,7 +237,7 @@ private module SsaDefReaches {
237237
* Unlike `Liveness::ref`, this includes `phi` nodes.
238238
*/
239239
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
240-
variableRead(bb, i, v) and
240+
variableRead(bb, i, v, _) and
241241
k = SsaRead()
242242
or
243243
exists(Definition def | def.definesAt(v, bb, i)) and
@@ -304,7 +304,7 @@ private module SsaDefReaches {
304304
exists(int rnk |
305305
ssaDefReachesRank(bb, def, rnk, v) and
306306
rnk = ssaRefRank(bb, i, v, SsaRead()) and
307-
variableRead(bb, i, v)
307+
variableRead(bb, i, v, _)
308308
)
309309
}
310310

@@ -368,7 +368,7 @@ private module SsaDefReaches {
368368
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
369369
varBlockReaches(def, bb1, bb2) and
370370
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
371-
variableRead(bb2, i2, _)
371+
variableRead(bb2, i2, _, _)
372372
}
373373
}
374374

@@ -394,6 +394,7 @@ private predicate ssaDefReachesEndOfBlockRec(BasicBlock bb, Definition def, Sour
394394
* block `bb`, at which point it is still live, without crossing another
395395
* SSA definition of `v`.
396396
*/
397+
pragma[nomagic]
397398
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
398399
exists(int last | last = maxSsaRefRank(bb, v) |
399400
ssaDefReachesRank(bb, def, last, v) and
@@ -412,10 +413,11 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
412413
* basic block `bb`, without crossing another SSA definition of `v`. The read
413414
* is of kind `rk`.
414415
*/
416+
pragma[nomagic]
415417
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
416418
ssaDefReachesReadWithinBlock(v, def, bb, i)
417419
or
418-
variableRead(bb, i, v) and
420+
variableRead(bb, i, v, _) and
419421
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
420422
not ssaDefReachesReadWithinBlock(v, _, bb, i)
421423
}
@@ -427,25 +429,51 @@ predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int
427429
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
428430
* path between them without any read of `def`.
429431
*/
432+
pragma[nomagic]
430433
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
431434
exists(int rnk |
432435
rnk = ssaDefRank(def, _, bb1, i1, _) and
433436
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaRead()) and
434-
variableRead(bb1, i2, _) and
437+
variableRead(bb1, i2, _, _) and
435438
bb2 = bb1
436439
)
437440
or
438441
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
439442
defAdjacentRead(def, bb1, bb2, i2)
440443
}
441444

445+
private predicate adjacentDefReachesRead(
446+
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
447+
) {
448+
adjacentDefRead(def, bb1, i1, bb2, i2) and
449+
not variableRead(bb1, i1, def.getSourceVariable(), false)
450+
or
451+
exists(BasicBlock bb3, int i3 |
452+
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
453+
variableRead(bb3, i3, _, false) and
454+
adjacentDefRead(def, bb3, i3, bb2, i2)
455+
)
456+
}
457+
458+
/**
459+
* NB: If this predicate is exposed, it should be cached.
460+
*
461+
* Same as `adjacentDefRead`, but ignores uncertain reads.
462+
*/
463+
pragma[nomagic]
464+
predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
465+
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
466+
variableRead(bb2, i2, _, true)
467+
}
468+
442469
/**
443470
* NB: If this predicate is exposed, it should be cached.
444471
*
445472
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
446473
* `def`. The reference is last because it can reach another write `next`,
447474
* without passing through another read or write.
448475
*/
476+
pragma[nomagic]
449477
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
450478
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
451479
// Next reference to `v` inside `bb` is a write
@@ -462,6 +490,29 @@ predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
462490
)
463491
}
464492

493+
private predicate adjacentDefReachesUncertainRead(
494+
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
495+
) {
496+
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
497+
variableRead(bb2, i2, _, false)
498+
}
499+
500+
/**
501+
* NB: If this predicate is exposed, it should be cached.
502+
*
503+
* Same as `lastRefRedef`, but ignores uncertain reads.
504+
*/
505+
pragma[nomagic]
506+
predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
507+
lastRefRedef(def, bb, i, next) and
508+
not variableRead(bb, i, def.getSourceVariable(), false)
509+
or
510+
exists(BasicBlock bb0, int i0 |
511+
lastRefRedef(def, bb0, i0, next) and
512+
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
513+
)
514+
}
515+
465516
/**
466517
* NB: If this predicate is exposed, it should be cached.
467518
*
@@ -472,6 +523,7 @@ predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
472523
* SSA definition for the underlying source variable, without passing through
473524
* another read.
474525
*/
526+
pragma[nomagic]
475527
predicate lastRef(Definition def, BasicBlock bb, int i) {
476528
lastRefRedef(def, bb, i, _)
477529
or
@@ -487,6 +539,22 @@ predicate lastRef(Definition def, BasicBlock bb, int i) {
487539
)
488540
}
489541

542+
/**
543+
* NB: If this predicate is exposed, it should be cached.
544+
*
545+
* Same as `lastRefRedef`, but ignores uncertain reads.
546+
*/
547+
pragma[nomagic]
548+
predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
549+
lastRef(def, bb, i) and
550+
not variableRead(bb, i, def.getSourceVariable(), false)
551+
or
552+
exists(BasicBlock bb0, int i0 |
553+
lastRef(def, bb0, i0) and
554+
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
555+
)
556+
}
557+
490558
/** A static single assignment (SSA) definition. */
491559
class Definition extends TDefinition {
492560
/** Gets the source variable underlying this SSA definition. */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ class SourceVariable = SsaImpl::TSourceVariable;
1616

1717
predicate variableWrite = SsaImpl::variableWrite/4;
1818

19-
predicate variableRead = SsaImpl::variableRead/3;
19+
predicate variableRead = SsaImpl::variableRead/4;

0 commit comments

Comments
 (0)