Skip to content

Commit 00bac7e

Browse files
committed
Go: Only compute most recent side effect for relevant nodes
1 parent c64223a commit 00bac7e

1 file changed

Lines changed: 93 additions & 64 deletions

File tree

go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -132,62 +132,74 @@ private predicate sideEffectCfg(ControlFlow::Node src, ControlFlow::Node dst) {
132132
private predicate iDomEffect(ControlFlow::Node dominator, ControlFlow::Node node) =
133133
idominance(entryNode/1, sideEffectCfg/2)(_, dominator, node)
134134

135-
/**
136-
* Gets the most recent side effect. To be more precise, `result` is a
137-
* dominator of `node` and no side-effects can occur between `result` and
138-
* `node`.
139-
*
140-
* `sideEffectCFG` has an edge from the function entry to every node with a
141-
* side-effect. This means that every node with a side-effect has the
142-
* function entry as its immediate dominator. So if node `x` dominates node
143-
* `y` then there can be no side effects between `x` and `y` unless `x` is
144-
* the function entry. So the optimal choice for `result` has the function
145-
* entry as its immediate dominator.
146-
*
147-
* Example:
148-
*
149-
* ```
150-
* 000: int f(int a, int b, int *p) {
151-
* 001: int r = 0;
152-
* 002: if (a) {
153-
* 003: if (b) {
154-
* 004: sideEffect1();
155-
* 005: }
156-
* 006: } else {
157-
* 007: sideEffect2();
158-
* 008: }
159-
* 009: if (a) {
160-
* 010: r++; // Not a side-effect, because r is an SSA variable.
161-
* 011: }
162-
* 012: if (b) {
163-
* 013: r++; // Not a side-effect, because r is an SSA variable.
164-
* 014: }
165-
* 015: return *p;
166-
* 016: }
167-
* ```
168-
*
169-
* Suppose we want to find the most recent side-effect for the dereference
170-
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
171-
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
172-
* the immediate dominator tree looks like this:
173-
*
174-
* 000 - 001 - 002 - 003
175-
* - 004
176-
* - 007
177-
* - 009 - 010
178-
* - 012 - 013
179-
* - 015
180-
*
181-
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
182-
* Therefore, the most recent side effect for line 015 is line 009.
183-
*/
184-
cached
185-
private ControlFlow::Node mostRecentSideEffect(ControlFlow::Node node) {
186-
exists(ControlFlow::Node entry |
187-
entryNode(entry) and
188-
iDomEffect(entry, result) and
189-
iDomEffect*(result, node)
190-
)
135+
private signature predicate relevantNodeSig(ControlFlow::Node n);
136+
137+
private module MostRecentSideEffect<relevantNodeSig/1 relevantNode> {
138+
private predicate iDomEntry(ControlFlow::Node node) {
139+
exists(ControlFlow::Node entry |
140+
entryNode(entry) and
141+
iDomEffect(entry, node)
142+
)
143+
}
144+
145+
private predicate iDomEffectPlus(ControlFlow::Node n1, ControlFlow::Node n2) =
146+
doublyBoundedFastTC(iDomEffect/2, iDomEntry/1, relevantNode/1)(n1, n2)
147+
148+
/**
149+
* Gets the most recent side effect. To be more precise, `result` is a
150+
* dominator of `node` and no side-effects can occur between `result` and
151+
* `node`.
152+
*
153+
* `sideEffectCFG` has an edge from the function entry to every node with a
154+
* side-effect. This means that every node with a side-effect has the
155+
* function entry as its immediate dominator. So if node `x` dominates node
156+
* `y` then there can be no side effects between `x` and `y` unless `x` is
157+
* the function entry. So the optimal choice for `result` has the function
158+
* entry as its immediate dominator.
159+
*
160+
* Example:
161+
*
162+
* ```
163+
* 000: int f(int a, int b, int *p) {
164+
* 001: int r = 0;
165+
* 002: if (a) {
166+
* 003: if (b) {
167+
* 004: sideEffect1();
168+
* 005: }
169+
* 006: } else {
170+
* 007: sideEffect2();
171+
* 008: }
172+
* 009: if (a) {
173+
* 010: r++; // Not a side-effect, because r is an SSA variable.
174+
* 011: }
175+
* 012: if (b) {
176+
* 013: r++; // Not a side-effect, because r is an SSA variable.
177+
* 014: }
178+
* 015: return *p;
179+
* 016: }
180+
* ```
181+
*
182+
* Suppose we want to find the most recent side-effect for the dereference
183+
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
184+
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
185+
* the immediate dominator tree looks like this:
186+
*
187+
* 000 - 001 - 002 - 003
188+
* - 004
189+
* - 007
190+
* - 009 - 010
191+
* - 012 - 013
192+
* - 015
193+
*
194+
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
195+
* Therefore, the most recent side effect for line 015 is line 009.
196+
*/
197+
ControlFlow::Node mostRecentSideEffect(ControlFlow::Node node) {
198+
iDomEntry(node) and
199+
result = node
200+
or
201+
iDomEffectPlus(node, result)
202+
}
191203
}
192204

193205
/** Used to represent the "global value number" of an expression. */
@@ -369,10 +381,12 @@ private predicate mkMethodAccess(DataFlow::Node access, GVN qualifier, Method m)
369381
)
370382
}
371383

