Merge pull request #474 from rdmarsh2/rdmarsh/cpp/call-side-effect

C++: Initital aliased SSA with Chi nodes and function side effects
This commit is contained in:
Jonas Jensen
2018-11-29 18:31:29 +01:00
committed by GitHub
40 changed files with 11742 additions and 9038 deletions

View File

@@ -542,6 +542,42 @@ class FunctionNode extends ASTNode {
}
}
/**
* A node representing an `ClassAggregateLiteral`.
*/
class ClassAggregateLiteralNode extends ExprNode {
ClassAggregateLiteral list;
ClassAggregateLiteralNode() {
list = ast
}
override string getChildEdgeLabel(int childIndex) {
exists(Field field |
list.getFieldExpr(field) = list.getChild(childIndex) and
result = "." + field.getName()
)
}
}
/**
* A node representing an `ArrayAggregateLiteral`.
*/
class ArrayAggregateLiteralNode extends ExprNode {
ArrayAggregateLiteral list;
ArrayAggregateLiteralNode() {
list = ast
}
override string getChildEdgeLabel(int childIndex) {
exists(int elementIndex |
list.getElementExpr(elementIndex) = list.getChild(childIndex) and
result = "[" + elementIndex.toString() + "]"
)
}
}
query predicate nodes(PrintASTNode node, string key, string value) {
node.shouldPrint() and
value = node.getProperty(key)

View File

@@ -1,10 +1,15 @@
import cpp
newtype TMemoryAccessKind =
private newtype TMemoryAccessKind =
TIndirectMemoryAccess() or
TIndirectMayMemoryAccess() or
TBufferMemoryAccess() or
TBufferMayMemoryAccess() or
TEscapedMemoryAccess() or
TPhiMemoryAccess() or
TUnmodeledMemoryAccess()
TUnmodeledMemoryAccess() or
TChiTotalMemoryAccess() or
TChiPartialMemoryAccess()
/**
* Describes the set of memory locations memory accessed by a memory operand or
@@ -15,8 +20,8 @@ class MemoryAccessKind extends TMemoryAccessKind {
}
/**
* The operand or result accesses memory at the address specified by the
* `AddressOperand` on the same instruction.
* The operand or result accesses memory at the address specified by the `AddressOperand` on the
* same instruction.
*/
class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
override string toString() {
@@ -24,6 +29,38 @@ class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
}
}
/**
* The operand or result may access some, all, or none of the memory at the address specified by the
* `AddressOperand` on the same instruction.
*/
class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess {
override string toString() {
result = "indirect(may)"
}
}
/**
* The operand or result accesses memory starting at the address specified by the `AddressOperand`
* on the same instruction, accessing a number of consecutive elements given by the
* `BufferSizeOperand`.
*/
class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
override string toString() {
result = "buffer"
}
}
/**
* The operand or result may access some, all, or none of the memory starting at the address
* specified by the `AddressOperand` on the same instruction, accessing a number of consecutive
* elements given by the `BufferSizeOperand`.
*/
class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess {
override string toString() {
result = "buffer(may)"
}
}
/**
* The operand or result accesses all memory whose address has escaped.
*/
@@ -43,6 +80,26 @@ class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess {
}
}
/**
* The operand is a ChiTotal operand, which accesses the same memory as its
* definition.
*/
class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
override string toString() {
result = "chi(total)"
}
}
/**
* The operand is a ChiPartial operand, which accesses the same memory as its
* definition.
*/
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
override string toString() {
result = "chi(partial)"
}
}
/**
* The operand accesses memory not modeled in SSA. Used only on the result of
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.

View File

@@ -54,11 +54,21 @@ private newtype TOpcode =
TUnwind() or
TUnmodeledDefinition() or
TUnmodeledUse() or
TAliasedDefinition() or
TPhi() or
TVarArgsStart() or
TVarArgsEnd() or
TVarArg() or
TVarArgCopy()
TVarArgCopy() or
TCallSideEffect() or
TCallReadSideEffect() or
TIndirectReadSideEffect() or
TIndirectWriteSideEffect() or
TIndirectMayWriteSideEffect() or
TBufferReadSideEffect() or
TBufferWriteSideEffect() or
TBufferMayWriteSideEffect() or
TChi()
class Opcode extends TOpcode {
string toString() {
@@ -92,6 +102,29 @@ abstract class OpcodeWithCondition extends Opcode {}
abstract class BuiltInOpcode extends Opcode {}
abstract class SideEffectOpcode extends Opcode {}
/**
* An opcode that reads from a set of memory locations as a side effect.
*/
abstract class ReadSideEffectOpcode extends SideEffectOpcode {}
/**
* An opcode that writes to a set of memory locations as a side effect.
*/
abstract class WriteSideEffectOpcode extends SideEffectOpcode {}
/**
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
* as a read of the original contents, plus a "may" write of the new contents.
*/
abstract class MayWriteSideEffectOpcode extends SideEffectOpcode {}
/**
* An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
*/
abstract class BufferAccessOpcode extends MemoryAccessOpcode {}
module Opcode {
class NoOp extends Opcode, TNoOp { override final string toString() { result = "NoOp" } }
class Uninitialized extends MemoryAccessOpcode, TUninitialized { override final string toString() { result = "Uninitialized" } }
@@ -148,9 +181,19 @@ module Opcode {
class Unwind extends Opcode, TUnwind { override final string toString() { result = "Unwind" } }
class UnmodeledDefinition extends Opcode, TUnmodeledDefinition { override final string toString() { result = "UnmodeledDefinition" } }
class UnmodeledUse extends Opcode, TUnmodeledUse { override final string toString() { result = "UnmodeledUse" } }
class AliasedDefinition extends Opcode, TAliasedDefinition { override final string toString() { result = "AliasedDefinition" } }
class Phi extends Opcode, TPhi { override final string toString() { result = "Phi" } }
class VarArgsStart extends BuiltInOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } }
class VarArgsEnd extends BuiltInOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } }
class VarArg extends BuiltInOpcode, TVarArg { override final string toString() { result = "VarArg" } }
class VarArgCopy extends BuiltInOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } }
class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect { override final string toString() { result = "CallSideEffect" } }
class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect { override final string toString() { result = "CallReadSideEffect" } }
class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode, TIndirectReadSideEffect { override final string toString() { result = "IndirectReadSideEffect" } }
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode, TIndirectWriteSideEffect { override final string toString() { result = "IndirectWriteSideEffect" } }
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode, TIndirectMayWriteSideEffect { override final string toString() { result = "IndirectMayWriteSideEffect" } }
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
class Chi extends Opcode, TChi {override final string toString() { result = "Chi" } }
}

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
private import Construction::BlockConstruction
import Cached
class IRBlock extends TIRBlock {
final string toString() {
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
getAPredecessor().isReachableFromFunctionEntry()
}
}
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
(
count(Instruction predecessor |
instr = predecessor.getASuccessor()
) != 1 or // Multiple predecessors or no predecessor
exists(Instruction predecessor |
instr = predecessor.getASuccessor() and
strictcount(Instruction other |
other = predecessor.getASuccessor()
) > 1
) or // Predecessor has multiple successors
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
)
}
private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
private cached module Cached {
cached newtype TIRBlock =
MkIRBlock(Instruction firstInstr) {
startsBasicBlock(firstInstr)
}
/** Holds if `i2` follows `i1` in a `IRBlock`. */
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
not startsBasicBlock(i2)
}
/** Gets the index of `i` in its `IRBlock`. */
private int getMemberIndex(Instruction i) {
startsBasicBlock(i) and
result = 0
or
exists(Instruction iPrev |
adjacentInBlock(iPrev, i) and
result = getMemberIndex(iPrev) + 1
)
}
/** Holds if `i` is the `index`th instruction in `block`. */
cached Instruction getInstruction(TIRBlock block, int index) {
exists(Instruction first |
block = MkIRBlock(first) and
index = getMemberIndex(result) and
adjacentInBlock*(first, result)
)
}
cached int getInstructionCount(TIRBlock block) {
result = strictcount(getInstruction(block, _))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = predLast.getSuccessor(kind) and
succ = MkIRBlock(succFirst)
)
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
Instruction getFirstInstruction(TIRBlock block) {
block = MkIRBlock(result)
}

View File

