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.
This commit is contained in:
Dave Bartolomeo
2020-10-14 18:02:45 -04:00
parent b409cf6cea
commit dfb687fd47
18 changed files with 537 additions and 21 deletions

View File

@@ -0,0 +1,152 @@
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
/**
* Gets a short ID for an IR dataflow node.
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
* instruction and a dot (e.g. `m128.left`).
* - For `Variable`s, this is the qualified name of the variable.
*/
private string nodeId(DataFlow::Node node, int order1, int order2) {
exists(Instruction instruction | instruction = node.asInstruction() |
result = instruction.getResultId() and
order1 = instruction.getBlock().getDisplayIndex() and
order2 = instruction.getDisplayIndexInBlock()
)
or
exists(Operand operand, Instruction instruction |
operand = node.asOperand() and
instruction = operand.getUse()
|
result = instruction.getResultId() + "." + operand.getDumpId() and
order1 = instruction.getBlock().getDisplayIndex() and
order2 = instruction.getDisplayIndexInBlock()
)
or
result = "var(" + node.asVariable().getQualifiedName() + ")" and
order1 = 1000000 and
order2 = 0
}
/**
* Gets the local dataflow from other nodes in the same function to this node.
*/
private string getFromFlow(DataFlow::Node useNode, int order1, int order2) {
exists(DataFlow::Node defNode, string prefix |
(
simpleLocalFlowStep(defNode, useNode) and prefix = ""
or
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
prefix = "+"
) and
if defNode.asInstruction() = useNode.asOperand().getAnyDef()
then
// Shorthand for flow from the def of this operand.
result = prefix + "def" and
order1 = -1 and
order2 = 0
else
if defNode.asOperand().getUse() = useNode.asInstruction()
then
// Shorthand for flow from an operand of this instruction
result = prefix + defNode.asOperand().getDumpId() and
order1 = -1 and
order2 = defNode.asOperand().getDumpSortOrder()
else result = prefix + nodeId(defNode, order1, order2)
)
}
/**
* Gets the local dataflow from this node to other nodes in the same function.
*/
private string getToFlow(DataFlow::Node defNode, int order1, int order2) {
exists(DataFlow::Node useNode, string prefix |
(
simpleLocalFlowStep(defNode, useNode) and prefix = ""
or
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
prefix = "+"
) and
if useNode.asInstruction() = defNode.asOperand().getUse()
then
// Shorthand for flow to this operand's instruction.
result = prefix + "result" and
order1 = -1 and
order2 = 0
else result = prefix + nodeId(useNode, order1, order2)
)
}
/**
* Gets the properties of the dataflow node `node`.
*/
private string getNodeProperty(DataFlow::Node node, string key) {
// List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow
// out of this node is printed as `@->dest`.
key = "flow" and
result =
strictconcat(string flow, boolean to, int order1, int order2 |
flow = getFromFlow(node, order1, order2) + "->@" and to = false
or
flow = "@->" + getToFlow(node, order1, order2) and to = true
|
flow, ", " order by to, order1, order2, flow
)
or
// Is this node a dataflow sink?
key = "sink" and
any(DataFlow::Configuration cfg).isSink(node) and
result = "true"
or
// Is this node a dataflow source?
key = "source" and
any(DataFlow::Configuration cfg).isSource(node) and
result = "true"
or
// Is this node a dataflow barrier, and if so, what kind?
key = "barrier" and
result =
strictconcat(string kind |
any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full"
or
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
or
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
or
exists(DataFlow::BarrierGuard guard |
any(DataFlow::Configuration cfg).isBarrierGuard(guard) and
node = guard.getAGuardedNode() and
kind = "guard(" + guard.getResultId() + ")"
)
|
kind, ", "
)
}
/**
* Property provider for local IR dataflow.
*/
class LocalFlowPropertyProvider extends IRPropertyProvider {
override string getOperandProperty(Operand operand, string key) {
exists(DataFlow::Node node |
operand = node.asOperand() and
result = getNodeProperty(node, key)
)
}
override string getInstructionProperty(Instruction instruction, string key) {
exists(DataFlow::Node node |
instruction = node.asInstruction() and
result = getNodeProperty(node, key)
)
}
}

View File

@@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified block.
*/
string getBlockProperty(IRBlock block, string key) { none() }
/**
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
}

View File

@@ -151,6 +151,11 @@ class Operand extends TOperand {
*/
string getDumpLabel() { result = "" }
/**
* Gets a string that uniquely identifies this operand on its use instruction.
*/
string getDumpId() { result = "" }
/**
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
* result ID of the instruction consumed by the operand, plus a label identifying the operand
@@ -280,6 +285,8 @@ class NonPhiOperand extends Operand {
final override string getDumpLabel() { result = tag.getLabel() }
final override string getDumpId() { result = tag.getId() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
/**
@@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.
*/

