Merge pull request #11541 from MathiasVP/add-node0

C++: Introduce a pre-SSA `DataFlow::Node` class
This commit is contained in:
Mathias Vorreiter Pedersen
2022-12-06 13:28:39 +00:00
committed by GitHub
5 changed files with 187 additions and 68 deletions

View File

@@ -6,6 +6,111 @@ private import DataFlowImplConsistency
private import semmle.code.cpp.ir.internal.IRCppLanguage
private import SsaInternals as Ssa
cached
private module Cached {
cached
newtype TIRDataFlowNode0 =
TInstructionNode0(Instruction i) {
not Ssa::ignoreInstruction(i) and
// We exclude `void`-typed instructions because they cannot contain data.
// However, if the instruction is a glvalue, and their type is `void`, then the result
// type of the instruction is really `void*`, and thus we still want to have a dataflow
// node for it.
(not i.getResultType() instanceof VoidType or i.isGLValue())
} or
TOperandNode0(Operand op) { not Ssa::ignoreOperand(op) }
}
private import Cached
class Node0Impl extends TIRDataFlowNode0 {
/**
* INTERNAL: Do not use.
*/
Declaration getEnclosingCallable() { none() } // overridden in subclasses
/** Gets the function to which this node belongs, if any. */
Declaration getFunction() { none() } // overridden in subclasses
/**
* Gets the type of this node.
*
* If `asInstruction().isGLValue()` holds, then the type of this node
* should be thought of as "pointer to `getType()`".
*/
DataFlowType getType() { none() } // overridden in subclasses
/** Gets the instruction corresponding to this node, if any. */
Instruction asInstruction() { result = this.(InstructionNode0).getInstruction() }
/** Gets the operands corresponding to this node, if any. */
Operand asOperand() { result = this.(OperandNode0).getOperand() }
/** INTERNAL: Do not use. */
Location getLocationImpl() {
none() // overridden by subclasses
}
/** INTERNAL: Do not use. */
string toStringImpl() {
none() // overridden by subclasses
}
/** Gets the location of this node. */
final Location getLocation() { result = this.getLocationImpl() }
/** Gets a textual representation of this node. */
final string toString() { result = this.toStringImpl() }
}
/**
* An instruction, viewed as a node in a data flow graph.
*/
class InstructionNode0 extends Node0Impl, TInstructionNode0 {
Instruction instr;
InstructionNode0() { this = TInstructionNode0(instr) }
/** Gets the instruction corresponding to this node. */
Instruction getInstruction() { result = instr }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = instr.getEnclosingFunction() }
override DataFlowType getType() { result = instr.getResultType() }
final override Location getLocationImpl() { result = instr.getLocation() }
override string toStringImpl() {
// This predicate is overridden in subclasses. This default implementation
// does not use `Instruction.toString` because that's expensive to compute.
result = this.getInstruction().getOpcode().toString()
}
}
/**
* An operand, viewed as a node in a data flow graph.
*/
class OperandNode0 extends Node0Impl, TOperandNode0 {
Operand op;
OperandNode0() { this = TOperandNode0(op) }
/** Gets the operand corresponding to this node. */
Operand getOperand() { result = op }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
override DataFlowType getType() { result = op.getType() }
final override Location getLocationImpl() { result = op.getLocation() }
override string toStringImpl() { result = this.getOperand().toString() }
}
/**
* INTERNAL: Do not use.
*
@@ -251,7 +356,7 @@ class ReturnNode extends Node instanceof IndirectReturnNode {
*/
private predicate hasNonInitializeParameterDef(IRVariable v) {
exists(Ssa::Def def |
not def.getDefiningInstruction() instanceof InitializeParameterInstruction and
not def.getValue().asInstruction() instanceof InitializeParameterInstruction and
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable()
)
}

View File

