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:
Dave Bartolomeo
2020-05-29 15:49:12 -04:00
parent 7265e94028
commit 1e863ac40b
28 changed files with 630 additions and 466 deletions

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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, _)
)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.Opcode as Opcode

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -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

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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, _)
)
}
}

View File

@@ -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

View File

@@ -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