mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
C++: Imprecise definitions in SSA
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user