Skip to content

Commit dfb687f

Browse files
author
Dave Bartolomeo
committed
C++: Add ability to dump local dataflow info in IR dumps
This change adds a new module, `PrintIRLocalFlow.qll`, which can be imported into any query that uses both `PrintIR.qll` and the IR dataflow library. The IR dump printed by `PrintIR.qll` will be annotated with information about how each operand and instruction participates in dataflow. For each operand and instruction, the following propeties are displayed: - `flow`: Which local operands/instructions have flow to this node, and which local operands/instruction this node has flow to. - `source`: `true` if this node is a source - `sink`: `true` if this node is a sink - `barrier`: Lists which kinds of barrier this node is. Can be zero or more of `full`, `in`, `out`, and `guard`. If the node is a guard barrier, the IR of the guarding instruction is also printed. We already had a way to print additional properties for instructions and blocks, but not for operands. I added support for operand properties to `IRPropertyProvider`. These are now printed in a curly-brace-enclosed list immediately after the corresponding operand. When printing flow, instructions are identified by their result ID (e.g., `m128`). Operands are identified by both the result ID of their instruction and their kind (e.g., `r145.left`). For flow from an operand to its use instruction, it just prints `result` at the operand, and prints only the operand kind on the instruction. Example output: ``` # 344| m344_34(vector<int, allocator<int>>) = Chi : total:m344_20{flow:def->@, @->result}, partial:m344_33{flow:def->@, @->result} # 344| flow = total->@, partial->@, +m344_33->@, @->+r347_3, @->v347_7.side_effect, @->m347_9.total, @->m344_20.1 ``` The `+` annotations indicate when the flow came from `isAdditionalFlowStep()`, rather than built-in local flow.
1 parent b409cf6 commit dfb687f

File tree

18 files changed

+537
-21
lines changed

18 files changed

