Skip to content

Commit 81a0013

Browse files
authored
Merge pull request #21753 from hvitved/go/most-recent-side-effect-multi-entry
Go: Avoid combinatorial explosion in `mostRecentSideEffect` when there are multiple entry points
2 parents cafb73a + 2e94b09 commit 81a0013

1 file changed

Lines changed: 47 additions & 34 deletions

File tree

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

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,19 @@ private predicate sideEffectCfg(ControlFlow::Node src, ControlFlow::Node dst) {
127127

128128
/**
129129
* Holds if `dominator` is the immediate dominator of `node` in
130-
* the side-effect CFG.
130+
* the side-effect CFG belonging to `entry`.
131131
*/
132-
private predicate iDomEffect(ControlFlow::Node dominator, ControlFlow::Node node) =
133-
idominance(entryNode/1, sideEffectCfg/2)(_, dominator, node)
132+
private predicate iDomEffect(
133+
ControlFlow::Node entry, ControlFlow::Node dominator, ControlFlow::Node node
134+
) = idominance(entryNode/1, sideEffectCfg/2)(entry, dominator, node)
134135

135136
/**
136137
* Gets the most recent side effect. To be more precise, `result` is a
137138
* dominator of `node` and no side-effects can occur between `result` and
138139
* `node`.
139140
*
141+
* `entry` is the entry node for the function containing `node` and `result`.
142+
*
140143
* `sideEffectCFG` has an edge from the function entry to every node with a
141144
* side-effect. This means that every node with a side-effect has the
142145
* function entry as its immediate dominator. So if node `x` dominates node
@@ -180,16 +183,26 @@ private predicate iDomEffect(ControlFlow::Node dominator, ControlFlow::Node node
180183
*
181184
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
182185
* Therefore, the most recent side effect for line 015 is line 009.
186+
* (Note that line 009 is not a side-effect itself. Instead, it is the
187+
* point where the control flow paths from the side-effects at 004 and 007
188+
* merge. Because its immediate dominator is the entry node 000, it serves
189+
* as the safe root for expressions evaluated after those side-effects.)
183190
*/
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)
191+
private ControlFlow::Node mostRecentSideEffect(ControlFlow::Node entry, ControlFlow::Node node) {
192+
iDomEffect(entry, entry, result) and
193+
result = node
194+
or
195+
exists(ControlFlow::Node mid |
196+
result = mostRecentSideEffect(entry, mid) and
197+
iDomEffect(entry, mid, node)
190198
)
191199
}
192200

201+
cached
202+
private ControlFlow::Node mostRecentSideEffectUnique(ControlFlow::Node node) {
203+
result = unique( | | mostRecentSideEffect(_, node))
204+
}
205+
193206
/** Used to represent the "global value number" of an expression. */
194207
cached
195208
private newtype GvnBase =
@@ -369,10 +382,12 @@ private predicate mkMethodAccess(DataFlow::Node access, GVN qualifier, Method m)
369382
)
370383
}
371384

