mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge pull request #3612 from dbartol/github/codeql-c-analysis-team/69_union
C++: Share `TInstruction` across IR stages
This commit is contained in:
@@ -96,10 +96,18 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
@@ -177,6 +185,11 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRFunctionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
@@ -287,6 +300,10 @@
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRFunctionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
|
||||
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
|
||||
class NullInstruction extends ConstantValueInstruction {
|
||||
NullInstruction() {
|
||||
this.getValue() = "0" and
|
||||
this.getResultType().getUnspecifiedType() instanceof PointerType
|
||||
this.getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
convert.getResultIRType() instanceof IRBooleanType and
|
||||
checked.getResultIRType() instanceof IRAddressType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -128,8 +128,13 @@ private class SideEffectOutNode extends OutNode {
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result.getCall() = call and
|
||||
result.getReturnKind() = kind
|
||||
// There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that
|
||||
// this is true helps it make better decisions downstream, especially in virtual dispatch.
|
||||
result =
|
||||
unique(OutNode outNode |
|
||||
outNode.getCall() = call and
|
||||
outNode.getReturnKind() = kind
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,8 @@ private class IRSizedType extends IRType {
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +130,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
@@ -137,13 +139,27 @@ class IRNumericType extends IRSizedType {
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
|
||||
*/
|
||||
class IRIntegerType extends IRNumericType {
|
||||
IRIntegerType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
@@ -158,7 +174,7 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
|
||||
*/
|
||||
|
||||
private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
* phases of the IR. Each instantiation of `IRFunction` extends this class.
|
||||
*/
|
||||
class IRFunctionBase extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunctionBase() { this = MkIRFunction(func) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/** Gets the function whose IR is represented. */
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/** Gets the location of the function. */
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
@@ -1,5 +1,5 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction
|
||||
private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
private import TInstructionInternal
|
||||
private import IRFunctionBase
|
||||
private import TInstructionImports as Imports
|
||||
private import Imports::IRType
|
||||
private import Imports::Opcode
|
||||
|
||||
/**
|
||||
* An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
|
||||
* branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
|
||||
* instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
|
||||
* `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
|
||||
* all of the branches that can appear in that particular stage. The public `Instruction` class for
|
||||
* each phase extends the `TStageInstruction` type for that stage.
|
||||
*/
|
||||
cached
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(tag1, tag2)
|
||||
} or
|
||||
TUnaliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
|
||||
TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
|
||||
AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
|
||||
} or
|
||||
TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSSAInstructions {
|
||||
class TPhiInstruction = TUnaliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* aliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAInstructions {
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase
|
||||
private import semmle.code.cpp.ir.implementation.internal.TInstruction
|
||||
private import semmle.code.cpp.ir.implementation.internal.TIRVariable
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
@@ -12,34 +15,36 @@ private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = MkInstruction(result, _)
|
||||
instruction = TRawInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
|
||||
|
||||
import Cached
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = TRawInstruction(_, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the initial
|
||||
* "raw" stage of the IR. The other stages of the IR do not expose these predicates.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
module Raw {
|
||||
class InstructionTag1 = TranslatedElement;
|
||||
|
||||
class InstructionTag2 = InstructionTag;
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Function func, Variable var, CppType type) {
|
||||
getTranslatedFunction(func).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasThisVariable(Function func, CppType type) {
|
||||
type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
|
||||
exists(TranslatedElement element |
|
||||
@@ -64,232 +69,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
cached
|
||||
CppType 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.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getAST()
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
TIRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
@@ -302,10 +82,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionField(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -324,10 +103,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionIndex(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionIndex(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -350,20 +128,11 @@ private module Cached {
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionElementSize(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -372,22 +141,225 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionResultSize(tag)
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
CppType 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.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
Locatable getInstructionAST(TStageInstruction instr) {
|
||||
result = getInstructionTranslatedElement(instr).getAST()
|
||||
}
|
||||
|
||||
CppType getInstructionResultType(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
|
||||
}
|
||||
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
|
||||
}
|
||||
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
|
||||
}
|
||||
|
||||
Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import IRConstruction::Raw as Raw
|
||||
|
||||
@@ -705,12 +705,8 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
int getInstructionElementSize(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` has a result of type `UnknownType`,
|
||||
* gets the size of the result in bytes. If the result does not have a knonwn
|
||||
* constant size, this predicate does not hold.
|
||||
* Holds if the generated IR refers to an opaque type with size `byteSize`.
|
||||
*/
|
||||
int getInstructionResultSize(InstructionTag tag) { none() }
|
||||
|
||||
predicate needsUnknownOpaqueType(int byteSize) { none() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
exists(int elementCount |
|
||||
zeroInitRange(_, elementCount) and
|
||||
(
|
||||
tag = ZeroPadStringConstantTag() or
|
||||
tag = ZeroPadStringStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
)
|
||||
}
|
||||
|
||||
private Type getElementType() {
|
||||
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
|
||||
}
|
||||
@@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
result = getZeroValue(getElementType())
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
elementCount > 1 and
|
||||
(
|
||||
tag = getElementDefaultValueTag() or
|
||||
tag = getElementDefaultValueStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
|
||||
or
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
|
||||
|
||||
@@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) {
|
||||
predicate hasOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag)
|
||||
or
|
||||
tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
|
||||
tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ private newtype TCppType =
|
||||
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
|
||||
TFunctionGLValueType() or
|
||||
TGLValueAddressType(Type type) or
|
||||
TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownType()
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,8 @@ private class IRSizedType extends IRType {
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +130,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
@@ -137,13 +139,27 @@ class IRNumericType extends IRSizedType {
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
|
||||
*/
|
||||
class IRIntegerType extends IRNumericType {
|
||||
IRIntegerType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
@@ -158,7 +174,7 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Provides a stub implementation of the required aliased SSA interface until we implement aliased
|
||||
* SSA construction for C#.
|
||||
*/
|
||||
|
||||
private import IRFunctionBase
|
||||
private import TInstruction
|
||||
|
||||
module SSA {
|
||||
class MemoryLocation = boolean;
|
||||
|
||||
predicate hasPhiInstruction(TRawInstruction blockStartInstr, MemoryLocation memoryLocation) {
|
||||
none()
|
||||
}
|
||||
|
||||
predicate hasChiInstruction(TRawInstruction primaryInstruction) { none() }
|
||||
|
||||
predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
|
||||
*/
|
||||
|
||||
private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
* phases of the IR. Each instantiation of `IRFunction` extends this class.
|
||||
*/
|
||||
class IRFunctionBase extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunctionBase() { this = MkIRFunction(func) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/** Gets the function whose IR is represented. */
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/** Gets the location of the function. */
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
@@ -1,5 +1,5 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction::Raw as Construction
|
||||
private import experimental.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
private import TInstructionInternal
|
||||
private import IRFunctionBase
|
||||
private import TInstructionImports as Imports
|
||||
private import Imports::IRType
|
||||
private import Imports::Opcode
|
||||
|
||||
/**
|
||||
* An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
|
||||
* branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
|
||||
* instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
|
||||
* `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
|
||||
* all of the branches that can appear in that particular stage. The public `Instruction` class for
|
||||
* each phase extends the `TStageInstruction` type for that stage.
|
||||
*/
|
||||
cached
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(tag1, tag2)
|
||||
} or
|
||||
TUnaliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
|
||||
TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
|
||||
AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
|
||||
} or
|
||||
TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSSAInstructions {
|
||||
class TPhiInstruction = TUnaliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* aliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAInstructions {
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.implementation.Opcode as Opcode
|
||||
@@ -0,0 +1,4 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import experimental.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
|
||||
import AliasedSSAStub as AliasedSSA
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import csharp
|
||||
import experimental.ir.implementation.raw.IR
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.internal.IRFunctionBase
|
||||
private import experimental.ir.implementation.internal.TInstruction
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.Overlap
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
@@ -15,23 +17,40 @@ private import experimental.ir.Util
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = MkInstruction(result, _)
|
||||
instruction = TRawInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = TRawInstruction(_, result)
|
||||
}
|
||||
|
||||
import Cached
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the initial
|
||||
* "raw" stage of the IR. The other stages of the IR do not expose these predicates.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
module Raw {
|
||||
class InstructionTag1 = TranslatedElement;
|
||||
|
||||
class InstructionTag2 = InstructionTag;
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Callable callable) { exists(getTranslatedFunction(callable)) }
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Callable callable, Variable var, CSharpType type) {
|
||||
@@ -63,16 +82,6 @@ private module Cached {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
@@ -89,6 +98,98 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
(
|
||||
result = element.getInstructionVariable(tag) or
|
||||
result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
Callable getInstructionFunction(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionFunction(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionConstantValue(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
CSharpType getInstructionExceptionType(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionExceptionType(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() }
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
Opcode getInstructionOpcode(TRawInstruction instr) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instr, element, tag) and
|
||||
element.hasInstruction(result, tag, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TRawInstruction instr) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
@@ -264,37 +365,6 @@ private module Cached {
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
(
|
||||
result = element.getInstructionVariable(tag) or
|
||||
result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
ArrayAccess getInstructionArrayAccess(Instruction instruction) {
|
||||
result =
|
||||
@@ -302,52 +372,6 @@ private module Cached {
|
||||
.getInstructionArrayAccess(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
Callable getInstructionFunction(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionFunction(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionConstantValue(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
CSharpType getInstructionExceptionType(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionExceptionType(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
@@ -363,9 +387,6 @@ private module Cached {
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() }
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import experimental.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -603,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -704,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -753,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1216,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1362,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import experimental.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import experimental.ir.implementation.Opcode
|
||||
import experimental.ir.implementation.internal.OperandTag
|
||||
import experimental.ir.internal.Overlap
|
||||
import experimental.ir.implementation.Opcode as Opcode
|
||||
import experimental.ir.implementation.internal.OperandTag as OperandTag
|
||||
import experimental.ir.internal.Overlap as Overlap
|
||||
import experimental.ir.implementation.internal.TInstruction as TInstruction
|
||||
import experimental.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,7 @@ import experimental.ir.implementation.raw.IR as OldIR
|
||||
import experimental.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import experimental.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import experimental.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import experimental.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
private import csharp
|
||||
private import experimental.ir.implementation.TempVariableTag
|
||||
private import experimental.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
private import experimental.ir.Util
|
||||
private import IRCSharpLanguage as Language
|
||||
|
||||
newtype TIRVariable =
|
||||
TIRAutomaticUserVariable(LocalScopeVariable var, Callable callable) {
|
||||
Construction::functionHasIR(callable) and
|
||||
var.getCallable() = callable
|
||||
} or
|
||||
TIRTempVariable(
|
||||
Callable callable, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasTempVariable(callable, ast, tag, type)
|
||||
}
|
||||
Reference in New Issue
Block a user