Skip to content

Commit da9e33a

Browse files
authored
Merge pull request #4477 from dbartol/dbartol/PrintIRLocalFlow
C++: Add ability to dump local dataflow info in IR dumps
2 parents 5142bfa + f32a7be commit da9e33a

File tree

18 files changed

+558
-21
lines changed

18 files changed

+558
-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: 9 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,8 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase {
477484
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
478485
}
479486

487+
final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() }
488+
480489
/**
481490
* Gets the predecessor block from which this value comes.
482491
*/

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,37 @@ 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 =
66+
strictconcat(string key, string value |
67+
value = getAdditionalOperandProperty(operand, key)
68+
|
69+
key + ":" + value, ", "
70+
)
71+
}
72+
73+
/**
74+
* Gets a string listing the properties of the operand and their corresponding values. The list is
75+
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
76+
* string.
77+
*/
78+
private string getOperandPropertyString(Operand operand) {
79+
result = "{" + getOperandPropertyListString(operand) + "}"
80+
or
81+
not exists(getOperandPropertyListString(operand)) and result = ""
82+
}
83+
5384
private newtype TPrintableIRNode =
5485
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
5586
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
@@ -190,7 +221,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
190221
|
191222
resultString = instr.getResultString() and
192223
operationString = instr.getOperationString() and
193-
operandsString = instr.getOperandsString() and
224+
operandsString = getOperandsString() and
194225
columnWidths(block, resultWidth, operationWidth) and
195226
result =
196227
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
@@ -210,6 +241,22 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
210241
result = PrintableIRNode.super.getProperty(key) or
211242
result = getAdditionalInstructionProperty(instr, key)
212243
}
244+
245+
/**
246+
* Gets the string representation of the operand list. This is the same as
247+
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
248+
* provided by active `IRPropertyProvider` instances.
249+
*/
250+
private string getOperandsString() {
251+
result =
252+
concat(Operand operand |
253+
operand = instr.getAnOperand()
254+
|
255+
operand.getDumpString() + getOperandPropertyString(operand), ", "
256+
order by
257+
operand.getDumpSortOrder()
258+
)
259+
}
213260
}
214261

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

0 commit comments

Comments
 (0)