mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge remote-tracking branch 'upstream/master' into ir-crement-load
Conflicts: cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected
This commit is contained in:
@@ -258,14 +258,7 @@ class FormatLiteral extends Literal {
|
||||
* Gets the position in the string at which the nth conversion specifier
|
||||
* starts.
|
||||
*/
|
||||
int getConvSpecOffset(int n) {
|
||||
n = 0 and result = this.getFormat().indexOf("%", 0, 0)
|
||||
or
|
||||
n > 0 and
|
||||
exists(int p |
|
||||
n = p + 1 and result = this.getFormat().indexOf("%", 0, this.getConvSpecOffset(p) + 2)
|
||||
)
|
||||
}
|
||||
int getConvSpecOffset(int n) { result = this.getFormat().indexOf("%", n, 0) }
|
||||
|
||||
/*
|
||||
* Each of these predicates gets a regular expressions to match each individual
|
||||
|
||||
@@ -6,7 +6,6 @@ private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -689,9 +688,9 @@ class BarrierGuard extends GuardCondition {
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(GVN value, boolean branch |
|
||||
result.getExpr() = value.getAnExpr() and
|
||||
this.checks(value.getAnExpr(), branch) and
|
||||
exists(SsaDefinition def, Variable v, boolean branch |
|
||||
result.getExpr() = def.getAUse(v) and
|
||||
this.checks(def.getAUse(v), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,31 +21,19 @@ private predicate predictableInstruction(Instruction instr) {
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
(
|
||||
result = DataFlow::exprNode(source)
|
||||
or
|
||||
result = DataFlow::definitionByReferenceNode(source)
|
||||
)
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(CallInstruction ci, WriteSideEffectInstruction wsei |
|
||||
userInputArgument(ci.getConvertedResultExpression(), wsei.getIndex()) and
|
||||
source.asInstruction() = wsei and
|
||||
wsei.getPrimaryInstruction() = ci
|
||||
)
|
||||
or
|
||||
userInputReturned(source.asExpr())
|
||||
or
|
||||
isUserInput(source.asExpr(), _)
|
||||
or
|
||||
source.asExpr() instanceof EnvironmentRead
|
||||
or
|
||||
source
|
||||
.asInstruction()
|
||||
.(LoadInstruction)
|
||||
.getSourceAddress()
|
||||
.(VariableAddressInstruction)
|
||||
.getASTVariable()
|
||||
.hasName("argv") and
|
||||
source.asInstruction().getEnclosingFunction().hasGlobalName("main")
|
||||
}
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
|
||||
@@ -59,7 +47,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(GlobalOrNamespaceVariable gv | writesVariable(sink.asInstruction(), gv))
|
||||
@@ -163,6 +151,22 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
// from `a`.
|
||||
i2.(PointerAddInstruction).getLeft() = i1
|
||||
or
|
||||
// Until we have from through indirections across calls, we'll take flow out
|
||||
// of the parameter and into its indirection.
|
||||
exists(IRFunction f, Parameter parameter |
|
||||
i1 = getInitializeParameter(f, parameter) and
|
||||
i2 = getInitializeIndirection(f, parameter)
|
||||
)
|
||||
or
|
||||
// Until we have flow through indirections across calls, we'll take flow out
|
||||
// of the indirection and into the argument.
|
||||
// When we get proper flow through indirections across calls, this code can be
|
||||
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
|
||||
read.getArgumentDef() = i2
|
||||
)
|
||||
or
|
||||
// Flow from argument to return value
|
||||
i2 =
|
||||
any(CallInstruction call |
|
||||
@@ -188,6 +192,18 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private InitializeIndirectionInstruction getInitializeIndirection(IRFunction f, Parameter p) {
|
||||
result.getParameter() = p and
|
||||
result.getEnclosingIRFunction() = f
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private InitializeParameterInstruction getInitializeParameter(IRFunction f, Parameter p) {
|
||||
result.getParameter() = p and
|
||||
result.getEnclosingIRFunction() = f
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instruction that goes into argument `argumentIndex` of `call`. This
|
||||
* can be either directly or through one pointer indirection.
|
||||
@@ -285,31 +301,11 @@ private Element adjustedSink(DataFlow::Node sink) {
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// For compatibility, send flow from argument read side effects to their
|
||||
// corresponding argument expression
|
||||
exists(IndirectReadSideEffectInstruction read |
|
||||
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
|
||||
read.getArgumentDef().getUnconvertedResultExpression() = result
|
||||
)
|
||||
or
|
||||
exists(BufferReadSideEffectInstruction read |
|
||||
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
|
||||
read.getArgumentDef().getUnconvertedResultExpression() = result
|
||||
)
|
||||
or
|
||||
exists(SizedBufferReadSideEffectInstruction read |
|
||||
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
|
||||
read.getArgumentDef().getUnconvertedResultExpression() = result
|
||||
)
|
||||
}
|
||||
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(DataFlow::exprNode(source), sink)
|
||||
or
|
||||
cfg.hasFlow(DataFlow::definitionByReferenceNode(source), sink)
|
||||
|
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
@@ -322,7 +318,7 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg, DataFlow::Node store,
|
||||
GlobalOrNamespaceVariable global, DataFlow::Node load, DataFlow::Node sink
|
||||
|
|
||||
toCfg.hasFlow(DataFlow::exprNode(source), store) and
|
||||
toCfg.hasFlow(getNodeForSource(source), store) and
|
||||
store
|
||||
.asInstruction()
|
||||
.(StoreInstruction)
|
||||
|
||||
@@ -6,6 +6,7 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
/**
|
||||
* A newtype wrapper to prevent accidental casts between `Node` and
|
||||
@@ -289,6 +290,51 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
// Flow through the partial operand belongs in the taint-tracking libraries
|
||||
// for now.
|
||||
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
|
||||
or
|
||||
// Flow through modeled functions
|
||||
modelFlow(iFrom, iTo)
|
||||
}
|
||||
|
||||
private predicate modelFlow(Instruction iFrom, Instruction iTo) {
|
||||
exists(
|
||||
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasDataFlow(modelIn, modelOut)
|
||||
|
|
||||
(
|
||||
modelOut.isReturnValue() and
|
||||
iTo = call
|
||||
or
|
||||
// TODO: Add write side effects for return values
|
||||
modelOut.isReturnValueDeref() and
|
||||
iTo = call
|
||||
or
|
||||
exists(WriteSideEffectInstruction outNode |
|
||||
modelOut.isParameterDeref(outNode.getIndex()) and
|
||||
iTo = outNode and
|
||||
outNode.getPrimaryInstruction() = call
|
||||
)
|
||||
// TODO: add write side effects for qualifiers
|
||||
) and
|
||||
(
|
||||
exists(int index |
|
||||
modelIn.isParameter(index) and
|
||||
iFrom = call.getPositionalArgument(index)
|
||||
)
|
||||
or
|
||||
exists(int index, ReadSideEffectInstruction read |
|
||||
modelIn.isParameterDeref(index) and
|
||||
read.getIndex() = index and
|
||||
read.getPrimaryInstruction() = call and
|
||||
iFrom = read.getSideEffectOperand().getAnyDef()
|
||||
)
|
||||
or
|
||||
modelIn.isQualifierAddress() and
|
||||
iFrom = call.getThisArgument()
|
||||
// TODO: add read side effects for qualifiers
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private newtype TMemoryAccessKind =
|
||||
TIndirectMemoryAccess() or
|
||||
TBufferMemoryAccess() or
|
||||
TEntireAllocationMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TNonLocalMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
@@ -43,6 +44,16 @@ class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
|
||||
final override predicate usesAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or results accesses all memory in the contiguous allocation that contains the address
|
||||
* specified by the `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class EntireAllocationMemoryAccess extends MemoryAccessKind, TEntireAllocationMemoryAccess {
|
||||
override string toString() { result = "alloc" }
|
||||
|
||||
final override predicate usesAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses all memory whose address has escaped.
|
||||
*/
|
||||
|
||||
@@ -232,6 +232,31 @@ abstract class BufferReadOpcode extends BufferAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that access an entire memory allocation.
|
||||
*/
|
||||
abstract class EntireAllocationAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that write to an entire memory allocation.
|
||||
*/
|
||||
abstract class EntireAllocationWriteOpcode extends EntireAllocationAccessOpcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() {
|
||||
result instanceof EntireAllocationMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from an entire memory allocation.
|
||||
*/
|
||||
abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() {
|
||||
result instanceof EntireAllocationMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`.
|
||||
*/
|
||||
@@ -325,7 +350,7 @@ module Opcode {
|
||||
final override string toString() { result = "InitializeParameter" }
|
||||
}
|
||||
|
||||
class InitializeIndirection extends IndirectWriteOpcode, TInitializeIndirection {
|
||||
class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection {
|
||||
final override string toString() { result = "InitializeIndirection" }
|
||||
}
|
||||
|
||||
@@ -349,7 +374,7 @@ module Opcode {
|
||||
final override string toString() { result = "ReturnVoid" }
|
||||
}
|
||||
|
||||
class ReturnIndirection extends IndirectReadOpcode, TReturnIndirection {
|
||||
class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection {
|
||||
final override string toString() { result = "ReturnIndirection" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
private import AliasAnalysisInternal
|
||||
private import cpp
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.implementation.IRConfiguration
|
||||
private import semmle.code.cpp.models.interfaces.Alias
|
||||
private import AliasAnalysisImports
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
/**
|
||||
* Gets the offset of field `field` in bits.
|
||||
*/
|
||||
private IntValue getFieldBitOffset(Field field) {
|
||||
if field instanceof BitField
|
||||
then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset())
|
||||
else result = Ints::mul(field.getByteOffset(), 8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the operand `tag` of instruction `instr` is used in a way that does
|
||||
* not result in any address held in that operand from escaping beyond the
|
||||
@@ -36,7 +24,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
instr instanceof PointerDiffInstruction
|
||||
or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -111,13 +99,10 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
exists(ConvertInstruction convert, IRType resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
resultType = convert.getResultIRType() and
|
||||
resultType instanceof IRAddressType and
|
||||
bitOffset = 0
|
||||
)
|
||||
or
|
||||
@@ -131,7 +116,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField())
|
||||
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
|
||||
or
|
||||
// A copy propagates the source value.
|
||||
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
|
||||
@@ -212,7 +197,7 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
|
||||
exists(Function f |
|
||||
exists(Language::Function f |
|
||||
ci = operand.getUse() and
|
||||
f = ci.getStaticCallTarget() and
|
||||
(
|
||||
@@ -223,27 +208,27 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
|
||||
init.getEnclosingFunction() = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not f.isVirtual() and
|
||||
not f instanceof AliasFunction
|
||||
not Language::isFunctionVirtual(f) and
|
||||
not f instanceof AliasModels::AliasFunction
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isAlwaysReturnedArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isNeverEscapesArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
@@ -265,61 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of the specified local variable or parameter escapes the
|
||||
* domain of the analysis.
|
||||
* Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur
|
||||
* either because the allocation's address is taken within the function and escapes, or because the
|
||||
* allocation is marked as always escaping via `alwaysEscapes()`.
|
||||
*/
|
||||
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
||||
// The variable's address escapes if the result of any
|
||||
// VariableAddressInstruction that computes the variable's address escapes.
|
||||
exists(VariableAddressInstruction instr |
|
||||
instr.getIRVariable() = var and
|
||||
resultEscapesNonReturn(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of the specified variable escapes the domain of the
|
||||
* analysis.
|
||||
*/
|
||||
predicate variableAddressEscapes(IRVariable var) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and
|
||||
automaticVariableAddressEscapes(var.(IRAutomaticVariable))
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
or
|
||||
// All variables with static storage duration have their address escape, even when escape analysis
|
||||
// is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global
|
||||
// variable. Normally, we rely on `AliasedDefinition` to handle that.
|
||||
not var instanceof IRAutomaticVariable
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result of instruction `instr` points within variable `var`, at
|
||||
* bit offset `bitOffset` within the variable. If the result points within
|
||||
* `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown.
|
||||
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
||||
*/
|
||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||
// The address of a variable points to that variable, at offset 0.
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
|
||||
operandIsPropagated(operand, bitOffset)
|
||||
or
|
||||
// A string literal is just a special read-only global variable.
|
||||
instr.(StringConstantInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
// offset by the bit offset from the propagation.
|
||||
resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and
|
||||
(
|
||||
operandIsPropagated(operand, propagatedBitOffset)
|
||||
or
|
||||
exists(CallInstruction ci, Instruction init |
|
||||
isArgumentForParameter(ci, operand, init) and
|
||||
resultReturned(init, propagatedBitOffset)
|
||||
)
|
||||
) and
|
||||
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
|
||||
exists(CallInstruction call, Instruction init |
|
||||
isArgumentForParameter(call, operand, init) and
|
||||
resultReturned(init, bitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset
|
||||
* may be `unknown()`.
|
||||
*/
|
||||
private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) {
|
||||
base = addrOperand.getDef() and bitOffset = 0 // Base case
|
||||
or
|
||||
exists(
|
||||
Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset
|
||||
|
|
||||
// We already have an offset from `middle`.
|
||||
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
||||
// `middle` is propagated from `base`.
|
||||
middleOperand = middle.getAnOperand() and
|
||||
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
|
||||
base = middleOperand.getDef() and
|
||||
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`.
|
||||
* Only holds for the `base` with the longest chain of propagation to `addrOperand`.
|
||||
*/
|
||||
predicate addressOperandBaseAndConstantOffset(
|
||||
AddressOperand addrOperand, Instruction base, int bitOffset
|
||||
) {
|
||||
hasBaseAndOffset(addrOperand, base, bitOffset) and
|
||||
Ints::hasValue(bitOffset) and
|
||||
not exists(Instruction previousBase, int previousBitOffset |
|
||||
hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and
|
||||
previousBase = base.getAnOperand().getDef() and
|
||||
Ints::hasValue(previousBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the allocation into which `addrOperand` points, if known.
|
||||
*/
|
||||
Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) {
|
||||
addressOperandAllocationAndOffset(addrOperand, result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The
|
||||
* offset may be `unknown()`.
|
||||
*/
|
||||
predicate addressOperandAllocationAndOffset(
|
||||
AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset
|
||||
) {
|
||||
exists(Instruction base |
|
||||
allocation.getABaseInstruction() = base and
|
||||
hasBaseAndOffset(addrOperand, base, bitOffset) and
|
||||
not exists(Instruction previousBase |
|
||||
hasBaseAndOffset(addrOperand, previousBase, _) and
|
||||
previousBase = base.getAnOperand().getDef()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
import semmle.code.cpp.models.interfaces.Alias as AliasModels
|
||||
@@ -1 +1,3 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR
|
||||
import AliasConfiguration as Configuration
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
private import AliasConfigurationInternal
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import cpp
|
||||
private import AliasAnalysis
|
||||
|
||||
private newtype TAllocation =
|
||||
TVariableAllocation(IRVariable var) or
|
||||
TIndirectParameterAllocation(IRAutomaticUserVariable var) {
|
||||
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory allocation that can be tracked by the AliasedSSA alias analysis.
|
||||
*/
|
||||
abstract class Allocation extends TAllocation {
|
||||
abstract string toString();
|
||||
|
||||
final string getAllocationString() { result = toString() }
|
||||
|
||||
abstract Instruction getABaseInstruction();
|
||||
|
||||
abstract IRFunction getEnclosingIRFunction();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
|
||||
abstract string getUniqueId();
|
||||
|
||||
abstract IRType getIRType();
|
||||
|
||||
abstract predicate isReadOnly();
|
||||
|
||||
abstract predicate alwaysEscapes();
|
||||
|
||||
abstract predicate isAlwaysAllocatedOnStack();
|
||||
|
||||
final predicate isUnaliased() { not allocationEscapes(this) }
|
||||
}
|
||||
|
||||
class VariableAllocation extends Allocation, TVariableAllocation {
|
||||
IRVariable var;
|
||||
|
||||
VariableAllocation() { this = TVariableAllocation(var) }
|
||||
|
||||
final override string toString() { result = var.toString() }
|
||||
|
||||
final override VariableInstruction getABaseInstruction() {
|
||||
result.getIRVariable() = var and
|
||||
(result instanceof VariableAddressInstruction or result instanceof StringConstantInstruction)
|
||||
}
|
||||
|
||||
final override IRFunction getEnclosingIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = var.getUniqueId() }
|
||||
|
||||
final override IRType getIRType() { result = var.getIRType() }
|
||||
|
||||
final override predicate isReadOnly() { var.isReadOnly() }
|
||||
|
||||
final override predicate isAlwaysAllocatedOnStack() { var instanceof IRAutomaticVariable }
|
||||
|
||||
final override predicate alwaysEscapes() {
|
||||
// All variables with static storage duration have their address escape, even when escape analysis
|
||||
// is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global
|
||||
// variable. Normally, we rely on `AliasedDefinition` to handle that.
|
||||
not var instanceof IRAutomaticVariable
|
||||
}
|
||||
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
}
|
||||
|
||||
class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocation {
|
||||
IRAutomaticUserVariable var;
|
||||
|
||||
IndirectParameterAllocation() { this = TIndirectParameterAllocation(var) }
|
||||
|
||||
final override string toString() { result = "*" + var.toString() }
|
||||
|
||||
final override InitializeParameterInstruction getABaseInstruction() {
|
||||
result.getIRVariable() = var
|
||||
}
|
||||
|
||||
final override IRFunction getEnclosingIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = var.getUniqueId() }
|
||||
|
||||
final override IRType getIRType() { result = var.getIRType() }
|
||||
|
||||
final override predicate isReadOnly() { none() }
|
||||
|
||||
final override predicate isAlwaysAllocatedOnStack() { none() }
|
||||
|
||||
final override predicate alwaysEscapes() { none() }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -6,38 +6,52 @@ private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import AliasConfiguration
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private predicate isIndirectOrBufferMemoryAccess(MemoryAccessKind kind) {
|
||||
kind instanceof IndirectMemoryAccess or
|
||||
kind instanceof BufferMemoryAccess
|
||||
}
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
Instruction instr, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
resultPointsTo(instr.getResultAddress(), var, startBitOffset) and
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
exists(AddressOperand addrOperand |
|
||||
addrOperand = instr.getResultAddressOperand() and
|
||||
addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if type.getByteSize() > 0
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
MemoryOperand operand, Allocation var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
exists(AddressOperand addrOperand |
|
||||
addrOperand = operand.getAddressOperand() and
|
||||
addressOperandAllocationAndOffset(addrOperand, var, startBitOffset) and
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if type.getByteSize() > 0
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation =
|
||||
TVariableMemoryLocation(
|
||||
IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
|
||||
Allocation var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
|
||||
IntValue endBitOffset, boolean isMayAccess
|
||||
) {
|
||||
(
|
||||
@@ -45,17 +59,18 @@ private newtype TMemoryLocation =
|
||||
or
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
or
|
||||
exists(IRAutomaticVariable autoVar |
|
||||
// Always create a memory location for the entire variable.
|
||||
autoVar = var and
|
||||
type = autoVar.getIRType() and
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = type.getByteSize() * 8 and
|
||||
isMayAccess = false
|
||||
)
|
||||
// For a stack variable, always create a memory location for the entire variable.
|
||||
var.isAlwaysAllocatedOnStack() and
|
||||
type = var.getIRType() and
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = type.getByteSize() * 8 and
|
||||
isMayAccess = false
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TEntireAllocationMemoryLocation(IndirectParameterAllocation var, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
} or
|
||||
@@ -94,6 +109,8 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract predicate isMayAccess();
|
||||
|
||||
Allocation getAllocation() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the location cannot be overwritten except by definition of a `MemoryLocation` for
|
||||
* which `def.canDefineReadOnly()` holds.
|
||||
@@ -114,27 +131,63 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract class VirtualVariable extends MemoryLocation { }
|
||||
|
||||
abstract class AllocationMemoryLocation extends MemoryLocation {
|
||||
Allocation var;
|
||||
boolean isMayAccess;
|
||||
|
||||
AllocationMemoryLocation() {
|
||||
this instanceof TMemoryLocation and
|
||||
isMayAccess = false
|
||||
or
|
||||
isMayAccess = true // Just ensures that `isMayAccess` is bound.
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if allocationEscapes(var)
|
||||
then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false)
|
||||
else result.(AllocationMemoryLocation).getAllocation() = var
|
||||
}
|
||||
|
||||
final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final override Allocation getAllocation() { result = var }
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
|
||||
final override predicate isReadOnly() { var.isReadOnly() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to memory within a single known `IRVariable`. The variable may be either an unescaped variable
|
||||
* (with its own `VirtualIRVariable`) or an escaped variable (assigned to `UnknownVirtualVariable`).
|
||||
*/
|
||||
class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
IRVariable var;
|
||||
class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLocation {
|
||||
IRType type;
|
||||
Language::LanguageType languageType;
|
||||
IntValue startBitOffset;
|
||||
IntValue endBitOffset;
|
||||
boolean isMayAccess;
|
||||
|
||||
VariableMemoryLocation() {
|
||||
this =
|
||||
TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset, isMayAccess)
|
||||
}
|
||||
|
||||
private string getIntervalString() {
|
||||
if coversEntireVariable()
|
||||
then result = ""
|
||||
else result = Interval::getIntervalString(startBitOffset, endBitOffset)
|
||||
}
|
||||
|
||||
private string getTypeString() {
|
||||
if coversEntireVariable() and type = var.getIRType()
|
||||
then result = ""
|
||||
else result = "<" + languageType.toString() + ">"
|
||||
}
|
||||
|
||||
final override string toStringInternal() {
|
||||
result =
|
||||
var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
type.toString() + ", " + languageType.toString() + ">"
|
||||
result = var.toString() + getIntervalString() + getTypeString()
|
||||
}
|
||||
|
||||
final override Language::LanguageType getType() {
|
||||
@@ -152,35 +205,17 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
result = type.getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final IntValue getStartBitOffset() { result = startBitOffset }
|
||||
|
||||
final IntValue getEndBitOffset() { result = endBitOffset }
|
||||
|
||||
final IRVariable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result =
|
||||
var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
type.getIdentityString() + ">"
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if variableAddressEscapes(var)
|
||||
then result = TAllAliasedMemory(var.getEnclosingIRFunction(), false)
|
||||
else
|
||||
result =
|
||||
TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8, false)
|
||||
}
|
||||
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
|
||||
final override predicate isReadOnly() { var.isReadOnly() }
|
||||
|
||||
final override predicate isAlwaysAllocatedOnStack() { var instanceof IRAutomaticVariable }
|
||||
final override predicate isAlwaysAllocatedOnStack() { var.isAlwaysAllocatedOnStack() }
|
||||
|
||||
/**
|
||||
* Holds if this memory location covers the entire variable.
|
||||
@@ -191,6 +226,26 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
}
|
||||
}
|
||||
|
||||
class EntireAllocationMemoryLocation extends TEntireAllocationMemoryLocation,
|
||||
AllocationMemoryLocation {
|
||||
EntireAllocationMemoryLocation() { this = TEntireAllocationMemoryLocation(var, isMayAccess) }
|
||||
|
||||
final override string toStringInternal() { result = var.toString() }
|
||||
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType unknownType).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = var.getUniqueId() }
|
||||
}
|
||||
|
||||
class EntireAllocationVirtualVariable extends EntireAllocationMemoryLocation, VirtualVariable {
|
||||
EntireAllocationVirtualVariable() {
|
||||
not allocationEscapes(var) and
|
||||
not isMayAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the `MemoryLocation` for an `IRVariable` that acts as its own `VirtualVariable`. Includes any
|
||||
* `VariableMemoryLocation` that exactly overlaps its entire `IRVariable`, and only if that `IRVariable` does not
|
||||
@@ -198,7 +253,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
*/
|
||||
class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
|
||||
VariableVirtualVariable() {
|
||||
not variableAddressEscapes(var) and
|
||||
not allocationEscapes(var) and
|
||||
type = var.getIRType() and
|
||||
coversEntireVariable() and
|
||||
not isMayAccess()
|
||||
@@ -356,13 +411,30 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
not use.isAlwaysAllocatedOnStack()
|
||||
)
|
||||
or
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
def instanceof EntireAllocationMemoryLocation and
|
||||
(
|
||||
// EntireAllocationMemoryLocation exactly overlaps itself.
|
||||
use instanceof EntireAllocationMemoryLocation and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
// EntireAllocationMemoryLocation totally overlaps any location within the same virtual
|
||||
// variable.
|
||||
not use instanceof EntireAllocationMemoryLocation and
|
||||
result instanceof MustTotallyOverlap
|
||||
)
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
defVariableLocation = def and
|
||||
(
|
||||
// A VariableMemoryLocation may partially overlap an unknown location within the same
|
||||
// virtual variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
(use instanceof UnknownMemoryLocation or use instanceof AllAliasedMemory) and
|
||||
(
|
||||
use instanceof UnknownMemoryLocation or
|
||||
use instanceof AllAliasedMemory or
|
||||
use instanceof EntireAllocationMemoryLocation
|
||||
) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// A VariableMemoryLocation that is not a local variable may partially overlap an
|
||||
@@ -423,19 +495,19 @@ private predicate isRelatableMemoryLocation(VariableMemoryLocation vml) {
|
||||
vml.getStartBitOffset() != Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate isCoveredOffset(IRVariable var, int offsetRank, VariableMemoryLocation vml) {
|
||||
private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemoryLocation vml) {
|
||||
exists(int startRank, int endRank, VirtualVariable vvar |
|
||||
vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
vml.getEndBitOffset() = rank[endRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
|
||||
var = vml.getVariable() and
|
||||
var = vml.getAllocation() and
|
||||
vvar = vml.getVirtualVariable() and
|
||||
isRelatableMemoryLocation(vml) and
|
||||
offsetRank in [startRank .. endRank]
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasUnknownOffset(IRVariable var, VariableMemoryLocation vml) {
|
||||
vml.getVariable() = var and
|
||||
private predicate hasUnknownOffset(Allocation var, VariableMemoryLocation vml) {
|
||||
vml.getAllocation() = var and
|
||||
(
|
||||
vml.getStartBitOffset() = Ints::unknown() or
|
||||
vml.getEndBitOffset() = Ints::unknown()
|
||||
@@ -445,14 +517,14 @@ private predicate hasUnknownOffset(IRVariable var, VariableMemoryLocation vml) {
|
||||
private predicate overlappingIRVariableMemoryLocations(
|
||||
VariableMemoryLocation def, VariableMemoryLocation use
|
||||
) {
|
||||
exists(IRVariable var, int offsetRank |
|
||||
exists(Allocation var, int offsetRank |
|
||||
isCoveredOffset(var, offsetRank, def) and
|
||||
isCoveredOffset(var, offsetRank, use)
|
||||
)
|
||||
or
|
||||
hasUnknownOffset(use.getVariable(), def)
|
||||
hasUnknownOffset(use.getAllocation(), def)
|
||||
or
|
||||
hasUnknownOffset(def.getVariable(), use)
|
||||
hasUnknownOffset(def.getAllocation(), use)
|
||||
}
|
||||
|
||||
private Overlap getVariableMemoryLocationOverlap(
|
||||
@@ -470,10 +542,10 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
isIndirectOrBufferMemoryAccess(kind) and
|
||||
if hasResultMemoryAccess(instr, _, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
@@ -481,6 +553,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
or
|
||||
kind instanceof EntireAllocationMemoryAccess and
|
||||
result =
|
||||
TEntireAllocationMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()),
|
||||
isMayAccess)
|
||||
or
|
||||
kind instanceof EscapedMemoryAccess and
|
||||
result = TAllAliasedMemory(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
or
|
||||
@@ -496,10 +573,10 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
isIndirectOrBufferMemoryAccess(kind) and
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
@@ -507,6 +584,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
or
|
||||
kind instanceof EntireAllocationMemoryAccess and
|
||||
result =
|
||||
TEntireAllocationMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand()),
|
||||
isMayAccess)
|
||||
or
|
||||
kind instanceof EscapedMemoryAccess and
|
||||
result = TAllAliasedMemory(operand.getEnclosingIRFunction(), isMayAccess)
|
||||
or
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
private import AliasAnalysisInternal
|
||||
private import cpp
|
||||
private import InputIR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.implementation.IRConfiguration
|
||||
private import semmle.code.cpp.models.interfaces.Alias
|
||||
private import AliasAnalysisImports
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
/**
|
||||
* Gets the offset of field `field` in bits.
|
||||
*/
|
||||
private IntValue getFieldBitOffset(Field field) {
|
||||
if field instanceof BitField
|
||||
then result = Ints::add(Ints::mul(field.getByteOffset(), 8), field.(BitField).getBitOffset())
|
||||
else result = Ints::mul(field.getByteOffset(), 8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the operand `tag` of instruction `instr` is used in a way that does
|
||||
* not result in any address held in that operand from escaping beyond the
|
||||
@@ -36,7 +24,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
instr instanceof PointerDiffInstruction
|
||||
or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -111,13 +99,10 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
exists(ConvertInstruction convert, IRType resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
resultType = convert.getResultIRType() and
|
||||
resultType instanceof IRAddressType and
|
||||
bitOffset = 0
|
||||
)
|
||||
or
|
||||
@@ -131,7 +116,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField())
|
||||
bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
|
||||
or
|
||||
// A copy propagates the source value.
|
||||
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
|
||||
@@ -212,7 +197,7 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
|
||||
exists(Function f |
|
||||
exists(Language::Function f |
|
||||
ci = operand.getUse() and
|
||||
f = ci.getStaticCallTarget() and
|
||||
(
|
||||
@@ -223,27 +208,27 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
|
||||
init.getEnclosingFunction() = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not f.isVirtual() and
|
||||
not f instanceof AliasFunction
|
||||
not Language::isFunctionVirtual(f) and
|
||||
not f instanceof AliasModels::AliasFunction
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isAlwaysReturnedArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isNeverEscapesArgument(Operand operand) {
|
||||
exists(AliasFunction f |
|
||||
exists(AliasModels::AliasFunction f |
|
||||
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
|
||||
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
|
||||
)
|
||||
@@ -265,61 +250,86 @@ private predicate resultEscapesNonReturn(Instruction instr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of the specified local variable or parameter escapes the
|
||||
* domain of the analysis.
|
||||
* Holds if the address of `allocation` escapes outside the domain of the analysis. This can occur
|
||||
* either because the allocation's address is taken within the function and escapes, or because the
|
||||
* allocation is marked as always escaping via `alwaysEscapes()`.
|
||||
*/
|
||||
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
||||
// The variable's address escapes if the result of any
|
||||
// VariableAddressInstruction that computes the variable's address escapes.
|
||||
exists(VariableAddressInstruction instr |
|
||||
instr.getIRVariable() = var and
|
||||
resultEscapesNonReturn(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address of the specified variable escapes the domain of the
|
||||
* analysis.
|
||||
*/
|
||||
predicate variableAddressEscapes(IRVariable var) {
|
||||
predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
allocation.alwaysEscapes()
|
||||
or
|
||||
exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and
|
||||
automaticVariableAddressEscapes(var.(IRAutomaticVariable))
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
or
|
||||
// All variables with static storage duration have their address escape, even when escape analysis
|
||||
// is allowed to be unsound. Otherwise, we won't have a definition for any non-escaped global
|
||||
// variable. Normally, we rely on `AliasedDefinition` to handle that.
|
||||
not var instanceof IRAutomaticVariable
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result of instruction `instr` points within variable `var`, at
|
||||
* bit offset `bitOffset` within the variable. If the result points within
|
||||
* `var`, but at an unknown or non-constant offset, then `bitOffset` is unknown.
|
||||
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
|
||||
*/
|
||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||
// The address of a variable points to that variable, at offset 0.
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
|
||||
operandIsPropagated(operand, bitOffset)
|
||||
or
|
||||
// A string literal is just a special read-only global variable.
|
||||
instr.(StringConstantInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
// offset by the bit offset from the propagation.
|
||||
resultPointsTo(operand.getAnyDef(), var, originalBitOffset) and
|
||||
(
|
||||
operandIsPropagated(operand, propagatedBitOffset)
|
||||
or
|
||||
exists(CallInstruction ci, Instruction init |
|
||||
isArgumentForParameter(ci, operand, init) and
|
||||
resultReturned(init, propagatedBitOffset)
|
||||
)
|
||||
) and
|
||||
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
|
||||
exists(CallInstruction call, Instruction init |
|
||||
isArgumentForParameter(call, operand, init) and
|
||||
resultReturned(init, bitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at offset `bitOffset` from the value of instruction `base`. The offset
|
||||
* may be `unknown()`.
|
||||
*/
|
||||
private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, IntValue bitOffset) {
|
||||
base = addrOperand.getDef() and bitOffset = 0 // Base case
|
||||
or
|
||||
exists(
|
||||
Instruction middle, int previousBitOffset, Operand middleOperand, IntValue additionalBitOffset
|
||||
|
|
||||
// We already have an offset from `middle`.
|
||||
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
|
||||
// `middle` is propagated from `base`.
|
||||
middleOperand = middle.getAnOperand() and
|
||||
operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
|
||||
base = middleOperand.getDef() and
|
||||
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at constant offset `bitOffset` from the value of instruction `base`.
|
||||
* Only holds for the `base` with the longest chain of propagation to `addrOperand`.
|
||||
*/
|
||||
predicate addressOperandBaseAndConstantOffset(
|
||||
AddressOperand addrOperand, Instruction base, int bitOffset
|
||||
) {
|
||||
hasBaseAndOffset(addrOperand, base, bitOffset) and
|
||||
Ints::hasValue(bitOffset) and
|
||||
not exists(Instruction previousBase, int previousBitOffset |
|
||||
hasBaseAndOffset(addrOperand, previousBase, previousBitOffset) and
|
||||
previousBase = base.getAnOperand().getDef() and
|
||||
Ints::hasValue(previousBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the allocation into which `addrOperand` points, if known.
|
||||
*/
|
||||
Configuration::Allocation getAddressOperandAllocation(AddressOperand addrOperand) {
|
||||
addressOperandAllocationAndOffset(addrOperand, result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `addrOperand` is at offset `bitOffset` from a base instruction of `allocation`. The
|
||||
* offset may be `unknown()`.
|
||||
*/
|
||||
predicate addressOperandAllocationAndOffset(
|
||||
AddressOperand addrOperand, Configuration::Allocation allocation, IntValue bitOffset
|
||||
) {
|
||||
exists(Instruction base |
|
||||
allocation.getABaseInstruction() = base and
|
||||
hasBaseAndOffset(addrOperand, base, bitOffset) and
|
||||
not exists(Instruction previousBase |
|
||||
hasBaseAndOffset(addrOperand, previousBase, _) and
|
||||
previousBase = base.getAnOperand().getDef()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
import semmle.code.cpp.models.interfaces.Alias as AliasModels
|
||||
@@ -1 +1,3 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as InputIR
|
||||
import AliasConfiguration as Configuration
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
private import AliasConfigurationImports
|
||||
|
||||
/**
|
||||
* A memory allocation that can be tracked by the SimpleSSA alias analysis.
|
||||
* All automatic variables are tracked.
|
||||
*/
|
||||
class Allocation extends IRAutomaticVariable {
|
||||
VariableAddressInstruction getABaseInstruction() { result.getIRVariable() = this }
|
||||
|
||||
final string getAllocationString() { result = toString() }
|
||||
|
||||
predicate alwaysEscapes() {
|
||||
// An automatic variable only escapes if its address is taken and escapes.
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
@@ -1,66 +1,57 @@
|
||||
import AliasAnalysis
|
||||
private import SimpleSSAImports
|
||||
import SimpleSSAPublicImports
|
||||
private import AliasConfiguration
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset
|
||||
) {
|
||||
resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and
|
||||
type = instr.getResultLanguageType()
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset
|
||||
) {
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and
|
||||
type = operand.getLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be modeled in SSA form. For unaliased SSA, we only model a variable if its
|
||||
* address never escapes and all reads and writes of that variable access the entire variable using the original type
|
||||
* of the variable.
|
||||
*/
|
||||
private predicate isVariableModeled(IRVariable var) {
|
||||
not variableAddressEscapes(var) and
|
||||
// There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for
|
||||
// `type = var.getType()` is sufficient.
|
||||
forall(Instruction instr, Language::LanguageType type, IntValue bitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, bitOffset) and
|
||||
not instr.hasResultMayMemoryAccess()
|
||||
|
|
||||
private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRType type) {
|
||||
exists(Instruction constantBase, int bitOffset |
|
||||
addressOperandBaseAndConstantOffset(addrOperand, constantBase, bitOffset) and
|
||||
bitOffset = 0 and
|
||||
type.getIRType() = var.getIRType() and
|
||||
not instr.hasResultMayMemoryAccess()
|
||||
) and
|
||||
forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, bitOffset)
|
||||
|
|
||||
bitOffset = 0 and
|
||||
type.getIRType() = var.getIRType() and
|
||||
not operand.hasMayReadMemoryAccess()
|
||||
constantBase = var.getABaseInstruction() and
|
||||
type = var.getIRType()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation = MkMemoryLocation(IRVariable var) { isVariableModeled(var) }
|
||||
/**
|
||||
* Holds if the specified variable should be modeled in SSA form. For unaliased SSA, we only model a
|
||||
* variable if its address never escapes and all reads and writes of that variable access the entire
|
||||
* variable using the original type of the variable.
|
||||
*/
|
||||
private predicate isVariableModeled(Allocation var) {
|
||||
not allocationEscapes(var) and
|
||||
forall(Instruction instr, AddressOperand addrOperand, IRType type |
|
||||
addrOperand = instr.getResultAddressOperand() and
|
||||
type = instr.getResultIRType() and
|
||||
var = getAddressOperandAllocation(addrOperand)
|
||||
|
|
||||
isTotalAccess(var, addrOperand, type) and not instr.hasResultMayMemoryAccess()
|
||||
) and
|
||||
forall(MemoryOperand memOperand, AddressOperand addrOperand, IRType type |
|
||||
addrOperand = memOperand.getAddressOperand() and
|
||||
type = memOperand.getIRType() and
|
||||
var = getAddressOperandAllocation(addrOperand)
|
||||
|
|
||||
isTotalAccess(var, addrOperand, type) and not memOperand.hasMayReadMemoryAccess()
|
||||
)
|
||||
}
|
||||
|
||||
private MemoryLocation getMemoryLocation(IRVariable var) { result.getIRVariable() = var }
|
||||
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
|
||||
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
|
||||
|
||||
class MemoryLocation extends TMemoryLocation {
|
||||
IRVariable var;
|
||||
Allocation var;
|
||||
|
||||
MemoryLocation() { this = MkMemoryLocation(var) }
|
||||
|
||||
final string toString() { result = var.toString() }
|
||||
final string toString() { result = var.getAllocationString() }
|
||||
|
||||
final Allocation getAllocation() { result = var }
|
||||
|
||||
final Language::Location getLocation() { result = var.getLocation() }
|
||||
|
||||
final IRFunction getIRFunction() { result = var.getEnclosingIRFunction() }
|
||||
|
||||
final IRVariable getIRVariable() { result = var }
|
||||
|
||||
final VirtualVariable getVirtualVariable() { result = this }
|
||||
|
||||
final Language::LanguageType getType() { result = var.getLanguageType() }
|
||||
@@ -77,15 +68,9 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
}
|
||||
|
||||
MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
exists(IRVariable var |
|
||||
hasResultMemoryAccess(instr, var, _, _) and
|
||||
result = getMemoryLocation(var)
|
||||
)
|
||||
result = getMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()))
|
||||
}
|
||||
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
exists(IRVariable var |
|
||||
hasOperandMemoryAccess(operand, var, _, _) and
|
||||
result = getMemoryLocation(var)
|
||||
)
|
||||
result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand()))
|
||||
}
|
||||
|
||||
@@ -80,3 +80,17 @@ predicate hasPotentialLoop(Function f) {
|
||||
}
|
||||
|
||||
predicate hasGoto(Function f) { exists(Cpp::GotoStmt s | s.getEnclosingFunction() = f) }
|
||||
|
||||
/**
|
||||
* Gets the offset of field `field` in bits.
|
||||
*/
|
||||
int getFieldBitOffset(Field field) {
|
||||
if field instanceof Cpp::BitField
|
||||
then result = (field.getByteOffset() * 8) + field.(Cpp::BitField).getBitOffset()
|
||||
else result = field.getByteOffset() * 8
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the specified `Function` can be overridden in a derived class.
|
||||
*/
|
||||
predicate isFunctionVirtual(Function f) { f.isVirtual() }
|
||||
|
||||
@@ -1,608 +1 @@
|
||||
/**
|
||||
* Provides an implementation of Global Value Numbering.
|
||||
* See https://en.wikipedia.org/wiki/Global_value_numbering
|
||||
*
|
||||
* The predicate `globalValueNumber` converts an expression into a `GVN`,
|
||||
* which is an abstract type representing the value of the expression. If
|
||||
* two expressions have the same `GVN` then they compute the same value.
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* void f(int x, int y) {
|
||||
* g(x+y, x+y);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, both arguments in the call to `g` compute the same value,
|
||||
* so both arguments have the same `GVN`. In other words, we can find
|
||||
* this call with the following query:
|
||||
*
|
||||
* ```
|
||||
* from FunctionCall call, GVN v
|
||||
* where v = globalValueNumber(call.getArgument(0))
|
||||
* and v = globalValueNumber(call.getArgument(1))
|
||||
* select call
|
||||
* ```
|
||||
*
|
||||
* The analysis is conservative, so two expressions might have different
|
||||
* `GVN`s even though the actually always compute the same value. The most
|
||||
* common reason for this is that the analysis cannot prove that there
|
||||
* are no side-effects that might cause the computed value to change.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note to developers: the correctness of this module depends on the
|
||||
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
|
||||
* sync with each other. If you change this module then make sure that the
|
||||
* change is symmetric across all three.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
/**
|
||||
* Holds if the result is a control flow node that might change the
|
||||
* value of any global variable. This is used in the implementation
|
||||
* of `GVN_OtherVariable`, because we need to be quite conservative when
|
||||
* we assign a value number to a global variable. For example:
|
||||
*
|
||||
* ```
|
||||
* x = g+1;
|
||||
* dosomething();
|
||||
* y = g+1;
|
||||
* ```
|
||||
*
|
||||
* It is not safe to assign the same value number to both instances
|
||||
* of `g+1` in this example, because the call to `dosomething` might
|
||||
* change the value of `g`.
|
||||
*/
|
||||
private ControlFlowNode nodeWithPossibleSideEffect() {
|
||||
result instanceof Call
|
||||
or
|
||||
// If the lhs of an assignment is not analyzable by SSA, then
|
||||
// we need to treat the assignment as having a possible side-effect.
|
||||
result instanceof Assignment and not result instanceof SsaDefinition
|
||||
or
|
||||
result instanceof CrementOperation and not result instanceof SsaDefinition
|
||||
or
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
|
||||
)
|
||||
or
|
||||
result instanceof AsmStmt
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry node of the control flow graph of which `node` is a
|
||||
* member.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
|
||||
result = node.getControlFlowScope().getEntryPoint() and
|
||||
result.getASuccessor*() = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control flow edge from `src` to `dst` or
|
||||
* if `dst` is an expression with a possible side-effect. The idea
|
||||
* is to treat side effects as entry points in the control flow
|
||||
* graph so that we can use the dominator tree to find the most recent
|
||||
* side-effect.
|
||||
*/
|
||||
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
|
||||
src.getASuccessor() = dst
|
||||
or
|
||||
// Add an edge from the entry point to any node that might have a side
|
||||
// effect.
|
||||
dst = nodeWithPossibleSideEffect() and
|
||||
src = getControlFlowEntry(dst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is the immediate dominator of `node` in
|
||||
* the side-effect CFG.
|
||||
*/
|
||||
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
|
||||
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
|
||||
|
||||
/**
|
||||
* Gets the most recent side effect. To be more precise, `result` is a
|
||||
* dominator of `node` and no side-effects can occur between `result` and
|
||||
* `node`.
|
||||
*
|
||||
* `sideEffectCFG` has an edge from the function entry to every node with a
|
||||
* side-effect. This means that every node with a side-effect has the
|
||||
* function entry as its immediate dominator. So if node `x` dominates node
|
||||
* `y` then there can be no side effects between `x` and `y` unless `x` is
|
||||
* the function entry. So the optimal choice for `result` has the function
|
||||
* entry as its immediate dominator.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* 000: int f(int a, int b, int *p) {
|
||||
* 001: int r = 0;
|
||||
* 002: if (a) {
|
||||
* 003: if (b) {
|
||||
* 004: sideEffect1();
|
||||
* 005: }
|
||||
* 006: } else {
|
||||
* 007: sideEffect2();
|
||||
* 008: }
|
||||
* 009: if (a) {
|
||||
* 010: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 011: }
|
||||
* 012: if (b) {
|
||||
* 013: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 014: }
|
||||
* 015: return *p;
|
||||
* 016: }
|
||||
* ```
|
||||
*
|
||||
* Suppose we want to find the most recent side-effect for the dereference
|
||||
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
|
||||
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
|
||||
* the immediate dominator tree looks like this:
|
||||
*
|
||||
* 000 - 001 - 002 - 003
|
||||
* - 004
|
||||
* - 007
|
||||
* - 009 - 010
|
||||
* - 012 - 013
|
||||
* - 015
|
||||
*
|
||||
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
|
||||
* Therefore, the most recent side effect for line 015 is line 009.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
|
||||
exists(ControlFlowNode entry |
|
||||
functionEntry(entry) and
|
||||
iDomEffect(entry, result) and
|
||||
iDomEffect*(result, node)
|
||||
)
|
||||
}
|
||||
|
||||
/** Used to represent the "global value number" of an expression. */
|
||||
cached
|
||||
private newtype GVNBase =
|
||||
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
|
||||
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
|
||||
// If the local variable does not have a defining value, then
|
||||
// we use the SsaDefinition as its global value number.
|
||||
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
|
||||
mk_UndefinedStackVariable(x, def, _)
|
||||
} or
|
||||
// Variables with no SSA information. As a crude (but safe)
|
||||
// approximation, we use `mostRecentSideEffect` to compute a definition
|
||||
// location for the variable. This ensures that two instances of the same
|
||||
// global variable will only get the same value number if they are
|
||||
// guaranteed to have the same value.
|
||||
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
|
||||
GVN_FieldAccess(GVN s, Field f) {
|
||||
mk_DotFieldAccess(s, f, _) or
|
||||
mk_PointerFieldAccess_with_deref(s, f, _) or
|
||||
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
|
||||
} or
|
||||
// Dereference a pointer. The value might have changed since the last
|
||||
// time the pointer was dereferenced, so we need to include a definition
|
||||
// location. As a crude (but safe) approximation, we use
|
||||
// `mostRecentSideEffect` to compute a definition location.
|
||||
GVN_Deref(GVN p, ControlFlowNode dominator) {
|
||||
mk_Deref(p, dominator, _) or
|
||||
mk_PointerFieldAccess(p, _, dominator, _) or
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
|
||||
} or
|
||||
GVN_ThisExpr(Function fcn) {
|
||||
mk_ThisExpr(fcn, _) or
|
||||
mk_ImplicitThisFieldAccess(fcn, _, _, _)
|
||||
} or
|
||||
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
|
||||
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
|
||||
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
|
||||
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
|
||||
// Any expression that is not handled by the cases above is
|
||||
// given a unique number based on the expression itself.
|
||||
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
|
||||
|
||||
/**
|
||||
* A Global Value Number. A GVN is an abstract representation of the value
|
||||
* computed by an expression. The relationship between `Expr` and `GVN` is
|
||||
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
|
||||
* expressions can have the same `GVN`. If two expressions have the same
|
||||
* `GVN`, it means that they compute the same value at run time. The `GVN`
|
||||
* is an opaque value, so you cannot deduce what the run-time value of an
|
||||
* expression will be from its `GVN`. The only use for the `GVN` of an
|
||||
* expression is to find other expressions that compute the same value.
|
||||
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
|
||||
*
|
||||
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
|
||||
* displayed in a results list. These work by picking an arbitrary
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
class GVN extends GVNBase {
|
||||
GVN() { this instanceof GVNBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
Expr getAnExpr() { this = globalValueNumber(result) }
|
||||
|
||||
/** Gets the kind of the GVN. This can be useful for debugging. */
|
||||
string getKind() {
|
||||
if this instanceof GVN_IntConst
|
||||
then result = "IntConst"
|
||||
else
|
||||
if this instanceof GVN_FloatConst
|
||||
then result = "FloatConst"
|
||||
else
|
||||
if this instanceof GVN_UndefinedStackVariable
|
||||
then result = "UndefinedStackVariable"
|
||||
else
|
||||
if this instanceof GVN_OtherVariable
|
||||
then result = "OtherVariable"
|
||||
else
|
||||
if this instanceof GVN_FieldAccess
|
||||
then result = "FieldAccess"
|
||||
else
|
||||
if this instanceof GVN_Deref
|
||||
then result = "Deref"
|
||||
else
|
||||
if this instanceof GVN_ThisExpr
|
||||
then result = "ThisExpr"
|
||||
else
|
||||
if this instanceof GVN_Conversion
|
||||
then result = "Conversion"
|
||||
else
|
||||
if this instanceof GVN_BinaryOp
|
||||
then result = "BinaryOp"
|
||||
else
|
||||
if this instanceof GVN_UnaryOp
|
||||
then result = "UnaryOp"
|
||||
else
|
||||
if this instanceof GVN_ArrayAccess
|
||||
then result = "ArrayAccess"
|
||||
else
|
||||
if this instanceof GVN_Unanalyzable
|
||||
then result = "Unanalyzable"
|
||||
else result = "error"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an example of an expression with this GVN.
|
||||
* This is useful for things like implementing toString().
|
||||
*/
|
||||
private Expr exampleExpr() {
|
||||
// Pick the expression with the minimum source location string. This is
|
||||
// just an arbitrary way to pick an expression with this `GVN`.
|
||||
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = exampleExpr().toString() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { result = exampleExpr().getLocation() }
|
||||
}
|
||||
|
||||
private predicate analyzableIntConst(Expr e) {
|
||||
strictcount(e.getValue().toInt()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1
|
||||
}
|
||||
|
||||
private predicate mk_IntConst(int val, Type t, Expr e) {
|
||||
analyzableIntConst(e) and
|
||||
val = e.getValue().toInt() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableFloatConst(Expr e) {
|
||||
strictcount(e.getValue().toFloat()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1 and
|
||||
not analyzableIntConst(e)
|
||||
}
|
||||
|
||||
private predicate mk_FloatConst(float val, Type t, Expr e) {
|
||||
analyzableFloatConst(e) and
|
||||
val = e.getValue().toFloat() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableStackVariable(VariableAccess access) {
|
||||
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
|
||||
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
|
||||
count(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = access
|
||||
|
|
||||
def.getDefiningValue(v).getFullyConverted()
|
||||
) <= 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
// Note: this predicate only has a result if the access has no
|
||||
// defining value. If there is a defining value, then there is no
|
||||
// need to generate a fresh `GVN` for the access because `globalValueNumber`
|
||||
// will follow the chain and use the GVN of the defining value.
|
||||
private predicate mk_UndefinedStackVariable(
|
||||
StackVariable x, SsaDefinition def, VariableAccess access
|
||||
) {
|
||||
analyzableStackVariable(access) and
|
||||
access = def.getAUse(x) and
|
||||
not exists(def.getDefiningValue(x))
|
||||
}
|
||||
|
||||
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
|
||||
analyzableDotFieldAccess(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_PointerFieldAccess(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
|
||||
) {
|
||||
analyzablePointerFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
|
||||
* extra `GVN_Deref` around the qualifier.
|
||||
*/
|
||||
private predicate mk_PointerFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, PointerFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_PointerFieldAccess(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess(
|
||||
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
analyzableImplicitThisFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
fcn = access.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(Function fcn |
|
||||
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
|
||||
qualifier = GVN_ThisExpr(fcn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `access` is an access of a variable that does
|
||||
* not have SSA information. (For example, because the variable
|
||||
* is global.)
|
||||
*/
|
||||
private predicate analyzableOtherVariable(VariableAccess access) {
|
||||
not access instanceof FieldAccess and
|
||||
not exists(SsaDefinition def | access = def.getAUse(_)) and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
|
||||
analyzableOtherVariable(access) and
|
||||
x = access.getTarget() and
|
||||
dominator = mostRecentSideEffect(access)
|
||||
}
|
||||
|
||||
private predicate analyzableConversion(Conversion conv) {
|
||||
strictcount(conv.getUnspecifiedType()) = 1 and
|
||||
strictcount(conv.getExpr()) = 1 and
|
||||
not analyzableConst(conv)
|
||||
}
|
||||
|
||||
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
|
||||
analyzableConversion(conv) and
|
||||
t = conv.getUnspecifiedType() and
|
||||
child = globalValueNumber(conv.getExpr())
|
||||
}
|
||||
|
||||
private predicate analyzableBinaryOp(BinaryOperation op) {
|
||||
op.isPure() and
|
||||
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
|
||||
analyzableBinaryOp(op) and
|
||||
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
|
||||
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableUnaryOp(UnaryOperation op) {
|
||||
not op instanceof PointerDereferenceExpr and
|
||||
op.isPure() and
|
||||
strictcount(op.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
|
||||
analyzableUnaryOp(op) and
|
||||
child = globalValueNumber(op.getOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableThisExpr(ThisExpr thisExpr) {
|
||||
strictcount(thisExpr.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(thisExpr)
|
||||
}
|
||||
|
||||
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
|
||||
analyzableThisExpr(thisExpr) and
|
||||
fcn = thisExpr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate analyzableArrayAccess(ArrayExpr ae) {
|
||||
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
|
||||
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(ae)) = 1 and
|
||||
not analyzableConst(ae)
|
||||
}
|
||||
|
||||
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
|
||||
analyzableArrayAccess(ae) and
|
||||
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
|
||||
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(ae)
|
||||
}
|
||||
|
||||
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
|
||||
strictcount(deref.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(deref)) = 1 and
|
||||
not analyzableConst(deref)
|
||||
}
|
||||
|
||||
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
|
||||
analyzablePointerDereferenceExpr(deref) and
|
||||
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(deref)
|
||||
}
|
||||
|
||||
/** Gets the global value number of expression `e`. */
|
||||
cached
|
||||
GVN globalValueNumber(Expr e) {
|
||||
exists(int val, Type t |
|
||||
mk_IntConst(val, t, e) and
|
||||
result = GVN_IntConst(val, t)
|
||||
)
|
||||
or
|
||||
exists(float val, Type t |
|
||||
mk_FloatConst(val, t, e) and
|
||||
result = GVN_FloatConst(val, t)
|
||||
)
|
||||
or
|
||||
// Local variable with a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
analyzableStackVariable(e) and
|
||||
e = def.getAUse(x) and
|
||||
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
|
||||
)
|
||||
or
|
||||
// Local variable without a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
mk_UndefinedStackVariable(x, def, e) and
|
||||
result = GVN_UndefinedStackVariable(x, def)
|
||||
)
|
||||
or
|
||||
// Variable with no SSA information.
|
||||
exists(Variable x, ControlFlowNode dominator |
|
||||
mk_OtherVariable(x, dominator, e) and
|
||||
result = GVN_OtherVariable(x, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_DotFieldAccess(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(Function fcn |
|
||||
mk_ThisExpr(fcn, e) and
|
||||
result = GVN_ThisExpr(fcn)
|
||||
)
|
||||
or
|
||||
exists(Type t, GVN child |
|
||||
mk_Conversion(t, child, e) and
|
||||
result = GVN_Conversion(t, child)
|
||||
)
|
||||
or
|
||||
exists(GVN lhs, GVN rhs, string opname |
|
||||
mk_BinaryOp(lhs, rhs, opname, e) and
|
||||
result = GVN_BinaryOp(lhs, rhs, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN child, string opname |
|
||||
mk_UnaryOp(child, opname, e) and
|
||||
result = GVN_UnaryOp(child, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN x, GVN i, ControlFlowNode dominator |
|
||||
mk_ArrayAccess(x, i, dominator, e) and
|
||||
result = GVN_ArrayAccess(x, i, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN p, ControlFlowNode dominator |
|
||||
mk_Deref(p, dominator, e) and
|
||||
result = GVN_Deref(p, dominator)
|
||||
)
|
||||
or
|
||||
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
|
||||
}
|
||||
|
||||
private predicate analyzableConst(Expr e) {
|
||||
analyzableIntConst(e) or
|
||||
analyzableFloatConst(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression is explicitly handled by `globalValueNumber`.
|
||||
* Unanalyzable expressions still need to be given a global value number,
|
||||
* but it will be a unique number that is not shared with any other
|
||||
* expression.
|
||||
*/
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
analyzableConst(e) or
|
||||
analyzableStackVariable(e) or
|
||||
analyzableDotFieldAccess(e) or
|
||||
analyzablePointerFieldAccess(e) or
|
||||
analyzableImplicitThisFieldAccess(e) or
|
||||
analyzableOtherVariable(e) or
|
||||
analyzableConversion(e) or
|
||||
analyzableBinaryOp(e) or
|
||||
analyzableUnaryOp(e) or
|
||||
analyzableThisExpr(e) or
|
||||
analyzableArrayAccess(e) or
|
||||
analyzablePointerDereferenceExpr(e)
|
||||
}
|
||||
import GlobalValueNumberingImpl
|
||||
|
||||
@@ -0,0 +1,608 @@
|
||||
/**
|
||||
* Provides an implementation of Global Value Numbering.
|
||||
* See https://en.wikipedia.org/wiki/Global_value_numbering
|
||||
*
|
||||
* The predicate `globalValueNumber` converts an expression into a `GVN`,
|
||||
* which is an abstract type representing the value of the expression. If
|
||||
* two expressions have the same `GVN` then they compute the same value.
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* void f(int x, int y) {
|
||||
* g(x+y, x+y);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, both arguments in the call to `g` compute the same value,
|
||||
* so both arguments have the same `GVN`. In other words, we can find
|
||||
* this call with the following query:
|
||||
*
|
||||
* ```
|
||||
* from FunctionCall call, GVN v
|
||||
* where v = globalValueNumber(call.getArgument(0))
|
||||
* and v = globalValueNumber(call.getArgument(1))
|
||||
* select call
|
||||
* ```
|
||||
*
|
||||
* The analysis is conservative, so two expressions might have different
|
||||
* `GVN`s even though the actually always compute the same value. The most
|
||||
* common reason for this is that the analysis cannot prove that there
|
||||
* are no side-effects that might cause the computed value to change.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note to developers: the correctness of this module depends on the
|
||||
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
|
||||
* sync with each other. If you change this module then make sure that the
|
||||
* change is symmetric across all three.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
/**
|
||||
* Holds if the result is a control flow node that might change the
|
||||
* value of any global variable. This is used in the implementation
|
||||
* of `GVN_OtherVariable`, because we need to be quite conservative when
|
||||
* we assign a value number to a global variable. For example:
|
||||
*
|
||||
* ```
|
||||
* x = g+1;
|
||||
* dosomething();
|
||||
* y = g+1;
|
||||
* ```
|
||||
*
|
||||
* It is not safe to assign the same value number to both instances
|
||||
* of `g+1` in this example, because the call to `dosomething` might
|
||||
* change the value of `g`.
|
||||
*/
|
||||
private ControlFlowNode nodeWithPossibleSideEffect() {
|
||||
result instanceof Call
|
||||
or
|
||||
// If the lhs of an assignment is not analyzable by SSA, then
|
||||
// we need to treat the assignment as having a possible side-effect.
|
||||
result instanceof Assignment and not result instanceof SsaDefinition
|
||||
or
|
||||
result instanceof CrementOperation and not result instanceof SsaDefinition
|
||||
or
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
|
||||
)
|
||||
or
|
||||
result instanceof AsmStmt
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry node of the control flow graph of which `node` is a
|
||||
* member.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
|
||||
result = node.getControlFlowScope().getEntryPoint() and
|
||||
result.getASuccessor*() = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control flow edge from `src` to `dst` or
|
||||
* if `dst` is an expression with a possible side-effect. The idea
|
||||
* is to treat side effects as entry points in the control flow
|
||||
* graph so that we can use the dominator tree to find the most recent
|
||||
* side-effect.
|
||||
*/
|
||||
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
|
||||
src.getASuccessor() = dst
|
||||
or
|
||||
// Add an edge from the entry point to any node that might have a side
|
||||
// effect.
|
||||
dst = nodeWithPossibleSideEffect() and
|
||||
src = getControlFlowEntry(dst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is the immediate dominator of `node` in
|
||||
* the side-effect CFG.
|
||||
*/
|
||||
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
|
||||
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
|
||||
|
||||
/**
|
||||
* Gets the most recent side effect. To be more precise, `result` is a
|
||||
* dominator of `node` and no side-effects can occur between `result` and
|
||||
* `node`.
|
||||
*
|
||||
* `sideEffectCFG` has an edge from the function entry to every node with a
|
||||
* side-effect. This means that every node with a side-effect has the
|
||||
* function entry as its immediate dominator. So if node `x` dominates node
|
||||
* `y` then there can be no side effects between `x` and `y` unless `x` is
|
||||
* the function entry. So the optimal choice for `result` has the function
|
||||
* entry as its immediate dominator.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* 000: int f(int a, int b, int *p) {
|
||||
* 001: int r = 0;
|
||||
* 002: if (a) {
|
||||
* 003: if (b) {
|
||||
* 004: sideEffect1();
|
||||
* 005: }
|
||||
* 006: } else {
|
||||
* 007: sideEffect2();
|
||||
* 008: }
|
||||
* 009: if (a) {
|
||||
* 010: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 011: }
|
||||
* 012: if (b) {
|
||||
* 013: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 014: }
|
||||
* 015: return *p;
|
||||
* 016: }
|
||||
* ```
|
||||
*
|
||||
* Suppose we want to find the most recent side-effect for the dereference
|
||||
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
|
||||
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
|
||||
* the immediate dominator tree looks like this:
|
||||
*
|
||||
* 000 - 001 - 002 - 003
|
||||
* - 004
|
||||
* - 007
|
||||
* - 009 - 010
|
||||
* - 012 - 013
|
||||
* - 015
|
||||
*
|
||||
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
|
||||
* Therefore, the most recent side effect for line 015 is line 009.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
|
||||
exists(ControlFlowNode entry |
|
||||
functionEntry(entry) and
|
||||
iDomEffect(entry, result) and
|
||||
iDomEffect*(result, node)
|
||||
)
|
||||
}
|
||||
|
||||
/** Used to represent the "global value number" of an expression. */
|
||||
cached
|
||||
private newtype GVNBase =
|
||||
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
|
||||
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
|
||||
// If the local variable does not have a defining value, then
|
||||
// we use the SsaDefinition as its global value number.
|
||||
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
|
||||
mk_UndefinedStackVariable(x, def, _)
|
||||
} or
|
||||
// Variables with no SSA information. As a crude (but safe)
|
||||
// approximation, we use `mostRecentSideEffect` to compute a definition
|
||||
// location for the variable. This ensures that two instances of the same
|
||||
// global variable will only get the same value number if they are
|
||||
// guaranteed to have the same value.
|
||||
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
|
||||
GVN_FieldAccess(GVN s, Field f) {
|
||||
mk_DotFieldAccess(s, f, _) or
|
||||
mk_PointerFieldAccess_with_deref(s, f, _) or
|
||||
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
|
||||
} or
|
||||
// Dereference a pointer. The value might have changed since the last
|
||||
// time the pointer was dereferenced, so we need to include a definition
|
||||
// location. As a crude (but safe) approximation, we use
|
||||
// `mostRecentSideEffect` to compute a definition location.
|
||||
GVN_Deref(GVN p, ControlFlowNode dominator) {
|
||||
mk_Deref(p, dominator, _) or
|
||||
mk_PointerFieldAccess(p, _, dominator, _) or
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
|
||||
} or
|
||||
GVN_ThisExpr(Function fcn) {
|
||||
mk_ThisExpr(fcn, _) or
|
||||
mk_ImplicitThisFieldAccess(fcn, _, _, _)
|
||||
} or
|
||||
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
|
||||
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
|
||||
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
|
||||
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
|
||||
// Any expression that is not handled by the cases above is
|
||||
// given a unique number based on the expression itself.
|
||||
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
|
||||
|
||||
/**
|
||||
* A Global Value Number. A GVN is an abstract representation of the value
|
||||
* computed by an expression. The relationship between `Expr` and `GVN` is
|
||||
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
|
||||
* expressions can have the same `GVN`. If two expressions have the same
|
||||
* `GVN`, it means that they compute the same value at run time. The `GVN`
|
||||
* is an opaque value, so you cannot deduce what the run-time value of an
|
||||
* expression will be from its `GVN`. The only use for the `GVN` of an
|
||||
* expression is to find other expressions that compute the same value.
|
||||
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
|
||||
*
|
||||
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
|
||||
* displayed in a results list. These work by picking an arbitrary
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
class GVN extends GVNBase {
|
||||
GVN() { this instanceof GVNBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
Expr getAnExpr() { this = globalValueNumber(result) }
|
||||
|
||||
/** Gets the kind of the GVN. This can be useful for debugging. */
|
||||
string getKind() {
|
||||
if this instanceof GVN_IntConst
|
||||
then result = "IntConst"
|
||||
else
|
||||
if this instanceof GVN_FloatConst
|
||||
then result = "FloatConst"
|
||||
else
|
||||
if this instanceof GVN_UndefinedStackVariable
|
||||
then result = "UndefinedStackVariable"
|
||||
else
|
||||
if this instanceof GVN_OtherVariable
|
||||
then result = "OtherVariable"
|
||||
else
|
||||
if this instanceof GVN_FieldAccess
|
||||
then result = "FieldAccess"
|
||||
else
|
||||
if this instanceof GVN_Deref
|
||||
then result = "Deref"
|
||||
else
|
||||
if this instanceof GVN_ThisExpr
|
||||
then result = "ThisExpr"
|
||||
else
|
||||
if this instanceof GVN_Conversion
|
||||
then result = "Conversion"
|
||||
else
|
||||
if this instanceof GVN_BinaryOp
|
||||
then result = "BinaryOp"
|
||||
else
|
||||
if this instanceof GVN_UnaryOp
|
||||
then result = "UnaryOp"
|
||||
else
|
||||
if this instanceof GVN_ArrayAccess
|
||||
then result = "ArrayAccess"
|
||||
else
|
||||
if this instanceof GVN_Unanalyzable
|
||||
then result = "Unanalyzable"
|
||||
else result = "error"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an example of an expression with this GVN.
|
||||
* This is useful for things like implementing toString().
|
||||
*/
|
||||
private Expr exampleExpr() {
|
||||
// Pick the expression with the minimum source location string. This is
|
||||
// just an arbitrary way to pick an expression with this `GVN`.
|
||||
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = exampleExpr().toString() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { result = exampleExpr().getLocation() }
|
||||
}
|
||||
|
||||
private predicate analyzableIntConst(Expr e) {
|
||||
strictcount(e.getValue().toInt()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1
|
||||
}
|
||||
|
||||
private predicate mk_IntConst(int val, Type t, Expr e) {
|
||||
analyzableIntConst(e) and
|
||||
val = e.getValue().toInt() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableFloatConst(Expr e) {
|
||||
strictcount(e.getValue().toFloat()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1 and
|
||||
not analyzableIntConst(e)
|
||||
}
|
||||
|
||||
private predicate mk_FloatConst(float val, Type t, Expr e) {
|
||||
analyzableFloatConst(e) and
|
||||
val = e.getValue().toFloat() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableStackVariable(VariableAccess access) {
|
||||
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
|
||||
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
|
||||
count(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = access
|
||||
|
|
||||
def.getDefiningValue(v).getFullyConverted()
|
||||
) <= 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
// Note: this predicate only has a result if the access has no
|
||||
// defining value. If there is a defining value, then there is no
|
||||
// need to generate a fresh `GVN` for the access because `globalValueNumber`
|
||||
// will follow the chain and use the GVN of the defining value.
|
||||
private predicate mk_UndefinedStackVariable(
|
||||
StackVariable x, SsaDefinition def, VariableAccess access
|
||||
) {
|
||||
analyzableStackVariable(access) and
|
||||
access = def.getAUse(x) and
|
||||
not exists(def.getDefiningValue(x))
|
||||
}
|
||||
|
||||
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
|
||||
analyzableDotFieldAccess(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_PointerFieldAccess(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
|
||||
) {
|
||||
analyzablePointerFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
|
||||
* extra `GVN_Deref` around the qualifier.
|
||||
*/
|
||||
private predicate mk_PointerFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, PointerFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_PointerFieldAccess(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess(
|
||||
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
analyzableImplicitThisFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
fcn = access.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(Function fcn |
|
||||
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
|
||||
qualifier = GVN_ThisExpr(fcn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `access` is an access of a variable that does
|
||||
* not have SSA information. (For example, because the variable
|
||||
* is global.)
|
||||
*/
|
||||
private predicate analyzableOtherVariable(VariableAccess access) {
|
||||
not access instanceof FieldAccess and
|
||||
not exists(SsaDefinition def | access = def.getAUse(_)) and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
|
||||
analyzableOtherVariable(access) and
|
||||
x = access.getTarget() and
|
||||
dominator = mostRecentSideEffect(access)
|
||||
}
|
||||
|
||||
private predicate analyzableConversion(Conversion conv) {
|
||||
strictcount(conv.getUnspecifiedType()) = 1 and
|
||||
strictcount(conv.getExpr()) = 1 and
|
||||
not analyzableConst(conv)
|
||||
}
|
||||
|
||||
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
|
||||
analyzableConversion(conv) and
|
||||
t = conv.getUnspecifiedType() and
|
||||
child = globalValueNumber(conv.getExpr())
|
||||
}
|
||||
|
||||
private predicate analyzableBinaryOp(BinaryOperation op) {
|
||||
op.isPure() and
|
||||
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
|
||||
analyzableBinaryOp(op) and
|
||||
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
|
||||
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableUnaryOp(UnaryOperation op) {
|
||||
not op instanceof PointerDereferenceExpr and
|
||||
op.isPure() and
|
||||
strictcount(op.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
|
||||
analyzableUnaryOp(op) and
|
||||
child = globalValueNumber(op.getOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableThisExpr(ThisExpr thisExpr) {
|
||||
strictcount(thisExpr.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(thisExpr)
|
||||
}
|
||||
|
||||
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
|
||||
analyzableThisExpr(thisExpr) and
|
||||
fcn = thisExpr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate analyzableArrayAccess(ArrayExpr ae) {
|
||||
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
|
||||
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(ae)) = 1 and
|
||||
not analyzableConst(ae)
|
||||
}
|
||||
|
||||
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
|
||||
analyzableArrayAccess(ae) and
|
||||
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
|
||||
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(ae)
|
||||
}
|
||||
|
||||
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
|
||||
strictcount(deref.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(deref)) = 1 and
|
||||
not analyzableConst(deref)
|
||||
}
|
||||
|
||||
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
|
||||
analyzablePointerDereferenceExpr(deref) and
|
||||
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(deref)
|
||||
}
|
||||
|
||||
/** Gets the global value number of expression `e`. */
|
||||
cached
|
||||
GVN globalValueNumber(Expr e) {
|
||||
exists(int val, Type t |
|
||||
mk_IntConst(val, t, e) and
|
||||
result = GVN_IntConst(val, t)
|
||||
)
|
||||
or
|
||||
exists(float val, Type t |
|
||||
mk_FloatConst(val, t, e) and
|
||||
result = GVN_FloatConst(val, t)
|
||||
)
|
||||
or
|
||||
// Local variable with a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
analyzableStackVariable(e) and
|
||||
e = def.getAUse(x) and
|
||||
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
|
||||
)
|
||||
or
|
||||
// Local variable without a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
mk_UndefinedStackVariable(x, def, e) and
|
||||
result = GVN_UndefinedStackVariable(x, def)
|
||||
)
|
||||
or
|
||||
// Variable with no SSA information.
|
||||
exists(Variable x, ControlFlowNode dominator |
|
||||
mk_OtherVariable(x, dominator, e) and
|
||||
result = GVN_OtherVariable(x, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_DotFieldAccess(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(Function fcn |
|
||||
mk_ThisExpr(fcn, e) and
|
||||
result = GVN_ThisExpr(fcn)
|
||||
)
|
||||
or
|
||||
exists(Type t, GVN child |
|
||||
mk_Conversion(t, child, e) and
|
||||
result = GVN_Conversion(t, child)
|
||||
)
|
||||
or
|
||||
exists(GVN lhs, GVN rhs, string opname |
|
||||
mk_BinaryOp(lhs, rhs, opname, e) and
|
||||
result = GVN_BinaryOp(lhs, rhs, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN child, string opname |
|
||||
mk_UnaryOp(child, opname, e) and
|
||||
result = GVN_UnaryOp(child, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN x, GVN i, ControlFlowNode dominator |
|
||||
mk_ArrayAccess(x, i, dominator, e) and
|
||||
result = GVN_ArrayAccess(x, i, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN p, ControlFlowNode dominator |
|
||||
mk_Deref(p, dominator, e) and
|
||||
result = GVN_Deref(p, dominator)
|
||||
)
|
||||
or
|
||||
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
|
||||
}
|
||||
|
||||
private predicate analyzableConst(Expr e) {
|
||||
analyzableIntConst(e) or
|
||||
analyzableFloatConst(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression is explicitly handled by `globalValueNumber`.
|
||||
* Unanalyzable expressions still need to be given a global value number,
|
||||
* but it will be a unique number that is not shared with any other
|
||||
* expression.
|
||||
*/
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
analyzableConst(e) or
|
||||
analyzableStackVariable(e) or
|
||||
analyzableDotFieldAccess(e) or
|
||||
analyzablePointerFieldAccess(e) or
|
||||
analyzableImplicitThisFieldAccess(e) or
|
||||
analyzableOtherVariable(e) or
|
||||
analyzableConversion(e) or
|
||||
analyzableBinaryOp(e) or
|
||||
analyzableUnaryOp(e) or
|
||||
analyzableThisExpr(e) or
|
||||
analyzableArrayAccess(e) or
|
||||
analyzablePointerDereferenceExpr(e)
|
||||
}
|
||||
285
cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
Normal file
285
cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
Normal file
@@ -0,0 +1,285 @@
|
||||
/**
|
||||
* Provides a library for writing QL tests whose success or failure is based on expected results
|
||||
* embedded in the test source code as comments, rather than a `.expected` file.
|
||||
*
|
||||
* To create a new inline expectations test:
|
||||
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
|
||||
* new class, bind `this` to a unique string (usually the name of the test).
|
||||
* - Override the `hasActualResult()` predicate to produce the actual results of the query. For each
|
||||
* result, specify a `Location`, a text description of the element for which the result was
|
||||
* reported, a short string to serve as the tag to identify expected results for this test, and the
|
||||
* expected value of the result.
|
||||
* - Override `getARelevantTag()` to return the set of tags that can be produced by
|
||||
* `hasActualResult()`. Often this is just a single tag.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* class ConstantValueTest extends InlineExpectationsTest {
|
||||
* ConstantValueTest() { this = "ConstantValueTest" }
|
||||
*
|
||||
* override string getARelevantTag() {
|
||||
* // We only use one tag for this test.
|
||||
* result = "const"
|
||||
* }
|
||||
*
|
||||
* override predicate hasActualResult(
|
||||
* Location location, string element, string tag, string valuesasas
|
||||
* ) {
|
||||
* exists(Expr e |
|
||||
* tag = "const" and // The tag for this test.
|
||||
* valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions.
|
||||
* location = e.getLocation() and // The location of the result to be reported.
|
||||
* element = e.toString() // The display text for the result.
|
||||
* )
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* There is no need to write a `select` clause or query predicate. All of the differences between
|
||||
* expected results and actual results will be reported in the `failures()` query predicate.
|
||||
*
|
||||
* To annotate the test source code with an expected result, place a C++-style (`//`) comment on the
|
||||
* same line as the expected result, with text of the following format as the body of the comment:
|
||||
*
|
||||
* `// $tag=expected-value`
|
||||
*
|
||||
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
|
||||
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
|
||||
* omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may
|
||||
* be placed in the same comment, as long as each is prefixed by a `$`. Any actual result that
|
||||
* appears on a line that does not contain a matching expected result comment will be reported with
|
||||
* a message of the form "Unexpected result: tag=value". Any expected result comment for which there
|
||||
* is no matching actual result will be reported with a message of the form
|
||||
* "Missing result: tag=expected-value".
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* int i = x + 5; // $const=5
|
||||
* int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant.
|
||||
* ```
|
||||
*
|
||||
* For tests that contain known false positives and false negatives, it is possible to further
|
||||
* annotate that a particular expected result is known to be a false positive, or that a particular
|
||||
* missing result is known to be a false negative:
|
||||
*
|
||||
* `// $f+:tag=expected-value` // False positive
|
||||
* `// $f-:tag=expected-value` // False negative
|
||||
*
|
||||
* A false positive expectation is treated as any other expected result, except that if there is no
|
||||
* matching actual result, the message will be of the form "Fixed false positive: tag=value". A
|
||||
* false negative expectation is treated as if there were no expected result, except that if a
|
||||
* matching expected result is found, the message will be of the form
|
||||
* "Fixed false negative: tag=value".
|
||||
*
|
||||
* If the same result value is expected for two or more tags on the same line, there is a shorthand
|
||||
* notation available:
|
||||
*
|
||||
* `// $tag1,tag2=expected-value`
|
||||
*
|
||||
* is equivalent to:
|
||||
*
|
||||
* `// $tag1=expected-value $tag2=expected-value`
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Base class for tests with inline expectations. The test extends this class to provide the actual
|
||||
* results of the query, which are then compared with the expected results in comments to produce a
|
||||
* list of failure messages that point out where the actual results differ from the expected
|
||||
* results.
|
||||
*/
|
||||
abstract class InlineExpectationsTest extends string {
|
||||
bindingset[this]
|
||||
InlineExpectationsTest() { any() }
|
||||
|
||||
/**
|
||||
* Returns all tags that can be generated by this test. Most tests will only ever produce a single
|
||||
* tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()`
|
||||
* predicate for an active test will be ignored. This makes it possible to write multiple tests in
|
||||
* different `.ql` files that all query the same source code.
|
||||
*/
|
||||
abstract string getARelevantTag();
|
||||
|
||||
/**
|
||||
* Returns the actual results of the query that is being tested. Each result consist of the
|
||||
* following values:
|
||||
* - `location` - The source code location of the result. Any expected result comment must appear
|
||||
* on the start line of this location.
|
||||
* - `element` - Display text for the element on which the result is reported.
|
||||
* - `tag` - The tag that marks this result as coming from this test. This must be one of the tags
|
||||
* returned by `getARelevantTag()`.
|
||||
* - `value` - The value of the result, which will be matched against the value associated with
|
||||
* `tag` in any expected result comment on that line.
|
||||
*/
|
||||
abstract predicate hasActualResult(Location location, string element, string tag, string value);
|
||||
|
||||
final predicate hasFailureMessage(FailureLocatable element, string message) {
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
element = actualResult and
|
||||
(
|
||||
exists(FalseNegativeExpectation falseNegative |
|
||||
falseNegative.matchesActualResult(actualResult) and
|
||||
message = "Fixed false negative:" + falseNegative.getExpectationText()
|
||||
)
|
||||
or
|
||||
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
|
||||
message = "Unexpected result: " + actualResult.getExpectationText()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ValidExpectation expectation |
|
||||
not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and
|
||||
expectation.getTag() = getARelevantTag() and
|
||||
element = expectation and
|
||||
(
|
||||
expectation instanceof GoodExpectation and
|
||||
message = "Missing result:" + expectation.getExpectationText()
|
||||
or
|
||||
expectation instanceof FalsePositiveExpectation and
|
||||
message = "Fixed false positive:" + expectation.getExpectationText()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(InvalidExpectation expectation |
|
||||
element = expectation and
|
||||
message = "Invalid expectation syntax: " + expectation.getExpectation()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RegEx pattern to match a comment containing one or more expected results. The comment must be a
|
||||
* C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character
|
||||
* is treated as part of the expected results, except that the comment may contain a `//` sequence
|
||||
* to treat the remainder of the line as a regular (non-interpreted) comment.
|
||||
*/
|
||||
private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
|
||||
|
||||
/**
|
||||
* RegEx pattern to match a single expected result, not including the leading `$`. It starts with an
|
||||
* optional `f+:` or `f-:`, followed by one or more comma-separated tags containing only letters,
|
||||
* `-`, and `_`, optionally followed by `=` and the expected value.
|
||||
*/
|
||||
private string expectationPattern() {
|
||||
result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?"
|
||||
}
|
||||
|
||||
private string getAnExpectation(CppStyleComment comment) {
|
||||
result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and
|
||||
result != ""
|
||||
}
|
||||
|
||||
private newtype TFailureLocatable =
|
||||
TActualResult(
|
||||
InlineExpectationsTest test, Location location, string element, string tag, string value
|
||||
) {
|
||||
test.hasActualResult(location, element, tag, value)
|
||||
} or
|
||||
TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) {
|
||||
exists(string expectation |
|
||||
expectation = getAnExpectation(comment) and
|
||||
expectation.regexpMatch(expectationPattern()) and
|
||||
tag = expectation.regexpCapture(expectationPattern(), 2).splitAt(",").trim() and
|
||||
(
|
||||
if exists(expectation.regexpCapture(expectationPattern(), 3))
|
||||
then value = expectation.regexpCapture(expectationPattern(), 3)
|
||||
else value = ""
|
||||
) and
|
||||
(
|
||||
if exists(expectation.regexpCapture(expectationPattern(), 1))
|
||||
then knownFailure = expectation.regexpCapture(expectationPattern(), 1)
|
||||
else knownFailure = ""
|
||||
)
|
||||
)
|
||||
} or
|
||||
TInvalidExpectation(CppStyleComment comment, string expectation) {
|
||||
expectation = getAnExpectation(comment) and
|
||||
not expectation.regexpMatch(expectationPattern())
|
||||
}
|
||||
|
||||
class FailureLocatable extends TFailureLocatable {
|
||||
string toString() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
|
||||
final string getExpectationText() { result = getTag() + "=" + getValue() }
|
||||
|
||||
string getTag() { none() }
|
||||
|
||||
string getValue() { none() }
|
||||
}
|
||||
|
||||
class ActualResult extends FailureLocatable, TActualResult {
|
||||
InlineExpectationsTest test;
|
||||
Location location;
|
||||
string element;
|
||||
string tag;
|
||||
string value;
|
||||
|
||||
ActualResult() { this = TActualResult(test, location, element, tag, value) }
|
||||
|
||||
override string toString() { result = element }
|
||||
|
||||
override Location getLocation() { result = location }
|
||||
|
||||
InlineExpectationsTest getTest() { result = test }
|
||||
|
||||
override string getTag() { result = tag }
|
||||
|
||||
override string getValue() { result = value }
|
||||
}
|
||||
|
||||
abstract private class Expectation extends FailureLocatable {
|
||||
CppStyleComment comment;
|
||||
|
||||
override string toString() { result = comment.toString() }
|
||||
|
||||
override Location getLocation() { result = comment.getLocation() }
|
||||
}
|
||||
|
||||
private class ValidExpectation extends Expectation, TValidExpectation {
|
||||
string tag;
|
||||
string value;
|
||||
string knownFailure;
|
||||
|
||||
ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) }
|
||||
|
||||
override string getTag() { result = tag }
|
||||
|
||||
override string getValue() { result = value }
|
||||
|
||||
string getKnownFailure() { result = knownFailure }
|
||||
|
||||
predicate matchesActualResult(ActualResult actualResult) {
|
||||
getLocation().getStartLine() = actualResult.getLocation().getStartLine() and
|
||||
getLocation().getFile() = actualResult.getLocation().getFile() and
|
||||
getTag() = actualResult.getTag() and
|
||||
getValue() = actualResult.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
class GoodExpectation extends ValidExpectation {
|
||||
GoodExpectation() { getKnownFailure() = "" }
|
||||
}
|
||||
|
||||
class FalsePositiveExpectation extends ValidExpectation {
|
||||
FalsePositiveExpectation() { getKnownFailure() = "f+" }
|
||||
}
|
||||
|
||||
class FalseNegativeExpectation extends ValidExpectation {
|
||||
FalseNegativeExpectation() { getKnownFailure() = "f-" }
|
||||
}
|
||||
|
||||
class InvalidExpectation extends Expectation, TInvalidExpectation {
|
||||
string expectation;
|
||||
|
||||
InvalidExpectation() { this = TInvalidExpectation(comment, expectation) }
|
||||
|
||||
string getExpectation() { result = expectation }
|
||||
}
|
||||
|
||||
query predicate failures(FailureLocatable element, string message) {
|
||||
exists(InlineExpectationsTest test | test.hasFailureMessage(element, message))
|
||||
}
|
||||
@@ -77,4 +77,13 @@ void test_dynamic_cast() {
|
||||
reinterpret_cast<D2*>(b2)->f(getenv("VAR"));
|
||||
|
||||
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // tainted [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template< class T >
|
||||
T&& move( T&& t ) noexcept;
|
||||
}
|
||||
|
||||
void test_std_move() {
|
||||
sink(std::move(getenv("VAR")));
|
||||
}
|
||||
@@ -21,14 +21,18 @@
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:25 | call to getenv |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:32 | (const char *)... |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | buf |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:32:11:32:26 | p#0 |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:30 | call to getenv |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:37 | (void *)... |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:26:39:34 | call to inet_addr |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:50:39:61 | & ... |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:40:10:40:10 | a |
|
||||
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
|
||||
@@ -89,6 +93,14 @@
|
||||
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:35 | call to getenv |
|
||||
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:42 | (const char *)... |
|
||||
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:84:17:84:17 | t |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
|
||||
| test_diff.cpp:92:10:92:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:1:11:1:20 | p#0 |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:3:21:3:22 | s1 | AST only |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:21:8:21:10 | buf | AST only |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:15:22:17 | buf | AST only |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address | AST only |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... | AST only |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... | IR only |
|
||||
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion | IR only |
|
||||
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
|
||||
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
|
||||
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
|
||||
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
|
||||
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
|
||||
| test_diff.cpp:111:10:111:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 | AST only |
|
||||
|
||||
@@ -48,7 +48,7 @@ struct XY {
|
||||
void bg_stackstruct(XY s1, XY s2) {
|
||||
s1.x = source();
|
||||
if (guarded(s1.x)) {
|
||||
sink(s1.x); // no flow
|
||||
sink(s1.x); // no flow [FALSE POSITIVE in AST]
|
||||
} else if (guarded(s1.y)) {
|
||||
sink(s1.x); // flow
|
||||
} else if (guarded(s2.y)) {
|
||||
@@ -61,8 +61,8 @@ void bg_structptr(XY *p1, XY *p2) {
|
||||
if (guarded(p1->x)) {
|
||||
sink(p1->x); // no flow [FALSE POSITIVE in AST]
|
||||
} else if (guarded(p1->y)) {
|
||||
sink(p1->x); // flow [NOT DETECTED in IR]
|
||||
sink(p1->x); // flow
|
||||
} else if (guarded(p2->x)) {
|
||||
sink(p1->x); // flow [NOT DETECTED in IR]
|
||||
sink(p1->x); // flow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
|
||||
| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
|
||||
| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
|
||||
| BarrierGuard.cpp:51:13:51:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
|
||||
| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
|
||||
| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
|
||||
| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
| BarrierGuard.cpp:49:10:49:15 | BarrierGuard.cpp:51:13:51:13 | AST only |
|
||||
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
|
||||
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:64:14:64:14 | AST only |
|
||||
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:66:14:66:14 | AST only |
|
||||
| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
|
||||
| clang.cpp:28:27:28:32 | clang.cpp:29:27:29:28 | AST only |
|
||||
| clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only |
|
||||
| clang.cpp:39:42:39:47 | clang.cpp:41:18:41:19 | IR only |
|
||||
| dispatch.cpp:16:37:16:42 | dispatch.cpp:32:16:32:24 | IR only |
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
|
||||
| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
|
||||
| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
|
||||
| BarrierGuard.cpp:64:14:64:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
|
||||
| BarrierGuard.cpp:66:14:66:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
|
||||
| acrossLinkTargets.cpp:12:8:12:8 | (int)... | acrossLinkTargets.cpp:19:27:19:32 | call to source |
|
||||
| acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source |
|
||||
| clang.cpp:18:8:18:19 | (const int *)... | clang.cpp:12:9:12:20 | sourceArray1 |
|
||||
| clang.cpp:18:8:18:19 | sourceArray1 | clang.cpp:12:9:12:20 | sourceArray1 |
|
||||
| clang.cpp:29:27:29:28 | m1 | clang.cpp:28:27:28:32 | call to source |
|
||||
| clang.cpp:37:10:37:11 | m2 | clang.cpp:34:32:34:37 | call to source |
|
||||
| clang.cpp:41:18:41:19 | m2 | clang.cpp:39:42:39:47 | call to source |
|
||||
| clang.cpp:45:17:45:18 | m2 | clang.cpp:43:35:43:40 | call to source |
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | IR only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | IR only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | IR only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | IR only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | IR only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | IR only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | IR only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |
|
||||
@@ -0,0 +1,16 @@
|
||||
import semmle.code.cpp.security.TaintTracking as AST
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
|
||||
import cpp
|
||||
|
||||
from Expr source, Element tainted, string side
|
||||
where
|
||||
AST::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not IR::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h" and
|
||||
side = "AST only"
|
||||
or
|
||||
IR::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not AST::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h" and
|
||||
side = "IR only"
|
||||
select source, tainted, side
|
||||
@@ -0,0 +1,49 @@
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |
|
||||
@@ -0,0 +1,7 @@
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
|
||||
|
||||
from Expr source, Element tainted, string globalVar
|
||||
where
|
||||
taintedIncludingGlobalVars(source, tainted, globalVar) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h"
|
||||
select source, tainted, globalVar
|
||||
@@ -34,7 +34,6 @@
|
||||
| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only |
|
||||
| taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only |
|
||||
| taint.cpp:374:7:374:7 | taint.cpp:365:24:365:29 | AST only |
|
||||
| taint.cpp:382:7:382:7 | taint.cpp:377:23:377:28 | AST only |
|
||||
| taint.cpp:391:7:391:7 | taint.cpp:385:27:385:32 | AST only |
|
||||
| taint.cpp:423:7:423:7 | taint.cpp:422:14:422:19 | AST only |
|
||||
| taint.cpp:424:9:424:17 | taint.cpp:422:14:422:19 | AST only |
|
||||
|
||||
@@ -17,5 +17,6 @@
|
||||
| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
|
||||
| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
|
||||
| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
|
||||
| taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source |
|
||||
| taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source |
|
||||
| taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source |
|
||||
|
||||
@@ -16,9 +16,9 @@ where
|
||||
exists(IRFunction irFunc |
|
||||
irFunc = var.getEnclosingIRFunction() and
|
||||
(
|
||||
shouldEscape(var) and variableAddressEscapes(var)
|
||||
shouldEscape(var) and allocationEscapes(var)
|
||||
or
|
||||
not shouldEscape(var) and not variableAddressEscapes(var)
|
||||
not shouldEscape(var) and not allocationEscapes(var)
|
||||
)
|
||||
)
|
||||
select var
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
| escape.cpp:111:18:111:21 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0 | no_+0 |
|
||||
| escape.cpp:115:20:115:23 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0 | no_+0 |
|
||||
| escape.cpp:116:20:116:23 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0 | no_+0 |
|
||||
| escape.cpp:117:23:117:26 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:118:9:118:12 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:120:12:120:15 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:123:14:123:17 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:124:15:124:18 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:127:9:127:12 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:129:12:129:15 | CopyValue | no_+0 | no_+0 |
|
||||
| escape.cpp:134:5:134:18 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:134:11:134:18 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:135:5:135:12 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:135:5:135:15 | PointerAdd[4] | no_Array+20 | no_Array+20 |
|
||||
| escape.cpp:136:5:136:15 | PointerAdd[4] | no_Array+20 | no_Array+20 |
|
||||
| escape.cpp:136:7:136:14 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:137:17:137:24 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:137:17:137:27 | PointerAdd[4] | no_Array+20 | no_Array+20 |
|
||||
| escape.cpp:138:17:138:27 | PointerAdd[4] | no_Array+20 | no_Array+20 |
|
||||
| escape.cpp:138:19:138:26 | Convert | no_Array+0 | no_Array+0 |
|
||||
| escape.cpp:140:21:140:32 | FieldAddress[x] | no_Point+0 | no_Point+0 |
|
||||
| escape.cpp:140:21:140:32 | FieldAddress[y] | no_Point+4 | no_Point+4 |
|
||||
| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0 | no_Point+0 |
|
||||
| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4 | no_Point+4 |
|
||||
| escape.cpp:143:19:143:27 | CopyValue | no_Point+0 | no_Point+0 |
|
||||
| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4 | no_Point+4 |
|
||||
| escape.cpp:144:6:144:14 | CopyValue | no_Point+0 | no_Point+0 |
|
||||
| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4 | no_Point+4 |
|
||||
| escape.cpp:145:20:145:30 | CopyValue | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:146:5:146:18 | CopyValue | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:146:7:146:17 | CopyValue | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8 | no_Point+8 |
|
||||
| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0 | no_Derived+0 |
|
||||
| escape.cpp:151:5:151:14 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12 | no_Derived+12 |
|
||||
| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16 | no_Derived+16 |
|
||||
| escape.cpp:152:19:152:28 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12 | no_Derived+12 |
|
||||
| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16 | no_Derived+16 |
|
||||
| escape.cpp:155:17:155:30 | CopyValue | no_ssa_addrOf+0 | no_ssa_addrOf+0 |
|
||||
| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0 | no_ssa_addrOf+0 |
|
||||
| escape.cpp:158:17:158:28 | CopyValue | no_ssa_refTo+0 | no_ssa_refTo+0 |
|
||||
| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0 | no_ssa_refTo+0 |
|
||||
| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0 | no_ssa_refToArrayElement+0 |
|
||||
| escape.cpp:161:19:161:45 | CopyValue | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 |
|
||||
| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 |
|
||||
| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20 | no_ssa_refToArrayElement+20 |
|
||||
| escape.cpp:164:24:164:40 | CopyValue | no_ssa_refToArray+0 | no_ssa_refToArray+0 |
|
||||
| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0 | no_ssa_refToArray+0 |
|
||||
| escape.cpp:167:19:167:28 | CopyValue | passByPtr+0 | passByPtr+0 |
|
||||
| escape.cpp:170:21:170:29 | CopyValue | passByRef+0 | passByRef+0 |
|
||||
| escape.cpp:173:22:173:38 | CopyValue | no_ssa_passByPtr+0 | no_ssa_passByPtr+0 |
|
||||
| escape.cpp:176:24:176:39 | CopyValue | no_ssa_passByRef+0 | no_ssa_passByRef+0 |
|
||||
| escape.cpp:179:22:179:42 | CopyValue | no_ssa_passByPtr_ret+0 | no_ssa_passByPtr_ret+0 |
|
||||
| escape.cpp:182:24:182:43 | CopyValue | no_ssa_passByRef_ret+0 | no_ssa_passByRef_ret+0 |
|
||||
| escape.cpp:185:30:185:40 | CopyValue | passByPtr2+0 | passByPtr2+0 |
|
||||
| escape.cpp:188:32:188:41 | CopyValue | passByRef2+0 | passByRef2+0 |
|
||||
| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0 |
|
||||
| escape.cpp:191:44:191:54 | CopyValue | passByPtr3+0 | passByPtr3+0 |
|
||||
| escape.cpp:194:32:194:46 | Call | none | passByRef3+0 |
|
||||
| escape.cpp:194:32:194:59 | CopyValue | none | passByRef3+0 |
|
||||
| escape.cpp:194:48:194:57 | CopyValue | passByRef3+0 | passByRef3+0 |
|
||||
| escape.cpp:199:17:199:34 | CopyValue | no_ssa_passByPtr4+0 | no_ssa_passByPtr4+0 |
|
||||
| escape.cpp:199:37:199:54 | CopyValue | no_ssa_passByPtr5+0 | no_ssa_passByPtr5+0 |
|
||||
| escape.cpp:202:5:202:19 | Call | none | passByRef6+0 |
|
||||
| escape.cpp:202:5:202:32 | CopyValue | none | passByRef6+0 |
|
||||
| escape.cpp:202:21:202:30 | CopyValue | passByRef6+0 | passByRef6+0 |
|
||||
| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0 |
|
||||
| escape.cpp:205:5:205:39 | CopyValue | none | no_ssa_passByRef7+0 |
|
||||
| escape.cpp:205:21:205:37 | CopyValue | no_ssa_passByRef7+0 | no_ssa_passByRef7+0 |
|
||||
| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0 |
|
||||
| escape.cpp:217:14:217:16 | CopyValue | c2+0 | c2+0 |
|
||||
| escape.cpp:221:8:221:19 | Call | none | c3+0 |
|
||||
| escape.cpp:225:17:225:28 | Call | none | c4+0 |
|
||||
| escape.cpp:247:2:247:27 | Store | condEscape1+0 | condEscape1+0 |
|
||||
| escape.cpp:247:16:247:27 | CopyValue | condEscape1+0 | condEscape1+0 |
|
||||
| escape.cpp:249:9:249:34 | Store | condEscape2+0 | condEscape2+0 |
|
||||
| escape.cpp:249:23:249:34 | CopyValue | condEscape2+0 | condEscape2+0 |
|
||||
@@ -1,35 +0,0 @@
|
||||
import default
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis as RawAA
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as Raw
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis as UnAA
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Un
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||
|
||||
from Raw::Instruction rawInstr, Un::Instruction unInstr, string rawPointsTo, string unPointsTo
|
||||
where
|
||||
rawInstr = getOldInstruction(unInstr) and
|
||||
not rawInstr instanceof Raw::VariableAddressInstruction and
|
||||
(
|
||||
exists(Variable var, int rawBitOffset, int unBitOffset |
|
||||
RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and
|
||||
rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and
|
||||
UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and
|
||||
unPointsTo = var.toString() + getBitOffsetString(unBitOffset)
|
||||
)
|
||||
or
|
||||
exists(Variable var, int unBitOffset |
|
||||
not RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), _) and
|
||||
rawPointsTo = "none" and
|
||||
UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and
|
||||
unPointsTo = var.toString() + getBitOffsetString(unBitOffset)
|
||||
)
|
||||
or
|
||||
exists(Variable var, int rawBitOffset |
|
||||
RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and
|
||||
rawPointsTo = var.toString() + getBitOffsetString(rawBitOffset) and
|
||||
not UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), _) and
|
||||
unPointsTo = "none"
|
||||
)
|
||||
)
|
||||
select rawInstr.getLocation().toString(), rawInstr.getOperationString(), rawPointsTo, unPointsTo
|
||||
@@ -1,23 +1,25 @@
|
||||
import default
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasConfiguration
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
import semmle.code.cpp.ir.implementation.UseSoundEscapeAnalysis
|
||||
|
||||
predicate shouldEscape(IRAutomaticUserVariable var) {
|
||||
exists(string name |
|
||||
name = var.getVariable().getName() and
|
||||
name.matches("no_%")
|
||||
)
|
||||
class InterestingAllocation extends VariableAllocation {
|
||||
IRUserVariable userVar;
|
||||
|
||||
InterestingAllocation() { userVar = this.getIRVariable() }
|
||||
|
||||
final predicate shouldEscape() { userVar.getVariable().getName().matches("no_%") }
|
||||
}
|
||||
|
||||
from IRAutomaticUserVariable var
|
||||
from InterestingAllocation var
|
||||
where
|
||||
exists(IRFunction irFunc |
|
||||
irFunc = var.getEnclosingIRFunction() and
|
||||
(
|
||||
shouldEscape(var) and variableAddressEscapes(var)
|
||||
var.shouldEscape() and allocationEscapes(var)
|
||||
or
|
||||
not shouldEscape(var) and not variableAddressEscapes(var)
|
||||
not var.shouldEscape() and not allocationEscapes(var)
|
||||
)
|
||||
)
|
||||
select var
|
||||
|
||||
@@ -5753,9 +5753,9 @@ ir.cpp:
|
||||
# 851| 0: [VariableDeclarationEntry] definition of d
|
||||
# 851| Type = [Struct] PolymorphicDerived
|
||||
# 851| init: [Initializer] initializer for d
|
||||
# 851| expr: [ConstructorCall] call to PolymorphicDerived
|
||||
# 851| Type = [VoidType] void
|
||||
# 851| ValueCategory = prvalue
|
||||
#-----| expr: [ConstructorCall] call to PolymorphicDerived
|
||||
#-----| Type = [VoidType] void
|
||||
#-----| ValueCategory = prvalue
|
||||
# 853| 2: [DeclStmt] declaration
|
||||
# 853| 0: [VariableDeclarationEntry] definition of pb
|
||||
# 853| Type = [PointerType] PolymorphicBase *
|
||||
|
||||
@@ -4223,10 +4223,10 @@ ir.cpp:
|
||||
#-----| mu0_4(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r850_1
|
||||
# 851| r851_1(glval<PolymorphicDerived>) = VariableAddress[d] :
|
||||
# 851| mu851_2(PolymorphicDerived) = Uninitialized[d] : &:r851_1
|
||||
# 851| r851_3(glval<unknown>) = FunctionAddress[PolymorphicDerived] :
|
||||
# 851| v851_4(void) = Call : func:r851_3, this:r851_1
|
||||
# 851| mu851_5(unknown) = ^CallSideEffect : ~mu849_3
|
||||
# 851| mu851_6(PolymorphicDerived) = ^IndirectMayWriteSideEffect[-1] : &:r851_1
|
||||
#-----| r0_5(glval<unknown>) = FunctionAddress[PolymorphicDerived] :
|
||||
#-----| v0_6(void) = Call : func:r0_5, this:r851_1
|
||||
#-----| mu0_7(unknown) = ^CallSideEffect : ~mu849_3
|
||||
#-----| mu0_8(PolymorphicDerived) = ^IndirectMayWriteSideEffect[-1] : &:r851_1
|
||||
# 853| r853_1(glval<PolymorphicBase *>) = VariableAddress[pb] :
|
||||
# 853| r853_2(glval<PolymorphicBase>) = VariableAddress[b] :
|
||||
# 853| r853_3(PolymorphicBase *) = CopyValue : r853_2
|
||||
|
||||
87
cpp/ql/test/library-tests/ir/points_to/points_to.cpp
Normal file
87
cpp/ql/test/library-tests/ir/points_to/points_to.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct Base1 {
|
||||
int b1;
|
||||
};
|
||||
|
||||
struct Base2 {
|
||||
int b2;
|
||||
};
|
||||
|
||||
struct DerivedSI : Base1 {
|
||||
int dsi;
|
||||
};
|
||||
|
||||
struct DerivedMI : Base1, Base2 {
|
||||
int dmi;
|
||||
};
|
||||
|
||||
struct DerivedVI : virtual Base1 {
|
||||
int dvi;
|
||||
};
|
||||
|
||||
void Locals() {
|
||||
Point pt = { //$ussa=pt
|
||||
1, //$ussa=pt[0..4)<int>
|
||||
2 //$ussa=pt[4..8)<int>
|
||||
};
|
||||
int i = pt.x; //$ussa=pt[0..4)<int>
|
||||
i = pt.y; //$ussa=pt[4..8)<int>
|
||||
int* p = &pt.x;
|
||||
i = *p; //$ussa=pt[0..4)<int>
|
||||
p = &pt.y;
|
||||
i = *p; //$ussa=pt[4..8)<int>
|
||||
}
|
||||
|
||||
void PointsTo(
|
||||
int a, //$raw,ussa=a
|
||||
Point& b, //$raw,ussa=b $ussa=*b
|
||||
Point* c, //$raw,ussa=c $ussa=*c
|
||||
int* d, //$raw,ussa=d $ussa=*d
|
||||
DerivedSI* e, //$raw,ussa=e $ussa=*e
|
||||
DerivedMI* f, //$raw,ussa=f $ussa=*f
|
||||
DerivedVI* g //$raw,ussa=g $ussa=*g
|
||||
) {
|
||||
|
||||
int i = a; //$raw,ussa=a
|
||||
i = *&a; //$raw,ussa=a
|
||||
i = *(&a + 0); //$raw,ussa=a
|
||||
i = b.x; //$raw,ussa=b $ussa=*b[0..4)<int>
|
||||
i = b.y; //$raw,ussa=b $ussa=*b[4..8)<int>
|
||||
i = c->x; //$raw,ussa=c $ussa=*c[0..4)<int>
|
||||
i = c->y; //$raw,ussa=c $ussa=*c[4..8)<int>
|
||||
i = *d; //$raw,ussa=d $ussa=*d[0..4)<int>
|
||||
i = *(d + 0); //$raw,ussa=d $ussa=*d[0..4)<int>
|
||||
i = d[5]; //$raw,ussa=d $ussa=*d[20..24)<int>
|
||||
i = 5[d]; //$raw,ussa=d $ussa=*d[20..24)<int>
|
||||
i = d[a]; //$raw,ussa=d $raw,ussa=a $ussa=*d[?..?)<int>
|
||||
i = a[d]; //$raw,ussa=d $raw,ussa=a $ussa=*d[?..?)<int>
|
||||
|
||||
int* p = &b.x; //$raw,ussa=b
|
||||
i = *p; //$ussa=*b[0..4)<int>
|
||||
p = &b.y; //$raw,ussa=b
|
||||
i = *p; //$ussa=*b[4..8)<int>
|
||||
p = &c->x; //$raw,ussa=c
|
||||
i = *p; //$ussa=*c[0..4)<int>
|
||||
p = &c->y; //$raw,ussa=c
|
||||
i = *p; //$ussa=*c[4..8)<int>
|
||||
p = &d[5]; //$raw,ussa=d
|
||||
i = *p; //$ussa=*d[20..24)<int>
|
||||
p = &d[a]; //$raw,ussa=d $raw,ussa=a
|
||||
i = *p; //$ussa=*d[?..?)<int>
|
||||
|
||||
Point* q = &c[a]; //$raw,ussa=c $raw,ussa=a
|
||||
i = q->x; //$ussa=*c[?..?)<int>
|
||||
i = q->y; //$ussa=*c[?..?)<int>
|
||||
|
||||
i = e->b1; //$raw,ussa=e $ussa=*e[0..4)<int>
|
||||
i = e->dsi; //$raw,ussa=e $ussa=*e[4..8)<int>
|
||||
i = f->b1; //$raw,ussa=f $ussa=*f[0..4)<int>
|
||||
i = f->b2; //$raw,ussa=f $ussa=*f[4..8)<int>
|
||||
i = f->dmi; //$raw,ussa=f $ussa=*f[8..12)<int>
|
||||
i = g->b1; //$raw,ussa=g $ussa=*g[?..?)<int>
|
||||
i = g->dvi; //$raw,ussa=g $ussa=*g[8..12)<int>
|
||||
}
|
||||
65
cpp/ql/test/library-tests/ir/points_to/points_to.ql
Normal file
65
cpp/ql/test/library-tests/ir/points_to/points_to.ql
Normal file
@@ -0,0 +1,65 @@
|
||||
import cpp
|
||||
private import TestUtilities.InlineExpectationsTest
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
|
||||
private predicate ignoreAllocation(string name) {
|
||||
name = "i" or
|
||||
name = "p" or
|
||||
name = "q"
|
||||
}
|
||||
|
||||
module Raw {
|
||||
private import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA
|
||||
|
||||
private MemoryLocation getAMemoryAccess(Instruction instr) {
|
||||
result = getResultMemoryLocation(instr) or
|
||||
result = getOperandMemoryLocation(instr.getAnOperand())
|
||||
}
|
||||
|
||||
class RawPointsToTest extends InlineExpectationsTest {
|
||||
RawPointsToTest() { this = "RawPointsToTest" }
|
||||
|
||||
override string getARelevantTag() { result = "raw" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Instruction instr, MemoryLocation memLocation |
|
||||
memLocation = getAMemoryAccess(instr) and
|
||||
tag = "raw" and
|
||||
not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and
|
||||
value = memLocation.toString() and
|
||||
element = instr.toString() and
|
||||
location = instr.getLocation()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module UnaliasedSSA {
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasedSSA
|
||||
|
||||
private MemoryLocation getAMemoryAccess(Instruction instr) {
|
||||
result = getResultMemoryLocation(instr) or
|
||||
result = getOperandMemoryLocation(instr.getAnOperand())
|
||||
}
|
||||
|
||||
class UnaliasedSSAPointsToTest extends InlineExpectationsTest {
|
||||
UnaliasedSSAPointsToTest() { this = "UnaliasedSSAPointsToTest" }
|
||||
|
||||
override string getARelevantTag() { result = "ussa" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Instruction instr, MemoryLocation memLocation |
|
||||
memLocation = getAMemoryAccess(instr) and
|
||||
not memLocation instanceof AliasedVirtualVariable and
|
||||
not memLocation instanceof AllNonLocalMemory and
|
||||
tag = "ussa" and
|
||||
not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and
|
||||
value = memLocation.toString() and
|
||||
element = instr.toString() and
|
||||
location = instr.getLocation()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,12 @@ ssa.cpp:
|
||||
# 13| m13_5(Point *) = InitializeParameter[p] : &:r13_4
|
||||
# 13| r13_6(Point *) = Load : &:r13_4, m13_5
|
||||
# 13| m13_7(unknown) = InitializeIndirection[p] : &:r13_6
|
||||
# 13| m13_8(unknown) = Chi : total:m13_2, partial:m13_7
|
||||
# 13| r13_9(glval<bool>) = VariableAddress[which1] :
|
||||
# 13| m13_10(bool) = InitializeParameter[which1] : &:r13_9
|
||||
# 13| r13_11(glval<bool>) = VariableAddress[which2] :
|
||||
# 13| m13_12(bool) = InitializeParameter[which2] : &:r13_11
|
||||
# 13| r13_8(glval<bool>) = VariableAddress[which1] :
|
||||
# 13| m13_9(bool) = InitializeParameter[which1] : &:r13_8
|
||||
# 13| r13_10(glval<bool>) = VariableAddress[which2] :
|
||||
# 13| m13_11(bool) = InitializeParameter[which2] : &:r13_10
|
||||
# 14| r14_1(glval<bool>) = VariableAddress[which1] :
|
||||
# 14| r14_2(bool) = Load : &:r14_1, m13_10
|
||||
# 14| r14_2(bool) = Load : &:r14_1, m13_9
|
||||
# 14| v14_3(void) = ConditionalBranch : r14_2
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
@@ -23,29 +22,31 @@ ssa.cpp:
|
||||
# 15| r15_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 15| r15_2(Point *) = Load : &:r15_1, m13_5
|
||||
# 15| r15_3(glval<int>) = FieldAddress[x] : r15_2
|
||||
# 15| r15_4(int) = Load : &:r15_3, ~m13_8
|
||||
# 15| r15_4(int) = Load : &:r15_3, ~m13_7
|
||||
# 15| r15_5(int) = Constant[1] :
|
||||
# 15| r15_6(int) = Add : r15_4, r15_5
|
||||
# 15| m15_7(int) = Store : &:r15_3, r15_6
|
||||
# 15| m15_8(unknown) = Chi : total:m13_8, partial:m15_7
|
||||
# 15| m15_8(unknown) = Chi : total:m13_7, partial:m15_7
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 18| Block 2
|
||||
# 18| r18_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 18| r18_2(Point *) = Load : &:r18_1, m13_5
|
||||
# 18| r18_3(glval<int>) = FieldAddress[y] : r18_2
|
||||
# 18| r18_4(int) = Load : &:r18_3, ~m13_8
|
||||
# 18| r18_4(int) = Load : &:r18_3, ~m13_7
|
||||
# 18| r18_5(int) = Constant[1] :
|
||||
# 18| r18_6(int) = Add : r18_4, r18_5
|
||||
# 18| m18_7(int) = Store : &:r18_3, r18_6
|
||||
# 18| m18_8(unknown) = Chi : total:m13_8, partial:m18_7
|
||||
# 18| m18_8(unknown) = Chi : total:m13_7, partial:m18_7
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 21| Block 3
|
||||
# 21| m21_1(unknown) = Phi : from 1:~m15_8, from 2:~m18_8
|
||||
# 21| r21_2(glval<bool>) = VariableAddress[which2] :
|
||||
# 21| r21_3(bool) = Load : &:r21_2, m13_12
|
||||
# 21| v21_4(void) = ConditionalBranch : r21_3
|
||||
# 21| m21_1(int) = Phi : from 1:~m13_7, from 2:m18_7
|
||||
# 21| m21_2(int) = Phi : from 1:m15_7, from 2:~m13_7
|
||||
# 21| m21_3(unknown) = Phi : from 1:m15_8, from 2:m18_8
|
||||
# 21| r21_4(glval<bool>) = VariableAddress[which2] :
|
||||
# 21| r21_5(bool) = Load : &:r21_4, m13_11
|
||||
# 21| v21_6(void) = ConditionalBranch : r21_5
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 4
|
||||
|
||||
@@ -53,43 +54,45 @@ ssa.cpp:
|
||||
# 22| r22_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 22| r22_2(Point *) = Load : &:r22_1, m13_5
|
||||
# 22| r22_3(glval<int>) = FieldAddress[x] : r22_2
|
||||
# 22| r22_4(int) = Load : &:r22_3, ~m21_1
|
||||
# 22| r22_4(int) = Load : &:r22_3, m21_2
|
||||
# 22| r22_5(int) = Constant[1] :
|
||||
# 22| r22_6(int) = Add : r22_4, r22_5
|
||||
# 22| m22_7(int) = Store : &:r22_3, r22_6
|
||||
# 22| m22_8(unknown) = Chi : total:m21_1, partial:m22_7
|
||||
# 22| m22_8(unknown) = Chi : total:m21_3, partial:m22_7
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 25| Block 5
|
||||
# 25| r25_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 25| r25_2(Point *) = Load : &:r25_1, m13_5
|
||||
# 25| r25_3(glval<int>) = FieldAddress[y] : r25_2
|
||||
# 25| r25_4(int) = Load : &:r25_3, ~m21_1
|
||||
# 25| r25_4(int) = Load : &:r25_3, m21_1
|
||||
# 25| r25_5(int) = Constant[1] :
|
||||
# 25| r25_6(int) = Add : r25_4, r25_5
|
||||
# 25| m25_7(int) = Store : &:r25_3, r25_6
|
||||
# 25| m25_8(unknown) = Chi : total:m21_1, partial:m25_7
|
||||
# 25| m25_8(unknown) = Chi : total:m21_3, partial:m25_7
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 28| Block 6
|
||||
# 28| m28_1(unknown) = Phi : from 4:~m22_8, from 5:~m25_8
|
||||
# 28| r28_2(glval<int>) = VariableAddress[#return] :
|
||||
# 28| r28_3(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_4(Point *) = Load : &:r28_3, m13_5
|
||||
# 28| r28_5(glval<int>) = FieldAddress[x] : r28_4
|
||||
# 28| r28_6(int) = Load : &:r28_5, ~m28_1
|
||||
# 28| r28_7(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_8(Point *) = Load : &:r28_7, m13_5
|
||||
# 28| r28_9(glval<int>) = FieldAddress[y] : r28_8
|
||||
# 28| r28_10(int) = Load : &:r28_9, ~m28_1
|
||||
# 28| r28_11(int) = Add : r28_6, r28_10
|
||||
# 28| m28_12(int) = Store : &:r28_2, r28_11
|
||||
# 13| v13_13(void) = ReturnIndirection : &:r13_6, ~m28_1
|
||||
# 13| r13_14(glval<int>) = VariableAddress[#return] :
|
||||
# 13| v13_15(void) = ReturnValue : &:r13_14, m28_12
|
||||
# 13| v13_16(void) = UnmodeledUse : mu*
|
||||
# 13| v13_17(void) = AliasedUse : ~m28_1
|
||||
# 13| v13_18(void) = ExitFunction :
|
||||
# 28| m28_1(int) = Phi : from 4:m21_1, from 5:m25_7
|
||||
# 28| m28_2(int) = Phi : from 4:m22_7, from 5:m21_2
|
||||
# 28| m28_3(unknown) = Phi : from 4:m22_8, from 5:m25_8
|
||||
# 28| r28_4(glval<int>) = VariableAddress[#return] :
|
||||
# 28| r28_5(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_6(Point *) = Load : &:r28_5, m13_5
|
||||
# 28| r28_7(glval<int>) = FieldAddress[x] : r28_6
|
||||
# 28| r28_8(int) = Load : &:r28_7, m28_2
|
||||
# 28| r28_9(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_10(Point *) = Load : &:r28_9, m13_5
|
||||
# 28| r28_11(glval<int>) = FieldAddress[y] : r28_10
|
||||
# 28| r28_12(int) = Load : &:r28_11, m28_1
|
||||
# 28| r28_13(int) = Add : r28_8, r28_12
|
||||
# 28| m28_14(int) = Store : &:r28_4, r28_13
|
||||
# 13| v13_12(void) = ReturnIndirection : &:r13_6, m28_3
|
||||
# 13| r13_13(glval<int>) = VariableAddress[#return] :
|
||||
# 13| v13_14(void) = ReturnValue : &:r13_13, m28_14
|
||||
# 13| v13_15(void) = UnmodeledUse : mu*
|
||||
# 13| v13_16(void) = AliasedUse : ~m13_2
|
||||
# 13| v13_17(void) = ExitFunction :
|
||||
|
||||
# 31| int UnreachableViaGoto()
|
||||
# 31| Block 0
|
||||
@@ -212,13 +215,12 @@ ssa.cpp:
|
||||
# 68| m68_7(char *) = InitializeParameter[p] : &:r68_6
|
||||
# 68| r68_8(char *) = Load : &:r68_6, m68_7
|
||||
# 68| m68_9(unknown) = InitializeIndirection[p] : &:r68_8
|
||||
# 68| m68_10(unknown) = Chi : total:m68_2, partial:m68_9
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 69| Block 1
|
||||
# 69| m69_1(char *) = Phi : from 0:m68_7, from 2:m70_6
|
||||
# 69| m69_2(int) = Phi : from 0:m68_5, from 2:m69_8
|
||||
# 69| m69_3(unknown) = Phi : from 0:~m68_10, from 2:~m70_10
|
||||
# 69| m69_3(unknown) = Phi : from 0:~m68_2, from 2:~m70_10
|
||||
# 69| r69_4(glval<int>) = VariableAddress[n] :
|
||||
# 69| r69_5(int) = Load : &:r69_4, m69_2
|
||||
# 69| r69_6(int) = Constant[1] :
|
||||
@@ -246,11 +248,11 @@ ssa.cpp:
|
||||
|
||||
# 71| Block 3
|
||||
# 71| v71_1(void) = NoOp :
|
||||
# 68| v68_11(void) = ReturnIndirection : &:r68_8, ~m69_3
|
||||
# 68| v68_12(void) = ReturnVoid :
|
||||
# 68| v68_13(void) = UnmodeledUse : mu*
|
||||
# 68| v68_14(void) = AliasedUse : ~m69_3
|
||||
# 68| v68_15(void) = ExitFunction :
|
||||
# 68| v68_10(void) = ReturnIndirection : &:r68_8, m68_9
|
||||
# 68| v68_11(void) = ReturnVoid :
|
||||
# 68| v68_12(void) = UnmodeledUse : mu*
|
||||
# 68| v68_13(void) = AliasedUse : ~m69_3
|
||||
# 68| v68_14(void) = ExitFunction :
|
||||
|
||||
# 75| void ScalarPhi(bool)
|
||||
# 75| Block 0
|
||||
@@ -761,20 +763,19 @@ ssa.cpp:
|
||||
# 179| m179_5(int *) = InitializeParameter[p] : &:r179_4
|
||||
# 179| r179_6(int *) = Load : &:r179_4, m179_5
|
||||
# 179| m179_7(unknown) = InitializeIndirection[p] : &:r179_6
|
||||
# 179| m179_8(unknown) = Chi : total:m179_2, partial:m179_7
|
||||
# 180| m180_1(unknown) = InlineAsm : ~m179_8
|
||||
# 180| m180_2(unknown) = Chi : total:m179_8, partial:m180_1
|
||||
# 180| m180_1(unknown) = InlineAsm : ~m179_2
|
||||
# 180| m180_2(unknown) = Chi : total:m179_2, partial:m180_1
|
||||
# 181| r181_1(glval<int>) = VariableAddress[#return] :
|
||||
# 181| r181_2(glval<int *>) = VariableAddress[p] :
|
||||
# 181| r181_3(int *) = Load : &:r181_2, m179_5
|
||||
# 181| r181_4(int) = Load : &:r181_3, ~m180_2
|
||||
# 181| r181_4(int) = Load : &:r181_3, ~m179_7
|
||||
# 181| m181_5(int) = Store : &:r181_1, r181_4
|
||||
# 179| v179_9(void) = ReturnIndirection : &:r179_6, ~m180_2
|
||||
# 179| r179_10(glval<int>) = VariableAddress[#return] :
|
||||
# 179| v179_11(void) = ReturnValue : &:r179_10, m181_5
|
||||
# 179| v179_12(void) = UnmodeledUse : mu*
|
||||
# 179| v179_13(void) = AliasedUse : ~m180_2
|
||||
# 179| v179_14(void) = ExitFunction :
|
||||
# 179| v179_8(void) = ReturnIndirection : &:r179_6, m179_7
|
||||
# 179| r179_9(glval<int>) = VariableAddress[#return] :
|
||||
# 179| v179_10(void) = ReturnValue : &:r179_9, m181_5
|
||||
# 179| v179_11(void) = UnmodeledUse : mu*
|
||||
# 179| v179_12(void) = AliasedUse : ~m180_2
|
||||
# 179| v179_13(void) = ExitFunction :
|
||||
|
||||
# 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&)
|
||||
# 184| Block 0
|
||||
@@ -785,45 +786,41 @@ ssa.cpp:
|
||||
# 184| m184_5(unsigned int &) = InitializeParameter[a] : &:r184_4
|
||||
# 184| r184_6(unsigned int &) = Load : &:r184_4, m184_5
|
||||
# 184| m184_7(unknown) = InitializeIndirection[a] : &:r184_6
|
||||
# 184| m184_8(unknown) = Chi : total:m184_2, partial:m184_7
|
||||
# 184| r184_9(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 184| m184_10(unsigned int &) = InitializeParameter[b] : &:r184_9
|
||||
# 184| r184_11(unsigned int &) = Load : &:r184_9, m184_10
|
||||
# 184| m184_12(unknown) = InitializeIndirection[b] : &:r184_11
|
||||
# 184| m184_13(unknown) = Chi : total:m184_8, partial:m184_12
|
||||
# 184| r184_14(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 184| m184_15(unsigned int &) = InitializeParameter[c] : &:r184_14
|
||||
# 184| r184_16(unsigned int &) = Load : &:r184_14, m184_15
|
||||
# 184| m184_17(unknown) = InitializeIndirection[c] : &:r184_16
|
||||
# 184| m184_18(unknown) = Chi : total:m184_13, partial:m184_17
|
||||
# 184| r184_19(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 184| m184_20(unsigned int &) = InitializeParameter[d] : &:r184_19
|
||||
# 184| r184_21(unsigned int &) = Load : &:r184_19, m184_20
|
||||
# 184| m184_22(unknown) = InitializeIndirection[d] : &:r184_21
|
||||
# 184| m184_23(unknown) = Chi : total:m184_18, partial:m184_22
|
||||
# 184| r184_8(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 184| m184_9(unsigned int &) = InitializeParameter[b] : &:r184_8
|
||||
# 184| r184_10(unsigned int &) = Load : &:r184_8, m184_9
|
||||
# 184| m184_11(unknown) = InitializeIndirection[b] : &:r184_10
|
||||
# 184| r184_12(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 184| m184_13(unsigned int &) = InitializeParameter[c] : &:r184_12
|
||||
# 184| r184_14(unsigned int &) = Load : &:r184_12, m184_13
|
||||
# 184| m184_15(unknown) = InitializeIndirection[c] : &:r184_14
|
||||
# 184| r184_16(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 184| m184_17(unsigned int &) = InitializeParameter[d] : &:r184_16
|
||||
# 184| r184_18(unsigned int &) = Load : &:r184_16, m184_17
|
||||
# 184| m184_19(unknown) = InitializeIndirection[d] : &:r184_18
|
||||
# 189| r189_1(glval<unsigned int &>) = VariableAddress[a] :
|
||||
# 189| r189_2(unsigned int &) = Load : &:r189_1, m184_5
|
||||
# 189| r189_3(glval<unsigned int>) = CopyValue : r189_2
|
||||
# 189| r189_4(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_10
|
||||
# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_9
|
||||
# 189| r189_6(glval<unsigned int>) = CopyValue : r189_5
|
||||
# 190| r190_1(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_15
|
||||
# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_23
|
||||
# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_13
|
||||
# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_15
|
||||
# 190| r190_4(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_20
|
||||
# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23
|
||||
# 186| m186_1(unknown) = InlineAsm : ~m184_23, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_2(unknown) = Chi : total:m184_23, partial:m186_1
|
||||
# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_17
|
||||
# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_19
|
||||
# 186| m186_1(unknown) = InlineAsm : ~m184_11, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_2(unknown) = Chi : total:m184_11, partial:m186_1
|
||||
# 192| v192_1(void) = NoOp :
|
||||
# 184| v184_24(void) = ReturnIndirection : &:r184_6, ~m186_2
|
||||
# 184| v184_25(void) = ReturnIndirection : &:r184_11, ~m186_2
|
||||
# 184| v184_26(void) = ReturnIndirection : &:r184_16, ~m186_2
|
||||
# 184| v184_27(void) = ReturnIndirection : &:r184_21, ~m186_2
|
||||
# 184| v184_28(void) = ReturnVoid :
|
||||
# 184| v184_29(void) = UnmodeledUse : mu*
|
||||
# 184| v184_30(void) = AliasedUse : ~m186_2
|
||||
# 184| v184_31(void) = ExitFunction :
|
||||
# 184| v184_20(void) = ReturnIndirection : &:r184_6, ~m186_2
|
||||
# 184| v184_21(void) = ReturnIndirection : &:r184_10, ~m186_2
|
||||
# 184| v184_22(void) = ReturnIndirection : &:r184_14, m184_15
|
||||
# 184| v184_23(void) = ReturnIndirection : &:r184_18, m184_19
|
||||
# 184| v184_24(void) = ReturnVoid :
|
||||
# 184| v184_25(void) = UnmodeledUse : mu*
|
||||
# 184| v184_26(void) = AliasedUse : ~m186_2
|
||||
# 184| v184_27(void) = ExitFunction :
|
||||
|
||||
# 198| int PureFunctions(char*, char*, int)
|
||||
# 198| Block 0
|
||||
@@ -834,41 +831,39 @@ ssa.cpp:
|
||||
# 198| m198_5(char *) = InitializeParameter[str1] : &:r198_4
|
||||
# 198| r198_6(char *) = Load : &:r198_4, m198_5
|
||||
# 198| m198_7(unknown) = InitializeIndirection[str1] : &:r198_6
|
||||
# 198| m198_8(unknown) = Chi : total:m198_2, partial:m198_7
|
||||
# 198| r198_9(glval<char *>) = VariableAddress[str2] :
|
||||
# 198| m198_10(char *) = InitializeParameter[str2] : &:r198_9
|
||||
# 198| r198_11(char *) = Load : &:r198_9, m198_10
|
||||
# 198| m198_12(unknown) = InitializeIndirection[str2] : &:r198_11
|
||||
# 198| m198_13(unknown) = Chi : total:m198_8, partial:m198_12
|
||||
# 198| r198_14(glval<int>) = VariableAddress[x] :
|
||||
# 198| m198_15(int) = InitializeParameter[x] : &:r198_14
|
||||
# 198| r198_8(glval<char *>) = VariableAddress[str2] :
|
||||
# 198| m198_9(char *) = InitializeParameter[str2] : &:r198_8
|
||||
# 198| r198_10(char *) = Load : &:r198_8, m198_9
|
||||
# 198| m198_11(unknown) = InitializeIndirection[str2] : &:r198_10
|
||||
# 198| r198_12(glval<int>) = VariableAddress[x] :
|
||||
# 198| m198_13(int) = InitializeParameter[x] : &:r198_12
|
||||
# 199| r199_1(glval<int>) = VariableAddress[ret] :
|
||||
# 199| r199_2(glval<unknown>) = FunctionAddress[strcmp] :
|
||||
# 199| r199_3(glval<char *>) = VariableAddress[str1] :
|
||||
# 199| r199_4(char *) = Load : &:r199_3, m198_5
|
||||
# 199| r199_5(char *) = Convert : r199_4
|
||||
# 199| r199_6(glval<char *>) = VariableAddress[str2] :
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_10
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_9
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_13
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_13
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_2
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_7
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_11
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_5
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_13
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_13
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_2
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_7
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_15
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_13
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
@@ -878,13 +873,13 @@ ssa.cpp:
|
||||
# 202| r202_2(glval<int>) = VariableAddress[ret] :
|
||||
# 202| r202_3(int) = Load : &:r202_2, m201_8
|
||||
# 202| m202_4(int) = Store : &:r202_1, r202_3
|
||||
# 198| v198_16(void) = ReturnIndirection : &:r198_6, ~m198_13
|
||||
# 198| v198_17(void) = ReturnIndirection : &:r198_11, ~m198_13
|
||||
# 198| r198_18(glval<int>) = VariableAddress[#return] :
|
||||
# 198| v198_19(void) = ReturnValue : &:r198_18, m202_4
|
||||
# 198| v198_20(void) = UnmodeledUse : mu*
|
||||
# 198| v198_21(void) = AliasedUse : ~m198_13
|
||||
# 198| v198_22(void) = ExitFunction :
|
||||
# 198| v198_14(void) = ReturnIndirection : &:r198_6, m198_7
|
||||
# 198| v198_15(void) = ReturnIndirection : &:r198_10, m198_11
|
||||
# 198| r198_16(glval<int>) = VariableAddress[#return] :
|
||||
# 198| v198_17(void) = ReturnValue : &:r198_16, m202_4
|
||||
# 198| v198_18(void) = UnmodeledUse : mu*
|
||||
# 198| v198_19(void) = AliasedUse : ~m198_2
|
||||
# 198| v198_20(void) = ExitFunction :
|
||||
|
||||
# 207| int ModeledCallTarget(int)
|
||||
# 207| Block 0
|
||||
@@ -1090,3 +1085,56 @@ ssa.cpp:
|
||||
# 239| v239_5(void) = UnmodeledUse : mu*
|
||||
# 239| v239_6(void) = AliasedUse : ~m244_5
|
||||
# 239| v239_7(void) = ExitFunction :
|
||||
|
||||
# 247| char* VoidStarIndirectParameters(char*, int)
|
||||
# 247| Block 0
|
||||
# 247| v247_1(void) = EnterFunction :
|
||||
# 247| m247_2(unknown) = AliasedDefinition :
|
||||
# 247| mu247_3(unknown) = UnmodeledDefinition :
|
||||
# 247| r247_4(glval<char *>) = VariableAddress[src] :
|
||||
# 247| m247_5(char *) = InitializeParameter[src] : &:r247_4
|
||||
# 247| r247_6(char *) = Load : &:r247_4, m247_5
|
||||
# 247| m247_7(unknown) = InitializeIndirection[src] : &:r247_6
|
||||
# 247| r247_8(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_9(int) = InitializeParameter[size] : &:r247_8
|
||||
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
|
||||
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
|
||||
# 248| r248_3(glval<int>) = VariableAddress[size] :
|
||||
# 248| r248_4(int) = Load : &:r248_3, m247_9
|
||||
# 248| r248_5(unsigned long) = Convert : r248_4
|
||||
# 248| r248_6(unsigned long) = Constant[1] :
|
||||
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
|
||||
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
|
||||
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_7
|
||||
# 248| m248_10(unknown) = Chi : total:m247_7, partial:m248_9
|
||||
# 248| r248_11(char *) = Convert : r248_8
|
||||
# 248| m248_12(char *) = Store : &:r248_1, r248_11
|
||||
# 249| r249_1(char) = Constant[97] :
|
||||
# 249| r249_2(glval<char *>) = VariableAddress[src] :
|
||||
# 249| r249_3(char *) = Load : &:r249_2, m247_5
|
||||
# 249| r249_4(glval<char>) = CopyValue : r249_3
|
||||
# 249| m249_5(char) = Store : &:r249_4, r249_1
|
||||
# 249| m249_6(unknown) = Chi : total:m248_10, partial:m249_5
|
||||
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 250| r250_3(char *) = Load : &:r250_2, m248_12
|
||||
# 250| r250_4(void *) = Convert : r250_3
|
||||
# 250| r250_5(glval<char *>) = VariableAddress[src] :
|
||||
# 250| r250_6(char *) = Load : &:r250_5, m247_5
|
||||
# 250| r250_7(void *) = Convert : r250_6
|
||||
# 250| r250_8(glval<int>) = VariableAddress[size] :
|
||||
# 250| r250_9(int) = Load : &:r250_8, m247_9
|
||||
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
|
||||
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
|
||||
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
|
||||
# 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12
|
||||
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
|
||||
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 251| r251_3(char *) = Load : &:r251_2, m248_12
|
||||
# 251| m251_4(char *) = Store : &:r251_1, r251_3
|
||||
# 247| v247_10(void) = ReturnIndirection : &:r247_6, ~m250_13
|
||||
# 247| r247_11(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_12(void) = ReturnValue : &:r247_11, m251_4
|
||||
# 247| v247_13(void) = UnmodeledUse : mu*
|
||||
# 247| v247_14(void) = AliasedUse : ~m250_13
|
||||
# 247| v247_15(void) = ExitFunction :
|
||||
|
||||
@@ -8,13 +8,12 @@ ssa.cpp:
|
||||
# 13| m13_5(Point *) = InitializeParameter[p] : &:r13_4
|
||||
# 13| r13_6(Point *) = Load : &:r13_4, m13_5
|
||||
# 13| m13_7(unknown) = InitializeIndirection[p] : &:r13_6
|
||||
# 13| m13_8(unknown) = Chi : total:m13_2, partial:m13_7
|
||||
# 13| r13_9(glval<bool>) = VariableAddress[which1] :
|
||||
# 13| m13_10(bool) = InitializeParameter[which1] : &:r13_9
|
||||
# 13| r13_11(glval<bool>) = VariableAddress[which2] :
|
||||
# 13| m13_12(bool) = InitializeParameter[which2] : &:r13_11
|
||||
# 13| r13_8(glval<bool>) = VariableAddress[which1] :
|
||||
# 13| m13_9(bool) = InitializeParameter[which1] : &:r13_8
|
||||
# 13| r13_10(glval<bool>) = VariableAddress[which2] :
|
||||
# 13| m13_11(bool) = InitializeParameter[which2] : &:r13_10
|
||||
# 14| r14_1(glval<bool>) = VariableAddress[which1] :
|
||||
# 14| r14_2(bool) = Load : &:r14_1, m13_10
|
||||
# 14| r14_2(bool) = Load : &:r14_1, m13_9
|
||||
# 14| v14_3(void) = ConditionalBranch : r14_2
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
@@ -23,29 +22,31 @@ ssa.cpp:
|
||||
# 15| r15_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 15| r15_2(Point *) = Load : &:r15_1, m13_5
|
||||
# 15| r15_3(glval<int>) = FieldAddress[x] : r15_2
|
||||
# 15| r15_4(int) = Load : &:r15_3, ~m13_8
|
||||
# 15| r15_4(int) = Load : &:r15_3, ~m13_7
|
||||
# 15| r15_5(int) = Constant[1] :
|
||||
# 15| r15_6(int) = Add : r15_4, r15_5
|
||||
# 15| m15_7(int) = Store : &:r15_3, r15_6
|
||||
# 15| m15_8(unknown) = Chi : total:m13_8, partial:m15_7
|
||||
# 15| m15_8(unknown) = Chi : total:m13_7, partial:m15_7
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 18| Block 2
|
||||
# 18| r18_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 18| r18_2(Point *) = Load : &:r18_1, m13_5
|
||||
# 18| r18_3(glval<int>) = FieldAddress[y] : r18_2
|
||||
# 18| r18_4(int) = Load : &:r18_3, ~m13_8
|
||||
# 18| r18_4(int) = Load : &:r18_3, ~m13_7
|
||||
# 18| r18_5(int) = Constant[1] :
|
||||
# 18| r18_6(int) = Add : r18_4, r18_5
|
||||
# 18| m18_7(int) = Store : &:r18_3, r18_6
|
||||
# 18| m18_8(unknown) = Chi : total:m13_8, partial:m18_7
|
||||
# 18| m18_8(unknown) = Chi : total:m13_7, partial:m18_7
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 21| Block 3
|
||||
# 21| m21_1(unknown) = Phi : from 1:~m15_8, from 2:~m18_8
|
||||
# 21| r21_2(glval<bool>) = VariableAddress[which2] :
|
||||
# 21| r21_3(bool) = Load : &:r21_2, m13_12
|
||||
# 21| v21_4(void) = ConditionalBranch : r21_3
|
||||
# 21| m21_1(int) = Phi : from 1:~m13_7, from 2:m18_7
|
||||
# 21| m21_2(int) = Phi : from 1:m15_7, from 2:~m13_7
|
||||
# 21| m21_3(unknown) = Phi : from 1:m15_8, from 2:m18_8
|
||||
# 21| r21_4(glval<bool>) = VariableAddress[which2] :
|
||||
# 21| r21_5(bool) = Load : &:r21_4, m13_11
|
||||
# 21| v21_6(void) = ConditionalBranch : r21_5
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 4
|
||||
|
||||
@@ -53,43 +54,45 @@ ssa.cpp:
|
||||
# 22| r22_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 22| r22_2(Point *) = Load : &:r22_1, m13_5
|
||||
# 22| r22_3(glval<int>) = FieldAddress[x] : r22_2
|
||||
# 22| r22_4(int) = Load : &:r22_3, ~m21_1
|
||||
# 22| r22_4(int) = Load : &:r22_3, m21_2
|
||||
# 22| r22_5(int) = Constant[1] :
|
||||
# 22| r22_6(int) = Add : r22_4, r22_5
|
||||
# 22| m22_7(int) = Store : &:r22_3, r22_6
|
||||
# 22| m22_8(unknown) = Chi : total:m21_1, partial:m22_7
|
||||
# 22| m22_8(unknown) = Chi : total:m21_3, partial:m22_7
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 25| Block 5
|
||||
# 25| r25_1(glval<Point *>) = VariableAddress[p] :
|
||||
# 25| r25_2(Point *) = Load : &:r25_1, m13_5
|
||||
# 25| r25_3(glval<int>) = FieldAddress[y] : r25_2
|
||||
# 25| r25_4(int) = Load : &:r25_3, ~m21_1
|
||||
# 25| r25_4(int) = Load : &:r25_3, m21_1
|
||||
# 25| r25_5(int) = Constant[1] :
|
||||
# 25| r25_6(int) = Add : r25_4, r25_5
|
||||
# 25| m25_7(int) = Store : &:r25_3, r25_6
|
||||
# 25| m25_8(unknown) = Chi : total:m21_1, partial:m25_7
|
||||
# 25| m25_8(unknown) = Chi : total:m21_3, partial:m25_7
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 28| Block 6
|
||||
# 28| m28_1(unknown) = Phi : from 4:~m22_8, from 5:~m25_8
|
||||
# 28| r28_2(glval<int>) = VariableAddress[#return] :
|
||||
# 28| r28_3(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_4(Point *) = Load : &:r28_3, m13_5
|
||||
# 28| r28_5(glval<int>) = FieldAddress[x] : r28_4
|
||||
# 28| r28_6(int) = Load : &:r28_5, ~m28_1
|
||||
# 28| r28_7(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_8(Point *) = Load : &:r28_7, m13_5
|
||||
# 28| r28_9(glval<int>) = FieldAddress[y] : r28_8
|
||||
# 28| r28_10(int) = Load : &:r28_9, ~m28_1
|
||||
# 28| r28_11(int) = Add : r28_6, r28_10
|
||||
# 28| m28_12(int) = Store : &:r28_2, r28_11
|
||||
# 13| v13_13(void) = ReturnIndirection : &:r13_6, ~m28_1
|
||||
# 13| r13_14(glval<int>) = VariableAddress[#return] :
|
||||
# 13| v13_15(void) = ReturnValue : &:r13_14, m28_12
|
||||
# 13| v13_16(void) = UnmodeledUse : mu*
|
||||
# 13| v13_17(void) = AliasedUse : ~m28_1
|
||||
# 13| v13_18(void) = ExitFunction :
|
||||
# 28| m28_1(int) = Phi : from 4:m21_1, from 5:m25_7
|
||||
# 28| m28_2(int) = Phi : from 4:m22_7, from 5:m21_2
|
||||
# 28| m28_3(unknown) = Phi : from 4:m22_8, from 5:m25_8
|
||||
# 28| r28_4(glval<int>) = VariableAddress[#return] :
|
||||
# 28| r28_5(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_6(Point *) = Load : &:r28_5, m13_5
|
||||
# 28| r28_7(glval<int>) = FieldAddress[x] : r28_6
|
||||
# 28| r28_8(int) = Load : &:r28_7, m28_2
|
||||
# 28| r28_9(glval<Point *>) = VariableAddress[p] :
|
||||
# 28| r28_10(Point *) = Load : &:r28_9, m13_5
|
||||
# 28| r28_11(glval<int>) = FieldAddress[y] : r28_10
|
||||
# 28| r28_12(int) = Load : &:r28_11, m28_1
|
||||
# 28| r28_13(int) = Add : r28_8, r28_12
|
||||
# 28| m28_14(int) = Store : &:r28_4, r28_13
|
||||
# 13| v13_12(void) = ReturnIndirection : &:r13_6, m28_3
|
||||
# 13| r13_13(glval<int>) = VariableAddress[#return] :
|
||||
# 13| v13_14(void) = ReturnValue : &:r13_13, m28_14
|
||||
# 13| v13_15(void) = UnmodeledUse : mu*
|
||||
# 13| v13_16(void) = AliasedUse : ~m13_2
|
||||
# 13| v13_17(void) = ExitFunction :
|
||||
|
||||
# 31| int UnreachableViaGoto()
|
||||
# 31| Block 0
|
||||
@@ -212,13 +215,12 @@ ssa.cpp:
|
||||
# 68| m68_7(char *) = InitializeParameter[p] : &:r68_6
|
||||
# 68| r68_8(char *) = Load : &:r68_6, m68_7
|
||||
# 68| m68_9(unknown) = InitializeIndirection[p] : &:r68_8
|
||||
# 68| m68_10(unknown) = Chi : total:m68_2, partial:m68_9
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 69| Block 1
|
||||
# 69| m69_1(char *) = Phi : from 0:m68_7, from 2:m70_6
|
||||
# 69| m69_2(int) = Phi : from 0:m68_5, from 2:m69_8
|
||||
# 69| m69_3(unknown) = Phi : from 0:~m68_10, from 2:~m70_10
|
||||
# 69| m69_3(unknown) = Phi : from 0:~m68_2, from 2:~m70_10
|
||||
# 69| r69_4(glval<int>) = VariableAddress[n] :
|
||||
# 69| r69_5(int) = Load : &:r69_4, m69_2
|
||||
# 69| r69_6(int) = Constant[1] :
|
||||
@@ -246,11 +248,11 @@ ssa.cpp:
|
||||
|
||||
# 71| Block 3
|
||||
# 71| v71_1(void) = NoOp :
|
||||
# 68| v68_11(void) = ReturnIndirection : &:r68_8, ~m69_3
|
||||
# 68| v68_12(void) = ReturnVoid :
|
||||
# 68| v68_13(void) = UnmodeledUse : mu*
|
||||
# 68| v68_14(void) = AliasedUse : ~m69_3
|
||||
# 68| v68_15(void) = ExitFunction :
|
||||
# 68| v68_10(void) = ReturnIndirection : &:r68_8, m68_9
|
||||
# 68| v68_11(void) = ReturnVoid :
|
||||
# 68| v68_12(void) = UnmodeledUse : mu*
|
||||
# 68| v68_13(void) = AliasedUse : ~m69_3
|
||||
# 68| v68_14(void) = ExitFunction :
|
||||
|
||||
# 75| void ScalarPhi(bool)
|
||||
# 75| Block 0
|
||||
@@ -758,20 +760,19 @@ ssa.cpp:
|
||||
# 179| m179_5(int *) = InitializeParameter[p] : &:r179_4
|
||||
# 179| r179_6(int *) = Load : &:r179_4, m179_5
|
||||
# 179| m179_7(unknown) = InitializeIndirection[p] : &:r179_6
|
||||
# 179| m179_8(unknown) = Chi : total:m179_2, partial:m179_7
|
||||
# 180| m180_1(unknown) = InlineAsm : ~m179_8
|
||||
# 180| m180_2(unknown) = Chi : total:m179_8, partial:m180_1
|
||||
# 180| m180_1(unknown) = InlineAsm : ~m179_2
|
||||
# 180| m180_2(unknown) = Chi : total:m179_2, partial:m180_1
|
||||
# 181| r181_1(glval<int>) = VariableAddress[#return] :
|
||||
# 181| r181_2(glval<int *>) = VariableAddress[p] :
|
||||
# 181| r181_3(int *) = Load : &:r181_2, m179_5
|
||||
# 181| r181_4(int) = Load : &:r181_3, ~m180_2
|
||||
# 181| r181_4(int) = Load : &:r181_3, ~m179_7
|
||||
# 181| m181_5(int) = Store : &:r181_1, r181_4
|
||||
# 179| v179_9(void) = ReturnIndirection : &:r179_6, ~m180_2
|
||||
# 179| r179_10(glval<int>) = VariableAddress[#return] :
|
||||
# 179| v179_11(void) = ReturnValue : &:r179_10, m181_5
|
||||
# 179| v179_12(void) = UnmodeledUse : mu*
|
||||
# 179| v179_13(void) = AliasedUse : ~m180_2
|
||||
# 179| v179_14(void) = ExitFunction :
|
||||
# 179| v179_8(void) = ReturnIndirection : &:r179_6, m179_7
|
||||
# 179| r179_9(glval<int>) = VariableAddress[#return] :
|
||||
# 179| v179_10(void) = ReturnValue : &:r179_9, m181_5
|
||||
# 179| v179_11(void) = UnmodeledUse : mu*
|
||||
# 179| v179_12(void) = AliasedUse : ~m180_2
|
||||
# 179| v179_13(void) = ExitFunction :
|
||||
|
||||
# 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&)
|
||||
# 184| Block 0
|
||||
@@ -782,45 +783,41 @@ ssa.cpp:
|
||||
# 184| m184_5(unsigned int &) = InitializeParameter[a] : &:r184_4
|
||||
# 184| r184_6(unsigned int &) = Load : &:r184_4, m184_5
|
||||
# 184| m184_7(unknown) = InitializeIndirection[a] : &:r184_6
|
||||
# 184| m184_8(unknown) = Chi : total:m184_2, partial:m184_7
|
||||
# 184| r184_9(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 184| m184_10(unsigned int &) = InitializeParameter[b] : &:r184_9
|
||||
# 184| r184_11(unsigned int &) = Load : &:r184_9, m184_10
|
||||
# 184| m184_12(unknown) = InitializeIndirection[b] : &:r184_11
|
||||
# 184| m184_13(unknown) = Chi : total:m184_8, partial:m184_12
|
||||
# 184| r184_14(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 184| m184_15(unsigned int &) = InitializeParameter[c] : &:r184_14
|
||||
# 184| r184_16(unsigned int &) = Load : &:r184_14, m184_15
|
||||
# 184| m184_17(unknown) = InitializeIndirection[c] : &:r184_16
|
||||
# 184| m184_18(unknown) = Chi : total:m184_13, partial:m184_17
|
||||
# 184| r184_19(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 184| m184_20(unsigned int &) = InitializeParameter[d] : &:r184_19
|
||||
# 184| r184_21(unsigned int &) = Load : &:r184_19, m184_20
|
||||
# 184| m184_22(unknown) = InitializeIndirection[d] : &:r184_21
|
||||
# 184| m184_23(unknown) = Chi : total:m184_18, partial:m184_22
|
||||
# 184| r184_8(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 184| m184_9(unsigned int &) = InitializeParameter[b] : &:r184_8
|
||||
# 184| r184_10(unsigned int &) = Load : &:r184_8, m184_9
|
||||
# 184| m184_11(unknown) = InitializeIndirection[b] : &:r184_10
|
||||
# 184| r184_12(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 184| m184_13(unsigned int &) = InitializeParameter[c] : &:r184_12
|
||||
# 184| r184_14(unsigned int &) = Load : &:r184_12, m184_13
|
||||
# 184| m184_15(unknown) = InitializeIndirection[c] : &:r184_14
|
||||
# 184| r184_16(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 184| m184_17(unsigned int &) = InitializeParameter[d] : &:r184_16
|
||||
# 184| r184_18(unsigned int &) = Load : &:r184_16, m184_17
|
||||
# 184| m184_19(unknown) = InitializeIndirection[d] : &:r184_18
|
||||
# 189| r189_1(glval<unsigned int &>) = VariableAddress[a] :
|
||||
# 189| r189_2(unsigned int &) = Load : &:r189_1, m184_5
|
||||
# 189| r189_3(glval<unsigned int>) = CopyValue : r189_2
|
||||
# 189| r189_4(glval<unsigned int &>) = VariableAddress[b] :
|
||||
# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_10
|
||||
# 189| r189_5(unsigned int &) = Load : &:r189_4, m184_9
|
||||
# 189| r189_6(glval<unsigned int>) = CopyValue : r189_5
|
||||
# 190| r190_1(glval<unsigned int &>) = VariableAddress[c] :
|
||||
# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_15
|
||||
# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_23
|
||||
# 190| r190_2(unsigned int &) = Load : &:r190_1, m184_13
|
||||
# 190| r190_3(unsigned int) = Load : &:r190_2, ~m184_15
|
||||
# 190| r190_4(glval<unsigned int &>) = VariableAddress[d] :
|
||||
# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_20
|
||||
# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_23
|
||||
# 186| m186_1(unknown) = InlineAsm : ~m184_23, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_2(unknown) = Chi : total:m184_23, partial:m186_1
|
||||
# 190| r190_5(unsigned int &) = Load : &:r190_4, m184_17
|
||||
# 190| r190_6(unsigned int) = Load : &:r190_5, ~m184_19
|
||||
# 186| m186_1(unknown) = InlineAsm : ~m184_2, 0:r189_3, 1:r189_6, 2:r190_3, 3:r190_6
|
||||
# 186| m186_2(unknown) = Chi : total:m184_2, partial:m186_1
|
||||
# 192| v192_1(void) = NoOp :
|
||||
# 184| v184_24(void) = ReturnIndirection : &:r184_6, ~m186_2
|
||||
# 184| v184_25(void) = ReturnIndirection : &:r184_11, ~m186_2
|
||||
# 184| v184_26(void) = ReturnIndirection : &:r184_16, ~m186_2
|
||||
# 184| v184_27(void) = ReturnIndirection : &:r184_21, ~m186_2
|
||||
# 184| v184_28(void) = ReturnVoid :
|
||||
# 184| v184_29(void) = UnmodeledUse : mu*
|
||||
# 184| v184_30(void) = AliasedUse : ~m186_2
|
||||
# 184| v184_31(void) = ExitFunction :
|
||||
# 184| v184_20(void) = ReturnIndirection : &:r184_6, m184_7
|
||||
# 184| v184_21(void) = ReturnIndirection : &:r184_10, m184_11
|
||||
# 184| v184_22(void) = ReturnIndirection : &:r184_14, m184_15
|
||||
# 184| v184_23(void) = ReturnIndirection : &:r184_18, m184_19
|
||||
# 184| v184_24(void) = ReturnVoid :
|
||||
# 184| v184_25(void) = UnmodeledUse : mu*
|
||||
# 184| v184_26(void) = AliasedUse : ~m186_2
|
||||
# 184| v184_27(void) = ExitFunction :
|
||||
|
||||
# 198| int PureFunctions(char*, char*, int)
|
||||
# 198| Block 0
|
||||
@@ -831,41 +828,39 @@ ssa.cpp:
|
||||
# 198| m198_5(char *) = InitializeParameter[str1] : &:r198_4
|
||||
# 198| r198_6(char *) = Load : &:r198_4, m198_5
|
||||
# 198| m198_7(unknown) = InitializeIndirection[str1] : &:r198_6
|
||||
# 198| m198_8(unknown) = Chi : total:m198_2, partial:m198_7
|
||||
# 198| r198_9(glval<char *>) = VariableAddress[str2] :
|
||||
# 198| m198_10(char *) = InitializeParameter[str2] : &:r198_9
|
||||
# 198| r198_11(char *) = Load : &:r198_9, m198_10
|
||||
# 198| m198_12(unknown) = InitializeIndirection[str2] : &:r198_11
|
||||
# 198| m198_13(unknown) = Chi : total:m198_8, partial:m198_12
|
||||
# 198| r198_14(glval<int>) = VariableAddress[x] :
|
||||
# 198| m198_15(int) = InitializeParameter[x] : &:r198_14
|
||||
# 198| r198_8(glval<char *>) = VariableAddress[str2] :
|
||||
# 198| m198_9(char *) = InitializeParameter[str2] : &:r198_8
|
||||
# 198| r198_10(char *) = Load : &:r198_8, m198_9
|
||||
# 198| m198_11(unknown) = InitializeIndirection[str2] : &:r198_10
|
||||
# 198| r198_12(glval<int>) = VariableAddress[x] :
|
||||
# 198| m198_13(int) = InitializeParameter[x] : &:r198_12
|
||||
# 199| r199_1(glval<int>) = VariableAddress[ret] :
|
||||
# 199| r199_2(glval<unknown>) = FunctionAddress[strcmp] :
|
||||
# 199| r199_3(glval<char *>) = VariableAddress[str1] :
|
||||
# 199| r199_4(char *) = Load : &:r199_3, m198_5
|
||||
# 199| r199_5(char *) = Convert : r199_4
|
||||
# 199| r199_6(glval<char *>) = VariableAddress[str2] :
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_10
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_9
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_13
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_13
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_2
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_7
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_11
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_5
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_13
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_13
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_2
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_7
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_15
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_13
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
@@ -875,13 +870,13 @@ ssa.cpp:
|
||||
# 202| r202_2(glval<int>) = VariableAddress[ret] :
|
||||
# 202| r202_3(int) = Load : &:r202_2, m201_8
|
||||
# 202| m202_4(int) = Store : &:r202_1, r202_3
|
||||
# 198| v198_16(void) = ReturnIndirection : &:r198_6, ~m198_13
|
||||
# 198| v198_17(void) = ReturnIndirection : &:r198_11, ~m198_13
|
||||
# 198| r198_18(glval<int>) = VariableAddress[#return] :
|
||||
# 198| v198_19(void) = ReturnValue : &:r198_18, m202_4
|
||||
# 198| v198_20(void) = UnmodeledUse : mu*
|
||||
# 198| v198_21(void) = AliasedUse : ~m198_13
|
||||
# 198| v198_22(void) = ExitFunction :
|
||||
# 198| v198_14(void) = ReturnIndirection : &:r198_6, m198_7
|
||||
# 198| v198_15(void) = ReturnIndirection : &:r198_10, m198_11
|
||||
# 198| r198_16(glval<int>) = VariableAddress[#return] :
|
||||
# 198| v198_17(void) = ReturnValue : &:r198_16, m202_4
|
||||
# 198| v198_18(void) = UnmodeledUse : mu*
|
||||
# 198| v198_19(void) = AliasedUse : ~m198_2
|
||||
# 198| v198_20(void) = ExitFunction :
|
||||
|
||||
# 207| int ModeledCallTarget(int)
|
||||
# 207| Block 0
|
||||
@@ -1085,3 +1080,56 @@ ssa.cpp:
|
||||
# 239| v239_5(void) = UnmodeledUse : mu*
|
||||
# 239| v239_6(void) = AliasedUse : ~m244_5
|
||||
# 239| v239_7(void) = ExitFunction :
|
||||
|
||||
# 247| char* VoidStarIndirectParameters(char*, int)
|
||||
# 247| Block 0
|
||||
# 247| v247_1(void) = EnterFunction :
|
||||
# 247| m247_2(unknown) = AliasedDefinition :
|
||||
# 247| mu247_3(unknown) = UnmodeledDefinition :
|
||||
# 247| r247_4(glval<char *>) = VariableAddress[src] :
|
||||
# 247| m247_5(char *) = InitializeParameter[src] : &:r247_4
|
||||
# 247| r247_6(char *) = Load : &:r247_4, m247_5
|
||||
# 247| m247_7(unknown) = InitializeIndirection[src] : &:r247_6
|
||||
# 247| r247_8(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_9(int) = InitializeParameter[size] : &:r247_8
|
||||
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
|
||||
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
|
||||
# 248| r248_3(glval<int>) = VariableAddress[size] :
|
||||
# 248| r248_4(int) = Load : &:r248_3, m247_9
|
||||
# 248| r248_5(unsigned long) = Convert : r248_4
|
||||
# 248| r248_6(unsigned long) = Constant[1] :
|
||||
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
|
||||
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
|
||||
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_2
|
||||
# 248| m248_10(unknown) = Chi : total:m247_2, partial:m248_9
|
||||
# 248| r248_11(char *) = Convert : r248_8
|
||||
# 248| m248_12(char *) = Store : &:r248_1, r248_11
|
||||
# 249| r249_1(char) = Constant[97] :
|
||||
# 249| r249_2(glval<char *>) = VariableAddress[src] :
|
||||
# 249| r249_3(char *) = Load : &:r249_2, m247_5
|
||||
# 249| r249_4(glval<char>) = CopyValue : r249_3
|
||||
# 249| m249_5(char) = Store : &:r249_4, r249_1
|
||||
# 249| m249_6(unknown) = Chi : total:m247_7, partial:m249_5
|
||||
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 250| r250_3(char *) = Load : &:r250_2, m248_12
|
||||
# 250| r250_4(void *) = Convert : r250_3
|
||||
# 250| r250_5(glval<char *>) = VariableAddress[src] :
|
||||
# 250| r250_6(char *) = Load : &:r250_5, m247_5
|
||||
# 250| r250_7(void *) = Convert : r250_6
|
||||
# 250| r250_8(glval<int>) = VariableAddress[size] :
|
||||
# 250| r250_9(int) = Load : &:r250_8, m247_9
|
||||
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
|
||||
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
|
||||
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
|
||||
# 250| m250_13(unknown) = Chi : total:m248_10, partial:m250_12
|
||||
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
|
||||
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 251| r251_3(char *) = Load : &:r251_2, m248_12
|
||||
# 251| m251_4(char *) = Store : &:r251_1, r251_3
|
||||
# 247| v247_10(void) = ReturnIndirection : &:r247_6, ~m249_6
|
||||
# 247| r247_11(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_12(void) = ReturnValue : &:r247_11, m251_4
|
||||
# 247| v247_13(void) = UnmodeledUse : mu*
|
||||
# 247| v247_14(void) = AliasedUse : ~m250_13
|
||||
# 247| v247_15(void) = ExitFunction :
|
||||
|
||||
@@ -243,3 +243,10 @@ void ExplicitConstructorCalls() {
|
||||
Constructible c2 = Constructible(2);
|
||||
c2.g();
|
||||
}
|
||||
|
||||
char *VoidStarIndirectParameters(char *src, int size) {
|
||||
char *dst = new char[size];
|
||||
*src = 'a';
|
||||
memcpy(dst, src, size);
|
||||
return dst;
|
||||
}
|
||||
@@ -1028,3 +1028,53 @@ ssa.cpp:
|
||||
# 239| v239_5(void) = UnmodeledUse : mu*
|
||||
# 239| v239_6(void) = AliasedUse : ~mu239_3
|
||||
# 239| v239_7(void) = ExitFunction :
|
||||
|
||||
# 247| char* VoidStarIndirectParameters(char*, int)
|
||||
# 247| Block 0
|
||||
# 247| v247_1(void) = EnterFunction :
|
||||
# 247| mu247_2(unknown) = AliasedDefinition :
|
||||
# 247| mu247_3(unknown) = UnmodeledDefinition :
|
||||
# 247| r247_4(glval<char *>) = VariableAddress[src] :
|
||||
# 247| m247_5(char *) = InitializeParameter[src] : &:r247_4
|
||||
# 247| r247_6(char *) = Load : &:r247_4, m247_5
|
||||
# 247| mu247_7(unknown) = InitializeIndirection[src] : &:r247_6
|
||||
# 247| r247_8(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_9(int) = InitializeParameter[size] : &:r247_8
|
||||
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
|
||||
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
|
||||
# 248| r248_3(glval<int>) = VariableAddress[size] :
|
||||
# 248| r248_4(int) = Load : &:r248_3, m247_9
|
||||
# 248| r248_5(unsigned long) = Convert : r248_4
|
||||
# 248| r248_6(unsigned long) = Constant[1] :
|
||||
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
|
||||
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
|
||||
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_3
|
||||
# 248| r248_10(char *) = Convert : r248_8
|
||||
# 248| m248_11(char *) = Store : &:r248_1, r248_10
|
||||
# 249| r249_1(char) = Constant[97] :
|
||||
# 249| r249_2(glval<char *>) = VariableAddress[src] :
|
||||
# 249| r249_3(char *) = Load : &:r249_2, m247_5
|
||||
# 249| r249_4(glval<char>) = CopyValue : r249_3
|
||||
# 249| mu249_5(char) = Store : &:r249_4, r249_1
|
||||
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 250| r250_3(char *) = Load : &:r250_2, m248_11
|
||||
# 250| r250_4(void *) = Convert : r250_3
|
||||
# 250| r250_5(glval<char *>) = VariableAddress[src] :
|
||||
# 250| r250_6(char *) = Load : &:r250_5, m247_5
|
||||
# 250| r250_7(void *) = Convert : r250_6
|
||||
# 250| r250_8(glval<int>) = VariableAddress[size] :
|
||||
# 250| r250_9(int) = Load : &:r250_8, m247_9
|
||||
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
|
||||
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_3
|
||||
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
|
||||
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
|
||||
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 251| r251_3(char *) = Load : &:r251_2, m248_11
|
||||
# 251| m251_4(char *) = Store : &:r251_1, r251_3
|
||||
# 247| v247_10(void) = ReturnIndirection : &:r247_6, ~mu247_3
|
||||
# 247| r247_11(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_12(void) = ReturnValue : &:r247_11, m251_4
|
||||
# 247| v247_13(void) = UnmodeledUse : mu*
|
||||
# 247| v247_14(void) = AliasedUse : ~mu247_3
|
||||
# 247| v247_15(void) = ExitFunction :
|
||||
|
||||
@@ -1028,3 +1028,53 @@ ssa.cpp:
|
||||
# 239| v239_5(void) = UnmodeledUse : mu*
|
||||
# 239| v239_6(void) = AliasedUse : ~mu239_3
|
||||
# 239| v239_7(void) = ExitFunction :
|
||||
|
||||
# 247| char* VoidStarIndirectParameters(char*, int)
|
||||
# 247| Block 0
|
||||
# 247| v247_1(void) = EnterFunction :
|
||||
# 247| mu247_2(unknown) = AliasedDefinition :
|
||||
# 247| mu247_3(unknown) = UnmodeledDefinition :
|
||||
# 247| r247_4(glval<char *>) = VariableAddress[src] :
|
||||
# 247| m247_5(char *) = InitializeParameter[src] : &:r247_4
|
||||
# 247| r247_6(char *) = Load : &:r247_4, m247_5
|
||||
# 247| mu247_7(unknown) = InitializeIndirection[src] : &:r247_6
|
||||
# 247| r247_8(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_9(int) = InitializeParameter[size] : &:r247_8
|
||||
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
|
||||
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
|
||||
# 248| r248_3(glval<int>) = VariableAddress[size] :
|
||||
# 248| r248_4(int) = Load : &:r248_3, m247_9
|
||||
# 248| r248_5(unsigned long) = Convert : r248_4
|
||||
# 248| r248_6(unsigned long) = Constant[1] :
|
||||
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
|
||||
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
|
||||
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_3
|
||||
# 248| r248_10(char *) = Convert : r248_8
|
||||
# 248| m248_11(char *) = Store : &:r248_1, r248_10
|
||||
# 249| r249_1(char) = Constant[97] :
|
||||
# 249| r249_2(glval<char *>) = VariableAddress[src] :
|
||||
# 249| r249_3(char *) = Load : &:r249_2, m247_5
|
||||
# 249| r249_4(glval<char>) = CopyValue : r249_3
|
||||
# 249| mu249_5(char) = Store : &:r249_4, r249_1
|
||||
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 250| r250_3(char *) = Load : &:r250_2, m248_11
|
||||
# 250| r250_4(void *) = Convert : r250_3
|
||||
# 250| r250_5(glval<char *>) = VariableAddress[src] :
|
||||
# 250| r250_6(char *) = Load : &:r250_5, m247_5
|
||||
# 250| r250_7(void *) = Convert : r250_6
|
||||
# 250| r250_8(glval<int>) = VariableAddress[size] :
|
||||
# 250| r250_9(int) = Load : &:r250_8, m247_9
|
||||
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
|
||||
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_3
|
||||
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
|
||||
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
|
||||
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 251| r251_3(char *) = Load : &:r251_2, m248_11
|
||||
# 251| m251_4(char *) = Store : &:r251_1, r251_3
|
||||
# 247| v247_10(void) = ReturnIndirection : &:r247_6, ~mu247_3
|
||||
# 247| r247_11(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_12(void) = ReturnValue : &:r247_11, m251_4
|
||||
# 247| v247_13(void) = UnmodeledUse : mu*
|
||||
# 247| v247_14(void) = AliasedUse : ~mu247_3
|
||||
# 247| v247_15(void) = ExitFunction :
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| test.cpp:9:12:9:35 | call to MyInt |
|
||||
| test.cpp:9:5:9:36 | call to MyInt |
|
||||
| test.cpp:26:18:26:23 | call to MyInt |
|
||||
| test.cpp:42:15:42:15 | call to operator+ |
|
||||
| test.cpp:43:5:43:5 | call to operator+= |
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
| non_permissive.cpp:6:3:6:3 | call to f | non_permissive.cpp:2:13:2:13 | f |
|
||||
| permissive.cpp:6:3:6:3 | call to f | permissive.cpp:2:13:2:13 | f |
|
||||
|
||||
@@ -35,14 +35,14 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| VacuousDestructorCall.cpp:2:29:2:29 | Chi: y |
|
||||
| VacuousDestructorCall.cpp:2:29:2:29 | InitializeIndirection: y |
|
||||
| condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt |
|
||||
| condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt |
|
||||
| condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt |
|
||||
| condition_decls.cpp:48:52:48:53 | Chi: call to BoxedInt |
|
||||
| cpp17.cpp:15:11:15:21 | Convert: (void *)... |
|
||||
| misc.c:171:10:171:13 | Uninitialized: definition of str2 |
|
||||
| misc.c:219:47:219:48 | Chi: sp |
|
||||
| misc.c:219:47:219:48 | InitializeIndirection: sp |
|
||||
| ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x |
|
||||
| ms_try_mix.cpp:11:12:11:15 | Chi: call to C |
|
||||
| ms_try_mix.cpp:28:12:28:15 | Chi: call to C |
|
||||
|
||||
@@ -266,8 +266,6 @@ test.cpp:
|
||||
# 39| valnum = m39_9
|
||||
# 39| m39_11(unknown) = InitializeIndirection[p2] : &:r39_10
|
||||
# 39| valnum = unique
|
||||
# 39| m39_12(unknown) = Chi : total:m39_2, partial:m39_11
|
||||
# 39| valnum = unique
|
||||
# 40| r40_1(glval<int>) = VariableAddress[x] :
|
||||
# 40| valnum = r40_1
|
||||
# 40| m40_2(int) = Uninitialized[x] : &:r40_1
|
||||
@@ -292,7 +290,7 @@ test.cpp:
|
||||
# 43| valnum = r43_5
|
||||
# 43| r43_6(glval<int>) = VariableAddress[global03] :
|
||||
# 43| valnum = r43_6
|
||||
# 43| r43_7(int) = Load : &:r43_6, ~m39_12
|
||||
# 43| r43_7(int) = Load : &:r43_6, ~m39_2
|
||||
# 43| valnum = unique
|
||||
# 43| r43_8(int) = Add : r43_5, r43_7
|
||||
# 43| valnum = r43_8
|
||||
@@ -310,7 +308,7 @@ test.cpp:
|
||||
# 44| valnum = m39_9
|
||||
# 44| m44_5(int) = Store : &:r44_4, r44_1
|
||||
# 44| valnum = r44_1
|
||||
# 44| m44_6(unknown) = Chi : total:m39_12, partial:m44_5
|
||||
# 44| m44_6(unknown) = Chi : total:m39_11, partial:m44_5
|
||||
# 44| valnum = unique
|
||||
# 45| r45_1(glval<int>) = VariableAddress[p0] :
|
||||
# 45| valnum = r39_4
|
||||
@@ -324,7 +322,7 @@ test.cpp:
|
||||
# 45| valnum = r43_5
|
||||
# 45| r45_6(glval<int>) = VariableAddress[global03] :
|
||||
# 45| valnum = r43_6
|
||||
# 45| r45_7(int) = Load : &:r45_6, ~m44_6
|
||||
# 45| r45_7(int) = Load : &:r45_6, ~m39_2
|
||||
# 45| valnum = unique
|
||||
# 45| r45_8(int) = Add : r45_5, r45_7
|
||||
# 45| valnum = r45_8
|
||||
@@ -341,13 +339,13 @@ test.cpp:
|
||||
# 46| m46_4(int) = Store : &:r46_3, r46_2
|
||||
# 46| valnum = r45_8
|
||||
# 47| v47_1(void) = NoOp :
|
||||
# 39| v39_13(void) = ReturnIndirection : &:r39_10, ~m44_6
|
||||
# 39| r39_14(glval<int>) = VariableAddress[#return] :
|
||||
# 39| v39_12(void) = ReturnIndirection : &:r39_10, ~m44_6
|
||||
# 39| r39_13(glval<int>) = VariableAddress[#return] :
|
||||
# 39| valnum = unique
|
||||
# 39| v39_15(void) = ReturnValue : &:r39_14
|
||||
# 39| v39_16(void) = UnmodeledUse : mu*
|
||||
# 39| v39_17(void) = AliasedUse : ~m44_6
|
||||
# 39| v39_18(void) = ExitFunction :
|
||||
# 39| v39_14(void) = ReturnValue : &:r39_13
|
||||
# 39| v39_15(void) = UnmodeledUse : mu*
|
||||
# 39| v39_16(void) = AliasedUse : ~m39_2
|
||||
# 39| v39_17(void) = ExitFunction :
|
||||
|
||||
# 49| unsigned int my_strspn(char const*, char const*)
|
||||
# 49| Block 0
|
||||
@@ -364,17 +362,13 @@ test.cpp:
|
||||
# 49| valnum = m49_5
|
||||
# 49| m49_7(unknown) = InitializeIndirection[str] : &:r49_6
|
||||
# 49| valnum = unique
|
||||
# 49| m49_8(unknown) = Chi : total:m49_2, partial:m49_7
|
||||
# 49| valnum = unique
|
||||
# 49| r49_9(glval<char *>) = VariableAddress[chars] :
|
||||
# 49| valnum = r49_9
|
||||
# 49| m49_10(char *) = InitializeParameter[chars] : &:r49_9
|
||||
# 49| valnum = m49_10
|
||||
# 49| r49_11(char *) = Load : &:r49_9, m49_10
|
||||
# 49| valnum = m49_10
|
||||
# 49| m49_12(unknown) = InitializeIndirection[chars] : &:r49_11
|
||||
# 49| valnum = unique
|
||||
# 49| m49_13(unknown) = Chi : total:m49_8, partial:m49_12
|
||||
# 49| r49_8(glval<char *>) = VariableAddress[chars] :
|
||||
# 49| valnum = r49_8
|
||||
# 49| m49_9(char *) = InitializeParameter[chars] : &:r49_8
|
||||
# 49| valnum = m49_9
|
||||
# 49| r49_10(char *) = Load : &:r49_8, m49_9
|
||||
# 49| valnum = m49_9
|
||||
# 49| m49_11(unknown) = InitializeIndirection[chars] : &:r49_10
|
||||
# 49| valnum = unique
|
||||
# 50| r50_1(glval<char *>) = VariableAddress[ptr] :
|
||||
# 50| valnum = r50_1
|
||||
@@ -395,7 +389,7 @@ test.cpp:
|
||||
# 53| valnum = r49_4
|
||||
# 53| r53_3(char *) = Load : &:r53_2, m49_5
|
||||
# 53| valnum = m49_5
|
||||
# 53| r53_4(char) = Load : &:r53_3, ~m49_13
|
||||
# 53| r53_4(char) = Load : &:r53_3, ~m49_7
|
||||
# 53| valnum = unique
|
||||
# 53| r53_5(int) = Convert : r53_4
|
||||
# 53| valnum = unique
|
||||
@@ -409,13 +403,13 @@ test.cpp:
|
||||
|
||||
# 55| Block 2
|
||||
# 55| r55_1(glval<char *>) = VariableAddress[chars] :
|
||||
# 55| valnum = r49_9
|
||||
# 55| r55_2(char *) = Load : &:r55_1, m49_10
|
||||
# 55| valnum = m49_10
|
||||
# 55| valnum = r49_8
|
||||
# 55| r55_2(char *) = Load : &:r55_1, m49_9
|
||||
# 55| valnum = m49_9
|
||||
# 55| r55_3(glval<char *>) = VariableAddress[ptr] :
|
||||
# 55| valnum = r50_1
|
||||
# 55| m55_4(char *) = Store : &:r55_3, r55_2
|
||||
# 55| valnum = m49_10
|
||||
# 55| valnum = m49_9
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 56| Block 3
|
||||
@@ -425,7 +419,7 @@ test.cpp:
|
||||
# 56| valnum = r50_1
|
||||
# 56| r56_3(char *) = Load : &:r56_2, m56_1
|
||||
# 56| valnum = m56_1
|
||||
# 56| r56_4(char) = Load : &:r56_3, ~m49_13
|
||||
# 56| r56_4(char) = Load : &:r56_3, ~m49_2
|
||||
# 56| valnum = unique
|
||||
# 56| r56_5(int) = Convert : r56_4
|
||||
# 56| valnum = unique
|
||||
@@ -433,7 +427,7 @@ test.cpp:
|
||||
# 56| valnum = r49_4
|
||||
# 56| r56_7(char *) = Load : &:r56_6, m49_5
|
||||
# 56| valnum = m49_5
|
||||
# 56| r56_8(char) = Load : &:r56_7, ~m49_13
|
||||
# 56| r56_8(char) = Load : &:r56_7, ~m49_7
|
||||
# 56| valnum = unique
|
||||
# 56| r56_9(int) = Convert : r56_8
|
||||
# 56| valnum = unique
|
||||
@@ -448,7 +442,7 @@ test.cpp:
|
||||
# 56| valnum = r50_1
|
||||
# 56| r56_13(char *) = Load : &:r56_12, m56_1
|
||||
# 56| valnum = m56_1
|
||||
# 56| r56_14(char) = Load : &:r56_13, ~m49_13
|
||||
# 56| r56_14(char) = Load : &:r56_13, ~m49_2
|
||||
# 56| valnum = unique
|
||||
# 56| r56_15(int) = Convert : r56_14
|
||||
# 56| valnum = unique
|
||||
@@ -478,7 +472,7 @@ test.cpp:
|
||||
# 59| valnum = r50_1
|
||||
# 59| r59_2(char *) = Load : &:r59_1, m56_1
|
||||
# 59| valnum = m56_1
|
||||
# 59| r59_3(char) = Load : &:r59_2, ~m49_13
|
||||
# 59| r59_3(char) = Load : &:r59_2, ~m49_2
|
||||
# 59| valnum = unique
|
||||
# 59| r59_4(int) = Convert : r59_3
|
||||
# 59| valnum = unique
|
||||
@@ -517,14 +511,14 @@ test.cpp:
|
||||
# 65| valnum = m53_1
|
||||
# 65| m65_4(unsigned int) = Store : &:r65_1, r65_3
|
||||
# 65| valnum = m53_1
|
||||
# 49| v49_14(void) = ReturnIndirection : &:r49_6, ~m49_13
|
||||
# 49| v49_15(void) = ReturnIndirection : &:r49_11, ~m49_13
|
||||
# 49| r49_16(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 49| v49_12(void) = ReturnIndirection : &:r49_6, m49_7
|
||||
# 49| v49_13(void) = ReturnIndirection : &:r49_10, m49_11
|
||||
# 49| r49_14(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 49| valnum = r65_1
|
||||
# 49| v49_17(void) = ReturnValue : &:r49_16, m65_4
|
||||
# 49| v49_18(void) = UnmodeledUse : mu*
|
||||
# 49| v49_19(void) = AliasedUse : ~m49_13
|
||||
# 49| v49_20(void) = ExitFunction :
|
||||
# 49| v49_15(void) = ReturnValue : &:r49_14, m65_4
|
||||
# 49| v49_16(void) = UnmodeledUse : mu*
|
||||
# 49| v49_17(void) = AliasedUse : ~m49_2
|
||||
# 49| v49_18(void) = ExitFunction :
|
||||
|
||||
# 75| void test04(two_values*)
|
||||
# 75| Block 0
|
||||
@@ -541,17 +535,15 @@ test.cpp:
|
||||
# 75| valnum = m75_5
|
||||
# 75| m75_7(unknown) = InitializeIndirection[vals] : &:r75_6
|
||||
# 75| valnum = unique
|
||||
# 75| m75_8(unknown) = Chi : total:m75_2, partial:m75_7
|
||||
# 75| valnum = unique
|
||||
# 77| r77_1(glval<signed short>) = VariableAddress[v] :
|
||||
# 77| valnum = r77_1
|
||||
# 77| r77_2(glval<unknown>) = FunctionAddress[getAValue] :
|
||||
# 77| valnum = unique
|
||||
# 77| r77_3(int) = Call : func:r77_2
|
||||
# 77| valnum = unique
|
||||
# 77| m77_4(unknown) = ^CallSideEffect : ~m75_8
|
||||
# 77| m77_4(unknown) = ^CallSideEffect : ~m75_2
|
||||
# 77| valnum = unique
|
||||
# 77| m77_5(unknown) = Chi : total:m75_8, partial:m77_4
|
||||
# 77| m77_5(unknown) = Chi : total:m75_2, partial:m77_4
|
||||
# 77| valnum = unique
|
||||
# 77| r77_6(signed short) = Convert : r77_3
|
||||
# 77| valnum = r77_6
|
||||
@@ -569,7 +561,7 @@ test.cpp:
|
||||
# 79| valnum = m75_5
|
||||
# 79| r79_6(glval<signed short>) = FieldAddress[val1] : r79_5
|
||||
# 79| valnum = unique
|
||||
# 79| r79_7(signed short) = Load : &:r79_6, ~m77_5
|
||||
# 79| r79_7(signed short) = Load : &:r79_6, ~m75_7
|
||||
# 79| valnum = unique
|
||||
# 79| r79_8(int) = Convert : r79_7
|
||||
# 79| valnum = unique
|
||||
@@ -579,7 +571,7 @@ test.cpp:
|
||||
# 79| valnum = m75_5
|
||||
# 79| r79_11(glval<signed short>) = FieldAddress[val2] : r79_10
|
||||
# 79| valnum = unique
|
||||
# 79| r79_12(signed short) = Load : &:r79_11, ~m77_5
|
||||
# 79| r79_12(signed short) = Load : &:r79_11, ~m75_7
|
||||
# 79| valnum = unique
|
||||
# 79| r79_13(int) = Convert : r79_12
|
||||
# 79| valnum = unique
|
||||
@@ -612,11 +604,11 @@ test.cpp:
|
||||
# 82| m82_1(unknown) = Phi : from 0:~m77_5, from 1:~m80_4
|
||||
# 82| valnum = unique
|
||||
# 82| v82_2(void) = NoOp :
|
||||
# 75| v75_9(void) = ReturnIndirection : &:r75_6, ~m82_1
|
||||
# 75| v75_10(void) = ReturnVoid :
|
||||
# 75| v75_11(void) = UnmodeledUse : mu*
|
||||
# 75| v75_12(void) = AliasedUse : ~m82_1
|
||||
# 75| v75_13(void) = ExitFunction :
|
||||
# 75| v75_8(void) = ReturnIndirection : &:r75_6, m75_7
|
||||
# 75| v75_9(void) = ReturnVoid :
|
||||
# 75| v75_10(void) = UnmodeledUse : mu*
|
||||
# 75| v75_11(void) = AliasedUse : ~m82_1
|
||||
# 75| v75_12(void) = ExitFunction :
|
||||
|
||||
# 84| void test05(int, int, void*)
|
||||
# 84| Block 0
|
||||
@@ -641,8 +633,6 @@ test.cpp:
|
||||
# 84| valnum = m84_9
|
||||
# 84| m84_11(unknown) = InitializeIndirection[p] : &:r84_10
|
||||
# 84| valnum = unique
|
||||
# 84| m84_12(unknown) = Chi : total:m84_2, partial:m84_11
|
||||
# 84| valnum = unique
|
||||
# 86| r86_1(glval<int>) = VariableAddress[v] :
|
||||
# 86| valnum = r86_1
|
||||
# 86| m86_2(int) = Uninitialized[v] : &:r86_1
|
||||
@@ -671,11 +661,11 @@ test.cpp:
|
||||
# 88| m88_10(int) = Store : &:r88_9, r88_8
|
||||
# 88| valnum = m88_6
|
||||
# 89| v89_1(void) = NoOp :
|
||||
# 84| v84_13(void) = ReturnIndirection : &:r84_10, ~m84_12
|
||||
# 84| v84_14(void) = ReturnVoid :
|
||||
# 84| v84_15(void) = UnmodeledUse : mu*
|
||||
# 84| v84_16(void) = AliasedUse : ~m84_12
|
||||
# 84| v84_17(void) = ExitFunction :
|
||||
# 84| v84_12(void) = ReturnIndirection : &:r84_10, m84_11
|
||||
# 84| v84_13(void) = ReturnVoid :
|
||||
# 84| v84_14(void) = UnmodeledUse : mu*
|
||||
# 84| v84_15(void) = AliasedUse : ~m84_2
|
||||
# 84| v84_16(void) = ExitFunction :
|
||||
|
||||
# 88| Block 2
|
||||
# 88| r88_11(glval<int>) = VariableAddress[x] :
|
||||
@@ -748,8 +738,6 @@ test.cpp:
|
||||
# 104| valnum = m104_5
|
||||
# 104| m104_7(unknown) = InitializeIndirection[pd] : &:r104_6
|
||||
# 104| valnum = unique
|
||||
# 104| m104_8(unknown) = Chi : total:m104_2, partial:m104_7
|
||||
# 104| valnum = unique
|
||||
# 105| r105_1(glval<int>) = VariableAddress[x] :
|
||||
# 105| valnum = unique
|
||||
# 105| r105_2(glval<Derived *>) = VariableAddress[pd] :
|
||||
@@ -760,7 +748,7 @@ test.cpp:
|
||||
# 105| valnum = r105_4
|
||||
# 105| r105_5(glval<int>) = FieldAddress[b] : r105_4
|
||||
# 105| valnum = r105_5
|
||||
# 105| r105_6(int) = Load : &:r105_5, ~m104_8
|
||||
# 105| r105_6(int) = Load : &:r105_5, ~m104_7
|
||||
# 105| valnum = r105_6
|
||||
# 105| m105_7(int) = Store : &:r105_1, r105_6
|
||||
# 105| valnum = r105_6
|
||||
@@ -782,7 +770,7 @@ test.cpp:
|
||||
# 107| valnum = r105_4
|
||||
# 107| r107_4(glval<int>) = FieldAddress[b] : r107_3
|
||||
# 107| valnum = r105_5
|
||||
# 107| r107_5(int) = Load : &:r107_4, ~m104_8
|
||||
# 107| r107_5(int) = Load : &:r107_4, ~m104_7
|
||||
# 107| valnum = r107_5
|
||||
# 107| m107_6(int) = Store : &:r107_1, r107_5
|
||||
# 107| valnum = r107_5
|
||||
@@ -794,13 +782,13 @@ test.cpp:
|
||||
# 109| valnum = r107_5
|
||||
# 109| m109_4(int) = Store : &:r109_1, r109_3
|
||||
# 109| valnum = r107_5
|
||||
# 104| v104_9(void) = ReturnIndirection : &:r104_6, ~m104_8
|
||||
# 104| r104_10(glval<int>) = VariableAddress[#return] :
|
||||
# 104| v104_8(void) = ReturnIndirection : &:r104_6, m104_7
|
||||
# 104| r104_9(glval<int>) = VariableAddress[#return] :
|
||||
# 104| valnum = r109_1
|
||||
# 104| v104_11(void) = ReturnValue : &:r104_10, m109_4
|
||||
# 104| v104_12(void) = UnmodeledUse : mu*
|
||||
# 104| v104_13(void) = AliasedUse : ~m104_8
|
||||
# 104| v104_14(void) = ExitFunction :
|
||||
# 104| v104_10(void) = ReturnValue : &:r104_9, m109_4
|
||||
# 104| v104_11(void) = UnmodeledUse : mu*
|
||||
# 104| v104_12(void) = AliasedUse : ~m104_2
|
||||
# 104| v104_13(void) = ExitFunction :
|
||||
|
||||
# 112| void test06()
|
||||
# 112| Block 0
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
| class.cpp:91:27:91:29 | num | class.cpp:87:17:87:19 | num | V |
|
||||
| class.cpp:100:24:100:34 | type mention | class.cpp:94:18:94:28 | string_type | T |
|
||||
| class.cpp:105:1:105:15 | type mention | class.cpp:97:7:97:21 | StringContainer | T |
|
||||
| class.cpp:106:9:106:23 | type mention | class.cpp:100:2:100:16 | StringContainer | M |
|
||||
| class.cpp:106:9:106:23 | type mention | class.cpp:97:7:97:21 | StringContainer | T |
|
||||
| class.cpp:106:25:106:27 | STR(x) | class.cpp:95:1:95:18 | #define STR(x) L ## x | X |
|
||||
| class.cpp:117:2:117:29 | type mention | class.cpp:109:7:109:34 | myClassWithConstructorParams | T |
|
||||
| class.cpp:117:37:117:37 | a | class.cpp:115:27:115:27 | a | V |
|
||||
|
||||
Reference in New Issue
Block a user