@@ -33,11 +33,18 @@ module InstructionSanity {
) or
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
(
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
tag instanceof SideEffectOperandTag
)
)
)
}
@@ -162,9 +169,9 @@ class Instruction extends Construction::TInstruction {
*/
final string getOperationString() {
if exists(getImmediateString()) then
result = opcode.toString() + "[" + getImmediateString() + "]"
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
else
result = opcode.toString()
result = getOperationPrefix() + opcode.toString()
}
/**
@@ -174,6 +181,13 @@ class Instruction extends Construction::TInstruction {
none()
}
private string getOperationPrefix() {
if this instanceof SideEffectInstruction then
result = "^"
else
result = ""
}
private string getResultPrefix() {
if resultType instanceof VoidType then
result = "v"
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
}
}
/**
* An instruction that calls a function.
*/
class CallInstruction extends Instruction {
CallInstruction() {
opcode instanceof Opcode::Call
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
}
}
/**
* An instruction representing a side effect of a function call.
*/
class SideEffectInstruction extends Instruction {
SideEffectInstruction() {
opcode instanceof SideEffectOpcode
}
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be
* accessed by that call.
*/
class CallSideEffectInstruction extends SideEffectInstruction {
CallSideEffectInstruction() {
opcode instanceof Opcode::CallSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be read
* by that call.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() {
opcode instanceof Opcode::CallReadSideEffect
}
}
/**
* An instruction representing the read of an indirect parameter within a function call.
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() {
opcode instanceof Opcode::IndirectReadSideEffect
}
}
/**
* An instruction representing the read of an indirect buffer parameter within a function call.
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() {
opcode instanceof Opcode::BufferReadSideEffect
}
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
IndirectWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
BufferWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMemoryAccess
}
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMayMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
BufferMayWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
}
/**
* An instruction that throws an exception.
*/
@@ -1195,6 +1322,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
}
}
/**
* An instruction that initializes all escaped memory.
*/
class AliasedDefinitionInstruction extends Instruction {
AliasedDefinitionInstruction() {
opcode instanceof Opcode::AliasedDefinition
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() {
opcode instanceof Opcode::UnmodeledUse
@@ -1205,6 +1345,16 @@ class UnmodeledUseInstruction extends Instruction {
}
}
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
* each control flow predecessor of the block, with that operand representing the version of the
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
* runtime.
*/
class PhiInstruction extends Instruction {
PhiInstruction() {
opcode instanceof Opcode::Phi
@@ -1215,6 +1365,73 @@ class PhiInstruction extends Instruction {
}
}
/**
* An instruction representing the effect that a write to a memory may have on potential aliases of
* that memory.
*
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
* the previous state of all of the memory that might be alised by the memory write. The second
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
* memory write. The result of the `ChiInstruction` represents the same memory as
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
* the memory write.
*
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
* same memory:
* ```
* *p = 5;
* x = *q;
* ```
*
* The IR would look like:
* ```
* r1_1 = VariableAddress[p]
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
* r1_3 = Constant[5]
* m1_4 = Store r1_2, r1_3 // Store to `*p`
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
* r1_6 = VariableAddress[x]
* r1_7 = VariableAddress[q]
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
* m1_10 = Store r1_6, r1_9 // Store to x
* ```
*
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
* pointed to by `q`.
*
* For more information about how `Chi` instructions are used to model memory side effects, see
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
*/
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
/**
* Gets the operand that represents the previous state of all memory that might be aliased by the
* memory write.
*/
final Instruction getTotalOperand() {
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
}
/**
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartialOperand() {
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
}
}
class SideEffectOperand extends NonPhiOperand {
SideEffectOperand() {
this = TNonPhiOperand(_, sideEffectOperand(), _)
}
override MemoryAccessKind getMemoryAccess() {
instr instanceof CallSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof CallReadSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof IndirectReadSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectMayWriteSideEffectInstruction and
result instanceof IndirectMayMemoryAccess
or
instr instanceof BufferMayWriteSideEffectInstruction and
result instanceof BufferMayMemoryAccess
}
}
/**
* An operand of a `PhiInstruction`.
*/
@@ -358,3 +390,38 @@ class MemoryOperand extends Operand {
exists(getMemoryAccess())
}
}
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends Operand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
override string toString() {
result = "ChiTotal"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
}
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends Operand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
override string toString() {
result = "ChiPartial"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiPartialMemoryAccess
}
}

View File

@@ -0,0 +1,268 @@
import cpp
import AliasAnalysis
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private class IntValue = Ints::IntValue;
private newtype TVirtualVariable =
TVirtualIRVariable(IRVariable var) {
not variableAddressEscapes(var)
} or
TUnknownVirtualVariable(FunctionIR f)
private VirtualIRVariable getVirtualVariable(IRVariable var) {
result.getIRVariable() = var
}
class VirtualVariable extends TVirtualVariable {
string toString() {
none()
}
string getUniqueId() {
none()
}
Type getType() {
none()
}
}
class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable {
IRVariable var;
VirtualIRVariable() {
this = TVirtualIRVariable(var)
}
override final string toString() {
result = var.toString()
}
final IRVariable getIRVariable() {
result = var
}
// REVIEW: This should just be on MemoryAccess
override final Type getType() {
result = var.getType()
}
override final string getUniqueId() {
result = var.getUniqueId()
}
}
class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable {
FunctionIR f;
UnknownVirtualVariable() {
this = TUnknownVirtualVariable(f)
}
override final string toString() {
result = "UnknownVvar(" + f + ")"
}
override final string getUniqueId() {
result = "UnknownVvar(" + f + ")"
}
override final Type getType() {
result instanceof UnknownType
}
final FunctionIR getFunctionIR() {
result = f
}
}
private newtype TMemoryAccess =
TVariableMemoryAccess(VirtualIRVariable vvar, IntValue offset, IntValue size) {
exists(Instruction instr |
exists(MemoryAccessKind mak | instr.getResultMemoryAccess() = mak and not mak instanceof PhiMemoryAccess) and
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), vvar.getIRVariable(), offset) and
if exists(instr.getResultSize())
then instr.getResultSize() = size
else size = Ints::unknown()
)
}
or
TUnknownMemoryAccess(UnknownVirtualVariable uvv) or
TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv)
private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue offset, IntValue size) {
result.getVirtualVariable() = getVirtualVariable(var) and
result.getOffset() = offset and
result.getSize() = size
}
class MemoryAccess extends TMemoryAccess {
string toString() {
none()
}
VirtualVariable getVirtualVariable() {
none()
}
predicate isPartialMemoryAccess() {
none()
}
}
class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
VirtualIRVariable vvar;
IntValue offset;
IntValue size;
VariableMemoryAccess() {
this = TVariableMemoryAccess(vvar, offset, size)
}
override final string toString() {
result = vvar.toString()
}
final override VirtualVariable getVirtualVariable() {
result = vvar
}
IntValue getOffset() {
result = offset
}
IntValue getSize() {
result = size
}
final override predicate isPartialMemoryAccess() {
getOffset() != 0
or
getSize() != vvar.getType().getSize()
}
}
class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess {
UnknownVirtualVariable vvar;
UnknownMemoryAccess() {
this = TUnknownMemoryAccess(vvar)
}
final override string toString() {
result = vvar.toString()
}
final override VirtualVariable getVirtualVariable() {
result = vvar
}
final override predicate isPartialMemoryAccess() {
any()
}
Type getType() {
result instanceof UnknownType
}
}
class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess {
UnknownVirtualVariable vvar;
TotalUnknownMemoryAccess() {
this = TTotalUnknownMemoryAccess(vvar)
}
final override string toString() {
result = vvar.toString()
}
final override VirtualVariable getVirtualVariable() {
result = vvar
}
Type getType() {
result instanceof UnknownType
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def instanceof VariableMemoryAccess and
def = use and
result instanceof MustExactlyOverlap
or
exists(VariableMemoryAccess defVMA, VariableMemoryAccess useVMA, int defOffset, int defEnd,
int useOffset, int useEnd |
defVMA = def and
useVMA = use and
defVMA.getVirtualVariable() = useVMA.getVirtualVariable() and
defVMA != useVMA and
defOffset = Ints::getValue(defVMA.getOffset()) and
defEnd = Ints::getValue(Ints::add(defVMA.getOffset(), defVMA.getSize())) and
useOffset = Ints::getValue(useVMA.getOffset()) and
useEnd = Ints::getValue(Ints::add(useVMA.getOffset(), useVMA.getSize()))
|
defOffset <= useOffset and
defEnd >= useEnd and
result instanceof MustTotallyOverlap
or
defOffset > useOffset and
defOffset < useEnd and
result instanceof MayPartiallyOverlap
or
defOffset = useOffset and
defEnd < useEnd and
result instanceof MayPartiallyOverlap
)
or
exists(UnknownVirtualVariable uvv |
def = TUnknownMemoryAccess(uvv) and
uvv = use.getVirtualVariable() and
result instanceof MayPartiallyOverlap
)
or
exists(UnknownVirtualVariable uvv |
def = TTotalUnknownMemoryAccess(uvv) and
uvv = use.getVirtualVariable() and
result instanceof MustTotallyOverlap
)
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
exists(instr.getResultMemoryAccess()) and
if exists(IRVariable var, IntValue i |
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i)
)
then exists(IRVariable var, IntValue i |
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(), var, i) and
result = getVariableMemoryAccess(var, i, instr.getResultSize())
)
else (
result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and
not instr instanceof UnmodeledDefinitionInstruction and
not instr instanceof AliasedDefinitionInstruction
or
result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getFunctionIR())) and
instr instanceof AliasedDefinitionInstruction
)
}
MemoryAccess getOperandMemoryAccess(Operand operand) {
exists(operand.getMemoryAccess()) and
if exists(IRVariable var, IntValue i |
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i)
)
then exists(IRVariable var, IntValue i |
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, i) and
result = getVariableMemoryAccess(var, i, operand.getDefinitionInstruction().getResultSize())
)
else (
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getInstruction().getFunctionIR())) and
not operand.getInstruction() instanceof UnmodeledUseInstruction
)
}

View File

@@ -1,38 +0,0 @@
import SSAConstructionInternal
private import SSAConstruction as Construction
private import NewIR
import Cached
private cached module Cached {
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
private OldIR::IRBlock getOldBlock(TIRBlock block) {
block = MkIRBlock(result)
}
cached Instruction getInstruction(TIRBlock block, int index) {
Construction::getOldInstruction(result) =
getOldBlock(block).getInstruction(index)
}
cached int getInstructionCount(TIRBlock block) {
result = getOldBlock(block).getInstructionCount()
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
}
cached Instruction getFirstInstruction(TIRBlock block) {
Construction::getOldInstruction(result) =
getOldBlock(block).getFirstInstruction()
}
}

View File