+537
-21
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
private import cpp
2+
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
3+
// that the cached IR gets the same checksum here as it does in queries that use
4+
// `ValueNumbering` without `DataFlow`.
5+
private import semmle.code.cpp.ir.ValueNumbering
6+
private import semmle.code.cpp.ir.IR
7+
private import semmle.code.cpp.ir.dataflow.DataFlow
8+
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
9+
10+
/**
11+
* Gets a short ID for an IR dataflow node.
12+
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
13+
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
14+
* instruction and a dot (e.g. `m128.left`).
15+
* - For `Variable`s, this is the qualified name of the variable.
16+
*/
17+
private string nodeId(DataFlow::Node node, int order1, int order2) {
18+
exists(Instruction instruction | instruction = node.asInstruction() |
19+
result = instruction.getResultId() and
20+
order1 = instruction.getBlock().getDisplayIndex() and
21+
order2 = instruction.getDisplayIndexInBlock()
22+
)
23+
or
24+
exists(Operand operand, Instruction instruction |
25+
operand = node.asOperand() and
26+
instruction = operand.getUse()
27+
|
28+
result = instruction.getResultId() + "." + operand.getDumpId() and
29+
order1 = instruction.getBlock().getDisplayIndex() and
30+
order2 = instruction.getDisplayIndexInBlock()
31+
)
32+
or
33+
result = "var(" + node.asVariable().getQualifiedName() + ")" and
34+
order1 = 1000000 and
35+
order2 = 0
36+
}
37+
38+
/**
39+
* Gets the local dataflow from other nodes in the same function to this node.
40+
*/
41+
private string getFromFlow(DataFlow::Node useNode, int order1, int order2) {
42+
exists(DataFlow::Node defNode, string prefix |
43+
(
44+
simpleLocalFlowStep(defNode, useNode) and prefix = ""
45+
or
46+
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
47+
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
48+
prefix = "+"
49+
) and
50+
if defNode.asInstruction() = useNode.asOperand().getAnyDef()
51+
then
52+
// Shorthand for flow from the def of this operand.
53+
result = prefix + "def" and
54+
order1 = -1 and
55+
order2 = 0
56+
else
57+
if defNode.asOperand().getUse() = useNode.asInstruction()
58+
then
59+
// Shorthand for flow from an operand of this instruction
60+
result = prefix + defNode.asOperand().getDumpId() and
61+
order1 = -1 and
62+
order2 = defNode.asOperand().getDumpSortOrder()
63+
else result = prefix + nodeId(defNode, order1, order2)
64+
)
65+
}
66+
67+
/**
68+
* Gets the local dataflow from this node to other nodes in the same function.
69+
*/
70+
private string getToFlow(DataFlow::Node defNode, int order1, int order2) {
71+
exists(DataFlow::Node useNode, string prefix |
72+
(
73+
simpleLocalFlowStep(defNode, useNode) and prefix = ""
74+
or
75+
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
76+
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
77+
prefix = "+"
78+
) and
79+
if useNode.asInstruction() = defNode.asOperand().getUse()
80+
then
81+
// Shorthand for flow to this operand's instruction.
82+
result = prefix + "result" and
83+
order1 = -1 and
84+
order2 = 0
85+
else result = prefix + nodeId(useNode, order1, order2)
86+
)
87+
}
88+
89+
/**
90+
* Gets the properties of the dataflow node `node`.
91+
*/
92+
private string getNodeProperty(DataFlow::Node node, string key) {
93+
// List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow
94+
// out of this node is printed as `@->dest`.
95+
key = "flow" and
96+
result =
97+
strictconcat(string flow, boolean to, int order1, int order2 |
98+
flow = getFromFlow(node, order1, order2) + "->@" and to = false
99+
or
100+
flow = "@->" + getToFlow(node, order1, order2) and to = true
101+
|
102+
flow, ", " order by to, order1, order2, flow
103+
)
104+
or
105+
// Is this node a dataflow sink?
106+
key = "sink" and
107+
any(DataFlow::Configuration cfg).isSink(node) and
108+
result = "true"
109+
or
110+
// Is this node a dataflow source?
111+
key = "source" and
112+
any(DataFlow::Configuration cfg).isSource(node) and
113+
result = "true"
114+
or
115+
// Is this node a dataflow barrier, and if so, what kind?
116+
key = "barrier" and
117+
result =
118+
strictconcat(string kind |
119+
any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full"
120+
or
121+
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
122+
or
123+
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
124+
or
125+
exists(DataFlow::BarrierGuard guard |
126+
any(DataFlow::Configuration cfg).isBarrierGuard(guard) and
127+
node = guard.getAGuardedNode() and
128+
kind = "guard(" + guard.getResultId() + ")"
129+
)
130+
|
131+
kind, ", "
132+
)
133+
}
134+
135+
/**
136+
* Property provider for local IR dataflow.
137+
*/
138+
class LocalFlowPropertyProvider extends IRPropertyProvider {
139+
override string getOperandProperty(Operand operand, string key) {
140+
exists(DataFlow::Node node |
141+
operand = node.asOperand() and
142+
result = getNodeProperty(node, key)
143+
)
144+
}
145+
146+
override string getInstructionProperty(Instruction instruction, string key) {
147+
exists(DataFlow::Node node |
148+
instruction = node.asInstruction() and
149+
result = getNodeProperty(node, key)
150+
)
151+
}
152+
}

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider {
7272
* Gets the value of the property named `key` for the specified block.
7373
*/
7474
string getBlockProperty(IRBlock block, string key) { none() }
75+
76+
/**
77+
* Gets the value of the property named `key` for the specified operand.
78+
*/
79+
string getOperandProperty(Operand operand, string key) { none() }
7580
}

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ class Operand extends TOperand {
151151
*/
152152
string getDumpLabel() { result = "" }
153153

154+
/**
155+
* Gets a string that uniquely identifies this operand on its use instruction.
156+
*/
157+
string getDumpId() { result = "" }
158+
154159
/**
155160
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
156161
* result ID of the instruction consumed by the operand, plus a label identifying the operand
@@ -280,6 +285,8 @@ class NonPhiOperand extends Operand {
280285

281286
final override string getDumpLabel() { result = tag.getLabel() }
282287

288+
final override string getDumpId() { result = tag.getId() }
289+
283290
final override int getDumpSortOrder() { result = tag.getSortOrder() }
284291

285292
/**
@@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase {
477484
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
478485
}
479486

487+
final override string getDumpId() {
488+
result = getPredecessorBlock().getDisplayIndex().toString()
489+
}
490+
480491
/**
481492
* Gets the predecessor block from which this value comes.
482493
*/

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) {
5050
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
5151
}
5252

53+
/**
54+
* Gets the properties of an operand from any active property providers.
55+
*/
56+
private string getAdditionalOperandProperty(Operand operand, string key) {
57+
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
58+
}
59+
60+
/**
61+
* Gets a string listing the properties of the operand and their corresponding values. If the
62+
* operand has no properties, this predicate has no result.
63+
*/
64+
private string getOperandPropertyListString(Operand operand) {
65+
result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ")
66+
}
67+
68+
/**
69+
* Gets a string listing the properties of the operand and their corresponding values. The list is
70+
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
71+
* string.
72+
*/
73+
private string getOperandPropertyString(Operand operand) {
74+
result = "{" + getOperandPropertyListString(operand) + "}"
75+
or
76+
not exists(getOperandPropertyListString(operand)) and result = ""
77+
}
78+
5379
private newtype TPrintableIRNode =
5480
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
5581
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
@@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
190216
|
191217
resultString = instr.getResultString() and
192218
operationString = instr.getOperationString() and
193-
operandsString = instr.getOperandsString() and
219+
operandsString = getOperandsString() and
194220
columnWidths(block, resultWidth, operationWidth) and
195221
result =
196222
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
@@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
210236
result = PrintableIRNode.super.getProperty(key) or
211237
result = getAdditionalInstructionProperty(instr, key)
212238
}
239+
240+
/**
241+
* Gets the string representation of the operand list. This is the same as
242+
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
243+
* provided by active `IRPropertyProvider` instances.
244+
*/
245+
private string getOperandsString() {
246+
result =
247+
concat(Operand operand |
248+
operand = instr.getAnOperand()
249+
|
250+
operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder()
251+
)
252+
}
213253
}
214254

215255
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {

0 commit comments

Comments
 (0)