@@ -33,15 +33,7 @@ private module Cached {
*/
cached
newtype TIRDataFlowNode =
TInstructionNode(Instruction i) {
not Ssa::ignoreInstruction(i) and
// We exclude `void`-typed instructions because they cannot contain data.
// However, if the instruction is a glvalue, and their type is `void`, then the result
// type of the instruction is really `void*`, and thus we still want to have a dataflow
// node for it.
(not i.getResultType() instanceof VoidType or i.isGLValue())
} or
TOperandNode(Operand op) { not Ssa::ignoreOperand(op) } or
TNode0(Node0Impl node) or
TVariableNode(Variable var) or
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
indirectionIndex =
@@ -336,51 +328,52 @@ private string toExprString(Node n) {
}
/**
* An instruction, viewed as a node in a data flow graph.
* A class that lifts pre-SSA dataflow nodes to regular dataflow nodes.
*/
class InstructionNode extends Node, TInstructionNode {
Instruction instr;
private class Node0 extends Node, TNode0 {
Node0Impl node;
InstructionNode() { this = TInstructionNode(instr) }
Node0() { this = TNode0(node) }
/** Gets the instruction corresponding to this node. */
Instruction getInstruction() { result = instr }
override Declaration getEnclosingCallable() { result = node.getEnclosingCallable() }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = node.getFunction() }
override Declaration getFunction() { result = instr.getEnclosingFunction() }
override DataFlowType getType() { result = node.getType() }
override DataFlowType getType() { result = instr.getResultType() }
final override Location getLocationImpl() { result = instr.getLocation() }
final override Location getLocationImpl() { result = node.getLocationImpl() }
override string toStringImpl() {
// This predicate is overridden in subclasses. This default implementation
// does not use `Instruction.toString` because that's expensive to compute.
result = this.getInstruction().getOpcode().toString()
result = node.toStringImpl()
}
}
/**
* An instruction, viewed as a node in a data flow graph.
*/
class InstructionNode extends Node0 {
override InstructionNode0 node;
Instruction instr;
InstructionNode() { instr = node.getInstruction() }
/** Gets the instruction corresponding to this node. */
Instruction getInstruction() { result = instr }
}
/**
* An operand, viewed as a node in a data flow graph.
*/
class OperandNode extends Node, TOperandNode {
class OperandNode extends Node, Node0 {
override OperandNode0 node;
Operand op;
OperandNode() { this = TOperandNode(op) }
OperandNode() { op = node.getOperand() }
/** Gets the operand corresponding to this node. */
Operand getOperand() { result = op }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
override DataFlowType getType() { result = op.getType() }
final override Location getLocationImpl() { result = op.getLocation() }
override string toStringImpl() { result = this.getOperand().toString() }
Operand getOperand() { result = node.getOperand() }
}
/**
@@ -681,7 +674,7 @@ class UninitializedNode extends Node {
UninitializedNode() {
exists(Ssa::Def def |
def.getDefiningInstruction() instanceof UninitializedInstruction and
def.getValue().asInstruction() instanceof UninitializedInstruction and
Ssa::nodeToDefOrUse(this, def) and
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
)

View File

@@ -222,16 +222,16 @@ class DefImpl extends DefOrUseImpl, TDefImpl {
override int getIndirectionIndex() { result = ind }
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
Node0Impl getValue() { isDef(_, result, address, _, _, _) }
override string toString() { result = "DefImpl" }
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
override IRBlock getBlock() { result = this.getAddressOperand().getUse().getBlock() }
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
this.getDefiningInstruction() = block.getInstruction(index)
this.getAddressOperand().getUse() = block.getInstruction(index)
}
predicate isCertain() { isDef(true, _, address, _, _, ind) }
@@ -308,7 +308,10 @@ predicate outNodeHasAddressAndIndex(
}
private predicate defToNode(Node nodeFrom, Def def) {
nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex())
// TODO: This is not yet needed (and in fact, the compiler rejects it because `exists(def.getValue().asOperand())` never holds).
// nodeHasOperand(nodeFrom, def.getValue().asOperand(), def.getIndirectionIndex())
// or
nodeHasInstruction(nodeFrom, def.getValue().asInstruction(), def.getIndirectionIndex())
}
/**
@@ -562,7 +565,7 @@ class Def extends DefOrUse {
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex()
}
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
Node0Impl getValue() { result = defOrUse.getValue() }
}
private module SsaImpl = SsaImplCommon::Make<SsaInput>;

View File

@@ -4,6 +4,7 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil
private import DataFlowPrivate
/**
* Holds if `operand` is an operand that is not used by the dataflow library.
@@ -126,15 +127,29 @@ class AllocationInstruction extends CallInstruction {
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
}
/**
* Holds if `i` is a base instruction that starts a sequence of uses
* of some variable that SSA can handle.
*
* This is either when `i` is a `VariableAddressInstruction` or when
* `i` is a fresh allocation produced by an `AllocationInstruction`.
*/
private predicate isSourceVariableBase(Instruction i) {
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
predicate isWrite(Node0Impl value, Operand address, boolean certain) {
certain = true and
(
exists(StoreInstruction store |
value.asInstruction() = store and
address = store.getDestinationAddressOperand()
)
or
exists(InitializeParameterInstruction init |
value.asInstruction() = init and
address = init.getAnOperand()
)
or
exists(InitializeDynamicAllocationInstruction init |
value.asInstruction() = init and
address = init.getAllocationAddressOperand()
)
or
exists(UninitializedInstruction uninitialized |
value.asInstruction() = uninitialized and
address = uninitialized.getAnOperand()
)
)
}
/**
@@ -263,6 +278,17 @@ private module Cached {
)
}
/**
* Holds if `i` is a base instruction that starts a sequence of uses
* of some variable that SSA can handle.
*
* This is either when `i` is a `VariableAddressInstruction` or when
* `i` is a fresh allocation produced by an `AllocationInstruction`.
*/
private predicate isSourceVariableBase(Instruction i) {
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
}
/**
* Holds if `address` is an address of an SSA variable rooted at `base`,
* and `instr` is a definition of the SSA variable with `ind` number of indirections.
@@ -273,25 +299,17 @@ private module Cached {
*/
cached
predicate isDef(
boolean certain, Instruction instr, Operand address, Instruction base, int ind,
boolean certain, Node0Impl value, Operand address, Instruction base, int ind,
int indirectionIndex
) {
certain = true and
exists(int ind0, CppType type, int lower, int upper |
address =
[
instr.(StoreInstruction).getDestinationAddressOperand(),
instr.(InitializeParameterInstruction).getAnOperand(),
instr.(InitializeDynamicAllocationInstruction).getAllocationAddressOperand(),
instr.(UninitializedInstruction).getAnOperand()
]
|
isWrite(value, address, certain) and
isDefImpl(address, base, ind0) and
type = getLanguageType(address) and
upper = countIndirectionsForCppType(type) and
ind = ind0 + [lower .. upper] and
indirectionIndex = ind - (ind0 + lower) and
if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1
(if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1)
)
}

View File

@@ -168,16 +168,16 @@ class DefImpl extends DefOrUseImpl, TDefImpl {
Operand getAddressOperand() { result = address }
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
Node0Impl getValue() { isDef(_, result, address, _, _, _) }
override string toString() { result = address.toString() }
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
override IRBlock getBlock() { result = this.getAddressOperand().getDef().getBlock() }
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
override Cpp::Location getLocation() { result = this.getAddressOperand().getLocation() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
this.getDefiningInstruction() = block.getInstruction(index)
this.getAddressOperand().getUse() = block.getInstruction(index)
}
predicate isCertain() { isDef(true, _, address, _, _, _) }
@@ -302,9 +302,9 @@ class Def extends DefOrUse {
Instruction getAddress() { result = this.getAddressOperand().getDef() }
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
Node0Impl getValue() { result = defOrUse.getValue() }
override string toString() { result = this.asDefOrUse().toString() + " (def)" }
override string toString() { result = this.asDefOrUse().toString() }
}
private module SsaImpl = SsaImplCommon::Make<SsaInput>;