Skip to content

Commit 0df9a62

Browse files
authored
Merge pull request github#1717 from hvitved/csharp/ssa/adjacent-perf
C#: Improve performance of SSA adjacent reads calculation
2 parents b28241a + 98ab2b2 commit 0df9a62

File tree

4 files changed

+170
-123
lines changed

4 files changed

+170
-123
lines changed

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,10 @@ module Internal {
10581058
conditionalAssign(guard, vGuard, def, vDef.getAnExpr())
10591059
}
10601060

1061-
private predicate relevantEq(PreSsa::Definition def, AbstractValue v) {
1062-
conditionalAssignVal(_, _, def, v)
1061+
pragma[noinline]
1062+
private predicate relevantEq(PreSsa::Definition def, AbstractValue v, AssignableRead ar) {
1063+
conditionalAssignVal(_, _, def, v) and
1064+
ar = def.getARead()
10631065
}
10641066

10651067
/**
@@ -1156,8 +1158,7 @@ module Internal {
11561158
private predicate guardImpliesNotEqual(
11571159
Expr guard, AbstractValue vGuard, PreSsa::Definition def, AbstractValue vDef
11581160
) {
1159-
relevantEq(def, vDef) and
1160-
exists(AssignableRead ar | ar = def.getARead() |
1161+
exists(AssignableRead ar | relevantEq(def, vDef, ar) |
11611162
// For example:
11621163
// if (de == null); vGuard = TBooleanValue(false); vDef = TNullValue(true)
11631164
// but not

csharp/ql/src/semmle/code/csharp/controlflow/internal/PreSsa.qll

Lines changed: 67 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -265,89 +265,97 @@ private int maxSsaRefRank(PreBasicBlock bb, SimpleAssignable a) {
265265
not result + 1 = ssaRefRank(bb, _, a, _)
266266
}
267267

268-
private predicate reachesEndOf(Definition def, SimpleAssignable a, PreBasicBlock bb) {
269-
exists(int last | last = maxSsaRefRank(bb, a) | defReachesRank(bb, def, a, last))
270-
or
271-
exists(PreBasicBlock mid |
272-
reachesEndOf(def, a, mid) and
273-
not exists(ssaRefRank(mid, _, a, SsaDef())) and
274-
bb = mid.getASuccessor()
275-
)
268+
pragma[noinline]
269+
private predicate ssaDefReachesEndOfBlockRec(PreBasicBlock bb, Definition def, SimpleAssignable a) {
270+
exists(PreBasicBlock idom | ssaDefReachesEndOfBlock(idom, def, a) | idom.immediatelyDominates(bb))
276271
}
277272

278-
private predicate varOccursInBlock(SimpleAssignable a, PreBasicBlock bb) {
279-
exists(ssaRefRank(bb, _, a, _))
273+
predicate ssaDefReachesEndOfBlock(PreBasicBlock bb, Definition def, SimpleAssignable a) {
274+
exists(int last | last = maxSsaRefRank(bb, a) |
275+
defReachesRank(bb, def, a, last) and
276+
liveAtExit(bb, a)
277+
)
278+
or
279+
ssaDefReachesEndOfBlockRec(bb, def, a) and
280+
liveAtExit(bb, a) and
281+
not ssaRef(bb, _, a, SsaDef())
280282
}
281283

282-
pragma[nomagic]
283-
private predicate blockPrecedesVar(SimpleAssignable a, PreBasicBlock bb) {
284-
varOccursInBlock(a, bb.getASuccessor*())
284+
private predicate ssaDefReachesReadWithinBlock(
285+
SimpleAssignable a, Definition def, PreBasicBlock bb, int i
286+
) {
287+
defReachesRank(bb, def, a, ssaRefRank(bb, i, a, SsaRead()))
285288
}
286289

287-
private predicate varBlockReaches(SimpleAssignable a, PreBasicBlock bb1, PreBasicBlock bb2) {
288-
varOccursInBlock(a, bb1) and
289-
bb2 = bb1.getASuccessor() and
290-
blockPrecedesVar(a, bb2)
290+
private predicate ssaDefReachesRead(SimpleAssignable a, Definition def, PreBasicBlock bb, int i) {
291+
ssaDefReachesReadWithinBlock(a, def, bb, i)
291292
or
292-
varBlockReachesRec(a, bb1, bb2) and
293-
blockPrecedesVar(a, bb2)
293+
ssaRef(bb, i, a, SsaRead()) and
294+
ssaDefReachesEndOfBlock(bb.getAPredecessor(), def, a) and
295+
not ssaDefReachesReadWithinBlock(a, _, bb, i)
294296
}
295297

296-
pragma[nomagic]
297-
private predicate varBlockReachesRec(SimpleAssignable a, PreBasicBlock bb1, PreBasicBlock bb2) {
298-
exists(PreBasicBlock mid | varBlockReaches(a, bb1, mid) |
299-
bb2 = mid.getASuccessor() and
300-
not varOccursInBlock(a, mid)
298+
private int ssaDefRank(Definition def, PreBasicBlock bb, int i) {
299+
exists(SimpleAssignable a |
300+
a = def.getAssignable() and
301+
result = ssaRefRank(bb, i, a, _)
302+
|
303+
ssaDefReachesRead(a, def, bb, i)
304+
or
305+
defAt(bb, i, def, a)
301306
)
302307
}
303308

304-
private predicate varBlockStep(SimpleAssignable a, PreBasicBlock bb1, PreBasicBlock bb2) {
305-
varBlockReaches(a, bb1, bb2) and
306-
varOccursInBlock(a, bb2)
309+
private predicate varOccursInBlock(Definition def, PreBasicBlock bb, SimpleAssignable a) {
310+
exists(ssaDefRank(def, bb, _)) and
311+
a = def.getAssignable()
307312
}
308313

309-
private predicate adjacentVarRefs(
310-
SimpleAssignable a, PreBasicBlock bb1, int i1, PreBasicBlock bb2, int i2
311-
) {
312-
exists(int rankix |
313-
bb1 = bb2 and
314-
rankix = ssaRefRank(bb1, i1, a, _) and
315-
rankix + 1 = ssaRefRank(bb2, i2, a, _)
316-
)
314+
pragma[noinline]
315+
private PreBasicBlock getAMaybeLiveSuccessor(Definition def, PreBasicBlock bb) {
316+
result = bb.getASuccessor() and
317+
not varOccursInBlock(_, bb, def.getAssignable()) and
318+
ssaDefReachesEndOfBlock(bb, def, _)
319+
}
320+
321+
private predicate varBlockReaches(Definition def, PreBasicBlock bb1, PreBasicBlock bb2) {
322+
varOccursInBlock(def, bb1, _) and
323+
bb2 = bb1.getASuccessor()
317324
or
318-
ssaRefRank(bb1, i1, a, _) = maxSsaRefRank(bb1, a) and
319-
varBlockStep(a, bb1, bb2) and
320-
ssaRefRank(bb2, i2, a, _) = 1
325+
exists(PreBasicBlock mid | varBlockReaches(def, bb1, mid) |
326+
bb2 = getAMaybeLiveSuccessor(def, mid)
327+
)
321328
}
322329

323-
predicate firstReadSameVar(Definition def, AssignableRead read) {
324-
exists(SimpleAssignable a, PreBasicBlock b1, int i1, PreBasicBlock b2, int i2 |
325-
adjacentVarRefs(a, b1, i1, b2, i2) and
326-
defAt(b1, i1, def, a) and
327-
readAt(b2, i2, read, a)
330+
private predicate varBlockReachesRead(Definition def, PreBasicBlock bb1, AssignableRead read) {
331+
exists(PreBasicBlock bb2, int i2 |
332+
varBlockReaches(def, bb1, bb2) and
333+
ssaRefRank(bb2, i2, def.getAssignable(), SsaRead()) = 1 and
334+
readAt(bb2, i2, read, _)
328335
)
329336
}
330337

331-
predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) {
332-
exists(SimpleAssignable a, PreBasicBlock bb1, int i1, PreBasicBlock bb2, int i2 |
333-
adjacentVarRefs(a, bb1, i1, bb2, i2) and
334-
readAt(bb1, i1, read1, a) and
335-
readAt(bb2, i2, read2, a)
338+
private predicate adjacentVarRead(Definition def, PreBasicBlock bb1, int i1, AssignableRead read) {
339+
exists(int rankix, int i2 |
340+
rankix = ssaDefRank(def, bb1, i1) and
341+
rankix + 1 = ssaDefRank(def, bb1, i2) and
342+
readAt(bb1, i2, read, _)
336343
)
344+
or
345+
ssaDefRank(def, bb1, i1) = maxSsaRefRank(bb1, def.getAssignable()) and
346+
varBlockReachesRead(def, bb1, read)
337347
}
338348

339-
pragma[noinline]
340-
private predicate ssaDefReachesEndOfBlockRec(PreBasicBlock bb, Definition def, SimpleAssignable a) {
341-
exists(PreBasicBlock idom | ssaDefReachesEndOfBlock(idom, def, a) | idom.immediatelyDominates(bb))
349+
predicate firstReadSameVar(Definition def, AssignableRead read) {
350+
exists(PreBasicBlock bb1, int i1 |
351+
defAt(bb1, i1, def, _) and
352+
adjacentVarRead(def, bb1, i1, read)
353+
)
342354
}
343355

344-
predicate ssaDefReachesEndOfBlock(PreBasicBlock bb, Definition def, SimpleAssignable a) {
345-
exists(int last | last = maxSsaRefRank(bb, a) |
346-
defReachesRank(bb, def, a, last) and
347-
liveAtExit(bb, a)
356+
predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) {
357+
exists(Definition def, PreBasicBlock bb1, int i1 |
358+
readAt(bb1, i1, read1, _) and
359+
adjacentVarRead(def, bb1, i1, read2)
348360
)
349-
or
350-
ssaDefReachesEndOfBlockRec(bb, def, a) and
351-
liveAtExit(bb, a) and
352-
not ssaRef(bb, _, a, SsaDef())
353361
}

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

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -667,63 +667,82 @@ module Ssa {
667667
)
668668
}
669669

670-
/** Holds if `v` is defined or read in basic block `bb`. */
671-
private predicate varOccursInBlock(TrackedVar v, BasicBlock bb) {
672-
exists(ssaRefRank(bb, _, v, _))
670+
/**
671+
* Same as `ssaRefRank()`, but restricted to actual reads of `def`, or
672+
* `def` itself.
673+
*/
674+
private int ssaDefRank(TrackedDefinition def, TrackedVar v, BasicBlock bb, int i) {
675+
v = def.getSourceVariable() and
676+
result = ssaRefRank(bb, i, v, _) and
677+
(
678+
ssaDefReachesRead(_, def, bb.getNode(i), ActualRead())
679+
or
680+
definesAt(def, bb, i, _)
681+
)
673682
}
674683

675-
/** Holds if `v` occurs in `bb` or one of `bb`'s transitive successors. */
676-
private predicate blockPrecedesVar(TrackedVar v, BasicBlock bb) {
677-
varOccursInBlock(v, bb)
678-
or
679-
blockPrecedesVar(v, bb.getASuccessor())
684+
private int maxSsaDefRefRank(BasicBlock bb, TrackedVar v) {
685+
result = ssaDefRank(_, v, bb, _) and
686+
not result + 1 = ssaDefRank(_, v, bb, _)
680687
}
681688

682-
/**
683-
* Holds if `bb2` is a transitive successor of `bb1` and `v` occurs in `bb1` and
684-
* in `bb2` or one of its transitive successors but not in any block on the path
685-
* between `bb1` and `bb2`.
686-
*/
687-
private predicate varBlockReaches(TrackedVar v, BasicBlock bb1, BasicBlock bb2) {
688-
varOccursInBlock(v, bb1) and
689-
bb2 = bb1.getASuccessor() and
690-
blockPrecedesVar(v, bb2)
691-
or
692-
varBlockReachesRec(v, bb1, bb2) and
693-
blockPrecedesVar(v, bb2)
689+
private predicate varOccursInBlock(TrackedDefinition def, BasicBlock bb, TrackedVar v) {
690+
exists(ssaDefRank(def, v, bb, _))
694691
}
695692

696693
pragma[noinline]
697-
private predicate varBlockReachesRec(TrackedVar v, BasicBlock bb1, BasicBlock bb2) {
698-
exists(BasicBlock mid | varBlockReaches(v, bb1, mid) |
699-
bb2 = mid.getASuccessor() and
700-
not varOccursInBlock(v, mid)
694+
private BasicBlock getAMaybeLiveSuccessor(Definition def, BasicBlock bb) {
695+
result = bb.getASuccessor() and
696+
not varOccursInBlock(_, bb, def.getSourceVariable()) and
697+
ssaDefReachesEndOfBlock(bb, def, _)
698+
}
699+
700+
/**
701+
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
702+
* `bb2` is a transitive successor of `bb1`, and `def` is *maybe* read in `bb2`
703+
* or one of its transitive successors, but not in any block on the path between
704+
* `bb1` and `bb2`.
705+
*/
706+
private predicate varBlockReaches(TrackedDefinition def, BasicBlock bb1, BasicBlock bb2) {
707+
varOccursInBlock(def, bb1, _) and
708+
bb2 = bb1.getASuccessor()
709+
or
710+
exists(BasicBlock mid | varBlockReaches(def, bb1, mid) |
711+
bb2 = getAMaybeLiveSuccessor(def, mid)
701712
)
702713
}
703714

704715
/**
705-
* Holds if `bb2` is a transitive successor of `bb1` and `v` occurs in `bb1` and
706-
* `bb2` but not in any block on the path between `bb1` and `bb2`.
716+
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
717+
* `def` is read at `cfn`, `cfn` is in a transitive successor block of `bb1`,
718+
* and `def` is not read in any block on the path between `bb1` and `cfn`.
707719
*/
708-
private predicate varBlockStep(TrackedVar v, BasicBlock bb1, BasicBlock bb2) {
709-
varBlockReaches(v, bb1, bb2) and
710-
varOccursInBlock(v, bb2)
720+
private predicate varBlockReachesRead(
721+
TrackedDefinition def, BasicBlock bb1, ControlFlow::Node cfn
722+
) {
723+
exists(BasicBlock bb2, int i2 |
724+
varBlockReaches(def, bb1, bb2) and
725+
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
726+
variableRead(bb2, i2, _, cfn, _)
727+
)
711728
}
712729

713730
/**
714-
* Holds if `v` is accessed at index `i1` in basic block `bb1` and at index `i2` in
715-
* basic block `bb2` and there is a path between them without any access to `v`.
731+
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
732+
* or a write), `def` is read at `cfn`, and there is a path between them without
733+
* any read of `def`.
716734
*/
717-
private predicate adjacentVarRefs(TrackedVar v, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
718-
exists(int rankix |
719-
bb1 = bb2 and
720-
rankix = ssaRefRank(bb1, i1, v, _) and
721-
rankix + 1 = ssaRefRank(bb2, i2, v, _)
735+
private predicate adjacentVarRead(
736+
TrackedDefinition def, BasicBlock bb1, int i1, ControlFlow::Node cfn
737+
) {
738+
exists(int rankix, int i2 |
739+
rankix = ssaDefRank(def, _, bb1, i1) and
740+
rankix + 1 = ssaDefRank(def, _, bb1, i2) and
741+
variableRead(bb1, i2, _, cfn, _)
722742
)
723743
or
724-
ssaRefRank(bb1, i1, v, _) = maxSsaRefRank(bb1, v) and
725-
varBlockStep(v, bb1, bb2) and
726-
ssaRefRank(bb2, i2, v, _) = 1
744+
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1) = maxSsaDefRefRank(bb1, v)) and
745+
varBlockReachesRead(def, bb1, cfn)
727746
}
728747

729748
cached
@@ -736,23 +755,26 @@ module Ssa {
736755
*/
737756
cached
738757
predicate lastRead(TrackedDefinition def, ControlFlow::Node cfn) {
739-
exists(TrackedVar v, BasicBlock bb, int i, int rnk |
740-
exists(def.getAReadAtNode(cfn)) and
741-
variableRead(bb, i, v, cfn, _) and
742-
rnk = ssaRefRank(bb, i, v, SsaRead())
758+
exists(BasicBlock bb1, int i1, int rnk, TrackedVar v |
759+
variableRead(bb1, i1, v, cfn, _) and
760+
rnk = ssaDefRank(def, v, bb1, i1)
743761
|
744-
// Next reference to `v` inside `bb` is a write
745-
rnk + 1 = ssaRefRank(bb, _, v, SsaDef())
762+
// Next reference to `v` inside `bb1` is a write
763+
rnk + 1 = ssaRefRank(bb1, _, v, SsaDef())
746764
or
747-
// No next reference to `v` inside `bb`
748-
rnk = maxSsaRefRank(bb, v) and
765+
// No more references to `v` inside `bb1`
766+
rnk = maxSsaDefRefRank(bb1, def.getSourceVariable()) and
749767
(
750-
// Read reaches end of enclosing callable
751-
not varBlockReaches(v, bb, _)
768+
// Can reach exit directly
769+
bb1 instanceof ControlFlow::BasicBlocks::ExitBlock
752770
or
753-
// Read reaches an SSA definition in a successor block
754-
exists(BasicBlock bb2 | varBlockReaches(v, bb, bb2) |
755-
1 = ssaRefRank(bb2, _, v, SsaDef())
771+
exists(BasicBlock bb2 | varBlockReaches(def, bb1, bb2) |
772+
// Can reach a write using one or more steps
773+
1 = ssaRefRank(bb2, _, def.getSourceVariable(), SsaDef())
774+
or
775+
// Can reach a block using one or more steps, where `def` is no longer live
776+
not varOccursInBlock(def, bb2, _) and
777+
not ssaDefReachesEndOfBlock(bb2, def, _)
756778
)
757779
)
758780
)
@@ -831,10 +853,9 @@ module Ssa {
831853
*/
832854
cached
833855
predicate firstReadSameVar(TrackedDefinition def, ControlFlow::Node cfn) {
834-
exists(TrackedVar v, BasicBlock b1, int i1, BasicBlock b2, int i2 |
835-
adjacentVarRefs(v, b1, i1, b2, i2) and
836-
definesAt(def, b1, i1, v) and
837-
variableRead(b2, i2, v, cfn, _)
856+
exists(BasicBlock bb1, int i1 |
857+
definesAt(def, bb1, i1, _) and
858+
adjacentVarRead(def, bb1, i1, cfn)
838859
)
839860
}
840861

@@ -845,10 +866,9 @@ module Ssa {
845866
*/
846867
cached
847868
predicate adjacentReadPairSameVar(ControlFlow::Node cfn1, ControlFlow::Node cfn2) {
848-
exists(TrackedVar v, BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
849-
adjacentVarRefs(v, bb1, i1, bb2, i2) and
850-
variableRead(bb1, i1, v, cfn1, _) and
851-
variableRead(bb2, i2, v, cfn2, _)
869+
exists(TrackedDefinition def, BasicBlock bb1, int i1 |
870+
variableRead(bb1, i1, _, cfn1, _) and
871+
adjacentVarRead(def, bb1, i1, cfn2)
852872
)
853873
}
854874
}

0 commit comments

Comments
 (0)