mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
C++: Initial attempt at IR-based value numbering
This commit is contained in:
@@ -54,5 +54,10 @@
|
||||
"C++ SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/gvn/ValueNumber.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/gvn/ValueNumber.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/gvn/ValueNumber.qll"
|
||||
]
|
||||
}
|
||||
|
||||
7
cpp/ql/src/semmle/code/cpp/ir/IR.md
Normal file
7
cpp/ql/src/semmle/code/cpp/ir/IR.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# The C/C++ Intermediate Representation
|
||||
|
||||
## Introduction
|
||||
|
||||
The Intermediate Representation (IR) library provides a representation of the semantics of the program, independent of the syntax used to express those semantics. The IR is similar in design to representations used in optimizing compilers, such as LLVM IR.
|
||||
|
||||
## Memory Access
|
||||
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.aliased_ssa.PrintIR
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -195,6 +195,10 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getResultExpression()
|
||||
}
|
||||
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
private import ValueNumberInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {}
|
||||
class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {}
|
||||
class ConstantValueNumber extends ValueNumber, TConstantValueNumber {}
|
||||
class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {}
|
||||
class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {}
|
||||
class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
(not instr instanceof FieldAddressInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import IRBlockConstruction as BlockConstruction
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
|
||||
class InstructionTagType extends TInstructionTag {
|
||||
@@ -73,6 +74,13 @@ cached private module Cached {
|
||||
none()
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
|
||||
instruction.getTag(), tag)
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
private import ValueNumberInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {}
|
||||
class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {}
|
||||
class ConstantValueNumber extends ValueNumber, TConstantValueNumber {}
|
||||
class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {}
|
||||
class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {}
|
||||
class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
(not instr instanceof FieldAddressInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +301,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +561,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -195,6 +195,10 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getResultExpression()
|
||||
}
|
||||
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
private import ValueNumberInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryValueNumber extends ValueNumber, TBinaryValueNumber {}
|
||||
class UnaryValueNumber extends ValueNumber, TUnaryValueNumber {}
|
||||
class ConstantValueNumber extends ValueNumber, TConstantValueNumber {}
|
||||
class FieldAddressValueNumber extends ValueNumber, TFieldAddressValueNumber {}
|
||||
class PointerArithmeticValueNumber extends ValueNumber, TPointerArithmeticValueNumber {}
|
||||
class UniqueValueNumber extends ValueNumber, TUniqueValueNumber {}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
(not instr instanceof FieldAddressInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
@@ -0,0 +1,664 @@
|
||||
test.cpp:
|
||||
# 1| test00(int, int) -> int
|
||||
# 1| Block 0
|
||||
# 1| v0_0(void) = EnterFunction :
|
||||
# 1| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 1| valnum = unique
|
||||
# 1| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 1| valnum = r0_2
|
||||
# 1| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 1| valnum = m0_3
|
||||
# 1| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 1| valnum = r0_4
|
||||
# 1| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 1| valnum = m0_5
|
||||
# 2| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 2| valnum = r0_6
|
||||
# 2| m0_7(int) = Uninitialized : r0_6
|
||||
# 2| valnum = unique
|
||||
# 2| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 2| valnum = r0_8
|
||||
# 2| m0_9(int) = Uninitialized : r0_8
|
||||
# 2| valnum = unique
|
||||
# 3| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 3| valnum = unique
|
||||
# 3| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 3| valnum = unique
|
||||
# 5| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 5| valnum = r0_2
|
||||
# 5| r0_13(int) = Load : r0_12, m0_3
|
||||
# 5| valnum = m0_3
|
||||
# 5| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 5| valnum = r0_4
|
||||
# 5| r0_15(int) = Load : r0_14, m0_5
|
||||
# 5| valnum = m0_5
|
||||
# 5| r0_16(int) = Add : r0_13, r0_15
|
||||
# 5| valnum = r0_16
|
||||
# 5| r0_17(glval<int>) = VariableAddress[x] :
|
||||
# 5| valnum = r0_6
|
||||
# 5| m0_18(int) = Store : r0_17, r0_16
|
||||
# 5| valnum = r0_16
|
||||
# 6| r0_19(glval<int>) = VariableAddress[p0] :
|
||||
# 6| valnum = r0_2
|
||||
# 6| r0_20(int) = Load : r0_19, m0_3
|
||||
# 6| valnum = m0_3
|
||||
# 6| r0_21(glval<int>) = VariableAddress[p1] :
|
||||
# 6| valnum = r0_4
|
||||
# 6| r0_22(int) = Load : r0_21, m0_5
|
||||
# 6| valnum = m0_5
|
||||
# 6| r0_23(int) = Add : r0_20, r0_22
|
||||
# 6| valnum = r0_16
|
||||
# 6| r0_24(glval<int>) = VariableAddress[x] :
|
||||
# 6| valnum = r0_6
|
||||
# 6| m0_25(int) = Store : r0_24, r0_23
|
||||
# 6| valnum = r0_16
|
||||
# 7| r0_26(glval<int>) = VariableAddress[x] :
|
||||
# 7| valnum = r0_6
|
||||
# 7| r0_27(int) = Load : r0_26, m0_25
|
||||
# 7| valnum = r0_16
|
||||
# 7| r0_28(glval<int>) = VariableAddress[y] :
|
||||
# 7| valnum = r0_8
|
||||
# 7| m0_29(int) = Store : r0_28, r0_27
|
||||
# 7| valnum = r0_16
|
||||
# 8| v0_30(void) = NoOp :
|
||||
# 1| r0_31(glval<int>) = VariableAddress[#return] :
|
||||
# 1| valnum = unique
|
||||
# 1| v0_32(void) = ReturnValue : r0_31
|
||||
# 1| v0_33(void) = UnmodeledUse : mu*
|
||||
# 1| v0_34(void) = ExitFunction :
|
||||
|
||||
# 53| 1644
|
||||
# 53| valnum = unique
|
||||
|
||||
# 56| 1646
|
||||
# 56| valnum = unique
|
||||
|
||||
# 88| 1755
|
||||
# 88| valnum = unique
|
||||
|
||||
# 12| test01(int, int) -> int
|
||||
# 12| Block 0
|
||||
# 12| v0_0(void) = EnterFunction :
|
||||
# 12| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 12| valnum = unique
|
||||
# 12| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 12| valnum = r0_2
|
||||
# 12| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 12| valnum = m0_3
|
||||
# 12| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 12| valnum = r0_4
|
||||
# 12| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 12| valnum = m0_5
|
||||
# 13| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 13| valnum = r0_6
|
||||
# 13| m0_7(int) = Uninitialized : r0_6
|
||||
# 13| valnum = unique
|
||||
# 13| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 13| valnum = r0_8
|
||||
# 13| m0_9(int) = Uninitialized : r0_8
|
||||
# 13| valnum = unique
|
||||
# 14| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 14| valnum = unique
|
||||
# 14| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 14| valnum = unique
|
||||
# 16| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 16| valnum = r0_2
|
||||
# 16| r0_13(int) = Load : r0_12, m0_3
|
||||
# 16| valnum = m0_3
|
||||
# 16| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 16| valnum = r0_4
|
||||
# 16| r0_15(int) = Load : r0_14, m0_5
|
||||
# 16| valnum = m0_5
|
||||
# 16| r0_16(int) = Add : r0_13, r0_15
|
||||
# 16| valnum = r0_16
|
||||
# 16| r0_17(glval<int>) = VariableAddress[global01] :
|
||||
# 16| valnum = r0_17
|
||||
# 16| r0_18(int) = Load : r0_17, mu0_1
|
||||
# 16| valnum = unique
|
||||
# 16| r0_19(int) = Add : r0_16, r0_18
|
||||
# 16| valnum = r0_19
|
||||
# 16| r0_20(glval<int>) = VariableAddress[x] :
|
||||
# 16| valnum = r0_6
|
||||
# 16| m0_21(int) = Store : r0_20, r0_19
|
||||
# 16| valnum = r0_19
|
||||
# 17| r0_22(glval<int>) = VariableAddress[p0] :
|
||||
# 17| valnum = r0_2
|
||||
# 17| r0_23(int) = Load : r0_22, m0_3
|
||||
# 17| valnum = m0_3
|
||||
# 17| r0_24(glval<int>) = VariableAddress[p1] :
|
||||
# 17| valnum = r0_4
|
||||
# 17| r0_25(int) = Load : r0_24, m0_5
|
||||
# 17| valnum = m0_5
|
||||
# 17| r0_26(int) = Add : r0_23, r0_25
|
||||
# 17| valnum = r0_16
|
||||
# 17| r0_27(glval<int>) = VariableAddress[global01] :
|
||||
# 17| valnum = r0_17
|
||||
# 17| r0_28(int) = Load : r0_27, mu0_1
|
||||
# 17| valnum = unique
|
||||
# 17| r0_29(int) = Add : r0_26, r0_28
|
||||
# 17| valnum = r0_29
|
||||
# 17| r0_30(glval<int>) = VariableAddress[x] :
|
||||
# 17| valnum = r0_6
|
||||
# 17| m0_31(int) = Store : r0_30, r0_29
|
||||
# 17| valnum = r0_29
|
||||
# 18| r0_32(glval<int>) = VariableAddress[x] :
|
||||
# 18| valnum = r0_6
|
||||
# 18| r0_33(int) = Load : r0_32, m0_31
|
||||
# 18| valnum = r0_29
|
||||
# 18| r0_34(glval<int>) = VariableAddress[y] :
|
||||
# 18| valnum = r0_8
|
||||
# 18| m0_35(int) = Store : r0_34, r0_33
|
||||
# 18| valnum = r0_29
|
||||
# 19| v0_36(void) = NoOp :
|
||||
# 12| r0_37(glval<int>) = VariableAddress[#return] :
|
||||
# 12| valnum = unique
|
||||
# 12| v0_38(void) = ReturnValue : r0_37
|
||||
# 12| v0_39(void) = UnmodeledUse : mu*
|
||||
# 12| v0_40(void) = ExitFunction :
|
||||
|
||||
# 25| test02(int, int) -> int
|
||||
# 25| Block 0
|
||||
# 25| v0_0(void) = EnterFunction :
|
||||
# 25| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 25| valnum = unique
|
||||
# 25| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 25| valnum = r0_2
|
||||
# 25| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 25| valnum = m0_3
|
||||
# 25| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 25| valnum = r0_4
|
||||
# 25| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 25| valnum = m0_5
|
||||
# 26| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 26| valnum = r0_6
|
||||
# 26| m0_7(int) = Uninitialized : r0_6
|
||||
# 26| valnum = unique
|
||||
# 26| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 26| valnum = r0_8
|
||||
# 26| m0_9(int) = Uninitialized : r0_8
|
||||
# 26| valnum = unique
|
||||
# 27| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 27| valnum = unique
|
||||
# 27| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 27| valnum = unique
|
||||
# 29| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 29| valnum = r0_2
|
||||
# 29| r0_13(int) = Load : r0_12, m0_3
|
||||
# 29| valnum = m0_3
|
||||
# 29| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 29| valnum = r0_4
|
||||
# 29| r0_15(int) = Load : r0_14, m0_5
|
||||
# 29| valnum = m0_5
|
||||
# 29| r0_16(int) = Add : r0_13, r0_15
|
||||
# 29| valnum = r0_16
|
||||
# 29| r0_17(glval<int>) = VariableAddress[global02] :
|
||||
# 29| valnum = r0_17
|
||||
# 29| r0_18(int) = Load : r0_17, mu0_1
|
||||
# 29| valnum = unique
|
||||
# 29| r0_19(int) = Add : r0_16, r0_18
|
||||
# 29| valnum = r0_19
|
||||
# 29| r0_20(glval<int>) = VariableAddress[x] :
|
||||
# 29| valnum = r0_6
|
||||
# 29| m0_21(int) = Store : r0_20, r0_19
|
||||
# 29| valnum = r0_19
|
||||
# 30| r0_22(glval<unknown>) = FunctionAddress[change_global02] :
|
||||
# 30| valnum = unique
|
||||
# 30| v0_23(void) = Call : r0_22
|
||||
# 31| r0_24(glval<int>) = VariableAddress[p0] :
|
||||
# 31| valnum = r0_2
|
||||
# 31| r0_25(int) = Load : r0_24, m0_3
|
||||
# 31| valnum = m0_3
|
||||
# 31| r0_26(glval<int>) = VariableAddress[p1] :
|
||||
# 31| valnum = r0_4
|
||||
# 31| r0_27(int) = Load : r0_26, m0_5
|
||||
# 31| valnum = m0_5
|
||||
# 31| r0_28(int) = Add : r0_25, r0_27
|
||||
# 31| valnum = r0_16
|
||||
# 31| r0_29(glval<int>) = VariableAddress[global02] :
|
||||
# 31| valnum = r0_17
|
||||
# 31| r0_30(int) = Load : r0_29, mu0_1
|
||||
# 31| valnum = unique
|
||||
# 31| r0_31(int) = Add : r0_28, r0_30
|
||||
# 31| valnum = r0_31
|
||||
# 31| r0_32(glval<int>) = VariableAddress[x] :
|
||||
# 31| valnum = r0_6
|
||||
# 31| m0_33(int) = Store : r0_32, r0_31
|
||||
# 31| valnum = r0_31
|
||||
# 32| r0_34(glval<int>) = VariableAddress[x] :
|
||||
# 32| valnum = r0_6
|
||||
# 32| r0_35(int) = Load : r0_34, m0_33
|
||||
# 32| valnum = r0_31
|
||||
# 32| r0_36(glval<int>) = VariableAddress[y] :
|
||||
# 32| valnum = r0_8
|
||||
# 32| m0_37(int) = Store : r0_36, r0_35
|
||||
# 32| valnum = r0_31
|
||||
# 33| v0_38(void) = NoOp :
|
||||
# 25| r0_39(glval<int>) = VariableAddress[#return] :
|
||||
# 25| valnum = unique
|
||||
# 25| v0_40(void) = ReturnValue : r0_39
|
||||
# 25| v0_41(void) = UnmodeledUse : mu*
|
||||
# 25| v0_42(void) = ExitFunction :
|
||||
|
||||
# 39| test03(int, int, int *) -> int
|
||||
# 39| Block 0
|
||||
# 39| v0_0(void) = EnterFunction :
|
||||
# 39| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 39| valnum = unique
|
||||
# 39| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 39| valnum = r0_2
|
||||
# 39| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 39| valnum = m0_3
|
||||
# 39| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 39| valnum = r0_4
|
||||
# 39| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 39| valnum = m0_5
|
||||
# 39| r0_6(glval<int *>) = VariableAddress[p2] :
|
||||
# 39| valnum = r0_6
|
||||
# 39| m0_7(int *) = InitializeParameter[p2] : r0_6
|
||||
# 39| valnum = m0_7
|
||||
# 40| r0_8(glval<int>) = VariableAddress[x] :
|
||||
# 40| valnum = r0_8
|
||||
# 40| m0_9(int) = Uninitialized : r0_8
|
||||
# 40| valnum = unique
|
||||
# 40| r0_10(glval<int>) = VariableAddress[y] :
|
||||
# 40| valnum = r0_10
|
||||
# 40| m0_11(int) = Uninitialized : r0_10
|
||||
# 40| valnum = unique
|
||||
# 41| r0_12(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 41| valnum = unique
|
||||
# 41| m0_13(unsigned char) = Uninitialized : r0_12
|
||||
# 41| valnum = unique
|
||||
# 43| r0_14(glval<int>) = VariableAddress[p0] :
|
||||
# 43| valnum = r0_2
|
||||
# 43| r0_15(int) = Load : r0_14, m0_3
|
||||
# 43| valnum = m0_3
|
||||
# 43| r0_16(glval<int>) = VariableAddress[p1] :
|
||||
# 43| valnum = r0_4
|
||||
# 43| r0_17(int) = Load : r0_16, m0_5
|
||||
# 43| valnum = m0_5
|
||||
# 43| r0_18(int) = Add : r0_15, r0_17
|
||||
# 43| valnum = r0_18
|
||||
# 43| r0_19(glval<int>) = VariableAddress[global03] :
|
||||
# 43| valnum = r0_19
|
||||
# 43| r0_20(int) = Load : r0_19, mu0_1
|
||||
# 43| valnum = unique
|
||||
# 43| r0_21(int) = Add : r0_18, r0_20
|
||||
# 43| valnum = r0_21
|
||||
# 43| r0_22(glval<int>) = VariableAddress[x] :
|
||||
# 43| valnum = r0_8
|
||||
# 43| m0_23(int) = Store : r0_22, r0_21
|
||||
# 43| valnum = r0_21
|
||||
# 44| r0_24(int) = Constant[0] :
|
||||
# 44| valnum = r0_24
|
||||
# 44| r0_25(glval<int *>) = VariableAddress[p2] :
|
||||
# 44| valnum = r0_6
|
||||
# 44| r0_26(int *) = Load : r0_25, m0_7
|
||||
# 44| valnum = m0_7
|
||||
# 44| mu0_27(int) = Store : r0_26, r0_24
|
||||
# 44| valnum = r0_24
|
||||
# 45| r0_28(glval<int>) = VariableAddress[p0] :
|
||||
# 45| valnum = r0_2
|
||||
# 45| r0_29(int) = Load : r0_28, m0_3
|
||||
# 45| valnum = m0_3
|
||||
# 45| r0_30(glval<int>) = VariableAddress[p1] :
|
||||
# 45| valnum = r0_4
|
||||
# 45| r0_31(int) = Load : r0_30, m0_5
|
||||
# 45| valnum = m0_5
|
||||
# 45| r0_32(int) = Add : r0_29, r0_31
|
||||
# 45| valnum = r0_18
|
||||
# 45| r0_33(glval<int>) = VariableAddress[global03] :
|
||||
# 45| valnum = r0_19
|
||||
# 45| r0_34(int) = Load : r0_33, mu0_1
|
||||
# 45| valnum = unique
|
||||
# 45| r0_35(int) = Add : r0_32, r0_34
|
||||
# 45| valnum = r0_35
|
||||
# 45| r0_36(glval<int>) = VariableAddress[x] :
|
||||
# 45| valnum = r0_8
|
||||
# 45| m0_37(int) = Store : r0_36, r0_35
|
||||
# 45| valnum = r0_35
|
||||
# 46| r0_38(glval<int>) = VariableAddress[x] :
|
||||
# 46| valnum = r0_8
|
||||
# 46| r0_39(int) = Load : r0_38, m0_37
|
||||
# 46| valnum = r0_35
|
||||
# 46| r0_40(glval<int>) = VariableAddress[y] :
|
||||
# 46| valnum = r0_10
|
||||
# 46| m0_41(int) = Store : r0_40, r0_39
|
||||
# 46| valnum = r0_35
|
||||
# 47| v0_42(void) = NoOp :
|
||||
# 39| r0_43(glval<int>) = VariableAddress[#return] :
|
||||
# 39| valnum = unique
|
||||
# 39| v0_44(void) = ReturnValue : r0_43
|
||||
# 39| v0_45(void) = UnmodeledUse : mu*
|
||||
# 39| v0_46(void) = ExitFunction :
|
||||
|
||||
# 49| my_strspn(const char *, const char *) -> unsigned int
|
||||
# 49| Block 0
|
||||
# 49| v0_0(void) = EnterFunction :
|
||||
# 49| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 49| valnum = unique
|
||||
# 49| r0_2(glval<char *>) = VariableAddress[str] :
|
||||
# 49| valnum = r0_2
|
||||
# 49| m0_3(char *) = InitializeParameter[str] : r0_2
|
||||
# 49| valnum = m0_3
|
||||
# 49| r0_4(glval<char *>) = VariableAddress[chars] :
|
||||
# 49| valnum = r0_4
|
||||
# 49| m0_5(char *) = InitializeParameter[chars] : r0_4
|
||||
# 49| valnum = m0_5
|
||||
# 50| r0_6(glval<char *>) = VariableAddress[ptr] :
|
||||
# 50| valnum = r0_6
|
||||
# 50| m0_7(char *) = Uninitialized : r0_6
|
||||
# 50| valnum = unique
|
||||
# 51| r0_8(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 51| valnum = r0_8
|
||||
# 51| r0_9(unsigned int) = Constant[0] :
|
||||
# 51| valnum = r0_9
|
||||
# 51| m0_10(unsigned int) = Store : r0_8, r0_9
|
||||
# 51| valnum = r0_9
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 53| Block 1
|
||||
# 53| m1_0(unsigned int) = Phi : from 0:m0_10, from 8:m8_4
|
||||
# 53| valnum = unique
|
||||
# 53| r1_1(glval<char *>) = VariableAddress[str] :
|
||||
# 53| valnum = r0_2
|
||||
# 53| r1_2(char *) = Load : r1_1, m0_3
|
||||
# 53| valnum = m0_3
|
||||
# 53| r1_3(char) = Load : r1_2, mu0_1
|
||||
# 53| valnum = unique
|
||||
# 53| r1_4(int) = Convert : r1_3
|
||||
# 53| valnum = unique
|
||||
# 53| r1_5(int) = Constant[0] :
|
||||
# 53| valnum = r1_5
|
||||
# 53| r1_6(bool) = CompareNE : r1_4, r1_5
|
||||
# 53| valnum = unique
|
||||
# 53| v1_7(void) = ConditionalBranch : r1_6
|
||||
#-----| False -> Block 9
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 55| Block 2
|
||||
# 55| r2_0(glval<char *>) = VariableAddress[chars] :
|
||||
# 55| valnum = r0_4
|
||||
# 55| r2_1(char *) = Load : r2_0, m0_5
|
||||
# 55| valnum = m0_5
|
||||
# 55| r2_2(glval<char *>) = VariableAddress[ptr] :
|
||||
# 55| valnum = r0_6
|
||||
# 55| m2_3(char *) = Store : r2_2, r2_1
|
||||
# 55| valnum = m0_5
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 56| Block 3
|
||||
# 56| m3_0(char *) = Phi : from 2:m2_3, from 5:m5_4
|
||||
# 56| valnum = unique
|
||||
# 56| r3_1(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r3_2(char *) = Load : r3_1, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r3_3(char) = Load : r3_2, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r3_4(int) = Convert : r3_3
|
||||
# 56| valnum = unique
|
||||
# 56| r3_5(glval<char *>) = VariableAddress[str] :
|
||||
# 56| valnum = r0_2
|
||||
# 56| r3_6(char *) = Load : r3_5, m0_3
|
||||
# 56| valnum = m0_3
|
||||
# 56| r3_7(char) = Load : r3_6, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r3_8(int) = Convert : r3_7
|
||||
# 56| valnum = unique
|
||||
# 56| r3_9(bool) = CompareNE : r3_4, r3_8
|
||||
# 56| valnum = unique
|
||||
# 56| v3_10(void) = ConditionalBranch : r3_9
|
||||
#-----| False -> Block 6
|
||||
#-----| True -> Block 4
|
||||
|
||||
# 56| Block 4
|
||||
# 56| r4_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r4_1(char *) = Load : r4_0, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r4_2(char) = Load : r4_1, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r4_3(int) = Convert : r4_2
|
||||
# 56| valnum = unique
|
||||
# 56| r4_4(int) = Constant[0] :
|
||||
# 56| valnum = r1_5
|
||||
# 56| r4_5(bool) = CompareNE : r4_3, r4_4
|
||||
# 56| valnum = unique
|
||||
# 56| v4_6(void) = ConditionalBranch : r4_5
|
||||
#-----| False -> Block 6
|
||||
#-----| True -> Block 5
|
||||
|
||||
# 56| Block 5
|
||||
# 56| r5_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r5_1(char *) = Load : r5_0, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r5_2(int) = Constant[1] :
|
||||
# 56| valnum = unique
|
||||
# 56| r5_3(char *) = PointerAdd[1] : r5_1, r5_2
|
||||
# 56| valnum = r5_3
|
||||
# 56| m5_4(char *) = Store : r5_0, r5_3
|
||||
# 56| valnum = r5_3
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 59| Block 6
|
||||
# 59| r6_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 59| valnum = r0_6
|
||||
# 59| r6_1(char *) = Load : r6_0, m3_0
|
||||
# 59| valnum = unique
|
||||
# 59| r6_2(char) = Load : r6_1, mu0_1
|
||||
# 59| valnum = unique
|
||||
# 59| r6_3(int) = Convert : r6_2
|
||||
# 59| valnum = unique
|
||||
# 59| r6_4(int) = Constant[0] :
|
||||
# 59| valnum = r1_5
|
||||
# 59| r6_5(bool) = CompareEQ : r6_3, r6_4
|
||||
# 59| valnum = unique
|
||||
# 59| v6_6(void) = ConditionalBranch : r6_5
|
||||
#-----| False -> Block 8
|
||||
#-----| True -> Block 7
|
||||
|
||||
# 60| Block 7
|
||||
# 60| v7_0(void) = NoOp :
|
||||
#-----| Goto -> Block 9
|
||||
|
||||
# 62| Block 8
|
||||
# 62| r8_0(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 62| valnum = r0_8
|
||||
# 62| r8_1(unsigned int) = Load : r8_0, m1_0
|
||||
# 62| valnum = unique
|
||||
# 62| r8_2(unsigned int) = Constant[1] :
|
||||
# 62| valnum = unique
|
||||
# 62| r8_3(unsigned int) = Add : r8_1, r8_2
|
||||
# 62| valnum = r8_3
|
||||
# 62| m8_4(unsigned int) = Store : r8_0, r8_3
|
||||
# 62| valnum = r8_3
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 63| Block 9
|
||||
# 63| v9_0(void) = NoOp :
|
||||
# 65| r9_1(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 65| valnum = r9_1
|
||||
# 65| r9_2(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 65| valnum = r0_8
|
||||
# 65| r9_3(unsigned int) = Load : r9_2, m1_0
|
||||
# 65| valnum = r9_3
|
||||
# 65| m9_4(unsigned int) = Store : r9_1, r9_3
|
||||
# 65| valnum = r9_3
|
||||
# 49| r9_5(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 49| valnum = r9_1
|
||||
# 49| v9_6(void) = ReturnValue : r9_5, m9_4
|
||||
# 49| v9_7(void) = UnmodeledUse : mu*
|
||||
# 49| v9_8(void) = ExitFunction :
|
||||
|
||||
# 75| test04(two_values *) -> void
|
||||
# 75| Block 0
|
||||
# 75| v0_0(void) = EnterFunction :
|
||||
# 75| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 75| valnum = unique
|
||||
# 75| r0_2(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 75| valnum = r0_2
|
||||
# 75| m0_3(two_values *) = InitializeParameter[vals] : r0_2
|
||||
# 75| valnum = m0_3
|
||||
# 77| r0_4(glval<signed short>) = VariableAddress[v] :
|
||||
# 77| valnum = r0_4
|
||||
# 77| r0_5(glval<unknown>) = FunctionAddress[getAValue] :
|
||||
# 77| valnum = unique
|
||||
# 77| r0_6(int) = Call : r0_5
|
||||
# 77| valnum = unique
|
||||
# 77| r0_7(signed short) = Convert : r0_6
|
||||
# 77| valnum = r0_7
|
||||
# 77| m0_8(signed short) = Store : r0_4, r0_7
|
||||
# 77| valnum = r0_7
|
||||
# 79| r0_9(glval<signed short>) = VariableAddress[v] :
|
||||
# 79| valnum = r0_4
|
||||
# 79| r0_10(signed short) = Load : r0_9, m0_8
|
||||
# 79| valnum = r0_7
|
||||
# 79| r0_11(int) = Convert : r0_10
|
||||
# 79| valnum = unique
|
||||
# 79| r0_12(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 79| valnum = r0_2
|
||||
# 79| r0_13(two_values *) = Load : r0_12, m0_3
|
||||
# 79| valnum = m0_3
|
||||
# 79| r0_14(glval<signed short>) = FieldAddress[val1] : r0_13
|
||||
# 79| valnum = unique
|
||||
# 79| r0_15(signed short) = Load : r0_14, mu0_1
|
||||
# 79| valnum = unique
|
||||
# 79| r0_16(int) = Convert : r0_15
|
||||
# 79| valnum = unique
|
||||
# 79| r0_17(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 79| valnum = r0_2
|
||||
# 79| r0_18(two_values *) = Load : r0_17, m0_3
|
||||
# 79| valnum = m0_3
|
||||
# 79| r0_19(glval<signed short>) = FieldAddress[val2] : r0_18
|
||||
# 79| valnum = unique
|
||||
# 79| r0_20(signed short) = Load : r0_19, mu0_1
|
||||
# 79| valnum = unique
|
||||
# 79| r0_21(int) = Convert : r0_20
|
||||
# 79| valnum = unique
|
||||
# 79| r0_22(int) = Add : r0_16, r0_21
|
||||
# 79| valnum = unique
|
||||
# 79| r0_23(bool) = CompareLT : r0_11, r0_22
|
||||
# 79| valnum = unique
|
||||
# 79| v0_24(void) = ConditionalBranch : r0_23
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
|
||||
# 80| Block 1
|
||||
# 80| r1_0(glval<unknown>) = FunctionAddress[getAValue] :
|
||||
# 80| valnum = unique
|
||||
# 80| r1_1(int) = Call : r1_0
|
||||
# 80| valnum = unique
|
||||
# 80| r1_2(signed short) = Convert : r1_1
|
||||
# 80| valnum = r1_2
|
||||
# 80| r1_3(glval<signed short>) = VariableAddress[v] :
|
||||
# 80| valnum = r0_4
|
||||
# 80| m1_4(signed short) = Store : r1_3, r1_2
|
||||
# 80| valnum = r1_2
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 82| Block 2
|
||||
# 82| v2_0(void) = NoOp :
|
||||
# 75| v2_1(void) = ReturnVoid :
|
||||
# 75| v2_2(void) = UnmodeledUse : mu*
|
||||
# 75| v2_3(void) = ExitFunction :
|
||||
|
||||
# 84| test05(int, int, void *) -> void
|
||||
# 84| Block 0
|
||||
# 84| v0_0(void) = EnterFunction :
|
||||
# 84| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 84| valnum = unique
|
||||
# 84| r0_2(glval<int>) = VariableAddress[x] :
|
||||
# 84| valnum = r0_2
|
||||
# 84| m0_3(int) = InitializeParameter[x] : r0_2
|
||||
# 84| valnum = m0_3
|
||||
# 84| r0_4(glval<int>) = VariableAddress[y] :
|
||||
# 84| valnum = r0_4
|
||||
# 84| m0_5(int) = InitializeParameter[y] : r0_4
|
||||
# 84| valnum = m0_5
|
||||
# 84| r0_6(glval<void *>) = VariableAddress[p] :
|
||||
# 84| valnum = r0_6
|
||||
# 84| m0_7(void *) = InitializeParameter[p] : r0_6
|
||||
# 84| valnum = m0_7
|
||||
# 86| r0_8(glval<int>) = VariableAddress[v] :
|
||||
# 86| valnum = r0_8
|
||||
# 86| m0_9(int) = Uninitialized : r0_8
|
||||
# 86| valnum = unique
|
||||
# 88| r0_10(glval<void *>) = VariableAddress[p] :
|
||||
# 88| valnum = r0_6
|
||||
# 88| r0_11(void *) = Load : r0_10, m0_7
|
||||
# 88| valnum = m0_7
|
||||
# 88| r0_12(void *) = Constant[0] :
|
||||
# 88| valnum = unique
|
||||
# 88| r0_13(bool) = CompareNE : r0_11, r0_12
|
||||
# 88| valnum = unique
|
||||
# 88| v0_14(void) = ConditionalBranch : r0_13
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
|
||||
# 88| Block 1
|
||||
# 88| r1_0(glval<int>) = VariableAddress[x] :
|
||||
# 88| valnum = r0_2
|
||||
# 88| r1_1(int) = Load : r1_0, m0_3
|
||||
# 88| valnum = m0_3
|
||||
# 88| r1_2(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| m1_3(int) = Store : r1_2, r1_1
|
||||
# 88| valnum = m0_3
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 88| Block 2
|
||||
# 88| r2_0(glval<int>) = VariableAddress[y] :
|
||||
# 88| valnum = r0_4
|
||||
# 88| r2_1(int) = Load : r2_0, m0_5
|
||||
# 88| valnum = m0_5
|
||||
# 88| r2_2(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| m2_3(int) = Store : r2_2, r2_1
|
||||
# 88| valnum = m0_5
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 88| Block 3
|
||||
# 88| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3
|
||||
# 88| valnum = unique
|
||||
# 88| r3_1(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| r3_2(int) = Load : r3_1, m3_0
|
||||
# 88| valnum = r3_2
|
||||
# 88| r3_3(glval<int>) = VariableAddress[v] :
|
||||
# 88| valnum = r0_8
|
||||
# 88| m3_4(int) = Store : r3_3, r3_2
|
||||
# 88| valnum = r3_2
|
||||
# 89| v3_5(void) = NoOp :
|
||||
# 84| v3_6(void) = ReturnVoid :
|
||||
# 84| v3_7(void) = UnmodeledUse : mu*
|
||||
# 84| v3_8(void) = ExitFunction :
|
||||
|
||||
# 91| regression_test00() -> int
|
||||
# 91| Block 0
|
||||
# 91| v0_0(void) = EnterFunction :
|
||||
# 91| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 91| valnum = unique
|
||||
# 92| r0_2(glval<int>) = VariableAddress[x] :
|
||||
# 92| valnum = r0_2
|
||||
# 92| r0_3(int) = Constant[10] :
|
||||
# 92| valnum = r0_3
|
||||
# 92| r0_4(glval<int>) = VariableAddress[x] :
|
||||
# 92| valnum = r0_2
|
||||
# 92| m0_5(int) = Store : r0_4, r0_3
|
||||
# 92| valnum = r0_3
|
||||
# 92| m0_6(int) = Store : r0_2, r0_3
|
||||
# 92| valnum = r0_3
|
||||
# 93| r0_7(glval<int>) = VariableAddress[#return] :
|
||||
# 93| valnum = r0_7
|
||||
# 93| r0_8(glval<int>) = VariableAddress[x] :
|
||||
# 93| valnum = r0_2
|
||||
# 93| r0_9(int) = Load : r0_8, m0_6
|
||||
# 93| valnum = r0_3
|
||||
# 93| m0_10(int) = Store : r0_7, r0_9
|
||||
# 93| valnum = r0_3
|
||||
# 91| r0_11(glval<int>) = VariableAddress[#return] :
|
||||
# 91| valnum = r0_7
|
||||
# 91| v0_12(void) = ReturnValue : r0_11, m0_10
|
||||
# 91| v0_13(void) = UnmodeledUse : mu*
|
||||
# 91| v0_14(void) = ExitFunction :
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @kind graph
|
||||
*/
|
||||
import semmle.code.cpp.ir.PrintIR
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.gvn.ValueNumber
|
||||
Reference in New Issue
Block a user