Skip to content

Commit 1a39f30

Browse files
committed
Java: Less caching
1 parent a93188c commit 1a39f30

File tree

2 files changed

+124
-72
lines changed

2 files changed

+124
-72
lines changed

java/ql/lib/semmle/code/java/dataflow/SSA.qll

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,7 @@ class SsaUpdate extends SsaVariable instanceof WriteDefinition {
206206
class SsaExplicitUpdate extends SsaUpdate {
207207
private VariableUpdate upd;
208208

209-
SsaExplicitUpdate() {
210-
exists(SsaSourceVariable v, BasicBlock bb, int i |
211-
this.definesAt(v, bb, i) and
212-
certainVariableUpdate(v, upd, bb, i) and
213-
getDestVar(upd) = this.getSourceVariable()
214-
)
215-
}
209+
SsaExplicitUpdate() { ssaExplicitUpdate(this, upd) }
216210

217211
override string toString() { result = "SSA def(" + this.getSourceVariable() + ")" }
218212

@@ -233,13 +227,27 @@ class SsaImplicitUpdate extends SsaUpdate {
233227
result = "SSA impl upd[" + this.getKind() + "](" + this.getSourceVariable() + ")"
234228
}
235229

230+
private predicate hasExplicitQualifierUpdate() {
231+
exists(SsaExplicitUpdate qdef, BasicBlock bb, int i |
232+
qdef.definesAt(this.getSourceVariable().getQualifier(), bb, i) and
233+
this.definesAt(_, bb, i)
234+
)
235+
}
236+
237+
private predicate hasImplicitQualifierUpdate() {
238+
exists(SsaUncertainImplicitUpdate qdef, BasicBlock bb, int i |
239+
qdef.definesAt(this.getSourceVariable().getQualifier(), bb, i) and
240+
this.definesAt(_, bb, i)
241+
)
242+
}
243+
236244
private string getKind() {
237245
this instanceof UntrackedDef and result = "untracked"
238246
or
239-
certainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCfgNode(), _, _) and
247+
this.hasExplicitQualifierUpdate() and
240248
result = "explicit qualifier"
241249
or
242-
if uncertainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCfgNode(), _, _)
250+
if this.hasImplicitQualifierUpdate()
243251
then
244252
if exists(this.getANonLocalUpdate())
245253
then result = "nonlocal + nonlocal qualifier"
@@ -252,13 +260,7 @@ class SsaImplicitUpdate extends SsaUpdate {
252260
/**
253261
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
254262
*/
255-
FieldWrite getANonLocalUpdate() {
256-
exists(SsaSourceField f, Callable setter |
257-
f = this.getSourceVariable() and
258-
relevantFieldUpdate(setter, f.getField(), result) and
259-
updatesNamedField(this.getCfgNode(), f, setter)
260-
)
261-
}
263+
FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
262264

263265
/**
264266
* Holds if this ssa variable might change the value to something unknown.
@@ -268,9 +270,11 @@ class SsaImplicitUpdate extends SsaUpdate {
268270
* of its qualifiers is volatile.
269271
*/
270272
predicate assignsUnknownValue() {
271-
this instanceof UntrackedDef or
272-
certainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCfgNode(), _, _) or
273-
uncertainVariableUpdate(this.getSourceVariable().getQualifier(), this.getCfgNode(), _, _)
273+
this instanceof UntrackedDef
274+
or
275+
this.hasExplicitQualifierUpdate()
276+
or
277+
this.hasImplicitQualifierUpdate()
274278
}
275279
}
276280