View File

@@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) {
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
}
/**
* Gets the properties of an operand from any active property providers.
*/
private string getAdditionalOperandProperty(Operand operand, string key) {
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
}
/**
* Gets a string listing the properties of the operand and their corresponding values. If the
* operand has no properties, this predicate has no result.
*/
private string getOperandPropertyListString(Operand operand) {
result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ")
}
/**
* Gets a string listing the properties of the operand and their corresponding values. The list is
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
* string.
*/
private string getOperandPropertyString(Operand operand) {
result = "{" + getOperandPropertyListString(operand) + "}"
or
not exists(getOperandPropertyListString(operand)) and result = ""
}
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
@@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
|
resultString = instr.getResultString() and
operationString = instr.getOperationString() and
operandsString = instr.getOperandsString() and
operandsString = getOperandsString() and
columnWidths(block, resultWidth, operationWidth) and
result =
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
@@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
result = PrintableIRNode.super.getProperty(key) or
result = getAdditionalInstructionProperty(instr, key)
}
/**
* Gets the string representation of the operand list. This is the same as
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
* provided by active `IRPropertyProvider` instances.
*/
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
|
operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder()
)
}
}
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {

View File

@@ -40,7 +40,19 @@ abstract class OperandTag extends TOperandTag {
/**
* Gets a label that will appear before the operand when the IR is printed.
*/
string getLabel() { result = "" }
final string getLabel() {
if alwaysPrintLabel() then result = getId() + ":" else result = ""
}
/**
* Gets an identifier that uniquely identifies this operand within its instruction.
*/
abstract string getId();
/**
* Holds if the operand should always be prefixed with its label in the dump of its instruction.
*/
predicate alwaysPrintLabel() { none() }
}
/**
@@ -69,7 +81,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand {
final override int getSortOrder() { result = 0 }
final override string getLabel() { result = "&:" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = "&" }
}
AddressOperandTag addressOperand() { result = TAddressOperand() }
@@ -82,6 +96,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
final override string toString() { result = "BufferSize" }
final override int getSortOrder() { result = 1 }
final override string getId() { result = "size" }
}
BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
@@ -93,6 +109,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand {
final override string toString() { result = "SideEffect" }
final override int getSortOrder() { result = 2 }
final override string getId() { result = "side_effect" }
}
SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() }
@@ -105,6 +123,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand {
final override string toString() { result = "Load" }
final override int getSortOrder() { result = 3 }
final override string getId() { result = "load" }
}
LoadOperandTag loadOperand() { result = TLoadOperand() }
@@ -116,6 +136,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand {
final override string toString() { result = "StoreValue" }
final override int getSortOrder() { result = 4 }
final override string getId() { result = "store" }
}
StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() }
@@ -127,6 +149,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand {
final override string toString() { result = "Unary" }
final override int getSortOrder() { result = 5 }
final override string getId() { result = "unary" }
}
UnaryOperandTag unaryOperand() { result = TUnaryOperand() }
@@ -138,6 +162,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand {
final override string toString() { result = "Left" }
final override int getSortOrder() { result = 6 }
final override string getId() { result = "left" }
}
LeftOperandTag leftOperand() { result = TLeftOperand() }
@@ -149,6 +175,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand {
final override string toString() { result = "Right" }
final override int getSortOrder() { result = 7 }
final override string getId() { result = "right" }
}
RightOperandTag rightOperand() { result = TRightOperand() }
@@ -160,6 +188,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand {
final override string toString() { result = "Condition" }
final override int getSortOrder() { result = 8 }
final override string getId() { result = "cond" }
}
ConditionOperandTag conditionOperand() { result = TConditionOperand() }
@@ -172,7 +202,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand {
final override int getSortOrder() { result = 10 }
final override string getLabel() { result = "func:" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = "func" }
}
CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() }
@@ -195,7 +227,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand {
final override int getSortOrder() { result = 11 }
final override string getLabel() { result = "this:" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = "this" }
}
ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() }
@@ -212,9 +246,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume
final override int getSortOrder() { result = 12 + argIndex }
final override string getLabel() { result = argIndex.toString() + ":" }
final override predicate alwaysPrintLabel() { any() }
final int getArgIndex() { result = argIndex }
final override string getId() { result = argIndex.toString() }
}
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
@@ -228,7 +264,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand {
final override int getSortOrder() { result = 13 }
final override string getLabel() { result = "total:" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = "total" }
}
ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() }
@@ -238,7 +276,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand {
final override int getSortOrder() { result = 14 }
final override string getLabel() { result = "partial:" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = "partial" }
}
ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() }
@@ -252,7 +292,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand {
final override int getSortOrder() { result = 15 + index }
final override string getLabel() { result = index.toString() + ":" }
final override predicate alwaysPrintLabel() { any() }
final override string getId() { result = index.toString() }
}
AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) }

View File

@@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified block.
*/
string getBlockProperty(IRBlock block, string key) { none() }
/**
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
}

View File

@@ -151,6 +151,11 @@ class Operand extends TOperand {
*/
string getDumpLabel() { result = "" }
/**
* Gets a string that uniquely identifies this operand on its use instruction.
*/
string getDumpId() { result = "" }
/**
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
* result ID of the instruction consumed by the operand, plus a label identifying the operand
@@ -280,6 +285,8 @@ class NonPhiOperand extends Operand {
final override string getDumpLabel() { result = tag.getLabel() }
final override string getDumpId() { result = tag.getId() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
/**
@@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.
*/

View File

@@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) {
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
}
/**
* Gets the properties of an operand from any active property providers.
*/
private string getAdditionalOperandProperty(Operand operand, string key) {
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
}
/**
* Gets a string listing the properties of the operand and their corresponding values. If the
* operand has no properties, this predicate has no result.
*/
private string getOperandPropertyListString(Operand operand) {
result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ")
}
/**
* Gets a string listing the properties of the operand and their corresponding values. The list is
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
* string.
*/
private string getOperandPropertyString(Operand operand) {
result = "{" + getOperandPropertyListString(operand) + "}"
or
not exists(getOperandPropertyListString(operand)) and result = ""
}
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
@@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
|
resultString = instr.getResultString() and
operationString = instr.getOperationString() and
operandsString = instr.getOperandsString() and
operandsString = getOperandsString() and
columnWidths(block, resultWidth, operationWidth) and
result =
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
@@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
result = PrintableIRNode.super.getProperty(key) or
result = getAdditionalInstructionProperty(instr, key)
}
/**
* Gets the string representation of the operand list. This is the same as
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
* provided by active `IRPropertyProvider` instances.
*/
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
|
operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder()
)
}
}
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {

View File

@@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider {
* Gets the value of the property named `key` for the specified block.
*/
string getBlockProperty(IRBlock block, string key) { none() }
/**
* Gets the value of the property named `key` for the specified operand.
*/
string getOperandProperty(Operand operand, string key) { none() }
}

View File

@@ -151,6 +151,11 @@ class Operand extends TOperand {
*/
string getDumpLabel() { result = "" }
/**
* Gets a string that uniquely identifies this operand on its use instruction.
*/
string getDumpId() { result = "" }
/**
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
* result ID of the instruction consumed by the operand, plus a label identifying the operand
@@ -280,6 +285,8 @@ class NonPhiOperand extends Operand {
final override string getDumpLabel() { result = tag.getLabel() }
final override string getDumpId() { result = tag.getId() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
/**
@@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase {
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
}
final override string getDumpId() {
result = getPredecessorBlock().getDisplayIndex().toString()
}
/**
* Gets the predecessor block from which this value comes.
*/

View File

@@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) {
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
}
/**
* Gets the properties of an operand from any active property providers.
*/
private string getAdditionalOperandProperty(Operand operand, string key) {
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
}
/**
* Gets a string listing the properties of the operand and their corresponding values. If the
* operand has no properties, this predicate has no result.
*/
private string getOperandPropertyListString(Operand operand) {
result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ")
}
/**
* Gets a string listing the properties of the operand and their corresponding values. The list is
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
* string.
*/
private string getOperandPropertyString(Operand operand) {
result = "{" + getOperandPropertyListString(operand) + "}"
or
not exists(getOperandPropertyListString(operand)) and result = ""
}
private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
@@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
|
resultString = instr.getResultString() and
operationString = instr.getOperationString() and
operandsString = instr.getOperandsString() and
operandsString = getOperandsString() and
columnWidths(block, resultWidth, operationWidth) and
result =
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
@@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio
result = PrintableIRNode.super.getProperty(key) or
result = getAdditionalInstructionProperty(instr, key)
}
/**
* Gets the string representation of the operand list. This is the same as
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
* provided by active `IRPropertyProvider` instances.
*/
private string getOperandsString() {
result =
concat(Operand operand |
operand = instr.getAnOperand()
|
operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder()
)
}
}
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {