mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
C++: Share TInstruction across IR stages
Each stage of the IR reuses the majority of the instructions from previous stages. Previously, we've been wrapping each reused old instruction in a branch of the `TInstruction` type for the next stage. This causes use to create roughly three times as many `TInstruction` objects as we actually need. Now that IPA union types are supported in the compiler, we can share a single `TInstruction` IPA type across stages. We create a single `TInstruction` IPA type, with 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. The interface that each stage exposes to the pyrameterized modules in the IR is now split into three pieces: - The `Raw` module, exposed only by the original IR construction stage. This module identifies which functions have IR, which `TRawInstruction`s exist, and which `IRVariable`s exist. - The `SSA` module, exposed only by the two SSA construction stages. This identifiers which `Phi`, `Chi`, and `Unreached` instructions exist. - The global module, exposed by all three stages. This module has all of the predicates whose implementation is different for each stage, like gathering definitions of `MemoryOperand`s. Similarly, there is now a single `TIRFunction` IPA type that is shared across all three stages. There is a single `IRFunctionBase` class that exposes the stage-indepdendent predicates; the `IRFunction` class for each stage extends `IRFunctionBase`. Most of the other changes are largely mechanical.
This commit is contained in:
@@ -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() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +256,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 +265,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.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -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,35 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
private TRawInstruction rawInstruction(
|
||||
IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType
|
||||
) {
|
||||
result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and
|
||||
result instanceof OldInstruction
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
not instr instanceof TRawInstruction
|
||||
}
|
||||
|
||||
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) }
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = 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 +60,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 +68,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 +115,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 +159,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 +180,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,7 +192,7 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
@@ -228,20 +217,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,84 +249,61 @@ 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(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, _, result, _)
|
||||
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(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, _, _, result)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
instr = phiInstruction(_, result, _, _)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
)
|
||||
instr = chiInstruction(_, result, _)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
)
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, result, _, _)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
instr = rawInstruction(result, _, _, _)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
instr = phiInstruction(result, _, _, _)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
instr = chiInstruction(result, _, _)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +367,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +375,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
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 +562,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 +581,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 +865,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 +875,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +935,44 @@ 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.
|
||||
*/
|
||||
cached
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
cached
|
||||
predicate hasPhiInstruction(
|
||||
IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr,
|
||||
Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
irFunc = oldBlock.getEnclosingIRFunction() and
|
||||
blockStartInstr = oldBlock.getFirstInstruction() and
|
||||
resultType = defLocation.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction
|
||||
) {
|
||||
exists(Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, primaryInstruction) and
|
||||
irFunc = primaryInstruction.getEnclosingIRFunction() and
|
||||
resultType = vvar.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstruction(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,23 @@
|
||||
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,111 @@
|
||||
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.
|
||||
*/
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType,
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(irFunc.getFunction(), opcode, ast, resultType, tag1, tag2)
|
||||
} or
|
||||
TUnaliasedSSAPhiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr,
|
||||
UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSSAChiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction
|
||||
) {
|
||||
none()
|
||||
} or
|
||||
TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSSAPhiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr,
|
||||
AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSSA::SSA::hasPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSSAChiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction
|
||||
) {
|
||||
AliasedSSA::SSA::hasChiInstruction(irFunc, resultType, 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(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr,
|
||||
UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction
|
||||
) {
|
||||
result = TUnaliasedSSAChiInstruction(irFunc, resultType, 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(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction blockStartInstr,
|
||||
AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSSAPhiInstruction(irFunc, resultType, blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, TRawInstruction primaryInstruction
|
||||
) {
|
||||
result = TAliasedSSAChiInstruction(irFunc, resultType, 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() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +256,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 +265,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.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
@@ -12,34 +14,41 @@ 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(
|
||||
Function func, Opcode opcode, Element ast, CppType resultType, TranslatedElement element,
|
||||
InstructionTag tag
|
||||
) {
|
||||
element.hasInstruction(opcode, tag, resultType) and
|
||||
ast = element.getAST() and
|
||||
func = element.getFunction()
|
||||
}
|
||||
|
||||
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 |
|
||||
@@ -62,6 +71,16 @@ private module Cached {
|
||||
var.hasDynamicInitialization() and
|
||||
type = getBoolType()
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
@@ -267,25 +286,23 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getAST()
|
||||
Locatable getInstructionAST(TStageInstruction instr) {
|
||||
instr = TRawInstruction(_, _, result, _, _, _)
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
CppType getInstructionResultType(TStageInstruction instr) {
|
||||
instr = TRawInstruction(_, _, _, result, _, _)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
instr = TRawInstruction(_, result, _, _, _, _)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
instr = TRawInstruction(result, _, _, _, _, _)
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -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() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +256,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 +265,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.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -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,35 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
private TRawInstruction rawInstruction(
|
||||
IRFunctionBase irFunc, Opcode opcode, Language::AST ast, Language::LanguageType resultType
|
||||
) {
|
||||
result = TRawInstruction(irFunc, opcode, ast, resultType, _, _) and
|
||||
result instanceof OldInstruction
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
not instr instanceof TRawInstruction
|
||||
}
|
||||
|
||||
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) }
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = 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 +60,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 +68,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 +115,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 +159,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 +180,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,7 +192,7 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
@@ -228,20 +217,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,84 +249,61 @@ 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(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, _, result, _)
|
||||
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(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, _, _, result)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
instr = phiInstruction(_, result, _, _)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
)
|
||||
instr = chiInstruction(_, result, _)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
)
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
instr = rawInstruction(_, result, _, _)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
instr = phiInstruction(_, _, _, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
instr = chiInstruction(_, _, _) and result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
instr = rawInstruction(result, _, _, _)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
instr = phiInstruction(result, _, _, _)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
instr = chiInstruction(result, _, _)
|
||||
or
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +367,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +375,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
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 +562,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 +581,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 +865,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 +875,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +935,44 @@ 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.
|
||||
*/
|
||||
cached
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
cached
|
||||
predicate hasPhiInstruction(
|
||||
IRFunction irFunc, Language::LanguageType resultType, OldInstruction blockStartInstr,
|
||||
Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
irFunc = oldBlock.getEnclosingIRFunction() and
|
||||
blockStartInstr = oldBlock.getFirstInstruction() and
|
||||
resultType = defLocation.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstruction(
|
||||
IRFunctionBase irFunc, Language::LanguageType resultType, OldInstruction primaryInstruction
|
||||
) {
|
||||
exists(Alias::VirtualVariable vvar |
|
||||
hasChiNode(vvar, primaryInstruction) and
|
||||
irFunc = primaryInstruction.getEnclosingIRFunction() and
|
||||
resultType = vvar.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstruction(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user