@@ -280,12 +284,7 @@ class SsaImplicitUpdate extends SsaUpdate {
280284
* its qualifiers.
281285
*/
282286
class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
283-
SsaUncertainImplicitUpdate() {
284-
exists(SsaSourceVariable v, BasicBlock bb, int i |
285-
this.definesAt(v, bb, i) and
286-
uncertainVariableUpdate(v, _, bb, i)
287-
)
288-
}
287+
SsaUncertainImplicitUpdate() { ssaUncertainImplicitUpdate(this) }
289288

290289
/**
291290
* Gets the immediately preceding definition. Since this update is uncertain
@@ -299,13 +298,7 @@ class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
299298
* includes initial values of parameters, fields, and closure variables.
300299
*/
301300
class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
302-
SsaImplicitInit() {
303-
exists(SsaSourceVariable v, BasicBlock bb, int i |
304-
this.definesAt(v, bb, i) and
305-
hasEntryDef(v, bb) and
306-
i = 0
307-
)
308-
}
301+
SsaImplicitInit() { ssaImplicitInit(this) }
309302

310303
override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" }
311304

java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll

Lines changed: 99 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -269,30 +269,41 @@ private module Cached {
269269
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
270270
}
271271

272-
/**
273-
* Holds if `f` is a field that is interesting as a basis for SSA.
274-
*
275-
* - A field that is read twice is interesting as we want to know whether the
276-
* reads refer to the same value.
277-
* - A field that is both written and read is interesting as we want to know
278-
* whether the read might get the written value.
279-
* - A field that is read in a loop is interesting as we want to know whether
280-
* the value is the same in different iterations (that is, whether the SSA
281-
* definition can be placed outside the loop).
282-
* - A volatile field is never interesting, since all reads must reread from
283-
* memory and we are forced to assume that the value can change at any point.
284-
*/
285-
cached
286-
predicate trackField(SsaSourceField f) { multiAccessed(f) and not f.isVolatile() }
272+
private module NotCached1 {
273+
/**
274+
* Holds if `f` is a field that is interesting as a basis for SSA.
275+
*
276+
* - A field that is read twice is interesting as we want to know whether the
277+
* reads refer to the same value.
278+
* - A field that is both written and read is interesting as we want to know
279+
* whether the read might get the written value.
280+
* - A field that is read in a loop is interesting as we want to know whether
281+
* the value is the same in different iterations (that is, whether the SSA
282+
* definition can be placed outside the loop).
283+
* - A volatile field is never interesting, since all reads must reread from
284+
* memory and we are forced to assume that the value can change at any point.
285+
*/
286+
pragma[nomagic]
287+
predicate trackField(SsaSourceField f) { multiAccessed(f) and not f.isVolatile() }
288+
289+
/** Holds if `n` must update the locally tracked variable `v`. */
290+
pragma[nomagic]
291+
predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
292+
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
293+
b.getNode(i) = n and
294+
hasDominanceInformation(b)
295+
or
296+
certainVariableUpdate(v.getQualifier(), n, b, i)
297+
}
298+
}
287299

288-
/** Holds if `n` must update the locally tracked variable `v`. */
289300
cached
290-
predicate certainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
291-
exists(VariableUpdate a | a = n | getDestVar(a) = v) and
292-
b.getNode(i) = n and
293-
hasDominanceInformation(b)
294-
or
295-
certainVariableUpdate(v.getQualifier(), n, b, i)
301+
predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
302+
exists(SsaSourceVariable v, BasicBlock bb, int i |
303+
def.definesAt(v, bb, i) and
304+
certainVariableUpdate(v, upd, bb, i) and
305+
getDestVar(upd) = def.getSourceVariable()
306+
)
296307
}
297308