@@ -3,7 +3,6 @@ import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import NewIR
import IRBlockConstruction as BlockConstruction
import Cached
cached private module Cached {
@@ -18,6 +17,10 @@ cached private module Cached {
} or
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
hasPhiNode(vvar, block)
} or
ChiTag(OldIR::Instruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
}
cached class InstructionTagType extends TInstructionTag {
@@ -40,12 +43,28 @@ cached private module Cached {
getOldInstruction(result) = instr
}
/**
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
* corresponding to `instr` if there is no `Chi` node.
*/
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
result = getChiInstruction(instr)
or
not exists(getChiInstruction(instr)) and
result = getNewInstruction(instr)
}
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
Alias::VirtualVariable vvar) {
result.getFunction() = func and
result.getAST() = oldBlock.getFirstInstruction().getAST() and
result.getTag() = PhiTag(vvar, oldBlock)
}
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
hasChiNode(_, instr) and
result.getTag() = ChiTag(instr)
}
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
result.getFunction() = var.getFunction() and
@@ -92,6 +111,15 @@ cached private module Cached {
tag = PhiTag(vvar, block) and
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
hasChiNode(vvar, instr) and
instr.getFunction() = func and
opcode instanceof Opcode::Chi and
ast = instr.getAST() and
tag = ChiTag(instr) and
resultType = vvar.getType() and
isGLValue = false
)
}
@@ -125,11 +153,11 @@ cached private module Cached {
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewInstruction(defBlock.getInstruction(defIndex))
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
)
)
)
else (
result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction()
)
@@ -146,7 +174,17 @@ cached private module Cached {
)
else
result = getNewInstruction(oldOperand.getDefinitionInstruction())
)
) or
instruction.getTag() = ChiTag(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag
or
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
result instanceof UnmodeledDefinitionInstruction and
instruction.getFunction() = result.getFunction()
or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction)
}
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
@@ -160,12 +198,27 @@ cached private module Cached {
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
if defIndex >= 0 then
result = getNewInstruction(defBlock.getInstruction(defIndex))
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(instr.getFunction(), defBlock, vvar)
)
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
ChiTag(oldInstr) = chiInstr.getTag() and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar)
)
}
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldIR::IRBlock oldBlock |
instr.getTag() = PhiTag(_, oldBlock) and
@@ -181,8 +234,24 @@ cached private module Cached {
result = getOldInstruction(instruction).getUnconvertedResultExpression()
}
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
* the new instructions generated from the successors of the old instruction
*/
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
if(hasChiNode(_, getOldInstruction(instruction)))
then
result = getChiInstruction(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
or
exists(OldIR::Instruction oldInstruction |
instruction = getChiInstruction(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
}
cached IRVariable getInstructionVariable(Instruction instruction) {
@@ -228,6 +297,18 @@ cached private module Cached {
)
}
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
exists(OldIR::SideEffectInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
result = getNewInstruction(oldInstruction.getPrimaryInstruction())
)
or
exists(OldIR::Instruction oldInstruction |
instruction.getTag() = ChiTag(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
block.getInstruction(index) = instr and
@@ -253,7 +334,16 @@ cached private module Cached {
private predicate hasUse(Alias::VirtualVariable vvar,
OldIR::Instruction use, OldIR::IRBlock block, int index) {
exists(Alias::MemoryAccess access |
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
(
access = Alias::getOperandMemoryAccess(use.getAnOperand())
or
/*
* a partial write to a virtual variable is going to generate a use of that variable when
* Chi nodes are inserted, so we need to mark it as a use in the old IR
*/
access = Alias::getResultMemoryAccess(use) and
access.isPartialMemoryAccess()
) and
block.getInstruction(index) = use and
vvar = access.getVirtualVariable()
)
@@ -384,6 +474,15 @@ cached private module Cached {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
}
private predicate hasChiNode(Alias::VirtualVariable vvar,
OldIR::Instruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
ma.getVirtualVariable() = vvar
)
}
}
import CachedForDebugging

View File

@@ -1,3 +1,3 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
import SimpleSSA as Alias
import AliasedSSA as Alias

View File

@@ -1,84 +0,0 @@
import SimpleSSAInternal
import cpp
import Alias
private import InputIR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
private newtype TVirtualVariable =
MkVirtualVariable(IRVariable var) {
not variableAddressEscapes(var)
}
private VirtualVariable getVirtualVariable(IRVariable var) {
result.getIRVariable() = var
}
class VirtualVariable extends TVirtualVariable {
IRVariable var;
VirtualVariable() {
this = MkVirtualVariable(var)
}
final string toString() {
result = var.toString()
}
final IRVariable getIRVariable() {
result = var
}
// REVIEW: This should just be on MemoryAccess
final Type getType() {
result = var.getType()
}
final string getUniqueId() {
result = var.getUniqueId()
}
}
private newtype TMemoryAccess =
MkMemoryAccess(VirtualVariable vvar)
private MemoryAccess getMemoryAccess(IRVariable var) {
result.getVirtualVariable() = getVirtualVariable(var)
}
class MemoryAccess extends TMemoryAccess {
VirtualVariable vvar;
MemoryAccess() {
this = MkMemoryAccess(vvar)
}
string toString() {
result = vvar.toString()
}
VirtualVariable getVirtualVariable() {
result = vvar
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def.getVirtualVariable() = use.getVirtualVariable() and
result instanceof MustExactlyOverlap
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
exists(IRVariable var |
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
var, 0) and
result = getMemoryAccess(var)
)
}
MemoryAccess getOperandMemoryAccess(Operand operand) {
exists(IRVariable var |
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
result = getMemoryAccess(var)
)
}

View File

@@ -1,3 +0,0 @@
import AliasAnalysis as Alias
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as InputIR

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
private import Construction::BlockConstruction
import Cached
class IRBlock extends TIRBlock {
final string toString() {
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
getAPredecessor().isReachableFromFunctionEntry()
}
}
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
(
count(Instruction predecessor |
instr = predecessor.getASuccessor()
) != 1 or // Multiple predecessors or no predecessor
exists(Instruction predecessor |
instr = predecessor.getASuccessor() and
strictcount(Instruction other |
other = predecessor.getASuccessor()
) > 1
) or // Predecessor has multiple successors
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
)
}
private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
private cached module Cached {
cached newtype TIRBlock =
MkIRBlock(Instruction firstInstr) {
startsBasicBlock(firstInstr)
}
/** Holds if `i2` follows `i1` in a `IRBlock`. */
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
not startsBasicBlock(i2)
}
/** Gets the index of `i` in its `IRBlock`. */
private int getMemberIndex(Instruction i) {
startsBasicBlock(i) and
result = 0
or
exists(Instruction iPrev |
adjacentInBlock(iPrev, i) and
result = getMemberIndex(iPrev) + 1
)
}
/** Holds if `i` is the `index`th instruction in `block`. */
cached Instruction getInstruction(TIRBlock block, int index) {
exists(Instruction first |
block = MkIRBlock(first) and
index = getMemberIndex(result) and
adjacentInBlock*(first, result)
)
}
cached int getInstructionCount(TIRBlock block) {
result = strictcount(getInstruction(block, _))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = predLast.getSuccessor(kind) and
succ = MkIRBlock(succFirst)
)
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
Instruction getFirstInstruction(TIRBlock block) {
block = MkIRBlock(result)
}

View File

@@ -33,11 +33,18 @@ module InstructionSanity {
) or
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
(
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
tag instanceof SideEffectOperandTag
)
)
)
}
@@ -162,9 +169,9 @@ class Instruction extends Construction::TInstruction {
*/
final string getOperationString() {
if exists(getImmediateString()) then
result = opcode.toString() + "[" + getImmediateString() + "]"
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
else
result = opcode.toString()
result = getOperationPrefix() + opcode.toString()
}
/**
@@ -174,6 +181,13 @@ class Instruction extends Construction::TInstruction {
none()
}
private string getOperationPrefix() {
if this instanceof SideEffectInstruction then
result = "^"
else
result = ""
}
private string getResultPrefix() {
if resultType instanceof VoidType then
result = "v"
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
}
}
/**
* An instruction that calls a function.
*/
class CallInstruction extends Instruction {
CallInstruction() {
opcode instanceof Opcode::Call
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
}
}
/**
* An instruction representing a side effect of a function call.
*/
class SideEffectInstruction extends Instruction {
SideEffectInstruction() {
opcode instanceof SideEffectOpcode
}
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be
* accessed by that call.
*/
class CallSideEffectInstruction extends SideEffectInstruction {
CallSideEffectInstruction() {
opcode instanceof Opcode::CallSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be read
* by that call.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() {
opcode instanceof Opcode::CallReadSideEffect
}
}
/**
* An instruction representing the read of an indirect parameter within a function call.
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() {
opcode instanceof Opcode::IndirectReadSideEffect
}
}
/**
* An instruction representing the read of an indirect buffer parameter within a function call.
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() {
opcode instanceof Opcode::BufferReadSideEffect
}
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
IndirectWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
BufferWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMemoryAccess
}
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMayMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
BufferMayWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
}
/**
* An instruction that throws an exception.
*/
@@ -1195,6 +1322,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
}
}
/**
* An instruction that initializes all escaped memory.
*/
class AliasedDefinitionInstruction extends Instruction {
AliasedDefinitionInstruction() {
opcode instanceof Opcode::AliasedDefinition
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() {
opcode instanceof Opcode::UnmodeledUse
@@ -1205,6 +1345,16 @@ class UnmodeledUseInstruction extends Instruction {
}
}
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
* each control flow predecessor of the block, with that operand representing the version of the
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
* runtime.
*/
class PhiInstruction extends Instruction {
PhiInstruction() {
opcode instanceof Opcode::Phi
@@ -1215,6 +1365,73 @@ class PhiInstruction extends Instruction {
}
}
/**
* An instruction representing the effect that a write to a memory may have on potential aliases of
* that memory.
*
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
* the previous state of all of the memory that might be alised by the memory write. The second
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
* memory write. The result of the `ChiInstruction` represents the same memory as
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
* the memory write.
*
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
* same memory:
* ```
* *p = 5;
* x = *q;
* ```
*
* The IR would look like:
* ```
* r1_1 = VariableAddress[p]
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
* r1_3 = Constant[5]
* m1_4 = Store r1_2, r1_3 // Store to `*p`
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
* r1_6 = VariableAddress[x]
* r1_7 = VariableAddress[q]
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
* m1_10 = Store r1_6, r1_9 // Store to x
* ```
*
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
* pointed to by `q`.
*
* For more information about how `Chi` instructions are used to model memory side effects, see
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
*/
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
/**
* Gets the operand that represents the previous state of all memory that might be aliased by the
* memory write.
*/
final Instruction getTotalOperand() {
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
}
/**
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartialOperand() {
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
}
}
class SideEffectOperand extends NonPhiOperand {
SideEffectOperand() {
this = TNonPhiOperand(_, sideEffectOperand(), _)
}
override MemoryAccessKind getMemoryAccess() {
instr instanceof CallSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof CallReadSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof IndirectReadSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectMayWriteSideEffectInstruction and
result instanceof IndirectMayMemoryAccess
or
instr instanceof BufferMayWriteSideEffectInstruction and
result instanceof BufferMayMemoryAccess
}
}
/**
* An operand of a `PhiInstruction`.
*/
@@ -358,3 +390,38 @@ class MemoryOperand extends Operand {
exists(getMemoryAccess())
}
}
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends Operand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
override string toString() {
result = "ChiTotal"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
}
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends Operand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
override string toString() {
result = "ChiPartial"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiPartialMemoryAccess
}
}

View File

@@ -1,83 +0,0 @@
import semmle.code.cpp.ir.implementation.raw.Instruction
import cpp
import semmle.code.cpp.ir.implementation.EdgeKind
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
(
count(Instruction predecessor |
instr = predecessor.getASuccessor()
) != 1 or // Multiple predecessors or no predecessor
exists(Instruction predecessor |
instr = predecessor.getASuccessor() and
strictcount(Instruction other |
other = predecessor.getASuccessor()
) > 1
) or // Predecessor has multiple successors
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
)
}
private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
import Cached
private cached module Cached {
cached newtype TIRBlock =
MkIRBlock(Instruction firstInstr) {
startsBasicBlock(firstInstr)
}
/** Holds if `i2` follows `i1` in a `IRBlock`. */
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
not startsBasicBlock(i2)
}
/** Gets the index of `i` in its `IRBlock`. */
private int getMemberIndex(Instruction i) {
startsBasicBlock(i) and
result = 0
or
exists(Instruction iPrev |
adjacentInBlock(iPrev, i) and
result = getMemberIndex(iPrev) + 1
)
}
/** Holds if `i` is the `index`th instruction in `block`. */
cached Instruction getInstruction(TIRBlock block, int index) {
exists(Instruction first |
block = MkIRBlock(first) and
index = getMemberIndex(result) and
adjacentInBlock*(first, result)
)
}
cached int getInstructionCount(TIRBlock block) {
result = strictcount(getInstruction(block, _))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = predLast.getSuccessor(kind) and
succ = MkIRBlock(succFirst)
)
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
Instruction getFirstInstruction(TIRBlock block) {
block = MkIRBlock(result)
}

View File

@@ -1,6 +1,5 @@
import cpp
import semmle.code.cpp.ir.implementation.raw.IR
import IRBlockConstruction as BlockConstruction
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
@@ -168,6 +167,13 @@ cached private module Cached {
result = element.getInstructionResultSize(tag)
)
}
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
exists(TranslatedElement element, InstructionTag tag |
instructionOrigin(instruction, element, tag) and
result = element.getPrimaryInstructionForSideEffect(tag)
)
}
}
import CachedForDebugging

