C++: Imprecise definitions in SSA

This commit is contained in:
Dave Bartolomeo
2019-02-28 16:59:06 -08:00
parent 9726428bcc
commit e0f7344676
14 changed files with 1478 additions and 760 deletions

View File

@@ -50,11 +50,17 @@ module InstructionSanity {
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
query predicate missingOperand(Instruction instr, OperandTag tag) {
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
query predicate missingOperand(Instruction instr, string message, FunctionIR func, string funcText) {
exists(OperandTag tag |
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
) and
message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" +
tag.toString() + "' in function '$@'." and
func = instr.getEnclosingFunctionIR() and
funcText = getIdentityString(func.getFunction())
)
}
@@ -302,7 +308,7 @@ class Instruction extends Construction::TInstruction {
result = type
}
private string getResultTypeString() {
string getResultTypeString() {
exists(string valcat |
valcat = getValueCategoryString(getResultType().toString()) and
if (getResultType() instanceof UnknownType and

View File

@@ -3,14 +3,18 @@ import Instruction
import IRBlock
import cpp
import semmle.code.cpp.ir.implementation.MemoryAccessKind
import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TNonPhiOperand(Instruction useInstr, OperandTag tag, Instruction defInstr) {
defInstr = Construction::getInstructionOperandDefinition(useInstr, tag)
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock) {
defInstr = Construction::getPhiInstructionOperandDefinition(useInstr, predecessorBlock)
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/**
@@ -25,8 +29,8 @@ class Operand extends TOperand {
result = getUseInstruction().getLocation()
}
final IRFunction getEnclosingIRFunction() {
result = getUseInstruction().getEnclosingIRFunction()
final FunctionIR getEnclosingFunctionIR() {
result = getUseInstruction().getEnclosingFunctionIR()
}
/**
@@ -43,6 +47,20 @@ class Operand extends TOperand {
none()
}
/**
* Gets the overlap relationship between the operand's definition and its use.
*/
Overlap getDefinitionOverlap() {
none()
}
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() {
not getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
*/
@@ -61,6 +79,13 @@ class Operand extends TOperand {
result = getDumpLabel() + getDefinitionInstruction().getResultId()
}
private string getInexactSpecifier() {
if isDefinitionInexact() then
result = "~"
else
result = ""
}
/**
* Get the order in which the operand should be sorted in the operand list.
*/
@@ -104,10 +129,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
exists(MemoryOperandTag tag |
this = TNonPhiOperand(_, tag, _)
) or
this = TPhiOperand(_, _, _)
this = TNonPhiMemoryOperand(_, _, _, _) or
this = TPhiOperand(_, _, _, _)
}
override predicate isGLValue() {
@@ -133,27 +156,17 @@ class MemoryOperand extends Operand {
}
}
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends Operand {
RegisterOperand() {
exists(RegisterOperandTag tag |
this = TNonPhiOperand(_, tag, _)
)
}
}
/**
* An operand that is not an operand of a `PhiInstruction`.
*/
class NonPhiOperand extends Operand, TNonPhiOperand {
class NonPhiOperand extends Operand {
Instruction useInstr;
Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
this = TNonPhiOperand(useInstr, tag, defInstr)
this = TRegisterOperand(useInstr, tag, defInstr) or
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
}
override final Instruction getUseInstruction() {
@@ -177,7 +190,32 @@ class NonPhiOperand extends Operand, TNonPhiOperand {
}
}
class TypedOperand extends NonPhiOperand, MemoryOperand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
override final Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
result instanceof MustExactlyOverlap
}
}
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
Overlap overlap;
NonPhiMemoryOperand() {
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap)
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
}
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
override final Type getType() {
@@ -189,7 +227,7 @@ class TypedOperand extends NonPhiOperand, MemoryOperand {
* The address operand of an instruction that loads or stores a value from
* memory (e.g. `Load`, `Store`).
*/
class AddressOperand extends NonPhiOperand, RegisterOperand {
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
override string toString() {
@@ -216,7 +254,7 @@ class LoadOperand extends TypedOperand {
/**
* The source value operand of a `Store` instruction.
*/
class StoreValueOperand extends NonPhiOperand, RegisterOperand {
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
override string toString() {
@@ -227,7 +265,7 @@ class StoreValueOperand extends NonPhiOperand, RegisterOperand {
/**
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
*/
class UnaryOperand extends NonPhiOperand, RegisterOperand {
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
override string toString() {
@@ -238,7 +276,7 @@ class UnaryOperand extends NonPhiOperand, RegisterOperand {
/**
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class LeftOperand extends NonPhiOperand, RegisterOperand {
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
override string toString() {
@@ -249,7 +287,7 @@ class LeftOperand extends NonPhiOperand, RegisterOperand {
/**
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class RightOperand extends NonPhiOperand, RegisterOperand {
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
override string toString() {
@@ -260,7 +298,7 @@ class RightOperand extends NonPhiOperand, RegisterOperand {
/**
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
*/
class ConditionOperand extends NonPhiOperand, RegisterOperand {
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
override string toString() {
@@ -272,7 +310,7 @@ class ConditionOperand extends NonPhiOperand, RegisterOperand {
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
class UnmodeledUseOperand extends NonPhiMemoryOperand {
override UnmodeledUseOperandTag tag;
override string toString() {
@@ -287,7 +325,7 @@ class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
/**
* The operand representing the target function of an `Call` instruction.
*/
class CallTargetOperand extends NonPhiOperand, RegisterOperand {
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
override string toString() {
@@ -300,7 +338,7 @@ class CallTargetOperand extends NonPhiOperand, RegisterOperand {
* positional arguments (represented by `PositionalArgumentOperand`) and the
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
*/
class ArgumentOperand extends NonPhiOperand, RegisterOperand {
class ArgumentOperand extends RegisterOperand {
override ArgumentOperandTag tag;
}
@@ -379,13 +417,14 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, TPhiOperand {
class PhiOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
PhiInputOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock)
PhiOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
override string toString() {
@@ -400,6 +439,10 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
result = defInstr
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
override final int getDumpSortOrder() {
result = 11 + getPredecessorBlock().getDisplayIndex()
}
@@ -423,10 +466,8 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends MemoryOperand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
override string toString() {
result = "ChiTotal"
@@ -441,10 +482,8 @@ class ChiTotalOperand extends MemoryOperand {
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends MemoryOperand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
override string toString() {
result = "ChiPartial"

View File

@@ -1,6 +1,7 @@
import cpp
import AliasAnalysis
import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.Print
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
@@ -8,167 +9,75 @@ private import semmle.code.cpp.ir.internal.OperandTag
private class IntValue = Ints::IntValue;
private newtype TVirtualVariable =
TVirtualIRVariable(IRVariable var) {
not variableAddressEscapes(var)
} or
TUnknownVirtualVariable(IRFunction f)
private VirtualIRVariable getVirtualVariable(IRVariable var) {
result.getIRVariable() = var
}
private UnknownVirtualVariable getUnknownVirtualVariable(IRFunction f) {
result.getEnclosingIRFunction() = f
}
class VirtualVariable extends TVirtualVariable {
string toString() {
none()
}
string getUniqueId() {
none()
}
Type getType() {
none()
}
}
/**
* A virtual variable representing a single non-escaped `IRVariable`.
*/
class VirtualIRVariable extends VirtualVariable, TVirtualIRVariable {
IRVariable var;
VirtualIRVariable() {
this = TVirtualIRVariable(var)
}
override final string toString() {
result = var.toString()
}
final IRVariable getIRVariable() {
result = var
}
override final Type getType() {
result = var.getType()
}
override final string getUniqueId() {
result = var.getUniqueId()
}
}
/**
* A virtual variable representing all escaped memory accessible by the function,
* including escaped local variables.
*/
class UnknownVirtualVariable extends VirtualVariable, TUnknownVirtualVariable {
IRFunction 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 IRFunction getEnclosingIRFunction() {
result = f
}
}
private predicate hasResultMemoryAccess(Instruction instr, IRVariable var, IntValue startBitOffset,
private predicate hasResultMemoryAccess(Instruction instr, IRVariable var, Type type, IntValue startBitOffset,
IntValue endBitOffset) {
resultPointsTo(instr.getResultAddressOperand().getDefinitionInstruction(), var, startBitOffset) and
type = instr.getResultType() and
if exists(instr.getResultSize()) then
endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8))
else
endBitOffset = Ints::unknown()
}
private predicate hasOperandMemoryAccess(MemoryOperand operand, IRVariable var, IntValue startBitOffset,
private predicate hasOperandMemoryAccess(MemoryOperand operand, IRVariable var, Type type, IntValue startBitOffset,
IntValue endBitOffset) {
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, startBitOffset) and
type = operand.getType() and
if exists(operand.getSize()) then
endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8))
else
endBitOffset = Ints::unknown()
}
private newtype TMemoryAccess =
TVariableMemoryAccess(IRVariable var, IntValue startBitOffset, IntValue endBitOffset) {
hasResultMemoryAccess(_, var, startBitOffset, endBitOffset) or
hasOperandMemoryAccess(_, var, startBitOffset, endBitOffset)
private newtype TMemoryLocation =
TVariableMemoryLocation(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset) {
hasResultMemoryAccess(_, var, type, startBitOffset, endBitOffset) or
hasOperandMemoryAccess(_, var, type, startBitOffset, endBitOffset)
}
or
TUnknownMemoryAccess(UnknownVirtualVariable uvv) or
TTotalUnknownMemoryAccess(UnknownVirtualVariable uvv)
TUnknownMemoryLocation(FunctionIR funcIR) or
TUnknownVirtualVariable(FunctionIR funcIR)
private VariableMemoryAccess getVariableMemoryAccess(IRVariable var, IntValue startBitOffset, IntValue endBitOffset) {
result = TVariableMemoryAccess(var, startBitOffset, endBitOffset)
abstract class MemoryLocation extends TMemoryLocation {
abstract string toString();
abstract VirtualVariable getVirtualVariable();
abstract Type getType();
abstract string getUniqueId();
}
class MemoryAccess extends TMemoryAccess {
string toString() {
none()
}
VirtualVariable getVirtualVariable() {
none()
}
predicate isPartialMemoryAccess() {
none()
}
abstract class VirtualVariable extends MemoryLocation {
}
/**
* An access to memory within a single known `IRVariable`. The variable may be either an unescaped variable
* (with its own `VirtualIRVariable`) or an escaped variable (assiged to `UnknownVirtualVariable`).
*/
class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
IRVariable var;
Type type;
IntValue startBitOffset;
IntValue endBitOffset;
VariableMemoryAccess() {
this = TVariableMemoryAccess(var, startBitOffset, endBitOffset)
VariableMemoryLocation() {
this = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
}
override final string toString() {
exists(string partialString |
result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + partialString and
if isPartialMemoryAccess() then
partialString = " (partial)"
else
partialString = ""
)
result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" + type.toString() + ">"
}
final override VirtualVariable getVirtualVariable() {
result = getVirtualVariable(var) or
not exists(getVirtualVariable(var)) and result = getUnknownVirtualVariable(var.getEnclosingIRFunction())
override final Type getType() {
result = type
}
IntValue getStartBitOffset() {
final IntValue getStartBitOffset() {
result = startBitOffset
}
IntValue getEndBitOffset() {
final IntValue getEndBitOffset() {
result = endBitOffset
}
@@ -176,138 +85,194 @@ class VariableMemoryAccess extends TVariableMemoryAccess, MemoryAccess {
result = var
}
final override predicate isPartialMemoryAccess() {
not exists(getVirtualVariable(var)) or
getStartBitOffset() != 0
or
not Ints::isEQ(getEndBitOffset(), Ints::add(getStartBitOffset(), Ints::mul(var.getType().getSize(), 8)))
override final string getUniqueId() {
result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
getTypeIdentityString(type) + ">"
}
override final VirtualVariable getVirtualVariable() {
if variableAddressEscapes(var) then
result = TUnknownVirtualVariable(var.getEnclosingFunctionIR())
else
result = TVariableMemoryLocation(var, var.getType(), 0, var.getType().getSize() * 8)
}
/**
* Holds if this memory location covers the entire variable.
*/
final predicate coversEntireVariable() {
startBitOffset = 0 and
Ints::isEQ(endBitOffset, Ints::mul(var.getType().getSize(), 8))
}
}
class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
VariableVirtualVariable() {
not variableAddressEscapes(var) and
type = var.getType() and
coversEntireVariable()
}
}
/**
* An access to memory that is not known to be confined to a specific `IRVariable`.
*/
class UnknownMemoryAccess extends TUnknownMemoryAccess, MemoryAccess {
UnknownVirtualVariable vvar;
UnknownMemoryAccess() {
this = TUnknownMemoryAccess(vvar)
class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
FunctionIR funcIR;
UnknownMemoryLocation() {
this = TUnknownMemoryLocation(funcIR)
}
final override string toString() {
result = vvar.toString()
override final string toString() {
result = "{Unknown}"
}
final override VirtualVariable getVirtualVariable() {
result = vvar
override final VirtualVariable getVirtualVariable() {
result = TUnknownVirtualVariable(funcIR)
}
final override predicate isPartialMemoryAccess() {
any()
override final Type getType() {
result instanceof UnknownType
}
override final string getUniqueId() {
result = "{Unknown}"
}
}
/**
* An access to all aliased memory.
*/
class TotalUnknownMemoryAccess extends TTotalUnknownMemoryAccess, MemoryAccess {
UnknownVirtualVariable vvar;
TotalUnknownMemoryAccess() {
this = TTotalUnknownMemoryAccess(vvar)
class UnknownVirtualVariable extends TUnknownVirtualVariable, VirtualVariable {
FunctionIR funcIR;
UnknownVirtualVariable() {
this = TUnknownVirtualVariable(funcIR)
}
final override string toString() {
result = vvar.toString()
override final string toString() {
result = "{AllAliased}"
}
final override VirtualVariable getVirtualVariable() {
result = vvar
override final Type getType() {
result instanceof UnknownType
}
override final string getUniqueId() {
result = " " + toString()
}
override final VirtualVariable getVirtualVariable() {
result = this
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
// The def and the use must have the same virtual variable, or no overlap is possible.
def.getVirtualVariable() = use.getVirtualVariable() and
(
// A TotalUnknownMemoryAccess must totally overlap any access to the same virtual variable.
def instanceof TotalUnknownMemoryAccess and result instanceof MustTotallyOverlap or
// An UnknownMemoryAccess may partially overlap any access to the same virtual variable.
def instanceof UnknownMemoryAccess and result instanceof MayPartiallyOverlap or
exists(VariableMemoryAccess defVariableAccess |
defVariableAccess = def and
// An UnknownVirtualVariable must totally overlap any location within the same virtual variable.
def instanceof UnknownVirtualVariable and result instanceof MustTotallyOverlap or
// An UnknownMemoryLocation may partially overlap any Location within the same virtual variable.
def instanceof UnknownMemoryLocation and result instanceof MayPartiallyOverlap or
exists(VariableMemoryLocation defVariableLocation |
defVariableLocation = def and
(
(
// A VariableMemoryAccess may partially overlap an unknown access to the same virtual variable.
((use instanceof UnknownMemoryAccess) or (use instanceof TotalUnknownMemoryAccess)) and
// A VariableMemoryLocation may partially overlap an unknown location within the same virtual variable.
((use instanceof UnknownMemoryLocation) or (use instanceof UnknownVirtualVariable)) and
result instanceof MayPartiallyOverlap
) or
// A VariableMemoryAccess overlaps another access to the same variable based on the relationship
// A VariableMemoryLocation overlaps another location within the same variable based on the relationship
// of the two offset intervals.
exists(VariableMemoryAccess useVariableAccess, IntValue defStartOffset, IntValue defEndOffset,
IntValue useStartOffset, IntValue useEndOffset |
useVariableAccess = use and
defStartOffset = defVariableAccess.getStartBitOffset() and
defEndOffset = defVariableAccess.getEndBitOffset() and
useStartOffset = useVariableAccess.getStartBitOffset() and
useEndOffset = useVariableAccess.getEndBitOffset() and
result = Interval::getOverlap(defStartOffset, defEndOffset, useStartOffset, useEndOffset)
exists(VariableMemoryLocation useVariableLocation, IntValue defStartOffset, IntValue defEndOffset,
IntValue useStartOffset, IntValue useEndOffset, Overlap intervalOverlap |
useVariableLocation = use and
// The def and use must access the same `IRVariable`.
defVariableLocation.getVariable() = useVariableLocation.getVariable() and
// The def and use intervals must overlap.
defStartOffset = defVariableLocation.getStartBitOffset() and
defEndOffset = defVariableLocation.getEndBitOffset() and
useStartOffset = useVariableLocation.getStartBitOffset() and
useEndOffset = useVariableLocation.getEndBitOffset() and
intervalOverlap = Interval::getOverlap(defStartOffset, defEndOffset, useStartOffset, useEndOffset) and
if intervalOverlap instanceof MustExactlyOverlap then (
if defVariableLocation.getType() = useVariableLocation.getType() then (
// The def and use types match, so it's an exact overlap.
result instanceof MustExactlyOverlap
)
else (
// The def and use types are not the same, so it's just a total overlap.
result instanceof MustTotallyOverlap
)
)
else if defVariableLocation.coversEntireVariable() then (
// The definition covers the entire variable, so assume that it totally overlaps the use, even if the
// interval for the use is unknown or outside the bounds of the variable.
result instanceof MustTotallyOverlap
)
else (
// Just use the overlap relation of the interval.
result = intervalOverlap
)
)
)
)
)
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
MemoryLocation getResultMemoryLocation(Instruction instr) {
exists(MemoryAccessKind kind |
kind = instr.getResultMemoryAccess() and
(
(
kind.usesAddressOperand() and
if hasResultMemoryAccess(instr, _, _, _) then (
exists(IRVariable var, IntValue startBitOffset, IntValue endBitOffset |
hasResultMemoryAccess(instr, var, startBitOffset, endBitOffset) and
result = getVariableMemoryAccess(var, startBitOffset, endBitOffset)
if hasResultMemoryAccess(instr, _, _, _, _) then (
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
hasResultMemoryAccess(instr, var, type, startBitOffset, endBitOffset) and
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
)
)
else (
result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingIRFunction()))
result = TUnknownMemoryLocation(instr.getEnclosingFunctionIR())
)
) or
(
kind instanceof EscapedMemoryAccess and
result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingIRFunction()))
result = TUnknownVirtualVariable(instr.getEnclosingFunctionIR())
) or
(
kind instanceof EscapedMayMemoryAccess and
result = TUnknownMemoryAccess(TUnknownVirtualVariable(instr.getEnclosingIRFunction()))
result = TUnknownMemoryLocation(instr.getEnclosingFunctionIR())
)
)
)
}
MemoryAccess getOperandMemoryAccess(MemoryOperand operand) {
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
exists(MemoryAccessKind kind |
kind = operand.getMemoryAccess() and
(
(
kind.usesAddressOperand() and
if hasOperandMemoryAccess(operand, _, _, _) then (
exists(IRVariable var, IntValue startBitOffset, IntValue endBitOffset |
hasOperandMemoryAccess(operand, var, startBitOffset, endBitOffset) and
result = getVariableMemoryAccess(var, startBitOffset, endBitOffset)
if hasOperandMemoryAccess(operand, _, _, _, _) then (
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
hasOperandMemoryAccess(operand, var, type, startBitOffset, endBitOffset) and
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
)
)
else (
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingIRFunction()))
result = TUnknownMemoryLocation(operand.getEnclosingFunctionIR())
)
) or
(
kind instanceof EscapedMemoryAccess and
result = TTotalUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingIRFunction()))
result = TUnknownVirtualVariable(operand.getEnclosingFunctionIR())
) or
(
kind instanceof EscapedMayMemoryAccess and
result = TUnknownMemoryAccess(TUnknownVirtualVariable(operand.getEnclosingIRFunction()))
result = TUnknownMemoryLocation(operand.getEnclosingFunctionIR())
)
)
)

View File

@@ -1,6 +1,8 @@
private import SSAConstructionInternal
private import OldIR
private import Alias
private import SSAConstruction
private import DebugSSA
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
@@ -8,13 +10,100 @@ private import Alias
*/
class PropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instruction, string key) {
(
key = "ResultMemoryAccess" and
result = getResultMemoryAccess(instruction).toString()
exists(MemoryLocation location |
location = getResultMemoryLocation(instruction) and
(
key = "ResultMemoryLocation" and result = location.toString() or
key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
)
)
or
exists(MemoryLocation location |
location = getOperandMemoryLocation(instruction.getAnOperand()) and
(
key = "OperandMemoryAccess" and result = location.toString() or
key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
)
) or
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
defBlock.getInstruction(defIndex) = instruction and
key = "DefinitionRank[" + useLocation.toString() + "]" and
result = defRank.toString()
) or
exists(MemoryLocation useLocation, IRBlock useBlock, int useRank |
hasUseAtRank(useLocation, useBlock, useRank, instruction) and
key = "UseRank[" + useLocation.toString() + "]" and
result = useRank.toString()
) or
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
defBlock.getInstruction(defIndex) = instruction and
key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
useBlock.getInstruction(useIndex) = useInstruction and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank)
) |
useBlock.getDisplayIndex().toString() + "_" + useIndex, ", " order by useBlock.getDisplayIndex(), useIndex
)
)
}
override string getBlockProperty(IRBlock block, string key) {
exists(MemoryLocation useLocation, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, block, defRank, defIndex) and
defIndex = -1 and
key = "DefinitionRank(Phi)[" + useLocation.toString() + "]" and
result = defRank.toString()
) or
exists(MemoryLocation useLocation, MemoryLocation defLocation, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, defLocation, block, defRank, defIndex) and
defIndex = -1 and
key = "DefinitionReachesUse(Phi)[" + useLocation.toString() + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
useBlock.getInstruction(useIndex) = useInstruction and
definitionReachesUse(useLocation, block, defRank, useBlock, useRank) and
exists(getOverlap(defLocation, useLocation))
) |
useBlock.getDisplayIndex().toString() + "_" + useIndex, ", " order by useBlock.getDisplayIndex(), useIndex
)
) or
exists(MemoryLocation useLocation, IRBlock predBlock, IRBlock defBlock, int defIndex, Overlap overlap |
hasPhiOperandDefinition(_, useLocation, block, predBlock, defBlock, defIndex, overlap) and
key = "PhiUse[" + useLocation.toString() + " from " + predBlock.getDisplayIndex().toString() + "]" and
result = defBlock.getDisplayIndex().toString() + "_" + defIndex + " (" + overlap.toString() + ")"
) or
(
key = "OperandMemoryAccess" and
result = getOperandMemoryAccess(instruction.getAnOperand().(MemoryOperand)).toString()
key = "LiveOnEntry" and
result = strictconcat(MemoryLocation useLocation |
locationLiveOnEntryToBlock(useLocation, block) |
useLocation.toString(), ", " order by useLocation.toString()
)
) or
(
key = "LiveOnExit" and
result = strictconcat(MemoryLocation useLocation |
locationLiveOnExitFromBlock(useLocation, block) |
useLocation.toString(), ", " order by useLocation.toString()
)
) or
(
key = "DefsLiveOnEntry" and
result = strictconcat(MemoryLocation defLocation |
definitionLiveOnEntryToBlock(defLocation, block) |
defLocation.toString(), ", " order by defLocation.toString()
)
) or
(
key = "DefsLiveOnExit" and
result = strictconcat(MemoryLocation defLocation |
definitionLiveOnExitFromBlock(defLocation, block) |
defLocation.toString(), ", " order by defLocation.toString()
)
)
}
}

View File

@@ -2,6 +2,7 @@ import SSAConstructionInternal
import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -15,8 +16,8 @@ cached private module Cached {
}
cached predicate functionHasIR(Function func) {
exists(OldIR::IRFunction irFunc |
irFunc.getFunction() = func
exists(OldIR::FunctionIR funcIR |
funcIR.getFunction() = func
)
}
@@ -24,21 +25,6 @@ cached private module Cached {
instr = WrappedInstruction(result)
}
private Instruction getNewInstruction(OldInstruction instr) {
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(OldInstruction instr) {
result = Chi(instr)
or
not exists(Chi(instr)) and
result = getNewInstruction(instr)
}
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
// This is just a type cast. Both classes derive from the same newtype.
result = var
@@ -48,8 +34,8 @@ cached private module Cached {
WrappedInstruction(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction
} or
Phi(OldBlock block, Alias::VirtualVariable vvar) {
hasPhiNode(vvar, block)
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
definitionHasPhiNode(defLocation, block)
} or
Chi(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
@@ -73,33 +59,41 @@ cached private module Cached {
}
cached predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
if oldOperand instanceof OldIR::MemoryOperand then (
result = getNewInstruction(oldOperand.getDefinitionInstruction())
)
}
cached Instruction getMemoryOperandDefinition(Instruction instruction, MemoryOperandTag tag, Overlap overlap) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
(
(
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldBlock defBlock, int defRank, int defIndex |
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = Phi(defBlock, vvar)
if exists(Alias::getOperandMemoryLocation(oldOperand)) then (
exists(OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset |
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
overlap = Alias::getOverlap(defLocation, useLocation) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
)
)
else (
result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction()
result = instruction.getEnclosingFunctionIR().getUnmodeledDefinitionInstruction() and
overlap instanceof MustTotallyOverlap
)
) or
// Connect any definitions that are not being modeled in SSA to the
@@ -108,24 +102,26 @@ cached private module Cached {
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
oldDefinition = oldOperand.getDefinitionInstruction() and
not exists(Alias::getResultMemoryAccess(oldDefinition)) and
result = getNewInstruction(oldDefinition)
not exists(Alias::getResultMemoryLocation(oldDefinition)) and
result = getNewInstruction(oldDefinition) and
overlap instanceof MustTotallyOverlap
)
)
else
result = getNewInstruction(oldOperand.getDefinitionInstruction())
) or
instruction = Chi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
exists(IRFunction f |
exists(FunctionIR f |
tag instanceof UnmodeledUseOperandTag and
result = f.getUnmodeledDefinitionInstruction() and
instruction = f.getUnmodeledUseInstruction()
instruction = f.getUnmodeledUseInstruction() and
overlap instanceof MustTotallyOverlap
)
or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction)
result = getChiInstructionTotalOperand(instruction) and
overlap instanceof MustExactlyOverlap
}
cached Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
@@ -148,35 +144,26 @@ cached private module Cached {
)
}
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock) {
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
hasPhiNode(vvar, phiBlock) and
predBlock = phiBlock.getAFeasiblePredecessor() and
instr = Phi(phiBlock, vvar) and
cached Instruction getPhiOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock, Overlap overlap) {
exists(Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock,
OldBlock defBlock, int defOffset |
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset, overlap) and
instr = Phi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = Phi(defBlock, vvar)
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
)
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
int defRank, int defIndex, OldBlock useBlock, int useRank |
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock,
int defRank, int defOffset, OldBlock useBlock, int useRank |
chiInstr = Chi(oldInstr) and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) 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 = Phi(defBlock, vvar)
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar)
)
}
@@ -274,9 +261,9 @@ cached private module Cached {
isGLValue = false
)
or
exists(Alias::VirtualVariable vvar |
instruction = Phi(_, vvar) and
type = vvar.getType() and
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
type = location.getType() and
isGLValue = false
)
or
@@ -301,7 +288,7 @@ cached private module Cached {
result instanceof Opcode::Unreached
}
cached IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
cached FunctionIR getInstructionEnclosingFunctionIR(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction)
or
@@ -372,97 +359,241 @@ cached private module Cached {
result = getNewInstruction(oldInstruction)
)
}
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldBlock block, int index, OldInstruction instr) {
block.getInstruction(index) = instr and
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
}
private Instruction getNewInstruction(OldInstruction instr) {
getOldInstruction(result) = instr
}
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
(
hasPhiNode(vvar, block) and
index = -1
) or
exists(Alias::MemoryAccess access, OldInstruction def |
access = Alias::getResultMemoryAccess(def) and
block.getInstruction(index) = def and
vvar = access.getVirtualVariable()
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
* original definition location is a member.
*/
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryLocation defLocation |
defLocation = Alias::getResultMemoryLocation(def) and
defLocation.getVirtualVariable() = vvar and
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
// instruction.
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
)
}
private import PhiInsertion
/**
* Module to handle insertion of `Phi` instructions at the correct blocks. We insert a `Phi` instruction at the
* beginning of a block for a given location when that block is on the dominance frontier of a definition of the
* location and there is a use of that location reachable from that block without an intervening definition of the
* location.
* Within the approach outlined above, we treat a location slightly differently depending on whether or not it is a
* virtual variable. For a virtual variable,
* location into the beginning of a block
*/
private module PhiInsertion {
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
)
}
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, block, j, _))
}
private predicate hasUse(Alias::VirtualVariable vvar, OldBlock block, int index,
OldInstruction use) {
exists(Alias::MemoryAccess access |
/**
* Holds if the virtual variable `vvar` has a definition in block `block`, either because of an existing instruction
* or because of a Phi node.
*/
private predicate definitionHasDefinitionInBlock(Alias::MemoryLocation defLocation, OldBlock block) {
definitionHasPhiNode(defLocation, block) or
exists(OldInstruction def, Alias::MemoryLocation resultLocation |
def.getBlock() = block and
resultLocation = Alias::getResultMemoryLocation(def) 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()
defLocation = resultLocation or
// For a virtual variable, any definition of a member location will either generate a `Chi` node that defines
// the virtual variable, or will totally overlap the virtual variable. Either way, treat this as a definition of
// the virtual variable.
defLocation = resultLocation.getVirtualVariable()
)
)
}
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
/**
* Holds if there is a use at (`block`, `index`) that could consume the result of a `Phi` instruction for
* `defLocation`.
*/
private predicate definitionHasUse(Alias::MemoryLocation defLocation, OldBlock block, int index) {
exists(OldInstruction use |
block.getInstruction(index) = use and
if defLocation instanceof Alias::VirtualVariable then (
exists(Alias::MemoryLocation useLocation |
// For a virtual variable, any use of a location that is a member of the virtual variable counts as a use.
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
defLocation = useLocation.getVirtualVariable()
) or
// A `Chi` instruction consumes the enclosing virtual variable of its use location.
hasChiNode(defLocation, use)
)
else (
// For other locations, only an exactly-overlapping use of the same location counts as a use.
defLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
Alias::getOverlap(defLocation, defLocation) instanceof MustExactlyOverlap
)
)
}
/**
* Holds if the location `defLocation` is redefined at (`block`, `index`). A location is considered "redefined" if
* there is a definition that would prevent a previous definition of `defLocation` from being consumed as the operand
* of a `Phi` node that occurs after the redefinition.
*/
private predicate definitionHasRedefinition(Alias::MemoryLocation defLocation, OldBlock block, int index) {
exists(OldInstruction redef, Alias::MemoryLocation redefLocation |
block.getInstruction(index) = redef and
redefLocation = Alias::getResultMemoryLocation(redef) and
if defLocation instanceof Alias::VirtualVariable then (
// For a virtual variable, the definition may be consumed by any use of a location that is a member of the
// virtual variable. Thus, the definition is live until a subsequent redefinition of the entire virtual
// variable.
exists(Overlap overlap |
overlap = Alias::getOverlap(redefLocation, defLocation) and
not overlap instanceof MayPartiallyOverlap
)
)
else (
// For other locations, the definition may only be consumed by an exactly-overlapping use of the same location.
// Thus, the definition is live until a subsequent definition of any location that may overlap the original
// definition location.
exists(Alias::getOverlap(redefLocation, defLocation))
)
)
}
/**
* Holds if the definition `defLocation` is live on entry to block `block`. The definition is live if there is at
* least one use of that definition before any intervening instruction that redefines the definition location.
*/
predicate definitionLiveOnEntryToBlock(Alias::MemoryLocation defLocation, OldBlock block) {
exists(int firstAccess |
hasUse(vvar, block, firstAccess, _) and
definitionHasUse(defLocation, block, firstAccess) and
firstAccess = min(int index |
hasUse(vvar, block, index, _)
definitionHasUse(defLocation, block, index)
or
ssa_variableUpdate(vvar, block, index, _)
definitionHasRedefinition(defLocation, block, index)
)
)
or
(variableLiveOnExitFromBlock(vvar, block) and not ssa_variableUpdate(vvar, block, _, _))
}
pragma[noinline]
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
(definitionLiveOnExitFromBlock(defLocation, block) and not definitionHasRedefinition(defLocation, block, _))
}
/**
* Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the
* block.
* Holds if the definition `defLocation` is live on exit from block `block`. The definition is live on exit if it is
* live on entry to any of the successors of `block`.
*/
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
pragma[noinline]
predicate definitionLiveOnExitFromBlock(Alias::MemoryLocation defLocation, OldBlock block) {
definitionLiveOnEntryToBlock(defLocation, block.getAFeasibleSuccessor())
}
}
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
int instructionIndex) {
hasDefinition(vvar, block, instructionIndex) and
defUseRank(vvar, block, rankIndex, instructionIndex)
}
private import DefUse
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
OldInstruction use) {
exists(int index |
hasUse(vvar, block, index, use) and
defUseRank(vvar, block, rankIndex, index)
/**
* Module containing the predicates that connect uses to their reaching definition. The reaching definitios are computed
* separately for each unique use `MemoryLocation`. An instruction is treated as a definition of a use location if the
* defined location overlaps the use location in any way. Thus, a single instruction may serve as a definition for
* multiple use locations, since a single definition location may overlap many use locations.
*
* Definitions and uses are identified by a block and an integer "offset". An offset of -1 indicates the definition
* from a `Phi` instruction at the beginning of the block. An offset of 2*i indicates a definition or use on the
* instruction at index `i` in the block. An offset of 2*i+1 indicates a definition or use on the `Chi` instruction that
* will be inserted immediately after the instruction at index `i` in the block.
*
* For a given use location, each definition and use is also assigned a "rank" within its block. The rank is simply the
* one-based index of that definition or use within the list of definitions and uses of that location within the block,
* ordered by offset. The rank allows the various reachability predicates to be computed more efficiently than they
* would if based solely on offset, since the set of possible ranks is dense while the set of possible offsets is
* potentially very sparse.
*/
module DefUse {
/**
* Gets the `Instruction` for the definition at offset `defOffset` in block `defBlock`.
*/
pragma[inline]
bindingset[defOffset, defLocation]
Instruction getDefinitionOrChiInstruction(OldBlock defBlock, int defOffset,
Alias::MemoryLocation defLocation) {
(
defOffset >= 0 and
exists(OldInstruction oldInstr |
oldInstr = defBlock.getInstruction(defOffset / 2) and
if (defOffset % 2) > 0 then (
// An odd offset corresponds to the `Chi` instruction.
result = Chi(oldInstr)
)
else (
// An even offset corresponds to the original instruction.
result = getNewInstruction(oldInstr)
)
)
) or
(
defOffset < 0 and
result = Phi(defBlock, defLocation)
)
}
/**
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
* Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the
* block.
*/
private int exitRank(Alias::MemoryLocation useLocation, OldBlock block) {
result = max(int rankIndex | defUseRank(useLocation, block, rankIndex, _)) + 1
}
/**
* Holds if a definition that overlaps `useLocation` at (`defBlock`, `defRank`) reaches the use of `useLocation` at
* (`useBlock`, `useRank`) without any intervening definitions that overlap `useLocation`, where `defBlock` and
* `useBlock` are the same block.
*/
private predicate definitionReachesUseWithinBlock(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(useLocation, _, defBlock, defRank, _) and
hasUseAtRank(useLocation, useBlock, useRank, _) and
definitionReachesRank(useLocation, defBlock, defRank, useRank)
}
/**
* Holds if a definition that overlaps `useLocation` at (`defBlock`, `defRank`) reaches the use of `useLocation` at
* (`useBlock`, `useRank`) without any intervening definitions that overlap `useLocation`.
*/
predicate definitionReachesUse(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(useLocation, useBlock, useRank, _) and
(
definitionReachesUseWithinBlock(useLocation, defBlock, defRank, useBlock,
useRank) or
(
definitionReachesEndOfBlock(useLocation, defBlock, defRank,
useBlock.getAFeasiblePredecessor()) and
not definitionReachesUseWithinBlock(useLocation, useBlock, _, useBlock, useRank)
)
)
}
/**
* Holds if the definition that overlaps `useLocation` at `(block, defRank)` reaches the rank
* index `reachesRank` in block `block`.
*/
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
private predicate definitionReachesRank(Alias::MemoryLocation useLocation, OldBlock block, int defRank,
int reachesRank) {
hasDefinitionAtRank(vvar, block, defRank, _) and
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
hasDefinitionAtRank(useLocation, _, block, defRank, _) and
reachesRank <= exitRank(useLocation, block) and // Without this, the predicate would be infinite.
(
// The def always reaches the next use, even if there is also a def on the
// use instruction.
@@ -470,87 +601,176 @@ cached private module Cached {
(
// If the def reached the previous rank, it also reaches the current rank,
// unless there was another def at the previous rank.
definitionReachesRank(vvar, block, defRank, reachesRank - 1) and
not hasDefinitionAtRank(vvar, block, reachesRank - 1, _)
definitionReachesRank(useLocation, block, defRank, reachesRank - 1) and
not hasDefinitionAtRank(useLocation, _, block, reachesRank - 1, _)
)
)
}
/**
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
* block `block`.
*/
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
* Holds if the definition that overlaps `useLocation` at `(defBlock, defRank)` reaches the end of
* block `block` without any intervening definitions that overlap `useLocation`.
*/
predicate definitionReachesEndOfBlock(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock block) {
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasDefinitionAtRank(useLocation, _, defBlock, defRank, _) and
(
(
// If we're looking at the def's own block, just see if it reaches the exit
// rank of the block.
block = defBlock and
variableLiveOnExitFromBlock(vvar, defBlock) and
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
locationLiveOnExitFromBlock(useLocation, defBlock) and
definitionReachesRank(useLocation, defBlock, defRank, exitRank(useLocation, defBlock))
) or
exists(OldBlock idom |
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(vvar, idom, block)
definitionReachesEndOfBlock(useLocation, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(useLocation, idom, block)
)
)
}
pragma[noinline]
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
private predicate noDefinitionsSinceIDominator(Alias::MemoryLocation useLocation, OldBlock idom,
OldBlock block) {
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
variableLiveOnExitFromBlock(vvar, block) and
not hasDefinition(vvar, block, _)
locationLiveOnExitFromBlock(useLocation, block) and
not hasDefinition(useLocation, _, block, _)
}
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasUseAtRank(vvar, useBlock, useRank, _) and
definitionReachesRank(vvar, defBlock, defRank, useRank)
/**
* Holds if the specified `useLocation` is live on entry to `block`. This holds if there is a use of `useLocation`
* that is reachable from the start of `block` without passing through a definition that overlaps `useLocation`.
*/
predicate locationLiveOnEntryToBlock(Alias::MemoryLocation useLocation, OldBlock block) {
definitionHasPhiNode(useLocation, block) or
exists(int firstAccess |
hasUse(useLocation, block, firstAccess, _) and
firstAccess = min(int offset |
hasUse(useLocation, block, offset, _)
or
hasNonPhiDefinition(useLocation, _, block, offset)
)
) or
(locationLiveOnExitFromBlock(useLocation, block) and not hasNonPhiDefinition(useLocation, _, block, _))
}
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(vvar, useBlock, useRank, _) and
/**
* Holds if the specified `useLocation` is live on exit from `block`.
*/
pragma[noinline]
predicate locationLiveOnExitFromBlock(Alias::MemoryLocation useLocation, OldBlock block) {
locationLiveOnEntryToBlock(useLocation, block.getAFeasibleSuccessor())
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* This predicate does not include definitions for Phi nodes.
*/
private predicate hasNonPhiDefinition(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock block, int offset) {
exists(OldInstruction def, Overlap overlap, int index |
defLocation = Alias::getResultMemoryLocation(def) and
block.getInstruction(index) = def and
overlap = Alias::getOverlap(defLocation, useLocation) and
if overlap instanceof MayPartiallyOverlap then
offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
else
offset = index * 2 // The use will be connected to the definition on the original instruction.
)
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* This predicate includes definitions for Phi nodes (at offset -1).
*/
private predicate hasDefinition(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation, OldBlock block,
int offset) {
(
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
useRank) or
// If there is a Phi node for the use location itself, treat that as a definition at offset -1.
offset = -1 and
if definitionHasPhiNode(useLocation, block) then (
defLocation = useLocation
)
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
)
) or
hasNonPhiDefinition(useLocation, defLocation, block, offset)
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* `rankIndex` is the rank of the definition as computed by `defUseRank()`.
*/
predicate hasDefinitionAtRank(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock block, int rankIndex, int offset) {
hasDefinition(useLocation, defLocation, block, offset) and
defUseRank(useLocation, block, rankIndex, offset)
}
/**
* Holds if there is a use of `useLocation` on instruction `use` at offset `offset` in block `block`.
*/
private predicate hasUse(Alias::MemoryLocation useLocation, OldBlock block, int offset, OldInstruction use) {
exists(int index |
block.getInstruction(index) = use and
(
definitionReachesEndOfBlock(vvar, defBlock, defRank,
useBlock.getAFeasiblePredecessor()) and
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
// A direct use of the location.
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2 or
// A `Chi` instruction will include a use of the virtual variable.
hasChiNode(useLocation, use) and offset = (index * 2) + 1
)
)
}
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
hasDefinition(vvar, defBlock, _) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
variableLiveOnEntryToBlock(vvar, phiBlock)
/**
* Holds if there is a use of memory location `useLocation` on instruction `use` in block `block`. `rankIndex` is the
* rank of the use use as computed by `defUseRank`.
*/
predicate hasUseAtRank(Alias::MemoryLocation useLocation, OldBlock block, int rankIndex, OldInstruction use) {
exists(int offset |
hasUse(useLocation, block, offset, use) and
defUseRank(useLocation, block, rankIndex, offset)
)
}
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`, or
* a use of `useLocation` at offset `offset` in block `block`. `rankIndex` is the sequence number of the definition
* or use within `block`, counting only uses of `useLocation` and definitions that overlap `useLocation`.
*/
private predicate defUseRank(Alias::MemoryLocation useLocation, OldBlock block, int rankIndex, int offset) {
offset = rank[rankIndex](int j | hasDefinition(useLocation, _, block, j) or hasUse(useLocation, block, j, _))
}
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
ma.getVirtualVariable() = vvar
/**
* Holds if the `Phi` instruction for location `useLocation` at the beginning of block `phiBlock` has an operand along
* the incoming edge from `predBlock`, where that operand's definition is at offset `defOffset` in block `defBlock`,
* and overlaps the use operand with overlap relationship `overlap`.
*/
pragma[inline]
predicate hasPhiOperandDefinition(Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation,
OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Overlap overlap) {
exists(int defRank |
definitionHasPhiNode(useLocation, phiBlock) and
predBlock = phiBlock.getAFeasiblePredecessor() and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
definitionReachesEndOfBlock(useLocation, defBlock, defRank, predBlock) and
overlap = Alias::getOverlap(defLocation, useLocation)
)
}
}
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
*/
module DebugSSA {
import PhiInsertion
import DefUse
}
import CachedForDebugging
cached private module CachedForDebugging {
cached string getTempVariableUniqueId(IRTempVariable var) {
@@ -562,9 +782,16 @@ cached private module CachedForDebugging {
oldInstr = getOldInstruction(instr) and
result = "NonSSA: " + oldInstr.getUniqueId()
) or
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
instr = Phi(phiBlock, vvar) and
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
instr = Phi(phiBlock, location) and
result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable then (
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
specificity = "g"
)
else (
specificity = "s"
)
) or
(
instr = Unreached(_) and

View File

@@ -50,11 +50,17 @@ module InstructionSanity {
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
query predicate missingOperand(Instruction instr, OperandTag tag) {
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
query predicate missingOperand(Instruction instr, string message, FunctionIR func, string funcText) {
exists(OperandTag tag |
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
) and
message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" +
tag.toString() + "' in function '$@'." and
func = instr.getEnclosingFunctionIR() and
funcText = getIdentityString(func.getFunction())
)
}
@@ -302,7 +308,7 @@ class Instruction extends Construction::TInstruction {
result = type
}
private string getResultTypeString() {
string getResultTypeString() {
exists(string valcat |
valcat = getValueCategoryString(getResultType().toString()) and
if (getResultType() instanceof UnknownType and

View File

@@ -3,14 +3,18 @@ import Instruction
import IRBlock
import cpp
import semmle.code.cpp.ir.implementation.MemoryAccessKind
import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TNonPhiOperand(Instruction useInstr, OperandTag tag, Instruction defInstr) {
defInstr = Construction::getInstructionOperandDefinition(useInstr, tag)
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock) {
defInstr = Construction::getPhiInstructionOperandDefinition(useInstr, predecessorBlock)
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/**
@@ -25,8 +29,8 @@ class Operand extends TOperand {
result = getUseInstruction().getLocation()
}
final IRFunction getEnclosingIRFunction() {
result = getUseInstruction().getEnclosingIRFunction()
final FunctionIR getEnclosingFunctionIR() {
result = getUseInstruction().getEnclosingFunctionIR()
}
/**
@@ -43,6 +47,20 @@ class Operand extends TOperand {
none()
}
/**
* Gets the overlap relationship between the operand's definition and its use.
*/
Overlap getDefinitionOverlap() {
none()
}
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() {
not getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
*/
@@ -61,6 +79,13 @@ class Operand extends TOperand {
result = getDumpLabel() + getDefinitionInstruction().getResultId()
}
private string getInexactSpecifier() {
if isDefinitionInexact() then
result = "~"
else
result = ""
}
/**
* Get the order in which the operand should be sorted in the operand list.
*/
@@ -104,10 +129,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
exists(MemoryOperandTag tag |
this = TNonPhiOperand(_, tag, _)
) or
this = TPhiOperand(_, _, _)
this = TNonPhiMemoryOperand(_, _, _, _) or
this = TPhiOperand(_, _, _, _)
}
override predicate isGLValue() {
@@ -133,27 +156,17 @@ class MemoryOperand extends Operand {
}
}
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends Operand {
RegisterOperand() {
exists(RegisterOperandTag tag |
this = TNonPhiOperand(_, tag, _)
)
}
}
/**
* An operand that is not an operand of a `PhiInstruction`.
*/
class NonPhiOperand extends Operand, TNonPhiOperand {
class NonPhiOperand extends Operand {
Instruction useInstr;
Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
this = TNonPhiOperand(useInstr, tag, defInstr)
this = TRegisterOperand(useInstr, tag, defInstr) or
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
}
override final Instruction getUseInstruction() {
@@ -177,7 +190,32 @@ class NonPhiOperand extends Operand, TNonPhiOperand {
}
}
class TypedOperand extends NonPhiOperand, MemoryOperand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
override final Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
result instanceof MustExactlyOverlap
}
}
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
Overlap overlap;
NonPhiMemoryOperand() {
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap)
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
}
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
override final Type getType() {
@@ -189,7 +227,7 @@ class TypedOperand extends NonPhiOperand, MemoryOperand {
* The address operand of an instruction that loads or stores a value from
* memory (e.g. `Load`, `Store`).
*/
class AddressOperand extends NonPhiOperand, RegisterOperand {
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
override string toString() {
@@ -216,7 +254,7 @@ class LoadOperand extends TypedOperand {
/**
* The source value operand of a `Store` instruction.
*/
class StoreValueOperand extends NonPhiOperand, RegisterOperand {
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
override string toString() {
@@ -227,7 +265,7 @@ class StoreValueOperand extends NonPhiOperand, RegisterOperand {
/**
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
*/
class UnaryOperand extends NonPhiOperand, RegisterOperand {
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
override string toString() {
@@ -238,7 +276,7 @@ class UnaryOperand extends NonPhiOperand, RegisterOperand {
/**
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class LeftOperand extends NonPhiOperand, RegisterOperand {
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
override string toString() {
@@ -249,7 +287,7 @@ class LeftOperand extends NonPhiOperand, RegisterOperand {
/**
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class RightOperand extends NonPhiOperand, RegisterOperand {
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
override string toString() {
@@ -260,7 +298,7 @@ class RightOperand extends NonPhiOperand, RegisterOperand {
/**
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
*/
class ConditionOperand extends NonPhiOperand, RegisterOperand {
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
override string toString() {
@@ -272,7 +310,7 @@ class ConditionOperand extends NonPhiOperand, RegisterOperand {
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
class UnmodeledUseOperand extends NonPhiMemoryOperand {
override UnmodeledUseOperandTag tag;
override string toString() {
@@ -287,7 +325,7 @@ class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
/**
* The operand representing the target function of an `Call` instruction.
*/
class CallTargetOperand extends NonPhiOperand, RegisterOperand {
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
override string toString() {
@@ -300,7 +338,7 @@ class CallTargetOperand extends NonPhiOperand, RegisterOperand {
* positional arguments (represented by `PositionalArgumentOperand`) and the
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
*/
class ArgumentOperand extends NonPhiOperand, RegisterOperand {
class ArgumentOperand extends RegisterOperand {
override ArgumentOperandTag tag;
}
@@ -379,13 +417,14 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, TPhiOperand {
class PhiOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
PhiInputOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock)
PhiOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
override string toString() {
@@ -400,6 +439,10 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
result = defInstr
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
override final int getDumpSortOrder() {
result = 11 + getPredecessorBlock().getDisplayIndex()
}
@@ -423,10 +466,8 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends MemoryOperand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
override string toString() {
result = "ChiTotal"
@@ -441,10 +482,8 @@ class ChiTotalOperand extends MemoryOperand {
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends MemoryOperand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
override string toString() {
result = "ChiPartial"

View File

@@ -59,11 +59,17 @@ cached private module Cached {
)
}
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
getInstructionTag(instruction), tag)
}
cached Instruction getMemoryOperandDefinition(Instruction instruction, MemoryOperandTag tag, Overlap overlap) {
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
getInstructionTag(instruction), tag) and
overlap instanceof MustTotallyOverlap
}
cached Type getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
// the result type of the load.
@@ -80,8 +86,7 @@ cached private module Cached {
getInstructionTag(instruction), tag)
}
cached Instruction getPhiInstructionOperandDefinition(Instruction instruction,
IRBlock predecessorBlock) {
cached Instruction getPhiOperandDefinition(PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap) {
none()
}

View File

@@ -50,11 +50,17 @@ module InstructionSanity {
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
query predicate missingOperand(Instruction instr, OperandTag tag) {
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
query predicate missingOperand(Instruction instr, string message, FunctionIR func, string funcText) {
exists(OperandTag tag |
expectsOperand(instr, tag) and
not exists(NonPhiOperand operand |
operand = instr.getAnOperand() and
operand.getOperandTag() = tag
) and
message = "Instruction '" + instr.getOpcode().toString() + "' is missing an expected operand with tag '" +
tag.toString() + "' in function '$@'." and
func = instr.getEnclosingFunctionIR() and
funcText = getIdentityString(func.getFunction())
)
}
@@ -302,7 +308,7 @@ class Instruction extends Construction::TInstruction {
result = type
}
private string getResultTypeString() {
string getResultTypeString() {
exists(string valcat |
valcat = getValueCategoryString(getResultType().toString()) and
if (getResultType() instanceof UnknownType and

View File

@@ -3,14 +3,18 @@ import Instruction
import IRBlock
import cpp
import semmle.code.cpp.ir.implementation.MemoryAccessKind
import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TNonPhiOperand(Instruction useInstr, OperandTag tag, Instruction defInstr) {
defInstr = Construction::getInstructionOperandDefinition(useInstr, tag)
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock) {
defInstr = Construction::getPhiInstructionOperandDefinition(useInstr, predecessorBlock)
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/**
@@ -25,8 +29,8 @@ class Operand extends TOperand {
result = getUseInstruction().getLocation()
}
final IRFunction getEnclosingIRFunction() {
result = getUseInstruction().getEnclosingIRFunction()
final FunctionIR getEnclosingFunctionIR() {
result = getUseInstruction().getEnclosingFunctionIR()
}
/**
@@ -43,6 +47,20 @@ class Operand extends TOperand {
none()
}
/**
* Gets the overlap relationship between the operand's definition and its use.
*/
Overlap getDefinitionOverlap() {
none()
}
/**
* Holds if the result of the definition instruction does not exactly overlap this use.
*/
final predicate isDefinitionInexact() {
not getDefinitionOverlap() instanceof MustExactlyOverlap
}
/**
* Gets a prefix to use when dumping the operand in an operand list.
*/
@@ -61,6 +79,13 @@ class Operand extends TOperand {
result = getDumpLabel() + getDefinitionInstruction().getResultId()
}
private string getInexactSpecifier() {
if isDefinitionInexact() then
result = "~"
else
result = ""
}
/**
* Get the order in which the operand should be sorted in the operand list.
*/
@@ -104,10 +129,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
exists(MemoryOperandTag tag |
this = TNonPhiOperand(_, tag, _)
) or
this = TPhiOperand(_, _, _)
this = TNonPhiMemoryOperand(_, _, _, _) or
this = TPhiOperand(_, _, _, _)
}
override predicate isGLValue() {
@@ -133,27 +156,17 @@ class MemoryOperand extends Operand {
}
}
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends Operand {
RegisterOperand() {
exists(RegisterOperandTag tag |
this = TNonPhiOperand(_, tag, _)
)
}
}
/**
* An operand that is not an operand of a `PhiInstruction`.
*/
class NonPhiOperand extends Operand, TNonPhiOperand {
class NonPhiOperand extends Operand {
Instruction useInstr;
Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
this = TNonPhiOperand(useInstr, tag, defInstr)
this = TRegisterOperand(useInstr, tag, defInstr) or
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
}
override final Instruction getUseInstruction() {
@@ -177,7 +190,32 @@ class NonPhiOperand extends Operand, TNonPhiOperand {
}
}
class TypedOperand extends NonPhiOperand, MemoryOperand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
override final Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
result instanceof MustExactlyOverlap
}
}
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
Overlap overlap;
NonPhiMemoryOperand() {
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap)
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
}
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
override final Type getType() {
@@ -189,7 +227,7 @@ class TypedOperand extends NonPhiOperand, MemoryOperand {
* The address operand of an instruction that loads or stores a value from
* memory (e.g. `Load`, `Store`).
*/
class AddressOperand extends NonPhiOperand, RegisterOperand {
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
override string toString() {
@@ -216,7 +254,7 @@ class LoadOperand extends TypedOperand {
/**
* The source value operand of a `Store` instruction.
*/
class StoreValueOperand extends NonPhiOperand, RegisterOperand {
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
override string toString() {
@@ -227,7 +265,7 @@ class StoreValueOperand extends NonPhiOperand, RegisterOperand {
/**
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
*/
class UnaryOperand extends NonPhiOperand, RegisterOperand {
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
override string toString() {
@@ -238,7 +276,7 @@ class UnaryOperand extends NonPhiOperand, RegisterOperand {
/**
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class LeftOperand extends NonPhiOperand, RegisterOperand {
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
override string toString() {
@@ -249,7 +287,7 @@ class LeftOperand extends NonPhiOperand, RegisterOperand {
/**
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
*/
class RightOperand extends NonPhiOperand, RegisterOperand {
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
override string toString() {
@@ -260,7 +298,7 @@ class RightOperand extends NonPhiOperand, RegisterOperand {
/**
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
*/
class ConditionOperand extends NonPhiOperand, RegisterOperand {
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
override string toString() {
@@ -272,7 +310,7 @@ class ConditionOperand extends NonPhiOperand, RegisterOperand {
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
class UnmodeledUseOperand extends NonPhiMemoryOperand {
override UnmodeledUseOperandTag tag;
override string toString() {
@@ -287,7 +325,7 @@ class UnmodeledUseOperand extends NonPhiOperand, MemoryOperand {
/**
* The operand representing the target function of an `Call` instruction.
*/
class CallTargetOperand extends NonPhiOperand, RegisterOperand {
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
override string toString() {
@@ -300,7 +338,7 @@ class CallTargetOperand extends NonPhiOperand, RegisterOperand {
* positional arguments (represented by `PositionalArgumentOperand`) and the
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
*/
class ArgumentOperand extends NonPhiOperand, RegisterOperand {
class ArgumentOperand extends RegisterOperand {
override ArgumentOperandTag tag;
}
@@ -379,13 +417,14 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, TPhiOperand {
class PhiOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
PhiInputOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock)
PhiOperand() {
this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
override string toString() {
@@ -400,6 +439,10 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
result = defInstr
}
override final Overlap getDefinitionOverlap() {
result = overlap
}
override final int getDumpSortOrder() {
result = 11 + getPredecessorBlock().getDisplayIndex()
}
@@ -423,10 +466,8 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
/**
* The total operand of a Chi node, representing the previous value of the memory.
*/
class ChiTotalOperand extends MemoryOperand {
ChiTotalOperand() {
this = TNonPhiOperand(_, chiTotalOperand(), _)
}
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
override string toString() {
result = "ChiTotal"
@@ -441,10 +482,8 @@ class ChiTotalOperand extends MemoryOperand {
/**
* The partial operand of a Chi node, representing the value being written to part of the memory.
*/
class ChiPartialOperand extends MemoryOperand {
ChiPartialOperand() {
this = TNonPhiOperand(_, chiPartialOperand(), _)
}
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
override string toString() {
result = "ChiPartial"

View File

@@ -1,6 +1,8 @@
private import SSAConstructionInternal
private import OldIR
private import Alias
private import SSAConstruction
private import DebugSSA
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
@@ -8,13 +10,100 @@ private import Alias
*/
class PropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instruction, string key) {
(
key = "ResultMemoryAccess" and
result = getResultMemoryAccess(instruction).toString()
exists(MemoryLocation location |
location = getResultMemoryLocation(instruction) and
(
key = "ResultMemoryLocation" and result = location.toString() or
key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
)
)
or
exists(MemoryLocation location |
location = getOperandMemoryLocation(instruction.getAnOperand()) and
(
key = "OperandMemoryAccess" and result = location.toString() or
key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
)
) or
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
defBlock.getInstruction(defIndex) = instruction and
key = "DefinitionRank[" + useLocation.toString() + "]" and
result = defRank.toString()
) or
exists(MemoryLocation useLocation, IRBlock useBlock, int useRank |
hasUseAtRank(useLocation, useBlock, useRank, instruction) and
key = "UseRank[" + useLocation.toString() + "]" and
result = useRank.toString()
) or
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
defBlock.getInstruction(defIndex) = instruction and
key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
useBlock.getInstruction(useIndex) = useInstruction and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank)
) |
useBlock.getDisplayIndex().toString() + "_" + useIndex, ", " order by useBlock.getDisplayIndex(), useIndex
)
)
}
override string getBlockProperty(IRBlock block, string key) {
exists(MemoryLocation useLocation, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, _, block, defRank, defIndex) and
defIndex = -1 and
key = "DefinitionRank(Phi)[" + useLocation.toString() + "]" and
result = defRank.toString()
) or
exists(MemoryLocation useLocation, MemoryLocation defLocation, int defRank, int defIndex |
hasDefinitionAtRank(useLocation, defLocation, block, defRank, defIndex) and
defIndex = -1 and
key = "DefinitionReachesUse(Phi)[" + useLocation.toString() + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
useBlock.getInstruction(useIndex) = useInstruction and
definitionReachesUse(useLocation, block, defRank, useBlock, useRank) and
exists(getOverlap(defLocation, useLocation))
) |
useBlock.getDisplayIndex().toString() + "_" + useIndex, ", " order by useBlock.getDisplayIndex(), useIndex
)
) or
exists(MemoryLocation useLocation, IRBlock predBlock, IRBlock defBlock, int defIndex, Overlap overlap |
hasPhiOperandDefinition(_, useLocation, block, predBlock, defBlock, defIndex, overlap) and
key = "PhiUse[" + useLocation.toString() + " from " + predBlock.getDisplayIndex().toString() + "]" and
result = defBlock.getDisplayIndex().toString() + "_" + defIndex + " (" + overlap.toString() + ")"
) or
(
key = "OperandMemoryAccess" and
result = getOperandMemoryAccess(instruction.getAnOperand().(MemoryOperand)).toString()
key = "LiveOnEntry" and
result = strictconcat(MemoryLocation useLocation |
locationLiveOnEntryToBlock(useLocation, block) |
useLocation.toString(), ", " order by useLocation.toString()
)
) or
(
key = "LiveOnExit" and
result = strictconcat(MemoryLocation useLocation |
locationLiveOnExitFromBlock(useLocation, block) |
useLocation.toString(), ", " order by useLocation.toString()
)
) or
(
key = "DefsLiveOnEntry" and
result = strictconcat(MemoryLocation defLocation |
definitionLiveOnEntryToBlock(defLocation, block) |
defLocation.toString(), ", " order by defLocation.toString()
)
) or
(
key = "DefsLiveOnExit" and
result = strictconcat(MemoryLocation defLocation |
definitionLiveOnExitFromBlock(defLocation, block) |
defLocation.toString(), ", " order by defLocation.toString()
)
)
}
}

View File

@@ -2,6 +2,7 @@ import SSAConstructionInternal
import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.Overlap
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -15,8 +16,8 @@ cached private module Cached {
}
cached predicate functionHasIR(Function func) {
exists(OldIR::IRFunction irFunc |
irFunc.getFunction() = func
exists(OldIR::FunctionIR funcIR |
funcIR.getFunction() = func
)
}
@@ -24,21 +25,6 @@ cached private module Cached {
instr = WrappedInstruction(result)
}
private Instruction getNewInstruction(OldInstruction instr) {
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(OldInstruction instr) {
result = Chi(instr)
or
not exists(Chi(instr)) and
result = getNewInstruction(instr)
}
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
// This is just a type cast. Both classes derive from the same newtype.
result = var
@@ -48,8 +34,8 @@ cached private module Cached {
WrappedInstruction(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction
} or
Phi(OldBlock block, Alias::VirtualVariable vvar) {
hasPhiNode(vvar, block)
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
definitionHasPhiNode(defLocation, block)
} or
Chi(OldInstruction oldInstruction) {
not oldInstruction instanceof OldIR::PhiInstruction and
@@ -73,33 +59,41 @@ cached private module Cached {
}
cached predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryAccess(getOldInstruction(instruction))) or
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::NonPhiOperand oldOperand |
cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
if oldOperand instanceof OldIR::MemoryOperand then (
result = getNewInstruction(oldOperand.getDefinitionInstruction())
)
}
cached Instruction getMemoryOperandDefinition(Instruction instruction, MemoryOperandTag tag, Overlap overlap) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
(
(
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
exists(OldBlock useBlock, int useRank, Alias::VirtualVariable vvar,
OldBlock defBlock, int defRank, int defIndex |
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = Phi(defBlock, vvar)
if exists(Alias::getOperandMemoryLocation(oldOperand)) then (
exists(OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset |
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
overlap = Alias::getOverlap(defLocation, useLocation) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
)
)
else (
result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction()
result = instruction.getEnclosingFunctionIR().getUnmodeledDefinitionInstruction() and
overlap instanceof MustTotallyOverlap
)
) or
// Connect any definitions that are not being modeled in SSA to the
@@ -108,24 +102,26 @@ cached private module Cached {
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
oldDefinition = oldOperand.getDefinitionInstruction() and
not exists(Alias::getResultMemoryAccess(oldDefinition)) and
result = getNewInstruction(oldDefinition)
not exists(Alias::getResultMemoryLocation(oldDefinition)) and
result = getNewInstruction(oldDefinition) and
overlap instanceof MustTotallyOverlap
)
)
else
result = getNewInstruction(oldOperand.getDefinitionInstruction())
) or
instruction = Chi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
exists(IRFunction f |
exists(FunctionIR f |
tag instanceof UnmodeledUseOperandTag and
result = f.getUnmodeledDefinitionInstruction() and
instruction = f.getUnmodeledUseInstruction()
instruction = f.getUnmodeledUseInstruction() and
overlap instanceof MustTotallyOverlap
)
or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction)
result = getChiInstructionTotalOperand(instruction) and
overlap instanceof MustExactlyOverlap
}
cached Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
@@ -148,35 +144,26 @@ cached private module Cached {
)
}
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock) {
exists(Alias::VirtualVariable vvar, OldBlock phiBlock,
OldBlock defBlock, int defRank, int defIndex, OldBlock predBlock |
hasPhiNode(vvar, phiBlock) and
predBlock = phiBlock.getAFeasiblePredecessor() and
instr = Phi(phiBlock, vvar) and
cached Instruction getPhiOperandDefinition(PhiInstruction instr,
IRBlock newPredecessorBlock, Overlap overlap) {
exists(Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, OldBlock predBlock,
OldBlock defBlock, int defOffset |
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset, overlap) and
instr = Phi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
if defIndex >= 0 then
result = getNewFinalInstruction(defBlock.getInstruction(defIndex))
else
result = Phi(defBlock, vvar)
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation)
)
}
cached Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock,
int defRank, int defIndex, OldBlock useBlock, int useRank |
exists(Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation, OldBlock defBlock,
int defRank, int defOffset, OldBlock useBlock, int useRank |
chiInstr = Chi(oldInstr) and
vvar = Alias::getResultMemoryAccess(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) 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 = Phi(defBlock, vvar)
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar)
)
}
@@ -274,9 +261,9 @@ cached private module Cached {
isGLValue = false
)
or
exists(Alias::VirtualVariable vvar |
instruction = Phi(_, vvar) and
type = vvar.getType() and
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
type = location.getType() and
isGLValue = false
)
or
@@ -301,7 +288,7 @@ cached private module Cached {
result instanceof Opcode::Unreached
}
cached IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
cached FunctionIR getInstructionEnclosingFunctionIR(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction)
or
@@ -372,97 +359,241 @@ cached private module Cached {
result = getNewInstruction(oldInstruction)
)
}
}
private predicate ssa_variableUpdate(Alias::VirtualVariable vvar,
OldBlock block, int index, OldInstruction instr) {
block.getInstruction(index) = instr and
Alias::getResultMemoryAccess(instr).getVirtualVariable() = vvar
}
private Instruction getNewInstruction(OldInstruction instr) {
getOldInstruction(result) = instr
}
private predicate hasDefinition(Alias::VirtualVariable vvar, OldBlock block, int index) {
(
hasPhiNode(vvar, block) and
index = -1
) or
exists(Alias::MemoryAccess access, OldInstruction def |
access = Alias::getResultMemoryAccess(def) and
block.getInstruction(index) = def and
vvar = access.getVirtualVariable()
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
* original definition location is a member.
*/
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryLocation defLocation |
defLocation = Alias::getResultMemoryLocation(def) and
defLocation.getVirtualVariable() = vvar and
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
// instruction.
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
)
}
private import PhiInsertion
/**
* Module to handle insertion of `Phi` instructions at the correct blocks. We insert a `Phi` instruction at the
* beginning of a block for a given location when that block is on the dominance frontier of a definition of the
* location and there is a use of that location reachable from that block without an intervening definition of the
* location.
* Within the approach outlined above, we treat a location slightly differently depending on whether or not it is a
* virtual variable. For a virtual variable,
* location into the beginning of a block
*/
private module PhiInsertion {
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
)
}
private predicate defUseRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex, int index) {
index = rank[rankIndex](int j | hasDefinition(vvar, block, j) or hasUse(vvar, block, j, _))
}
private predicate hasUse(Alias::VirtualVariable vvar, OldBlock block, int index,
OldInstruction use) {
exists(Alias::MemoryAccess access |
/**
* Holds if the virtual variable `vvar` has a definition in block `block`, either because of an existing instruction
* or because of a Phi node.
*/
private predicate definitionHasDefinitionInBlock(Alias::MemoryLocation defLocation, OldBlock block) {
definitionHasPhiNode(defLocation, block) or
exists(OldInstruction def, Alias::MemoryLocation resultLocation |
def.getBlock() = block and
resultLocation = Alias::getResultMemoryLocation(def) 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()
defLocation = resultLocation or
// For a virtual variable, any definition of a member location will either generate a `Chi` node that defines
// the virtual variable, or will totally overlap the virtual variable. Either way, treat this as a definition of
// the virtual variable.
defLocation = resultLocation.getVirtualVariable()
)
)
}
private predicate variableLiveOnEntryToBlock(Alias::VirtualVariable vvar, OldBlock block) {
/**
* Holds if there is a use at (`block`, `index`) that could consume the result of a `Phi` instruction for
* `defLocation`.
*/
private predicate definitionHasUse(Alias::MemoryLocation defLocation, OldBlock block, int index) {
exists(OldInstruction use |
block.getInstruction(index) = use and
if defLocation instanceof Alias::VirtualVariable then (
exists(Alias::MemoryLocation useLocation |
// For a virtual variable, any use of a location that is a member of the virtual variable counts as a use.
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
defLocation = useLocation.getVirtualVariable()
) or
// A `Chi` instruction consumes the enclosing virtual variable of its use location.
hasChiNode(defLocation, use)
)
else (
// For other locations, only an exactly-overlapping use of the same location counts as a use.
defLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and
Alias::getOverlap(defLocation, defLocation) instanceof MustExactlyOverlap
)
)
}
/**
* Holds if the location `defLocation` is redefined at (`block`, `index`). A location is considered "redefined" if
* there is a definition that would prevent a previous definition of `defLocation` from being consumed as the operand
* of a `Phi` node that occurs after the redefinition.
*/
private predicate definitionHasRedefinition(Alias::MemoryLocation defLocation, OldBlock block, int index) {
exists(OldInstruction redef, Alias::MemoryLocation redefLocation |
block.getInstruction(index) = redef and
redefLocation = Alias::getResultMemoryLocation(redef) and
if defLocation instanceof Alias::VirtualVariable then (
// For a virtual variable, the definition may be consumed by any use of a location that is a member of the
// virtual variable. Thus, the definition is live until a subsequent redefinition of the entire virtual
// variable.
exists(Overlap overlap |
overlap = Alias::getOverlap(redefLocation, defLocation) and
not overlap instanceof MayPartiallyOverlap
)
)
else (
// For other locations, the definition may only be consumed by an exactly-overlapping use of the same location.
// Thus, the definition is live until a subsequent definition of any location that may overlap the original
// definition location.
exists(Alias::getOverlap(redefLocation, defLocation))
)
)
}
/**
* Holds if the definition `defLocation` is live on entry to block `block`. The definition is live if there is at
* least one use of that definition before any intervening instruction that redefines the definition location.
*/
predicate definitionLiveOnEntryToBlock(Alias::MemoryLocation defLocation, OldBlock block) {
exists(int firstAccess |
hasUse(vvar, block, firstAccess, _) and
definitionHasUse(defLocation, block, firstAccess) and
firstAccess = min(int index |
hasUse(vvar, block, index, _)
definitionHasUse(defLocation, block, index)
or
ssa_variableUpdate(vvar, block, index, _)
definitionHasRedefinition(defLocation, block, index)
)
)
or
(variableLiveOnExitFromBlock(vvar, block) and not ssa_variableUpdate(vvar, block, _, _))
}
pragma[noinline]
private predicate variableLiveOnExitFromBlock(Alias::VirtualVariable vvar, OldBlock block) {
variableLiveOnEntryToBlock(vvar, block.getAFeasibleSuccessor())
(definitionLiveOnExitFromBlock(defLocation, block) and not definitionHasRedefinition(defLocation, block, _))
}
/**
* Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the
* block.
* Holds if the definition `defLocation` is live on exit from block `block`. The definition is live on exit if it is
* live on entry to any of the successors of `block`.
*/
private int exitRank(Alias::VirtualVariable vvar, OldBlock block) {
result = max(int rankIndex | defUseRank(vvar, block, rankIndex, _)) + 1
pragma[noinline]
predicate definitionLiveOnExitFromBlock(Alias::MemoryLocation defLocation, OldBlock block) {
definitionLiveOnEntryToBlock(defLocation, block.getAFeasibleSuccessor())
}
}
private predicate hasDefinitionAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
int instructionIndex) {
hasDefinition(vvar, block, instructionIndex) and
defUseRank(vvar, block, rankIndex, instructionIndex)
}
private import DefUse
private predicate hasUseAtRank(Alias::VirtualVariable vvar, OldBlock block, int rankIndex,
OldInstruction use) {
exists(int index |
hasUse(vvar, block, index, use) and
defUseRank(vvar, block, rankIndex, index)
/**
* Module containing the predicates that connect uses to their reaching definition. The reaching definitios are computed
* separately for each unique use `MemoryLocation`. An instruction is treated as a definition of a use location if the
* defined location overlaps the use location in any way. Thus, a single instruction may serve as a definition for
* multiple use locations, since a single definition location may overlap many use locations.
*
* Definitions and uses are identified by a block and an integer "offset". An offset of -1 indicates the definition
* from a `Phi` instruction at the beginning of the block. An offset of 2*i indicates a definition or use on the
* instruction at index `i` in the block. An offset of 2*i+1 indicates a definition or use on the `Chi` instruction that
* will be inserted immediately after the instruction at index `i` in the block.
*
* For a given use location, each definition and use is also assigned a "rank" within its block. The rank is simply the
* one-based index of that definition or use within the list of definitions and uses of that location within the block,
* ordered by offset. The rank allows the various reachability predicates to be computed more efficiently than they
* would if based solely on offset, since the set of possible ranks is dense while the set of possible offsets is
* potentially very sparse.
*/
module DefUse {
/**
* Gets the `Instruction` for the definition at offset `defOffset` in block `defBlock`.
*/
pragma[inline]
bindingset[defOffset, defLocation]
Instruction getDefinitionOrChiInstruction(OldBlock defBlock, int defOffset,
Alias::MemoryLocation defLocation) {
(
defOffset >= 0 and
exists(OldInstruction oldInstr |
oldInstr = defBlock.getInstruction(defOffset / 2) and
if (defOffset % 2) > 0 then (
// An odd offset corresponds to the `Chi` instruction.
result = Chi(oldInstr)
)
else (
// An even offset corresponds to the original instruction.
result = getNewInstruction(oldInstr)
)
)
) or
(
defOffset < 0 and
result = Phi(defBlock, defLocation)
)
}
/**
* Holds if the definition of `vvar` at `(block, defRank)` reaches the rank
* Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the
* block.
*/
private int exitRank(Alias::MemoryLocation useLocation, OldBlock block) {
result = max(int rankIndex | defUseRank(useLocation, block, rankIndex, _)) + 1
}
/**
* Holds if a definition that overlaps `useLocation` at (`defBlock`, `defRank`) reaches the use of `useLocation` at
* (`useBlock`, `useRank`) without any intervening definitions that overlap `useLocation`, where `defBlock` and
* `useBlock` are the same block.
*/
private predicate definitionReachesUseWithinBlock(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(useLocation, _, defBlock, defRank, _) and
hasUseAtRank(useLocation, useBlock, useRank, _) and
definitionReachesRank(useLocation, defBlock, defRank, useRank)
}
/**
* Holds if a definition that overlaps `useLocation` at (`defBlock`, `defRank`) reaches the use of `useLocation` at
* (`useBlock`, `useRank`) without any intervening definitions that overlap `useLocation`.
*/
predicate definitionReachesUse(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(useLocation, useBlock, useRank, _) and
(
definitionReachesUseWithinBlock(useLocation, defBlock, defRank, useBlock,
useRank) or
(
definitionReachesEndOfBlock(useLocation, defBlock, defRank,
useBlock.getAFeasiblePredecessor()) and
not definitionReachesUseWithinBlock(useLocation, useBlock, _, useBlock, useRank)
)
)
}
/**
* Holds if the definition that overlaps `useLocation` at `(block, defRank)` reaches the rank
* index `reachesRank` in block `block`.
*/
private predicate definitionReachesRank(Alias::VirtualVariable vvar, OldBlock block, int defRank,
private predicate definitionReachesRank(Alias::MemoryLocation useLocation, OldBlock block, int defRank,
int reachesRank) {
hasDefinitionAtRank(vvar, block, defRank, _) and
reachesRank <= exitRank(vvar, block) and // Without this, the predicate would be infinite.
hasDefinitionAtRank(useLocation, _, block, defRank, _) and
reachesRank <= exitRank(useLocation, block) and // Without this, the predicate would be infinite.
(
// The def always reaches the next use, even if there is also a def on the
// use instruction.
@@ -470,87 +601,176 @@ cached private module Cached {
(
// If the def reached the previous rank, it also reaches the current rank,
// unless there was another def at the previous rank.
definitionReachesRank(vvar, block, defRank, reachesRank - 1) and
not hasDefinitionAtRank(vvar, block, reachesRank - 1, _)
definitionReachesRank(useLocation, block, defRank, reachesRank - 1) and
not hasDefinitionAtRank(useLocation, _, block, reachesRank - 1, _)
)
)
}
/**
* Holds if the definition of `vvar` at `(defBlock, defRank)` reaches the end of
* block `block`.
*/
private predicate definitionReachesEndOfBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
* Holds if the definition that overlaps `useLocation` at `(defBlock, defRank)` reaches the end of
* block `block` without any intervening definitions that overlap `useLocation`.
*/
predicate definitionReachesEndOfBlock(Alias::MemoryLocation useLocation, OldBlock defBlock,
int defRank, OldBlock block) {
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasDefinitionAtRank(useLocation, _, defBlock, defRank, _) and
(
(
// If we're looking at the def's own block, just see if it reaches the exit
// rank of the block.
block = defBlock and
variableLiveOnExitFromBlock(vvar, defBlock) and
definitionReachesRank(vvar, defBlock, defRank, exitRank(vvar, defBlock))
locationLiveOnExitFromBlock(useLocation, defBlock) and
definitionReachesRank(useLocation, defBlock, defRank, exitRank(useLocation, defBlock))
) or
exists(OldBlock idom |
definitionReachesEndOfBlock(vvar, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(vvar, idom, block)
definitionReachesEndOfBlock(useLocation, defBlock, defRank, idom) and
noDefinitionsSinceIDominator(useLocation, idom, block)
)
)
}
pragma[noinline]
private predicate noDefinitionsSinceIDominator(Alias::VirtualVariable vvar, OldBlock idom,
private predicate noDefinitionsSinceIDominator(Alias::MemoryLocation useLocation, OldBlock idom,
OldBlock block) {
Dominance::blockImmediatelyDominates(idom, block) and // It is sufficient to traverse the dominator graph, cf. discussion above.
variableLiveOnExitFromBlock(vvar, block) and
not hasDefinition(vvar, block, _)
locationLiveOnExitFromBlock(useLocation, block) and
not hasDefinition(useLocation, _, block, _)
}
private predicate definitionReachesUseWithinBlock(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
defBlock = useBlock and
hasDefinitionAtRank(vvar, defBlock, defRank, _) and
hasUseAtRank(vvar, useBlock, useRank, _) and
definitionReachesRank(vvar, defBlock, defRank, useRank)
/**
* Holds if the specified `useLocation` is live on entry to `block`. This holds if there is a use of `useLocation`
* that is reachable from the start of `block` without passing through a definition that overlaps `useLocation`.
*/
predicate locationLiveOnEntryToBlock(Alias::MemoryLocation useLocation, OldBlock block) {
definitionHasPhiNode(useLocation, block) or
exists(int firstAccess |
hasUse(useLocation, block, firstAccess, _) and
firstAccess = min(int offset |
hasUse(useLocation, block, offset, _)
or
hasNonPhiDefinition(useLocation, _, block, offset)
)
) or
(locationLiveOnExitFromBlock(useLocation, block) and not hasNonPhiDefinition(useLocation, _, block, _))
}
private predicate definitionReachesUse(Alias::VirtualVariable vvar, OldBlock defBlock,
int defRank, OldBlock useBlock, int useRank) {
hasUseAtRank(vvar, useBlock, useRank, _) and
/**
* Holds if the specified `useLocation` is live on exit from `block`.
*/
pragma[noinline]
predicate locationLiveOnExitFromBlock(Alias::MemoryLocation useLocation, OldBlock block) {
locationLiveOnEntryToBlock(useLocation, block.getAFeasibleSuccessor())
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* This predicate does not include definitions for Phi nodes.
*/
private predicate hasNonPhiDefinition(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock block, int offset) {
exists(OldInstruction def, Overlap overlap, int index |
defLocation = Alias::getResultMemoryLocation(def) and
block.getInstruction(index) = def and
overlap = Alias::getOverlap(defLocation, useLocation) and
if overlap instanceof MayPartiallyOverlap then
offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
else
offset = index * 2 // The use will be connected to the definition on the original instruction.
)
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* This predicate includes definitions for Phi nodes (at offset -1).
*/
private predicate hasDefinition(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation, OldBlock block,
int offset) {
(
definitionReachesUseWithinBlock(vvar, defBlock, defRank, useBlock,
useRank) or
// If there is a Phi node for the use location itself, treat that as a definition at offset -1.
offset = -1 and
if definitionHasPhiNode(useLocation, block) then (
defLocation = useLocation
)
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
)
) or
hasNonPhiDefinition(useLocation, defLocation, block, offset)
}
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`.
* `rankIndex` is the rank of the definition as computed by `defUseRank()`.
*/
predicate hasDefinitionAtRank(Alias::MemoryLocation useLocation, Alias::MemoryLocation defLocation,
OldBlock block, int rankIndex, int offset) {
hasDefinition(useLocation, defLocation, block, offset) and
defUseRank(useLocation, block, rankIndex, offset)
}
/**
* Holds if there is a use of `useLocation` on instruction `use` at offset `offset` in block `block`.
*/
private predicate hasUse(Alias::MemoryLocation useLocation, OldBlock block, int offset, OldInstruction use) {
exists(int index |
block.getInstruction(index) = use and
(
definitionReachesEndOfBlock(vvar, defBlock, defRank,
useBlock.getAFeasiblePredecessor()) and
not definitionReachesUseWithinBlock(vvar, useBlock, _, useBlock, useRank)
// A direct use of the location.
useLocation = Alias::getOperandMemoryLocation(use.getAnOperand()) and offset = index * 2 or
// A `Chi` instruction will include a use of the virtual variable.
hasChiNode(useLocation, use) and offset = (index * 2) + 1
)
)
}
private predicate hasFrontierPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
hasDefinition(vvar, defBlock, _) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
variableLiveOnEntryToBlock(vvar, phiBlock)
/**
* Holds if there is a use of memory location `useLocation` on instruction `use` in block `block`. `rankIndex` is the
* rank of the use use as computed by `defUseRank`.
*/
predicate hasUseAtRank(Alias::MemoryLocation useLocation, OldBlock block, int rankIndex, OldInstruction use) {
exists(int offset |
hasUse(useLocation, block, offset, use) and
defUseRank(useLocation, block, rankIndex, offset)
)
}
private predicate hasPhiNode(Alias::VirtualVariable vvar, OldBlock phiBlock) {
hasFrontierPhiNode(vvar, phiBlock)
//or ssa_sanitized_custom_phi_node(vvar, block)
/**
* Holds if there is a definition at offset `offset` in block `block` that overlaps memory location `useLocation`, or
* a use of `useLocation` at offset `offset` in block `block`. `rankIndex` is the sequence number of the definition
* or use within `block`, counting only uses of `useLocation` and definitions that overlap `useLocation`.
*/
private predicate defUseRank(Alias::MemoryLocation useLocation, OldBlock block, int rankIndex, int offset) {
offset = rank[rankIndex](int j | hasDefinition(useLocation, _, block, j) or hasUse(useLocation, block, j, _))
}
private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
exists(Alias::MemoryAccess ma |
ma = Alias::getResultMemoryAccess(def) and
ma.isPartialMemoryAccess() and
ma.getVirtualVariable() = vvar
/**
* Holds if the `Phi` instruction for location `useLocation` at the beginning of block `phiBlock` has an operand along
* the incoming edge from `predBlock`, where that operand's definition is at offset `defOffset` in block `defBlock`,
* and overlaps the use operand with overlap relationship `overlap`.
*/
pragma[inline]
predicate hasPhiOperandDefinition(Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation,
OldBlock phiBlock, OldBlock predBlock, OldBlock defBlock, int defOffset, Overlap overlap) {
exists(int defRank |
definitionHasPhiNode(useLocation, phiBlock) and
predBlock = phiBlock.getAFeasiblePredecessor() and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
definitionReachesEndOfBlock(useLocation, defBlock, defRank, predBlock) and
overlap = Alias::getOverlap(defLocation, useLocation)
)
}
}
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
*/
module DebugSSA {
import PhiInsertion
import DefUse
}
import CachedForDebugging
cached private module CachedForDebugging {
cached string getTempVariableUniqueId(IRTempVariable var) {
@@ -562,9 +782,16 @@ cached private module CachedForDebugging {
oldInstr = getOldInstruction(instr) and
result = "NonSSA: " + oldInstr.getUniqueId()
) or
exists(Alias::VirtualVariable vvar, OldBlock phiBlock |
instr = Phi(phiBlock, vvar) and
result = "Phi Block(" + phiBlock.getUniqueId() + "): " + vvar.getUniqueId()
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
instr = Phi(phiBlock, location) and
result = "Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable then (
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
specificity = "g"
)
else (
specificity = "s"
)
) or
(
instr = Unreached(_) and

View File

@@ -38,20 +38,20 @@ private predicate isVariableModeled(IRVariable var) {
)
}
private newtype TVirtualVariable =
MkVirtualVariable(IRVariable var) {
private newtype TMemoryLocation =
MkMemoryLocation(IRVariable var) {
isVariableModeled(var)
}
private VirtualVariable getVirtualVariable(IRVariable var) {
private MemoryLocation getMemoryLocation(IRVariable var) {
result.getIRVariable() = var
}
class VirtualVariable extends TVirtualVariable {
class MemoryLocation extends TMemoryLocation {
IRVariable var;
VirtualVariable() {
this = MkVirtualVariable(var)
MemoryLocation() {
this = MkMemoryLocation(var)
}
final string toString() {
@@ -62,6 +62,10 @@ class VirtualVariable extends TVirtualVariable {
result = var
}
final VirtualVariable getVirtualVariable() {
result = this
}
final Type getType() {
result = var.getType()
}
@@ -71,50 +75,25 @@ class VirtualVariable extends TVirtualVariable {
}
}
private newtype TMemoryAccess =
MkMemoryAccess(VirtualVariable vvar)
private MemoryAccess getMemoryAccess(IRVariable var) {
result.getVirtualVariable() = getVirtualVariable(var)
class VirtualVariable extends MemoryLocation {
}
class MemoryAccess extends TMemoryAccess {
VirtualVariable vvar;
MemoryAccess() {
this = MkMemoryAccess(vvar)
}
string toString() {
result = vvar.toString()
}
VirtualVariable getVirtualVariable() {
result = vvar
}
predicate isPartialMemoryAccess() {
none()
}
}
Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
def.getVirtualVariable() = use.getVirtualVariable() and
result instanceof MustExactlyOverlap
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
def = use and result instanceof MustExactlyOverlap
or
none() // Avoid compiler error in SSAConstruction
}
MemoryAccess getResultMemoryAccess(Instruction instr) {
MemoryLocation getResultMemoryLocation(Instruction instr) {
exists(IRVariable var |
hasResultMemoryAccess(instr, var, _, _) and
result = getMemoryAccess(var)
result = getMemoryLocation(var)
)
}
MemoryAccess getOperandMemoryAccess(MemoryOperand operand) {
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
exists(IRVariable var |
hasOperandMemoryAccess(operand, var, _, _) and
result = getMemoryAccess(var)
result = getMemoryLocation(var)
)
}