298309
/*
@@ -333,8 +344,8 @@ private module Cached {
333344
/**
334345
* Holds if `fw` is an update of `f` in `c` that is relevant for SSA construction.
335346
*/
336-
cached
337-
predicate relevantFieldUpdate(Callable c, Field f, FieldWrite fw) {
347+
pragma[nomagic]
348+
private predicate relevantFieldUpdate(Callable c, Field f, FieldWrite fw) {
338349
fw = f.getAnAccess() and
339350
not init(fw) and
340351
fw.getEnclosingCallable() = c and
@@ -483,34 +494,66 @@ private module Cached {
483494
* `FieldRead` of `f` is reachable from `call`.
484495
*/
485496
pragma[noopt]
486-
cached
487-
predicate updatesNamedField(Call call, TrackedField f, Callable setter) {
497+
private predicate updatesNamedField(Call call, TrackedField f, Callable setter) {
488498
exists(TCallableNode src, TCallableNode sink, Field field |
489499
source(call, f, field, src) and
490500
sink(setter, field, sink) and
491501
(src = sink or edgePlus(src, sink))
492502
)
493503
}
494504

495-
/** Holds if `n` might update the locally tracked variable `v`. */
505+
/**
506+
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
507+
*/
496508
cached
497-
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
498-
exists(Call c | c = n | updatesNamedField(c, v, _)) and
499-
b.getNode(i) = n and
500-
hasDominanceInformation(b)
501-
or
502-
uncertainVariableUpdate(v.getQualifier(), n, b, i)
509+
FieldWrite getANonLocalUpdate(SsaImplicitUpdate def) {
510+
exists(SsaSourceField f, Callable setter |
511+
f = def.getSourceVariable() and
512+
relevantFieldUpdate(setter, f.getField(), result) and
513+
updatesNamedField(def.getCfgNode(), f, setter)
514+
)
515+
}
516+
517+
private module NotCached2 {
518+
/** Holds if `n` might update the locally tracked variable `v`. */
519+
pragma[nomagic]
520+
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
521+
exists(Call c | c = n | updatesNamedField(c, v, _)) and
522+
b.getNode(i) = n and
523+
hasDominanceInformation(b)
524+
or
525+
uncertainVariableUpdate(v.getQualifier(), n, b, i)
526+
}
503527
}
504528

505-
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
506529
cached
507-
predicate hasEntryDef(TrackedVar v, BasicBlock b) {
508-
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
509-
l instanceof Parameter or
510-
l.getCallable() != c
530+
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
531+
exists(SsaSourceVariable v, BasicBlock bb, int i |
532+
def.definesAt(v, bb, i) and
533+
uncertainVariableUpdate(v, _, bb, i)
534+
)
535+
}
536+
537+
private module NotCached3 {
538+
/** Holds if `v` has an implicit definition at the entry, `b`, of the callable. */
539+
pragma[nomagic]
540+
predicate hasEntryDef(TrackedVar v, BasicBlock b) {
541+
exists(LocalScopeVariable l, Callable c | v = TLocalVar(c, l) and c.getBody() = b |
542+
l instanceof Parameter or
543+
l.getCallable() != c
544+
)
545+
or
546+
v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b
547+
}
548+
}
549+
550+
cached
551+
predicate ssaImplicitInit(WriteDefinition def) {
552+
exists(SsaSourceVariable v, BasicBlock bb, int i |
553+
def.definesAt(v, bb, i) and
554+
hasEntryDef(v, bb) and
555+
i = 0
511556
)
512-
or
513-
v instanceof SsaSourceField and v.getEnclosingCallable().getBody() = b
514557
}
515558

516559
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
@@ -636,9 +679,21 @@ private module Cached {
636679
)
637680
}
638681
}
682+
683+
/**
684+
* Provides internal implementation predicates that are not cached and should not
685+
* be used outside of this file.
686+
*/
687+
cached // needed to avoid compilation error; has no actual effect
688+
module Internal {
689+
import NotCached1
690+
import NotCached2
691+
import NotCached3
692+
}
639693
}
640694

641695
import Cached
696+
private import Internal
642697

643698
/**
644699
* An SSA definition excluding those variables that use a trivial SSA construction.
@@ -682,6 +737,10 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
682737
)
683738
}
684739

740+
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
741+
def instanceof SsaUncertainImplicitUpdate
742+
}
743+
685744
class Guard extends Guards::Guard {
686745
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
687746
}

0 commit comments

Comments
 (0)