View File

@@ -40,9 +40,11 @@ newtype TInstructionTag =
ExitFunctionTag() or
UnmodeledDefinitionTag() or
UnmodeledUseTag() or
AliasedDefinitionTag() or
SwitchBranchTag() or
CallTargetTag() or
CallTag() or
CallSideEffectTag() or
AllocationSizeTag() or
AllocationElementSizeTag() or
AllocationExtentConvertTag() or
@@ -61,6 +63,7 @@ newtype TInstructionTag =
CatchTag() or
ThrowTag() or
UnwindTag() or
InitializerUninitializedTag() or
InitializerFieldAddressTag(Field field) {
fieldIsInitialized(field)
} or
@@ -91,6 +94,7 @@ string getInstructionTagId(TInstructionTag tag) {
tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load)
tag = InitializerVariableAddressTag() and result = "InitVarAddr" or
tag = InitializerStoreTag() and result = "InitStore" or
tag = InitializerUninitializedTag() and result = "InitUninit" or
tag = ZeroPadStringConstantTag() and result = "ZeroPadConst" or
tag = ZeroPadStringElementIndexTag() and result = "ZeroPadElemIndex" or
tag = ZeroPadStringElementAddressTag() and result = "ZeroPadElemAddr" or
@@ -110,9 +114,11 @@ string getInstructionTagId(TInstructionTag tag) {
tag = ExitFunctionTag() and result = "ExitFunc" or
tag = UnmodeledDefinitionTag() and result = "UnmodeledDef" or
tag = UnmodeledUseTag() and result = "UnmodeledUse" or
tag = AliasedDefinitionTag() and result = "AliasedDef" or
tag = SwitchBranchTag() and result = "SwitchBranch" or
tag = CallTargetTag() and result = "CallTarget" or
tag = CallTag() and result = "Call" or
tag = CallSideEffectTag() and result = "CallSideEffect" or
tag = AllocationSizeTag() and result = "AllocSize" or
tag = AllocationElementSizeTag() and result = "AllocElemSize" or
tag = AllocationExtentConvertTag() and result = "AllocExtConv" or

View File

@@ -0,0 +1,305 @@
import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
/**
* The IR translation of a call to a function. The call may be from an actual
* call in the source code, or could be a call that is part of the translation
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
*/
abstract class TranslatedCall extends TranslatedExpr {
override final TranslatedElement getChild(int id) {
// We choose the child's id in the order of evaluation.
// The qualifier is evaluated before the call target, because the value of
// the call target may depend on the value of the qualifier for virtual
// calls.
id = -2 and result = getQualifier() or
id = -1 and result = getCallTarget() or
result = getArgument(id)
}
override final Instruction getFirstInstruction() {
if exists(getQualifier()) then
result = getQualifier().getFirstInstruction()
else
result = getFirstCallTargetInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
(
tag = CallTag() and
opcode instanceof Opcode::Call and
resultType = getCallResultType() and
isGLValue = false
) or
(
tag = CallSideEffectTag() and
opcode instanceof Opcode::CallSideEffect and
resultType instanceof UnknownType and
isGLValue = false
)
}
override Instruction getChildSuccessor(TranslatedElement child) {
(
child = getQualifier() and
result = getFirstCallTargetInstruction()
) or
(
child = getCallTarget() and
result = getFirstArgumentOrCallInstruction()
) or
exists(int argIndex |
child = getArgument(argIndex) and
if exists(getArgument(argIndex + 1)) then
result = getArgument(argIndex + 1).getFirstInstruction()
else
result = getInstruction(CallTag())
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
kind instanceof GotoEdge and
(
(
tag = CallTag() and
result = getInstruction(CallSideEffectTag())
) or
(
tag = CallSideEffectTag() and
result = getParent().getChildSuccessor(this)
)
)
}
override Instruction getInstructionOperand(InstructionTag tag,
OperandTag operandTag) {
(
tag = CallTag() and
(
(
operandTag instanceof CallTargetOperandTag and
result = getCallTargetResult()
) or
(
operandTag instanceof ThisArgumentOperandTag and
result = getQualifierResult()
) or
exists(PositionalArgumentOperandTag argTag |
argTag = operandTag and
result = getArgument(argTag.getArgIndex()).getResult()
)
)
) or
(
tag = CallSideEffectTag() and
operandTag instanceof SideEffectOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
}
override final Instruction getResult() {
result = getInstruction(CallTag())
}
/**
* Gets the result type of the call.
*/
abstract Type getCallResultType();
/**
* Holds if the call has a `this` argument.
*/
predicate hasQualifier() {
exists(getQualifier())
}
/**
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
*/
TranslatedExpr getCallTarget() {
none()
}
/**
* Gets the first instruction of the sequence to evaluate the call target.
* By default, this is just the first instruction of `getCallTarget()`, but
* it can be overridden by a subclass for cases where there is a call target
* that is not computed from an expression (e.g. a direct call).
*/
Instruction getFirstCallTargetInstruction() {
result = getCallTarget().getFirstInstruction()
}
/**
* Gets the instruction whose result value is the target of the call. By
* default, this is just the result of `getCallTarget()`, but it can be
* overridden by a subclass for cases where there is a call target that is not
* computed from an expression (e.g. a direct call).
*/
Instruction getCallTargetResult() {
result = getCallTarget().getResult()
}
/**
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
* that is passed as the `this` argument.
*/
abstract TranslatedExpr getQualifier();
/**
* Gets the instruction whose result value is the `this` argument of the call.
* By default, this is just the result of `getQualifier()`, but it can be
* overridden by a subclass for cases where there is a `this` argument that is
* not computed from a child expression (e.g. a constructor call).
*/
Instruction getQualifierResult() {
result = getQualifier().getResult()
}
/**
* Gets the argument with the specified `index`. Does not include the `this`
* argument.
*/
abstract TranslatedExpr getArgument(int index);
/**
* If there are any arguments, gets the first instruction of the first
* argument. Otherwise, returns the call instruction.
*/
final Instruction getFirstArgumentOrCallInstruction() {
if hasArguments() then
result = getArgument(0).getFirstInstruction()
else
result = getInstruction(CallTag())
}
/**
* Holds if the call has any arguments, not counting the `this` argument.
*/
abstract predicate hasArguments();
}
/**
* IR translation of a direct call to a specific function. Used for both
* explicit calls (`TranslatedFunctionCall`) and implicit calls
* (`TranslatedAllocatorCall`).
*/
abstract class TranslatedDirectCall extends TranslatedCall {
override final Instruction getFirstCallTargetInstruction() {
result = getInstruction(CallTargetTag())
}
override final Instruction getCallTargetResult() {
result = getInstruction(CallTargetTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
(
tag = CallTargetTag() and
opcode instanceof Opcode::FunctionAddress and
// The database does not contain a `FunctionType` for a function unless
// its address was taken, so we'll just use glval<Unknown> instead of
// glval<FunctionType>.
resultType instanceof UnknownType and
isGLValue = true
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
(
tag = CallTargetTag() and
kind instanceof GotoEdge and
result = getFirstArgumentOrCallInstruction()
)
}
}
/**
* The IR translation of a call to a function.
*/
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
TranslatedCall {
Call call;
TranslatedCallExpr() {
expr = call
}
override final Type getCallResultType() {
result = getResultType()
}
override final predicate hasArguments() {
exists(call.getArgument(0))
}
override final TranslatedExpr getQualifier() {
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
}
override final TranslatedExpr getArgument(int index) {
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
}
}
/**
* Represents the IR translation of a call through a function pointer.
*/
class TranslatedExprCall extends TranslatedCallExpr {
ExprCall exprCall;
TranslatedExprCall() {
expr = exprCall
}
override TranslatedExpr getCallTarget() {
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
}
}
/**
* Represents the IR translation of a direct function call.
*/
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
FunctionCall funcCall;
TranslatedFunctionCall() {
expr = funcCall
}
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = funcCall.getTarget()
}
}
/**
* Represents the IR translation of a call to a constructor.
*/
class TranslatedStructorCall extends TranslatedFunctionCall {
TranslatedStructorCall() {
funcCall instanceof ConstructorCall or
funcCall instanceof DestructorCall
}
override Instruction getQualifierResult() {
exists(StructorCallContext context |
context = getParent() and
result = context.getReceiver()
)
}
override predicate hasQualifier() {
any()
}
}

View File

@@ -534,6 +534,14 @@ abstract class TranslatedElement extends TTranslatedElement {
result = getParent().getExceptionSuccessorInstruction()
}
/**
* Gets the primary instruction for the side effect instruction that was
* generated by this element for tag `tag`.
*/
Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
none()
}
/**
* Holds if this element generates a temporary variable with type `type`.
* `tag` must be unique for each variable generated from the same AST node

View File

@@ -8,6 +8,7 @@ private import TranslatedDeclarationEntry
private import TranslatedElement
private import TranslatedFunction
private import TranslatedInitialization
import TranslatedCall
/**
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
@@ -1769,163 +1770,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
}
/**
* The IR translation of a call to a function. The call may be from an actual
* call in the source code, or could be a call that is part of the translation
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
*/
abstract class TranslatedCall extends TranslatedExpr {
override final TranslatedElement getChild(int id) {
// We choose the child's id in the order of evaluation.
// The qualifier is evaluated before the call target, because the value of
// the call target may depend on the value of the qualifier for virtual
// calls.
id = -2 and result = getQualifier() or
id = -1 and result = getCallTarget() or
result = getArgument(id)
}
override final Instruction getFirstInstruction() {
if exists(getQualifier()) then
result = getQualifier().getFirstInstruction()
else
result = getFirstCallTargetInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
tag = CallTag() and
opcode instanceof Opcode::Call and
resultType = getCallResultType() and
isGLValue = false
}
override Instruction getChildSuccessor(TranslatedElement child) {
(
child = getQualifier() and
result = getFirstCallTargetInstruction()
) or
(
child = getCallTarget() and
result = getFirstArgumentOrCallInstruction()
) or
exists(int argIndex |
child = getArgument(argIndex) and
if exists(getArgument(argIndex + 1)) then
result = getArgument(argIndex + 1).getFirstInstruction()
else
result = getInstruction(CallTag())
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
kind instanceof GotoEdge and
tag = CallTag() and
result = getParent().getChildSuccessor(this)
}
override Instruction getInstructionOperand(InstructionTag tag,
OperandTag operandTag) {
tag = CallTag() and
(
(
operandTag instanceof CallTargetOperandTag and
result = getCallTargetResult()
) or
(
operandTag instanceof ThisArgumentOperandTag and
result = getQualifierResult()
) or
exists(PositionalArgumentOperandTag argTag |
argTag = operandTag and
result = getArgument(argTag.getArgIndex()).getResult()
)
)
}
override final Instruction getResult() {
result = getInstruction(CallTag())
}
/**
* Gets the result type of the call.
*/
abstract Type getCallResultType();
/**
* Holds if the call has a `this` argument.
*/
predicate hasQualifier() {
exists(getQualifier())
}
/**
* Gets the `TranslatedExpr` for the indirect target of the call, if any.
*/
TranslatedExpr getCallTarget() {
none()
}
/**
* Gets the first instruction of the sequence to evaluate the call target.
* By default, this is just the first instruction of `getCallTarget()`, but
* it can be overridden by a subclass for cases where there is a call target
* that is not computed from an expression (e.g. a direct call).
*/
Instruction getFirstCallTargetInstruction() {
result = getCallTarget().getFirstInstruction()
}
/**
* Gets the instruction whose result value is the target of the call. By
* default, this is just the result of `getCallTarget()`, but it can be
* overridden by a subclass for cases where there is a call target that is not
* computed from an expression (e.g. a direct call).
*/
Instruction getCallTargetResult() {
result = getCallTarget().getResult()
}
/**
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
* that is passed as the `this` argument.
*/
abstract TranslatedExpr getQualifier();
/**
* Gets the instruction whose result value is the `this` argument of the call.
* By default, this is just the result of `getQualifier()`, but it can be
* overridden by a subclass for cases where there is a `this` argument that is
* not computed from a child expression (e.g. a constructor call).
*/
Instruction getQualifierResult() {
result = getQualifier().getResult()
}
/**
* Gets the argument with the specified `index`. Does not include the `this`
* argument.
*/
abstract TranslatedExpr getArgument(int index);
/**
* If there are any arguments, gets the first instruction of the first
* argument. Otherwise, returns the call instruction.
*/
final Instruction getFirstArgumentOrCallInstruction() {
if hasArguments() then
result = getArgument(0).getFirstInstruction()
else
result = getInstruction(CallTag())
}
/**
* Holds if the call has any arguments, not counting the `this` argument.
*/
abstract predicate hasArguments();
}
/**
* The IR translation of the allocation size argument passed to `operator new`
* in a `new` expression.
@@ -2089,45 +1933,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
}
}
/**
* IR translation of a direct call to a specific function. Used for both
* explicit calls (`TranslatedFunctionCall`) and implicit calls
* (`TranslatedAllocatorCall`).
*/
abstract class TranslatedDirectCall extends TranslatedCall {
override final Instruction getFirstCallTargetInstruction() {
result = getInstruction(CallTargetTag())
}
override final Instruction getCallTargetResult() {
result = getInstruction(CallTargetTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
Type resultType, boolean isGLValue) {
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
(
tag = CallTargetTag() and
opcode instanceof Opcode::FunctionAddress and
// The database does not contain a `FunctionType` for a function unless
// its address was taken, so we'll just use glval<Unknown> instead of
// glval<FunctionType>.
resultType instanceof UnknownType and
isGLValue = true
)
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
(
tag = CallTargetTag() and
kind instanceof GotoEdge and
result = getFirstArgumentOrCallInstruction()
)
}
}
/**
* The IR translation of a call to `operator new` as part of a `new` or `new[]`
* expression.
@@ -2185,65 +1990,6 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall,
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
result.getAST() = newExpr
}
/**
* The IR translation of a call to a function.
*/
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
TranslatedCall {
Call call;
TranslatedCallExpr() {
expr = call
}
override final Type getCallResultType() {
result = getResultType()
}
override final predicate hasArguments() {
exists(call.getArgument(0))
}
override final TranslatedExpr getQualifier() {
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
}
override final TranslatedExpr getArgument(int index) {
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
}
}
/**
* Represents the IR translation of a call through a function pointer.
*/
class TranslatedExprCall extends TranslatedCallExpr {
ExprCall exprCall;
TranslatedExprCall() {
expr = exprCall
}
override TranslatedExpr getCallTarget() {
result = getTranslatedExpr(exprCall.getExpr().getFullyConverted())
}
}
/**
* Represents the IR translation of a direct function call.
*/
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
FunctionCall funcCall;
TranslatedFunctionCall() {
expr = funcCall
}
override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = funcCall.getTarget()
}
}
/**
* Abstract class implemented by any `TranslatedElement` that has a child
* expression that is a call to a constructor or destructor, in order to
@@ -2257,27 +2003,6 @@ abstract class StructorCallContext extends TranslatedElement {
abstract Instruction getReceiver();
}
/**
* Represents the IR translation of a call to a constructor.
*/
class TranslatedStructorCall extends TranslatedFunctionCall {
TranslatedStructorCall() {
funcCall instanceof ConstructorCall or
funcCall instanceof DestructorCall
}
override Instruction getQualifierResult() {
exists(StructorCallContext context |
context = getParent() and
result = context.getReceiver()
)
}
override predicate hasQualifier() {
any()
}
}
/**
* Represents the IR translation of the destruction of a field from within
* the destructor of the field's declaring class.

View File

@@ -76,6 +76,9 @@ class TranslatedFunction extends TranslatedElement,
(
(
tag = EnterFunctionTag() and
result = getInstruction(AliasedDefinitionTag())
) or (
tag = AliasedDefinitionTag() and
result = getInstruction(UnmodeledDefinitionTag())
) or
(
@@ -153,6 +156,12 @@ class TranslatedFunction extends TranslatedElement,
resultType instanceof UnknownType and
isGLValue = false
) or
(
tag = AliasedDefinitionTag() and
opcode instanceof Opcode::AliasedDefinition and
resultType instanceof UnknownType and
isGLValue = false
) or
(
tag = InitializeThisTag() and
opcode instanceof Opcode::InitializeThis and
@@ -218,6 +227,11 @@ class TranslatedFunction extends TranslatedElement,
result.getFunction() = func and
result.hasMemoryResult()
) or
(
tag = UnmodeledUseTag() and
operandTag instanceof UnmodeledUseOperandTag and
result = getUnmodeledDefinitionInstruction()
) or
(
tag = ReturnTag() and
not getReturnType() instanceof VoidType and

View File

@@ -148,7 +148,11 @@ class TranslatedArrayListInitialization extends
}
override TranslatedElement getChild(int id) {
result = getTranslatedElementInitialization(initList, id)
// The children are in initialization order
result = rank[id + 1](TranslatedElementInitialization init |
init.getInitList() = initList |
init order by init.getElementIndex()
)
}
}
@@ -668,15 +672,6 @@ class TranslatedFieldValueInitialization extends
}
}
/**
* Gets the `TranslatedElementInitialization` for element `elementIndex` in
* initializer list `initList`.
*/
TranslatedElementInitialization getTranslatedElementInitialization(
ArrayAggregateLiteral initList, int elementIndex) {
result.getInitList() = initList and result.getElementIndex() = elementIndex
}
/**
* Represents the IR translation of the initialization of an array element from
* an element of an initializer list.
@@ -717,7 +712,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
EdgeKind kind) {
tag = getElementIndexTag() and
result = getInstruction(getElementAddressTag()) and
kind instanceof GotoEdge
@@ -767,9 +762,8 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
* Represents the IR translation of the initialization of an array element from
* an explicit element in an initializer list.
*/
class TranslatedExplicitElementInitialization extends
TranslatedElementInitialization, TTranslatedExplicitElementInitialization,
InitializationContext {
class TranslatedExplicitElementInitialization extends TranslatedElementInitialization,
TTranslatedExplicitElementInitialization, InitializationContext {
int elementIndex;
TranslatedExplicitElementInitialization() {
@@ -785,7 +779,7 @@ class TranslatedExplicitElementInitialization extends
}
override Instruction getInstructionSuccessor(InstructionTag tag,
EdgeKind kind) {
EdgeKind kind) {
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or
(
tag = getElementAddressTag() and
@@ -816,8 +810,8 @@ class TranslatedExplicitElementInitialization extends
* Represents the IR translation of the initialization of a range of array
* elements without corresponding elements in the initializer list.
*/
class TranslatedElementValueInitialization extends
TranslatedElementInitialization, TTranslatedElementValueInitialization {
class TranslatedElementValueInitialization extends TranslatedElementInitialization,
TTranslatedElementValueInitialization {
int elementIndex;
int elementCount;

View File

@@ -1,7 +1,7 @@
private import internal.IRInternal
import Instruction
import semmle.code.cpp.ir.implementation.EdgeKind
private import Construction::BlockConstruction
import Cached
class IRBlock extends TIRBlock {
final string toString() {
@@ -98,3 +98,82 @@ class IRBlock extends TIRBlock {
getAPredecessor().isReachableFromFunctionEntry()
}
}
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
(
count(Instruction predecessor |
instr = predecessor.getASuccessor()
) != 1 or // Multiple predecessors or no predecessor
exists(Instruction predecessor |
instr = predecessor.getASuccessor() and
strictcount(Instruction other |
other = predecessor.getASuccessor()
) > 1
) or // Predecessor has multiple successors
exists(Instruction predecessor, EdgeKind kind |
instr = predecessor.getSuccessor(kind) and
not kind instanceof GotoEdge
) // Incoming edge is not a GotoEdge
)
}
private predicate isEntryBlock(TIRBlock block) {
block = MkIRBlock(any(EnterFunctionInstruction enter))
}
private cached module Cached {
cached newtype TIRBlock =
MkIRBlock(Instruction firstInstr) {
startsBasicBlock(firstInstr)
}
/** Holds if `i2` follows `i1` in a `IRBlock`. */
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
not startsBasicBlock(i2)
}
/** Gets the index of `i` in its `IRBlock`. */
private int getMemberIndex(Instruction i) {
startsBasicBlock(i) and
result = 0
or
exists(Instruction iPrev |
adjacentInBlock(iPrev, i) and
result = getMemberIndex(iPrev) + 1
)
}
/** Holds if `i` is the `index`th instruction in `block`. */
cached Instruction getInstruction(TIRBlock block, int index) {
exists(Instruction first |
block = MkIRBlock(first) and
index = getMemberIndex(result) and
adjacentInBlock*(first, result)
)
}
cached int getInstructionCount(TIRBlock block) {
result = strictcount(getInstruction(block, _))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
exists(Instruction predLast, Instruction succFirst |
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
succFirst = predLast.getSuccessor(kind) and
succ = MkIRBlock(succFirst)
)
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
Instruction getFirstInstruction(TIRBlock block) {
block = MkIRBlock(result)
}

View File

@@ -33,11 +33,18 @@ module InstructionSanity {
) or
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiTotalOperandTag or
opcode instanceof Opcode::Chi and tag instanceof ChiPartialOperandTag or
(
(opcode instanceof ReadSideEffectOpcode or opcode instanceof MayWriteSideEffectOpcode) and
tag instanceof SideEffectOperandTag
)
)
)
}
@@ -162,9 +169,9 @@ class Instruction extends Construction::TInstruction {
*/
final string getOperationString() {
if exists(getImmediateString()) then
result = opcode.toString() + "[" + getImmediateString() + "]"
result = getOperationPrefix() + opcode.toString() + "[" + getImmediateString() + "]"
else
result = opcode.toString()
result = getOperationPrefix() + opcode.toString()
}
/**
@@ -174,6 +181,13 @@ class Instruction extends Construction::TInstruction {
none()
}
private string getOperationPrefix() {
if this instanceof SideEffectInstruction then
result = "^"
else
result = ""
}
private string getResultPrefix() {
if resultType instanceof VoidType then
result = "v"
@@ -1084,6 +1098,9 @@ class SwitchInstruction extends Instruction {
}
}
/**
* An instruction that calls a function.
*/
class CallInstruction extends Instruction {
CallInstruction() {
opcode instanceof Opcode::Call
@@ -1094,6 +1111,116 @@ class CallInstruction extends Instruction {
}
}
/**
* An instruction representing a side effect of a function call.
*/
class SideEffectInstruction extends Instruction {
SideEffectInstruction() {
opcode instanceof SideEffectOpcode
}
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be
* accessed by that call.
*/
class CallSideEffectInstruction extends SideEffectInstruction {
CallSideEffectInstruction() {
opcode instanceof Opcode::CallSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
/**
* An instruction representing the side effect of a function call on any memory that might be read
* by that call.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() {
opcode instanceof Opcode::CallReadSideEffect
}
}
/**
* An instruction representing the read of an indirect parameter within a function call.
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() {
opcode instanceof Opcode::IndirectReadSideEffect
}
}
/**
* An instruction representing the read of an indirect buffer parameter within a function call.
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() {
opcode instanceof Opcode::BufferReadSideEffect
}
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
IndirectWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
BufferWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMemoryAccess
}
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
opcode instanceof Opcode::IndirectMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof IndirectMayMemoryAccess
}
}
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
BufferMayWriteSideEffectInstruction() {
opcode instanceof Opcode::BufferMayWriteSideEffect
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof BufferMayMemoryAccess
}
}
/**
* An instruction that throws an exception.
*/
@@ -1195,6 +1322,19 @@ class UnmodeledDefinitionInstruction extends Instruction {
}
}
/**
* An instruction that initializes all escaped memory.
*/
class AliasedDefinitionInstruction extends Instruction {
AliasedDefinitionInstruction() {
opcode instanceof Opcode::AliasedDefinition
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof EscapedMemoryAccess
}
}
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() {
opcode instanceof Opcode::UnmodeledUse
@@ -1205,6 +1345,16 @@ class UnmodeledUseInstruction extends Instruction {
}
}
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
* A `PhiInstruction` is inserted at the beginning of a block whenever two different definitions of
* the same variable reach that block. The `PhiInstruction` will have one operand corresponding to
* each control flow predecessor of the block, with that operand representing the version of the
* variable that flows from that predecessor. The result value of the `PhiInstruction` will be
* a copy of whichever operand corresponds to the actual predecessor that entered the block at
* runtime.
*/
class PhiInstruction extends Instruction {
PhiInstruction() {
opcode instanceof Opcode::Phi
@@ -1215,6 +1365,73 @@ class PhiInstruction extends Instruction {
}
}
/**
* An instruction representing the effect that a write to a memory may have on potential aliases of
* that memory.
*
* A `ChiInstruction` is inserted immediately after an instruction that writes to memory. The
* `ChiInstruction` has two operands. The first operand, given by `getTotalOperand()`, represents
* the previous state of all of the memory that might be alised by the memory write. The second
* operand, given by `getPartialOperand()`, represents the memory that was actually modified by the
* memory write. The result of the `ChiInstruction` represents the same memory as
* `getTotalOperand()`, updated to include the changes due to the value that was actually stored by
* the memory write.
*
* As an example, suppose that variable `p` and `q` are pointers that may or may not point to the
* same memory:
* ```
* *p = 5;
* x = *q;
* ```
*
* The IR would look like:
* ```
* r1_1 = VariableAddress[p]
* r1_2 = Load r1_1, m0_0 // Load the value of `p`
* r1_3 = Constant[5]
* m1_4 = Store r1_2, r1_3 // Store to `*p`
* m1_5 = ^Chi m0_1, m1_4 // Side effect of the previous Store on aliased memory
* r1_6 = VariableAddress[x]
* r1_7 = VariableAddress[q]
* r1_8 = Load r1_7, m0_2 // Load the value of `q`
* r1_9 = Load r1_8, m1_5 // Load the value of `*q`
* m1_10 = Store r1_6, r1_9 // Store to x
* ```
*
* Note the `Chi` instruction after the store to `*p`. The indicates that the previous contents of
* aliased memory (`m0_1`) are merged with the new value written by the store (`m1_4`), producing a
* new version of aliased memory (`m1_5`). On the subsequent load from `*q`, the source operand of
* `*q` is `m1_5`, indicating that the store to `*p` may (or may not) have updated the memory
* pointed to by `q`.
*
* For more information about how `Chi` instructions are used to model memory side effects, see
* https://link.springer.com/content/pdf/10.1007%2F3-540-61053-7_66.pdf.
*/
class ChiInstruction extends Instruction {
ChiInstruction() {
opcode instanceof Opcode::Chi
}
override final MemoryAccessKind getResultMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
/**
* Gets the operand that represents the previous state of all memory that might be aliased by the
* memory write.
*/
final Instruction getTotalOperand() {
result = getAnOperand().(ChiTotalOperand).getDefinitionInstruction()
}
/**
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartialOperand() {
result = getAnOperand().(ChiPartialOperand).getDefinitionInstruction()
}
}
/**
* An instruction representing a built-in operation. This is used to represent
* operations such as access to variable argument lists.

View File

@@ -306,6 +306,38 @@ class PositionalArgumentOperand extends ArgumentOperand {
}
}
class SideEffectOperand extends NonPhiOperand {
SideEffectOperand() {
this = TNonPhiOperand(_, sideEffectOperand(), _)
}
override MemoryAccessKind getMemoryAccess() {
instr instanceof CallSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof CallReadSideEffectInstruction and
result instanceof EscapedMemoryAccess
or
instr instanceof IndirectReadSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
instr instanceof BufferWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
instr instanceof IndirectMayWriteSideEffectInstruction and
result instanceof IndirectMayMemoryAccess
or
instr instanceof BufferMayWriteSideEffectInstruction and
result instanceof BufferMayMemoryAccess
}
}
/**
* An operand of a `PhiInstruction`.
*/
@@ -358,3 +390,38 @@ class MemoryOperand extends Operand {
exists(getMemoryAccess())
}
}
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends Operand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
override string toString() {
result = "ChiTotal"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiTotalMemoryAccess
}
}
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends Operand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
override string toString() {
result = "ChiPartial"
}
override final MemoryAccessKind getMemoryAccess() {
result instanceof ChiPartialMemoryAccess
}
}

View File

@@ -1,38 +0,0 @@
import SSAConstructionInternal
private import SSAConstruction as Construction
private import NewIR
import Cached
private cached module Cached {
cached newtype TIRBlock = MkIRBlock(OldIR::IRBlock oldBlock)
private OldIR::IRBlock getOldBlock(TIRBlock block) {
block = MkIRBlock(result)
}
cached Instruction getInstruction(TIRBlock block, int index) {
Construction::getOldInstruction(result) =
getOldBlock(block).getInstruction(index)
}
cached int getInstructionCount(TIRBlock block) {
result = getOldBlock(block).getInstructionCount()
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
succ = MkIRBlock(getOldBlock(pred).getSuccessor(kind))
}
cached predicate blockSuccessor(TIRBlock pred, TIRBlock succ) {
blockSuccessor(pred, succ, _)
}
cached predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) {
getOldBlock(dominator).immediatelyDominates(getOldBlock(block))
}
cached Instruction getFirstInstruction(TIRBlock block) {
Construction::getOldInstruction(result) =
getOldBlock(block).getFirstInstruction()
}
}

View File

@@ -3,7 +3,6 @@ import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import NewIR
import IRBlockConstruction as BlockConstruction
import Cached
cached private module Cached {
@@ -18,6 +17,10 @@ cached private module Cached {
} or
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
hasPhiNode(vvar, block)
} or
ChiTag(OldIR::Instruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
}
cached class InstructionTagType extends TInstructionTag {
@@ -40,12 +43,28 @@ cached private module Cached {
getOldInstruction(result) = instr
}
/**
* Gets the chi node corresponding to `instr` if one is present, or the new `Instruction`
* corresponding to `instr` if there is no `Chi` node.
*/
private Instruction getNewFinalInstruction(OldIR::Instruction instr) {
result = getChiInstruction(instr)
or
not exists(getChiInstruction(instr)) and
result = getNewInstruction(instr)
}
private PhiInstruction getPhiInstruction(Function func, OldIR::IRBlock oldBlock,
Alias::VirtualVariable vvar) {
result.getFunction() = func and
result.getAST() = oldBlock.getFirstInstruction().getAST() and
result.getTag() = PhiTag(vvar, oldBlock)
}
private ChiInstruction getChiInstruction (OldIR::Instruction instr) {
hasChiNode(_, instr) and
result.getTag() = ChiTag(instr)
}
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
result.getFunction() = var.getFunction() and
@@ -92,6 +111,15 @@ cached private module Cached {
tag = PhiTag(vvar, block) and
resultType = vvar.getType() and
isGLValue = false
) or
exists(OldIR::Instruction instr, Alias::VirtualVariable vvar |
hasChiNode(vvar, instr) and
instr.getFunction() = func and
opcode instanceof Opcode::Chi and
ast = instr.getAST() and
tag = ChiTag(instr) and
resultType = vvar.getType() and
isGLValue = false
)
}
@@ -125,11 +153,11 @@ cached private module Cached {
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewInstruction(defBlock.getInstruction(defIndex))
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
result = getPhiInstruction(instruction.getFunction(), defBlock, vvar)
)
)
)
else (
result = instruction.getFunctionIR().getUnmodeledDefinitionInstruction()
)
@@ -146,7 +174,17 @@ cached private module Cached {
)
else
result = getNewInstruction(oldOperand.getDefinitionInstruction())
)
) or
instruction.getTag() = ChiTag(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag
or
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
result instanceof UnmodeledDefinitionInstruction and
instruction.getFunction() = result.getFunction()
or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction)
}
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
@@ -160,12 +198,27 @@ cached private module Cached {
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
if defIndex >= 0 then
result = getNewInstruction(defBlock.getInstruction(defIndex))
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(instr.getFunction(), defBlock, vvar)
)
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldIR::Instruction oldInstr, OldIR::IRBlock defBlock,
int defRank, int defIndex, OldIR::IRBlock useBlock, int useRank |
ChiTag(oldInstr) = chiInstr.getTag() and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = getPhiInstruction(chiInstr.getFunction(), defBlock, vvar)
)
}
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldIR::IRBlock oldBlock |
instr.getTag() = PhiTag(_, oldBlock) and
@@ -181,8 +234,24 @@ cached private module Cached {
result = getOldInstruction(instruction).getUnconvertedResultExpression()
}
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
* the new instructions generated from the successors of the old instruction
*/
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
if(hasChiNode(_, getOldInstruction(instruction)))
then
result = getChiInstruction(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
or
exists(OldIR::Instruction oldInstruction |
instruction = getChiInstruction(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
}
cached IRVariable getInstructionVariable(Instruction instruction) {
@@ -228,6 +297,18 @@ cached private module Cached {
)
}
cached Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
exists(OldIR::SideEffectInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
result = getNewInstruction(oldInstruction.getPrimaryInstruction())
)
or
exists(OldIR::Instruction oldInstruction |
instruction.getTag() = ChiTag(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldIR::Instruction instr, OldIR::IRBlock block, int index) {
block.getInstruction(index) = instr and
@@ -253,7 +334,16 @@ cached private module Cached {
private predicate hasUse(Alias::VirtualVariable vvar,
OldIR::Instruction use, OldIR::IRBlock block, int index) {
exists(Alias::MemoryAccess access |
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
(
access = Alias::getOperandMemoryAccess(use.getAnOperand())
or
/*
* a partial write to a virtual variable is going to generate a use of that variable when
* Chi nodes are inserted, so we need to mark it as a use in the old IR
*/
access = Alias::getResultMemoryAccess(use) and
access.isPartialMemoryAccess()
) and
block.getInstruction(index) = use and
vvar = access.getVirtualVariable()
)
@@ -384,6 +474,15 @@ cached private module Cached {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
}
private predicate hasChiNode(Alias::VirtualVariable vvar,
OldIR::Instruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
ma.getVirtualVariable() = vvar
)
}
}
import CachedForDebugging

View File

@@ -1,7 +1,6 @@
import SimpleSSAInternal
import AliasAnalysis
import cpp
import Alias
private import InputIR
private import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
@@ -60,11 +59,17 @@ class MemoryAccess extends TMemoryAccess {
VirtualVariable getVirtualVariable() {
result = vvar
}
predicate isPartialMemoryAccess() {
none()
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def.getVirtualVariable() = use.getVirtualVariable() and
result instanceof MustExactlyOverlap
or
none() // Avoid compiler error in SSAConstruction
}
MemoryAccess getResultMemoryAccess(Instruction instr) {

View File

@@ -1,2 +0,0 @@
import AliasAnalysis as Alias
import semmle.code.cpp.ir.implementation.raw.IR as InputIR

View File

@@ -10,6 +10,8 @@ private int getMaxCallArgIndex() {
private newtype TOperandTag =
TAddressOperand() or
TBufferSizeOperand() or
TSideEffectOperand() or
TCopySourceOperand() or
TUnaryOperand() or
TLeftOperand() or
@@ -25,7 +27,9 @@ private newtype TOperandTag =
exists(BuiltInOperation op |
exists(op.getChild(argIndex))
)
}
} or
TChiTotalOperand() or
TChiPartialOperand()
/**
* Identifies the kind of operand on an instruction. Each `Instruction` has at
@@ -47,7 +51,7 @@ abstract class OperandTag extends TOperandTag {
/**
* The address operand of an instruction that loads or stores a value from
* memory (e.g. `Load`, `Store`).
* memory (e.g. `Load`, `Store`, `InitializeParameter`, `IndirectReadSideEffect`).
*/
class AddressOperandTag extends OperandTag, TAddressOperand {
override final string toString() {
@@ -63,6 +67,37 @@ AddressOperandTag addressOperand() {
result = TAddressOperand()
}
/**
* The buffer size operand of an instruction that represents a read or write of
* a buffer.
*/
class BufferSizeOperand extends OperandTag, TBufferSizeOperand {
override final string toString() {
result = "BufferSize"
}
override final int getSortOrder() {
result = 1
}
}
/**
* The operand representing the read side effect of a `SideEffectInstruction`.
*/
class SideEffectOperandTag extends OperandTag, TSideEffectOperand {
override final string toString() {
result = "SideEffect"
}
override final int getSortOrder() {
result = 2
}
}
SideEffectOperandTag sideEffectOperand() {
result = TSideEffectOperand()
}
/**
* The source value operand of an instruction that copies this value to its
* result (e.g. `Copy`, `Load`, `Store`).
@@ -73,7 +108,7 @@ class CopySourceOperandTag extends OperandTag, TCopySourceOperand {
}
override final int getSortOrder() {
result = 1
result = 3
}
}
@@ -90,7 +125,7 @@ class UnaryOperandTag extends OperandTag, TUnaryOperand {
}
override final int getSortOrder() {
result = 2
result = 4
}
}
@@ -107,7 +142,7 @@ class LeftOperandTag extends OperandTag, TLeftOperand {
}
override final int getSortOrder() {
result = 3
result = 5
}
}
@@ -124,7 +159,7 @@ class RightOperandTag extends OperandTag, TRightOperand {
}
override final int getSortOrder() {
result = 4
result = 6
}
}
@@ -141,7 +176,7 @@ class ReturnValueOperandTag extends OperandTag, TReturnValueOperand {
}
override final int getSortOrder() {
result = 5
result = 7
}
}
@@ -158,7 +193,7 @@ class ExceptionOperandTag extends OperandTag, TExceptionOperand {
}
override final int getSortOrder() {
result = 6
result = 8
}
}
@@ -175,7 +210,7 @@ class ConditionOperandTag extends OperandTag, TConditionOperand {
}
override final int getSortOrder() {
result = 7
result = 9
}
}
@@ -193,7 +228,7 @@ class UnmodeledUseOperandTag extends OperandTag, TUnmodeledUseOperand {
}
override final int getSortOrder() {
result = 8
result = 10
}
}
@@ -210,7 +245,7 @@ class CallTargetOperandTag extends OperandTag, TCallTargetOperand {
}
override final int getSortOrder() {
result = 9
result = 11
}
}
@@ -240,7 +275,7 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand {
}
override final int getSortOrder() {
result = 10
result = 12
}
override final string getLabel() {
@@ -268,7 +303,7 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag,
}
override final int getSortOrder() {
result = 11 + argIndex
result = 14 + argIndex
}
final int getArgIndex() {
@@ -279,3 +314,31 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag,
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
result = TPositionalArgumentOperand(argIndex)
}
class ChiTotalOperandTag extends OperandTag, TChiTotalOperand {
override final string toString() {
result = "ChiTotal"
}
override final int getSortOrder() {
result = 14
}
}
ChiTotalOperandTag chiTotalOperand() {
result = TChiTotalOperand()
}
class ChiPartialOperandTag extends OperandTag, TChiPartialOperand {
override final string toString() {
result = "ChiPartial"
}
override final int getSortOrder() {
result = 15
}
}
ChiPartialOperandTag chiPartialOperand() {
result = TChiPartialOperand()
}

View File

@@ -193,6 +193,13 @@ private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck)
/** Holds if the sign of `e` is too complicated to determine. */
private predicate unknownSign(Instruction i) {
// REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than
// the ones we don't understand. Currently, if we try to compute the sign of an instruction that
// we don't understand, and it isn't on this list, we incorrectly compute the sign as "none"
// instead of "+,0,-".
// Even better, we could track the state of each instruction as a power set of {non-negative,
// non-positive, non-zero}, which would mean that the representation of the sign of an unknown
// value would be the empty set.
(
i instanceof UnmodeledDefinitionInstruction
or
@@ -203,6 +210,8 @@ private predicate unknownSign(Instruction i) {
i instanceof BuiltInInstruction
or
i instanceof CallInstruction
or
i instanceof ChiInstruction
)
}

View File

@@ -3325,10 +3325,10 @@ ir.cpp:
# 504| expr: {...}
# 504| Type = Point
# 504| ValueCategory = prvalue
# 504| 0: x
# 504| .x: x
# 504| Type = int
# 504| ValueCategory = prvalue(load)
# 504| 1: (int)...
# 504| .y: (int)...
# 504| Conversion = floating point to integral conversion
# 504| Type = int
# 504| ValueCategory = prvalue
@@ -3342,7 +3342,7 @@ ir.cpp:
# 505| expr: {...}
# 505| Type = Point
# 505| ValueCategory = prvalue
# 505| 0: x
# 505| .x: x
# 505| Type = int
# 505| ValueCategory = prvalue(load)
# 506| 2: declaration
@@ -3390,13 +3390,13 @@ ir.cpp:
# 514| expr: {...}
# 514| Type = Rect
# 514| ValueCategory = prvalue
# 514| 0: {...}
# 514| .topLeft: {...}
# 514| Type = Point
# 514| ValueCategory = prvalue
# 514| 0: x
# 514| .x: x
# 514| Type = int
# 514| ValueCategory = prvalue(load)
# 514| 1: (int)...
# 514| .y: (int)...
# 514| Conversion = floating point to integral conversion
# 514| Type = int
# 514| ValueCategory = prvalue
@@ -3410,26 +3410,26 @@ ir.cpp:
# 515| expr: {...}
# 515| Type = Rect
# 515| ValueCategory = prvalue
# 515| 0: {...}
# 515| .topLeft: {...}
# 515| Type = Point
# 515| ValueCategory = prvalue
# 515| 0: x
# 515| .x: x
# 515| Type = int
# 515| ValueCategory = prvalue(load)
# 515| 1: (int)...
# 515| .y: (int)...
# 515| Conversion = floating point to integral conversion
# 515| Type = int
# 515| ValueCategory = prvalue
# 515| expr: f
# 515| Type = float
# 515| ValueCategory = prvalue(load)
# 515| 1: {...}
# 515| .bottomRight: {...}
# 515| Type = Point
# 515| ValueCategory = prvalue
# 515| 0: x
# 515| .x: x
# 515| Type = int
# 515| ValueCategory = prvalue(load)
# 515| 1: (int)...
# 515| .y: (int)...
# 515| Conversion = floating point to integral conversion
# 515| Type = int
# 515| ValueCategory = prvalue
@@ -3443,16 +3443,16 @@ ir.cpp:
# 516| expr: {...}
# 516| Type = Rect
# 516| ValueCategory = prvalue
# 516| 0: {...}
# 516| .topLeft: {...}
# 516| Type = Point
# 516| ValueCategory = prvalue
# 516| 0: x
# 516| .x: x
# 516| Type = int
# 516| ValueCategory = prvalue(load)
# 516| 1: {...}
# 516| .bottomRight: {...}
# 516| Type = Point
# 516| ValueCategory = prvalue
# 516| 0: x
# 516| .x: x
# 516| Type = int
# 516| ValueCategory = prvalue(load)
# 517| 4: return ...
@@ -3477,17 +3477,17 @@ ir.cpp:
# 521| expr: {...}
# 521| Type = int[3]
# 521| ValueCategory = prvalue
# 521| 0: x
# 521| [0]: x
# 521| Type = int
# 521| ValueCategory = prvalue(load)
# 521| 1: (int)...
# 521| [1]: (int)...
# 521| Conversion = floating point to integral conversion
# 521| Type = int
# 521| ValueCategory = prvalue
# 521| expr: f
# 521| Type = float
# 521| ValueCategory = prvalue(load)
# 521| 2: 0
# 521| [2]: 0
# 521| Type = int
# 521| Value = 0
# 521| ValueCategory = prvalue
@@ -3498,7 +3498,7 @@ ir.cpp:
# 522| expr: {...}
# 522| Type = int[3]
# 522| ValueCategory = prvalue
# 522| 0: x
# 522| [0]: x
# 522| Type = int
# 522| ValueCategory = prvalue(load)
# 523| 3: return ...
@@ -3524,7 +3524,7 @@ ir.cpp:
# 531| expr: {...}
# 531| Type = U
# 531| ValueCategory = prvalue
# 531| 0: (double)...
# 531| .d: (double)...
# 531| Conversion = floating point conversion
# 531| Type = double
# 531| ValueCategory = prvalue
@@ -3708,7 +3708,7 @@ ir.cpp:
# 577| expr: {...}
# 577| Type = char[2]
# 577| ValueCategory = prvalue
# 577| 0: (char)...
# 577| [0]: (char)...
# 577| Conversion = integral conversion
# 577| Type = char
# 577| Value = 0
@@ -3724,7 +3724,7 @@ ir.cpp:
# 578| expr: {...}
# 578| Type = char[2]
# 578| ValueCategory = prvalue
# 578| 0: (char)...
# 578| [0]: (char)...
# 578| Conversion = integral conversion
# 578| Type = char
# 578| Value = 0
@@ -3733,7 +3733,7 @@ ir.cpp:
# 578| Type = int
# 578| Value = 0
# 578| ValueCategory = prvalue
# 578| 1: (char)...
# 578| [1]: (char)...
# 578| Conversion = integral conversion
# 578| Type = char
# 578| Value = 1
@@ -3749,7 +3749,7 @@ ir.cpp:
# 579| expr: {...}
# 579| Type = char[3]
# 579| ValueCategory = prvalue
# 579| 0: (char)...
# 579| [0]: (char)...
# 579| Conversion = integral conversion
# 579| Type = char
# 579| Value = 0
@@ -6372,7 +6372,7 @@ ir.cpp:
# 954| 1: {...}
# 954| Type = String[]
# 954| ValueCategory = prvalue
# 954| 0: call to String
# 954| [0]: call to String
# 954| Type = void
# 954| ValueCategory = prvalue
# 954| 2: n
@@ -6414,7 +6414,7 @@ ir.cpp:
# 957| 1: {...}
# 957| Type = DefaultCtorWithDefaultParam[]
# 957| ValueCategory = prvalue
# 957| 0: call to DefaultCtorWithDefaultParam
# 957| [0]: call to DefaultCtorWithDefaultParam
# 957| Type = void
# 957| ValueCategory = prvalue
# 957| 2: n
@@ -6427,15 +6427,15 @@ ir.cpp:
# 958| 1: {...}
# 958| Type = int[3]
# 958| ValueCategory = prvalue
# 958| 0: 0
# 958| [0]: 0
# 958| Type = int
# 958| Value = 0
# 958| ValueCategory = prvalue
# 958| 1: 1
# 958| [1]: 1
# 958| Type = int
# 958| Value = 1
# 958| ValueCategory = prvalue
# 958| 2: 2
# 958| [2]: 2
# 958| Type = int
# 958| Value = 2
# 958| ValueCategory = prvalue
@@ -6453,11 +6453,11 @@ ir.cpp:
# 962| expr: {...}
# 962| Type = int[1000]
# 962| ValueCategory = prvalue
# 962| 0: 10002
# 962| [2]: 10002
# 962| Type = int
# 962| Value = 10002
# 962| ValueCategory = prvalue
# 962| 1: 10900
# 962| [900]: 10900
# 962| Type = int
# 962| Value = 10900
# 962| ValueCategory = prvalue
@@ -6591,3 +6591,80 @@ ir.cpp:
# 983| ValueCategory = prvalue(load)
# 983| 1: { ... }
# 985| 3: return ...
# 1005| ChiPhiNode(Point *, bool, bool) -> int
# 1005| params:
# 1005| 0: p
# 1005| Type = Point *
# 1005| 1: which1
# 1005| Type = bool
# 1005| 2: which2
# 1005| Type = bool
# 1005| body: { ... }
# 1006| 0: if (...) ...
# 1006| 0: which1
# 1006| Type = bool
# 1006| ValueCategory = prvalue(load)
# 1006| 1: { ... }
# 1007| 0: ExprStmt
# 1007| 0: ... ++
# 1007| Type = int
# 1007| ValueCategory = prvalue
# 1007| 0: x
# 1007| Type = int
# 1007| ValueCategory = lvalue
# 1007| -1: p
# 1007| Type = Point *
# 1007| ValueCategory = prvalue(load)
# 1008| 2: { ... }
# 1009| 0: ExprStmt
# 1009| 0: ... ++
# 1009| Type = int
# 1009| ValueCategory = prvalue
# 1009| 0: y
# 1009| Type = int
# 1009| ValueCategory = lvalue
# 1009| -1: p
# 1009| Type = Point *
# 1009| ValueCategory = prvalue(load)
# 1012| 1: if (...) ...
# 1012| 0: which2
# 1012| Type = bool
# 1012| ValueCategory = prvalue(load)
# 1012| 1: { ... }
# 1013| 0: ExprStmt
# 1013| 0: ... ++
# 1013| Type = int
# 1013| ValueCategory = prvalue
# 1013| 0: x
# 1013| Type = int
# 1013| ValueCategory = lvalue
# 1013| -1: p
# 1013| Type = Point *
# 1013| ValueCategory = prvalue(load)
# 1014| 2: { ... }
# 1015| 0: ExprStmt
# 1015| 0: ... ++
# 1015| Type = int
# 1015| ValueCategory = prvalue
# 1015| 0: y
# 1015| Type = int
# 1015| ValueCategory = lvalue
# 1015| -1: p
# 1015| Type = Point *
# 1015| ValueCategory = prvalue(load)
# 1018| 2: return ...
# 1018| 0: ... + ...
# 1018| Type = int
# 1018| ValueCategory = prvalue
# 1018| 0: x
# 1018| Type = int
# 1018| ValueCategory = prvalue(load)
# 1018| -1: p
# 1018| Type = Point *
# 1018| ValueCategory = prvalue(load)
# 1018| 1: y
# 1018| Type = int
# 1018| ValueCategory = prvalue(load)
# 1018| -1: p
# 1018| Type = Point *
# 1018| ValueCategory = prvalue(load)

File diff suppressed because it is too large Load Diff

View File

@@ -1002,4 +1002,20 @@ void OperatorDeleteArray() {
}
#endif
int ChiPhiNode(Point *p, bool which1, bool which2) {
if (which1) {
p->x++;
} else {
p->y++;
}
if (which2) {
p->x++;
} else {
p->y++;
}
return p->x + p->y;
}
// semmle-extractor-options: -std=c++17

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@
| IR: CallNestedTemplateFunc | 1 |
| IR: CallViaFuncPtr | 1 |
| IR: CastToVoid | 1 |
| IR: ChiPhiNode | 7 |
| IR: Comma | 1 |
| IR: CompoundAssignment | 1 |
| IR: ConditionValues | 13 |
@@ -97,7 +98,7 @@
| IR: VirtualMemberFunction | 1 |
| IR: WhileStatements | 4 |
| IR: WhileStmtWithDeclaration | 8 |
| IR: designatedInit | 4 |
| IR: designatedInit | 1 |
| IR: min | 4 |
| IR: operator= | 1 |
| IR: ~Base | 1 |

File diff suppressed because it is too large Load Diff