Skip to content

Commit ba600b0

Browse files
authored
Merge pull request github#17829 from hvitved/rust/cfg-stage
Rust: Collapse cached CFG logic into one stage
2 parents 9dc5e2f + 09c3ac6 commit ba600b0

File tree

4 files changed

+76
-4
lines changed

4 files changed

+76
-4
lines changed

rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ private import Scope as Scope
55
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
66

77
private module CfgInput implements InputSig<Location> {
8+
private import codeql.rust.internal.CachedStages
89
private import rust as Rust
910
private import Completion as C
1011

@@ -21,7 +22,10 @@ private module CfgInput implements InputSig<Location> {
2122
/** An AST node with an associated control-flow graph. */
2223
class CfgScope = Scope::CfgScope;
2324

24-
CfgScope getCfgScope(AstNode n) { result = n.getEnclosingCallable() }
25+
CfgScope getCfgScope(AstNode n) {
26+
result = n.getEnclosingCallable() and
27+
Stages::CfgStage::ref()
28+
}
2529

2630
class SuccessorType = Cfg::SuccessorType;
2731

rust/ql/lib/codeql/rust/controlflow/internal/Splitting.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ private import Scope
44

55
cached
66
private module Cached {
7+
private import codeql.rust.internal.CachedStages
8+
79
cached
8-
newtype TSplitKind = TConditionalCompletionSplitKind()
10+
newtype TSplitKind = TConditionalCompletionSplitKind() { Stages::CfgStage::ref() }
911

1012
cached
1113
newtype TSplit = TConditionalCompletionSplit(ConditionalCompletion c)

rust/ql/lib/codeql/rust/controlflow/internal/SuccessorType.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
private import rust
22
private import codeql.util.Boolean
33
private import Completion
4+
private import codeql.rust.internal.CachedStages
45

56
cached
67
newtype TSuccessorType =
7-
TSuccessorSuccessor() or
8+
TNormalSuccessor() { Stages::CfgStage::ref() } or
89
TBooleanSuccessor(Boolean b) or
910
TMatchSuccessor(Boolean b) or
1011
TBreakSuccessor() or
@@ -18,7 +19,7 @@ abstract class SuccessorTypeImpl extends TSuccessorType {
1819
}
1920

2021
/** A normal control flow successor. */
21-
class NormalSuccessorImpl extends SuccessorTypeImpl, TSuccessorSuccessor {
22+
class NormalSuccessorImpl extends SuccessorTypeImpl, TNormalSuccessor {
2223
override string toString() { result = "successor" }
2324
}
2425

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* The purpose of this file is to control which cached predicates belong to the same stage.
3+
*
4+
* Combining stages can improve performance as we are more likely to reuse shared, non-cached predicates.
5+
*
6+
* To make a predicate `p` belong to a stage `A`:
7+
* - make `p` depend on `A::ref()`, and
8+
* - make `A::backref()` depend on `p`.
9+
*
10+
* Since `A` is a cached module, `ref` and `backref` must be in the same stage, and the dependency
11+
* chain above thus forces `p` to be in that stage as well.
12+
*
13+
* With these two predicates in a `cached module` we ensure that all the cached predicates will be in a single stage at runtime.
14+
*
15+
* Grouping stages can cause unnecessary computation, as a concrete query might not depend on
16+
* all the cached predicates in a stage.
17+
* Care should therefore be taken not to combine two stages, if it is likely that a query only depend
18+
* on some but not all the cached predicates in the combined stage.
19+
*/
20+
21+
import rust
22+
23+
/**
24+
* Contains a `cached module` for each stage.
25+
* Each `cached module` ensures that predicates that are supposed to be in the same stage, are in the same stage.
26+
*
27+
* Each `cached module` contain two predicates:
28+
* The first, `ref`, always holds, and is referenced from `cached` predicates.
29+
* The second, `backref`, contains references to the same `cached` predicates.
30+
* The `backref` predicate starts with `1 = 1 or` to ensure that the predicate will be optimized down to a constant by the optimizer.
31+
*/
32+
module Stages {
33+
/**
34+
* The control flow graph (CFG) stage.
35+
*/
36+
cached
37+
module CfgStage {
38+
private import codeql.rust.controlflow.internal.Splitting
39+
private import codeql.rust.controlflow.internal.SuccessorType
40+
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl
41+
42+
/**
43+
* Always holds.
44+
* Ensures that a predicate is evaluated as part of the BasicBlocks stage.
45+
*/
46+
cached
47+
predicate ref() { 1 = 1 }
48+
49+
/**
50+
* DO NOT USE!
51+
*
52+
* Contains references to each predicate that use the above `ref` predicate.
53+
*/
54+
cached
55+
predicate backref() {
56+
1 = 1
57+
or
58+
exists(TConditionalCompletionSplitKind())
59+
or
60+
exists(TNormalSuccessor())
61+
or
62+
exists(AstCfgNode n)
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)