372-
private predicate analyzableFieldRead(Read fread, DataFlow::Node base, Field f) {
385+
private predicate analyzableFieldRead(
386+
Read fread, DataFlow::Node base, Field f, ControlFlow::Node dominator
387+
) {
373388
exists(IR::ReadInstruction r | r = fread.asInstruction() |
374389
r.readsField(base.asInstruction(), f) and
375-
strictcount(mostRecentSideEffect(r)) = 1 and
390+
dominator = mostRecentSideEffectUnique(r) and
376391
not r.isConst()
377392
)
378393
}
@@ -381,9 +396,8 @@ private predicate mkFieldRead(
381396
DataFlow::Node fread, GVN qualifier, Field v, ControlFlow::Node dominator
382397
) {
383398
exists(DataFlow::Node base |
384-
analyzableFieldRead(fread, base, v) and
385-
qualifier = globalValueNumber(base) and
386-
dominator = mostRecentSideEffect(fread.asInstruction())
399+
analyzableFieldRead(fread, base, v, dominator) and
400+
qualifier = globalValueNumber(base)
387401
)
388402
}
389403

@@ -421,18 +435,17 @@ private predicate incompleteSsa(ValueEntity v) {
421435
/**
422436
* Holds if `access` is an access to a variable `target` for which SSA information is incomplete.
423437
*/
424-
private predicate analyzableOtherVariable(DataFlow::Node access, ValueEntity target) {
438+
private predicate analyzableOtherVariable(
439+
DataFlow::Node access, ValueEntity target, ControlFlow::Node dominator
440+
) {
425441
access.asInstruction().reads(target) and
426442
incompleteSsa(target) and
427-
strictcount(mostRecentSideEffect(access.asInstruction())) = 1 and
443+
dominator = mostRecentSideEffectUnique(access.asInstruction()) and
428444
not access.isConst() and
429445
not target instanceof Function
430446
}
431447

432-
private predicate mkOtherVariable(DataFlow::Node access, ValueEntity x, ControlFlow::Node dominator) {
433-
analyzableOtherVariable(access, x) and
434-
dominator = mostRecentSideEffect(access.asInstruction())
435-
}
448+
private predicate mkOtherVariable = analyzableOtherVariable/3;
436449

437450
private predicate analyzableBinaryOp(
438451
DataFlow::BinaryOperationNode op, string opname, DataFlow::Node lhs, DataFlow::Node rhs
@@ -463,29 +476,29 @@ private predicate mkUnaryOp(DataFlow::UnaryOperationNode op, GVN child, string o
463476
opname = op.getOperator()
464477
}
465478

466-
private predicate analyzableIndexExpr(DataFlow::ElementReadNode ae) {
467-
strictcount(mostRecentSideEffect(ae.asInstruction())) = 1 and
479+
private predicate analyzableIndexExpr(DataFlow::ElementReadNode ae, ControlFlow::Node dominator) {
480+
dominator = mostRecentSideEffectUnique(ae.asInstruction()) and
468481
not ae.isConst()
469482
}
470483

471484
private predicate mkIndex(
472485
DataFlow::ElementReadNode ae, GVN base, GVN offset, ControlFlow::Node dominator
473486
) {
474-
analyzableIndexExpr(ae) and
487+
analyzableIndexExpr(ae, dominator) and
475488
base = globalValueNumber(ae.getBase()) and
476-
offset = globalValueNumber(ae.getIndex()) and
477-
dominator = mostRecentSideEffect(ae.asInstruction())
489+
offset = globalValueNumber(ae.getIndex())
478490
}
479491

480-
private predicate analyzablePointerDereferenceExpr(DataFlow::PointerDereferenceNode deref) {
481-
strictcount(mostRecentSideEffect(deref.asInstruction())) = 1 and
492+
private predicate analyzablePointerDereferenceExpr(
493+
DataFlow::PointerDereferenceNode deref, ControlFlow::Node dominator
494+
) {
495+
dominator = mostRecentSideEffectUnique(deref.asInstruction()) and
482496
not deref.isConst()
483497
}
484498

485499
private predicate mkDeref(DataFlow::PointerDereferenceNode deref, GVN p, ControlFlow::Node dominator) {
486-
analyzablePointerDereferenceExpr(deref) and
487-
p = globalValueNumber(deref.getOperand()) and
488-
dominator = mostRecentSideEffect(deref.asInstruction())
500+
analyzablePointerDereferenceExpr(deref, dominator) and
501+
p = globalValueNumber(deref.getOperand())
489502
}
490503

491504
private predicate ssaInit(SsaExplicitDefinition ssa, DataFlow::Node rhs) {
@@ -587,12 +600,12 @@ private predicate analyzableExpr(DataFlow::Node e) {
587600
analyzableConst(e) or
588601
any(DataFlow::SsaNode ssa).getAUse() = e or
589602
e instanceof DataFlow::SsaNode or
590-
analyzableOtherVariable(e, _) or
603+
analyzableOtherVariable(e, _, _) or
591604
analyzableMethodAccess(e, _, _) or
592-
analyzableFieldRead(e, _, _) or
605+
analyzableFieldRead(e, _, _, _) or
593606
analyzableCall(e, _) or
594607
analyzableBinaryOp(e, _, _, _) or
595608
analyzableUnaryOp(e) or
596-
analyzableIndexExpr(e) or
597-
analyzablePointerDereferenceExpr(e)
609+
analyzableIndexExpr(e, _) or
610+
analyzablePointerDereferenceExpr(e, _)
598611
}

0 commit comments

Comments
 (0)