Skip to content

Commit b19fd7b

Browse files
committed
C#: Only cache TDefinition in the shared SSA implementation
1 parent 74fd2c1 commit b19fd7b

File tree

3 files changed

+77
-65
lines changed

3 files changed

+77
-65
lines changed

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

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,18 @@ module Ssa {
384384
result.getAControlFlowNode() = cfn
385385
}
386386

387+
/**
388+
* Gets an SSA definition whose value can flow to this one in one step. This
389+
* includes inputs to phi nodes and the prior definitions of uncertain writes.
390+
*/
391+
private Definition getAPhiInputOrPriorDefinition() {
392+
result = this.(PhiNode).getAnInput() or
393+
result = this.(UncertainDefinition).getPriorDefinition()
394+
}
395+
387396
/**
388397
* Gets a definition that ultimately defines this SSA definition and is
389-
* not itself a pseudo node. Example:
398+
* not itself a phi node. Example:
390399
*
391400
* ```csharp
392401
* int Field;
@@ -413,8 +422,9 @@ module Ssa {
413422
* definition on line 4, the explicit definition on line 7, and the implicit
414423
* definition on line 9.
415424
*/
416-
final override Definition getAnUltimateDefinition() {
417-
result = SsaImpl::Definition.super.getAnUltimateDefinition()
425+
final Definition getAnUltimateDefinition() {
426+
result = this.getAPhiInputOrPriorDefinition*() and
427+
not result instanceof PhiNode
418428
}
419429

420430
/**
@@ -425,7 +435,7 @@ module Ssa {
425435
/**
426436
* Gets the syntax element associated with this SSA definition, if any.
427437
* This is either an expression, for example `x = 0`, a parameter, or a
428-
* callable. Pseudo nodes have no associated syntax element.
438+
* callable. Phi nodes have no associated syntax element.
429439
*/
430440
Element getElement() { result = this.getControlFlowNode().getElement() }
431441

@@ -681,7 +691,12 @@ module Ssa {
681691
* definition on line 4, the explicit definition on line 7, and the implicit
682692
* call definition on line 9 as inputs.
683693
*/
684-
final override Definition getAnInput() { result = SsaImpl::PhiNode.super.getAnInput() }
694+
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
695+
696+
/** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */
697+
predicate hasInputFromBlock(Definition inp, ControlFlow::BasicBlock bb) {
698+
inp = SsaImpl::phiHasInputFromBlock(this, bb)
699+
}
685700

686701
override string toString() {
687702
result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")"
@@ -704,5 +719,11 @@ module Ssa {
704719
* need not be certain), an implicit non-local update via a call, or an
705720
* uncertain update of the qualifier.
706721
*/
707-
class UncertainDefinition extends Definition, SsaImpl::UncertainWriteDefinition { }
722+
class UncertainDefinition extends Definition, SsaImpl::UncertainWriteDefinition {
723+
/**
724+
* Gets the immediately preceding definition. Since this update is uncertain,
725+
* the value from the preceding definition might still be valid.
726+
*/
727+
Definition getPriorDefinition() { result = SsaImpl::uncertainWriteDefinitionInput(this) }
728+
}
708729
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ private module Cached {
10751075
Ssa::ExplicitDefinition def, Ssa::ImplicitEntryDefinition edef,
10761076
ControlFlow::Nodes::ElementNode c, boolean additionalCalls
10771077
) {
1078-
exists(Ssa::SourceVariable v, Definition def0, ControlFlow::BasicBlock bb, int i |
1078+
exists(Ssa::SourceVariable v, Ssa::Definition def0, ControlFlow::BasicBlock bb, int i |
10791079
v = def.getSourceVariable() and
10801080
capturedReadIn(_, _, v, edef.getSourceVariable(), c, additionalCalls) and
10811081
def = def0.getAnUltimateDefinition() and
@@ -1109,6 +1109,11 @@ private module Cached {
11091109
ssaDefReachesEndOfBlock(bb, def, _)
11101110
}
11111111

1112+
cached
1113+
Definition phiHasInputFromBlock(PhiNode phi, ControlFlow::BasicBlock bb) {
1114+
phiHasInputFromBlock(phi, result, bb)
1115+
}
1116+
11121117
cached
11131118
AssignableRead getAReadAtNode(Definition def, ControlFlow::Node cfn) {
11141119
exists(Ssa::SourceVariable v, ControlFlow::BasicBlock bb, int i |
@@ -1161,6 +1166,11 @@ private module Cached {
11611166
)
11621167
}
11631168

1169+
cached
1170+
Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) {
1171+
uncertainWriteDefinitionInput(def, result)
1172+
}
1173+
11641174
cached
11651175
predicate isLiveOutRefParameterDefinition(Ssa::Definition def, Parameter p) {
11661176
p.isOutOrRef() and

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

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -174,34 +174,15 @@ private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
174174
}
175175

176176
cached
177-
private module Cached {
178-
cached
179-
newtype TDefinition =
180-
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
181-
variableWrite(bb, i, v, _) and
182-
liveAfterWrite(bb, i, v)
183-
} or
184-
TPhiNode(SourceVariable v, BasicBlock bb) {
185-
inDefDominanceFrontier(bb, v) and
186-
liveAtEntry(bb, v)
187-
}
188-
189-
cached
190-
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
191-
lastRefRedef(inp, _, _, def)
192-
}
193-
194-
cached
195-
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
196-
exists(SourceVariable v, BasicBlock bbDef |
197-
phi.definesAt(v, bbDef, _) and
198-
getABasicBlockPredecessor(bbDef) = bb and
199-
ssaDefReachesEndOfBlock(bb, inp, v)
200-
)
177+
newtype TDefinition =
178+
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
179+
variableWrite(bb, i, v, _) and
180+
liveAfterWrite(bb, i, v)
181+
} or
182+
TPhiNode(SourceVariable v, BasicBlock bb) {
183+
inDefDominanceFrontier(bb, v) and
184+
liveAtEntry(bb, v)
201185
}
202-
}
203-
204-
import Cached
205186

206187
private module SsaDefReaches {
207188
newtype TSsaRefKind =
@@ -406,6 +387,20 @@ predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable
406387
not ssaRef(bb, _, v, SsaDef())
407388
}
408389

390+
/**
391+
* NB: If this predicate is exposed, it should be cached.
392+
*
393+
* Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
394+
*/
395+
pragma[nomagic]
396+
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
397+
exists(SourceVariable v, BasicBlock bbDef |
398+
phi.definesAt(v, bbDef, _) and
399+
getABasicBlockPredecessor(bbDef) = bb and
400+
ssaDefReachesEndOfBlock(bb, inp, v)
401+
)
402+
}
403+
409404
/**
410405
* NB: If this predicate is exposed, it should be cached.
411406
*
@@ -446,7 +441,11 @@ private predicate adjacentDefReachesRead(
446441
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
447442
) {
448443
adjacentDefRead(def, bb1, i1, bb2, i2) and
449-
not variableRead(bb1, i1, def.getSourceVariable(), false)
444+
exists(SourceVariable v | v = def.getSourceVariable() |
445+
ssaRef(bb1, i1, v, SsaDef())
446+
or
447+
variableRead(bb1, i1, v, true)
448+
)
450449
or
451450
exists(BasicBlock bb3, int i3 |
452451
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
@@ -490,6 +489,18 @@ predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
490489
)
491490
}
492491

492+
/**
493+
* NB: If this predicate is exposed, it should be cached.
494+
*
495+
* Holds if `inp` is an immediately preceding definition of uncertain definition
496+
* `def`. Since `def` is uncertain, the value from the preceding definition might
497+
* still be valid.
498+
*/
499+
pragma[nomagic]
500+
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
501+
lastRefRedef(inp, _, _, def)
502+
}
503+
493504
private predicate adjacentDefReachesUncertainRead(
494505
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
495506
) {
@@ -574,24 +585,6 @@ class Definition extends TDefinition {
574585
/** Gets the basic block to which this SSA definition belongs. */
575586
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
576587

577-
/**
578-
* Gets an SSA definition whose value can flow to this one in one step. This
579-
* includes inputs to phi nodes and the prior definitions of uncertain writes.
580-
*/
581-
private Definition getAPseudoInputOrPriorDefinition() {
582-
result = this.(PhiNode).getAnInput() or
583-
result = this.(UncertainWriteDefinition).getPriorDefinition()
584-
}
585-
586-
/**
587-
* Gets a definition that ultimately defines this SSA definition and is
588-
* not itself a phi node.
589-
*/
590-
Definition getAnUltimateDefinition() {
591-
result = this.getAPseudoInputOrPriorDefinition*() and
592-
not result instanceof PhiNode
593-
}
594-
595588
/** Gets a textual representation of this SSA definition. */
596589
string toString() { none() }
597590
}
@@ -609,12 +602,6 @@ class WriteDefinition extends Definition, TWriteDef {
609602

610603
/** A phi node. */
611604
class PhiNode extends Definition, TPhiNode {
612-
/** Gets an input of this phi node. */
613-
Definition getAnInput() { this.hasInputFromBlock(result, _) }
614-
615-
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
616-
predicate hasInputFromBlock(Definition inp, BasicBlock bb) { phiHasInputFromBlock(this, inp, bb) }
617-
618605
override string toString() { result = "Phi" }
619606
}
620607

@@ -629,10 +616,4 @@ class UncertainWriteDefinition extends WriteDefinition {
629616
variableWrite(bb, i, v, false)
630617
)
631618
}
632-
633-
/**
634-
* Gets the immediately preceding definition. Since this update is uncertain,
635-
* the value from the preceding definition might still be valid.
636-
*/
637-
Definition getPriorDefinition() { uncertainWriteDefinitionInput(this, result) }
638619
}

0 commit comments

Comments
 (0)