384+
private predicate isReadInstruction(ControlFlow::Node node) { node instanceof IR::ReadInstruction }
385+
372386
private predicate analyzableFieldRead(Read fread, DataFlow::Node base, Field f) {
373387
exists(IR::ReadInstruction r | r = fread.asInstruction() |
374388
r.readsField(base.asInstruction(), f) and
375-
strictcount(mostRecentSideEffect(r)) = 1 and
389+
strictcount(MostRecentSideEffect<isReadInstruction/1>::mostRecentSideEffect(r)) = 1 and
376390
not r.isConst()
377391
)
378392
}
@@ -383,7 +397,8 @@ private predicate mkFieldRead(
383397
exists(DataFlow::Node base |
384398
analyzableFieldRead(fread, base, v) and
385399
qualifier = globalValueNumber(base) and
386-
dominator = mostRecentSideEffect(fread.asInstruction())
400+
dominator =
401+
MostRecentSideEffect<isReadInstruction/1>::mostRecentSideEffect(fread.asInstruction())
387402
)
388403
}
389404

@@ -418,20 +433,23 @@ private predicate incompleteSsa(ValueEntity v) {
418433
)
419434
}
420435

436+
private predicate instructionReads(ControlFlow::Node node) { node.(IR::Instruction).reads(_) }
437+
421438
/**
422439
* Holds if `access` is an access to a variable `target` for which SSA information is incomplete.
423440
*/
424441
private predicate analyzableOtherVariable(DataFlow::Node access, ValueEntity target) {
425442
access.asInstruction().reads(target) and
426443
incompleteSsa(target) and
427-
strictcount(mostRecentSideEffect(access.asInstruction())) = 1 and
444+
strictcount(MostRecentSideEffect<instructionReads/1>::mostRecentSideEffect(access.asInstruction())) =
445+
1 and
428446
not access.isConst() and
429447
not target instanceof Function
430448
}
431449

432450
private predicate mkOtherVariable(DataFlow::Node access, ValueEntity x, ControlFlow::Node dominator) {
433451
analyzableOtherVariable(access, x) and
434-
dominator = mostRecentSideEffect(access.asInstruction())
452+
dominator = MostRecentSideEffect<instructionReads/1>::mostRecentSideEffect(access.asInstruction())
435453
}
436454

437455
private predicate analyzableBinaryOp(
@@ -463,8 +481,12 @@ private predicate mkUnaryOp(DataFlow::UnaryOperationNode op, GVN child, string o
463481
opname = op.getOperator()
464482
}
465483

484+
private predicate isElementRead(ControlFlow::Node node) {
485+
node instanceof IR::ElementReadInstruction
486+
}
487+
466488
private predicate analyzableIndexExpr(DataFlow::ElementReadNode ae) {
467-
strictcount(mostRecentSideEffect(ae.asInstruction())) = 1 and
489+
strictcount(MostRecentSideEffect<isElementRead/1>::mostRecentSideEffect(ae.asInstruction())) = 1 and
468490
not ae.isConst()
469491
}
470492

@@ -474,18 +496,25 @@ private predicate mkIndex(
474496
analyzableIndexExpr(ae) and
475497
base = globalValueNumber(ae.getBase()) and
476498
offset = globalValueNumber(ae.getIndex()) and
477-
dominator = mostRecentSideEffect(ae.asInstruction())
499+
dominator = MostRecentSideEffect<isElementRead/1>::mostRecentSideEffect(ae.asInstruction())
500+
}
501+
502+
private predicate isPointerDereference(ControlFlow::Node node) {
503+
node instanceof IR::EvalImplicitDerefInstruction
478504
}
479505

480506
private predicate analyzablePointerDereferenceExpr(DataFlow::PointerDereferenceNode deref) {
481-
strictcount(mostRecentSideEffect(deref.asInstruction())) = 1 and
507+
strictcount(MostRecentSideEffect<isPointerDereference/1>::mostRecentSideEffect(deref
508+
.asInstruction())
509+
) = 1 and
482510
not deref.isConst()
483511
}
484512

485513
private predicate mkDeref(DataFlow::PointerDereferenceNode deref, GVN p, ControlFlow::Node dominator) {
486514
analyzablePointerDereferenceExpr(deref) and
487515
p = globalValueNumber(deref.getOperand()) and
488-
dominator = mostRecentSideEffect(deref.asInstruction())
516+
dominator =
517+
MostRecentSideEffect<isPointerDereference/1>::mostRecentSideEffect(deref.asInstruction())
489518
}
490519

491520
private predicate ssaInit(SsaExplicitDefinition ssa, DataFlow::Node rhs) {

0 commit comments

Comments
 (0)