mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
C++: Reuse SSA from earlier stages
This refactors the SSA stages of the IR so that instructions which have a modeled memory result in the unaliased SSA stage do not have SSA recomputed in the aliased SSA stage.
This commit is contained in:
@@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction {
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
|
||||
|
||||
/**
|
||||
* Gets the input operand representing the value that flows from the specified predecessor block.
|
||||
*/
|
||||
final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
|
||||
result = this.getAnOperand() and
|
||||
result.getPredecessorBlock() = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,8 @@ class Operand extends TStageOperand {
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
@@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
PhiInputOperand() {
|
||||
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
or
|
||||
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
|
||||
or
|
||||
instr instanceof CallInstruction and
|
||||
not exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis()
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -2,9 +2,13 @@ private import AliasConfigurationInternal
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import cpp
|
||||
private import AliasAnalysis
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA as UnaliasedSSA
|
||||
|
||||
private newtype TAllocation =
|
||||
TVariableAllocation(IRVariable var) or
|
||||
TVariableAllocation(IRVariable var) {
|
||||
// Only model variables that were not already handled in unaliased SSA.
|
||||
not UnaliasedSSA::canReuseSSAForVariable(var)
|
||||
} or
|
||||
TIndirectParameterAllocation(IRAutomaticVariable var) {
|
||||
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
|
||||
} or
|
||||
|
||||
@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as OldSSA
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
@@ -131,6 +132,8 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
* with automatic storage duration).
|
||||
*/
|
||||
predicate isAlwaysAllocatedOnStack() { none() }
|
||||
|
||||
final predicate canReuseSSA() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,10 +565,19 @@ private Overlap getVariableMemoryLocationOverlap(
|
||||
use.getEndBitOffset())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the def/use information for the result of `instr` can be reused from the previous
|
||||
* iteration of the IR.
|
||||
*/
|
||||
predicate canReuseSSAForOldResult(Instruction instr) {
|
||||
OldSSA::canReuseSSAForMemoryResult(instr)
|
||||
}
|
||||
|
||||
bindingset[result, b]
|
||||
private boolean unbindBool(boolean b) { result != b.booleanNot() }
|
||||
|
||||
MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
not(canReuseSSAForOldResult(instr)) and
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
kind = instr.getResultMemoryAccess() and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
@@ -598,6 +610,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
}
|
||||
|
||||
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
not(canReuseSSAForOldResult(operand.getAnyDef())) and
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
kind = operand.getMemoryAccess() and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
|
||||
@@ -58,9 +58,28 @@ private module Cached {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block from the old IR that corresponds to `newBlock`.
|
||||
*/
|
||||
private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
|
||||
|
||||
/**
|
||||
* Holds if this iteration of SSA can model the def/use information for the result of
|
||||
* `oldInstruction`, either because alias analysis has determined a memory location for that
|
||||
* result, or because a previous iteration of the IR already computed that def/use information
|
||||
* completely.
|
||||
*/
|
||||
private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
|
||||
// We're modeling the result's memory location ourselves.
|
||||
exists(Alias::getResultMemoryLocation(oldInstruction))
|
||||
or
|
||||
// This result was already modeled by a previous iteration of SSA.
|
||||
Alias::canReuseSSAForOldResult(oldInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
@@ -117,6 +136,30 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
|
||||
* old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
|
||||
* corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
|
||||
* instruction that is now degenerate due all but one of its predecessor branches being
|
||||
* unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
|
||||
* true definition.
|
||||
*/
|
||||
private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
|
||||
exists(Overlap originalOverlap |
|
||||
originalOverlap = oldOperand.getDefinitionOverlap() and
|
||||
(
|
||||
result = getNewInstruction(oldOperand.getAnyDef()) and
|
||||
overlap = originalOverlap
|
||||
/*or
|
||||
exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
|
||||
phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
|
||||
result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
|
||||
overlap = combineOverlap(phiOperandOverlap, originalOverlap)
|
||||
) */
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private Instruction getMemoryOperandDefinition0(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
@@ -148,6 +191,12 @@ private module Cached {
|
||||
overlap instanceof MustExactlyOverlap and
|
||||
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
|
||||
)
|
||||
or
|
||||
exists(OldIR::NonPhiMemoryOperand oldOperand |
|
||||
result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
|
||||
oldOperand.getUse() = instruction and
|
||||
tag = oldOperand.getOperandTag()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,10 +263,24 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new definition instruction for the operand of `instr` that flows from the block
|
||||
* `newPredecessorBlock`, based on that operand's definition in the old IR.
|
||||
*/
|
||||
private Instruction getNewPhiOperandDefinitionFromOldSSA(
|
||||
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
|
||||
oldPhi = getOldInstruction(instr) and
|
||||
oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
|
||||
result = getNewDefinitionFromOldSSA(oldOperand, overlap)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
@@ -229,6 +292,8 @@ private module Cached {
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
)
|
||||
or
|
||||
result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -248,8 +313,12 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = getPhi(oldBlock, _) and
|
||||
exists(OldBlock oldBlock | (
|
||||
instr = getPhi(oldBlock, _)
|
||||
or
|
||||
// Any `Phi` that we propagated from the previous iteration stays in the same block.
|
||||
getOldInstruction(instr).getBlock() = oldBlock
|
||||
) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
@@ -335,6 +404,9 @@ private module Cached {
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
result = instr.(OldInstruction).getResultLanguageType()
|
||||
or
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
@@ -862,6 +934,26 @@ module DefUse {
|
||||
}
|
||||
}
|
||||
|
||||
predicate canReuseSSAForMemoryResult(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
// The previous iteration said it was reusable, so we should mark it as reusable as well.
|
||||
Alias::canReuseSSAForOldResult(oldInstruction)
|
||||
or
|
||||
// The current alias analysis says it is reusable.
|
||||
Alias::getResultMemoryLocation(oldInstruction).canReuseSSA()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
// This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
|
||||
instruction = phiInstruction(_, defLocation) and
|
||||
defLocation.canReuseSSA()
|
||||
)
|
||||
// We don't support reusing SSA for any location that could create a `Chi` instruction.
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
|
||||
@@ -55,6 +55,11 @@ module UnaliasedSSAInstructions {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TRawInstruction reusedPhiInstruction(
|
||||
TRawInstruction blockStartInstr) {
|
||||
none()
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
@@ -75,7 +80,7 @@ module UnaliasedSSAInstructions {
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAInstructions {
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction;
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
@@ -83,6 +88,11 @@ module AliasedSSAInstructions {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TPhiInstruction reusedPhiInstruction(
|
||||
TRawInstruction blockStartInstr) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, _)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
|
||||
@@ -84,7 +84,7 @@ private module Shared {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* raw IR stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
@@ -109,6 +109,12 @@ module RawOperands {
|
||||
none()
|
||||
}
|
||||
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
|
||||
Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
@@ -140,6 +146,12 @@ module UnaliasedSSAOperands {
|
||||
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
@@ -155,7 +167,7 @@ module UnaliasedSSAOperands {
|
||||
module AliasedSSAOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TAliasedPhiOperand;
|
||||
class TPhiOperand = Internal::TAliasedPhiOperand or Internal::TUnaliasedPhiOperand;
|
||||
|
||||
class TChiOperand = Internal::TAliasedChiOperand;
|
||||
|
||||
@@ -165,12 +177,24 @@ module AliasedSSAOperands {
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
|
||||
Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
|
||||
Aliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
|
||||
Aliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(Unaliased::IRBlock oldBlock |
|
||||
predecessorBlock.getFirstInstruction() = oldBlock.getFirstInstruction() and
|
||||
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, oldBlock, overlap)
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
|
||||
@@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction {
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
|
||||
|
||||
/**
|
||||
* Gets the input operand representing the value that flows from the specified predecessor block.
|
||||
*/
|
||||
final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
|
||||
result = this.getAnOperand() and
|
||||
result.getPredecessorBlock() = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,8 @@ class Operand extends TStageOperand {
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
@@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
PhiInputOperand() {
|
||||
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
or
|
||||
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
|
||||
@@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction {
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
|
||||
|
||||
/**
|
||||
* Gets the input operand representing the value that flows from the specified predecessor block.
|
||||
*/
|
||||
final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
|
||||
result = this.getAnOperand() and
|
||||
result.getPredecessorBlock() = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,8 @@ class Operand extends TStageOperand {
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
@@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
PhiInputOperand() {
|
||||
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
or
|
||||
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
|
||||
or
|
||||
instr instanceof CallInstruction and
|
||||
not exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis()
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -58,9 +58,28 @@ private module Cached {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block from the old IR that corresponds to `newBlock`.
|
||||
*/
|
||||
private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
|
||||
|
||||
/**
|
||||
* Holds if this iteration of SSA can model the def/use information for the result of
|
||||
* `oldInstruction`, either because alias analysis has determined a memory location for that
|
||||
* result, or because a previous iteration of the IR already computed that def/use information
|
||||
* completely.
|
||||
*/
|
||||
private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
|
||||
// We're modeling the result's memory location ourselves.
|
||||
exists(Alias::getResultMemoryLocation(oldInstruction))
|
||||
or
|
||||
// This result was already modeled by a previous iteration of SSA.
|
||||
Alias::canReuseSSAForOldResult(oldInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
canModelResultForOldInstruction(getOldInstruction(instruction)) or
|
||||
instruction instanceof PhiInstruction or // Phis always have modeled results
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
@@ -117,6 +136,30 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
|
||||
* old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
|
||||
* corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
|
||||
* instruction that is now degenerate due all but one of its predecessor branches being
|
||||
* unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
|
||||
* true definition.
|
||||
*/
|
||||
private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
|
||||
exists(Overlap originalOverlap |
|
||||
originalOverlap = oldOperand.getDefinitionOverlap() and
|
||||
(
|
||||
result = getNewInstruction(oldOperand.getAnyDef()) and
|
||||
overlap = originalOverlap
|
||||
/*or
|
||||
exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
|
||||
phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
|
||||
result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
|
||||
overlap = combineOverlap(phiOperandOverlap, originalOverlap)
|
||||
) */
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private Instruction getMemoryOperandDefinition0(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
@@ -148,6 +191,12 @@ private module Cached {
|
||||
overlap instanceof MustExactlyOverlap and
|
||||
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
|
||||
)
|
||||
or
|
||||
exists(OldIR::NonPhiMemoryOperand oldOperand |
|
||||
result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
|
||||
oldOperand.getUse() = instruction and
|
||||
tag = oldOperand.getOperandTag()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,10 +263,24 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new definition instruction for the operand of `instr` that flows from the block
|
||||
* `newPredecessorBlock`, based on that operand's definition in the old IR.
|
||||
*/
|
||||
private Instruction getNewPhiOperandDefinitionFromOldSSA(
|
||||
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
|
||||
oldPhi = getOldInstruction(instr) and
|
||||
oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
|
||||
result = getNewDefinitionFromOldSSA(oldOperand, overlap)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
@@ -229,6 +292,8 @@ private module Cached {
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
)
|
||||
or
|
||||
result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -248,8 +313,12 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = getPhi(oldBlock, _) and
|
||||
exists(OldBlock oldBlock | (
|
||||
instr = getPhi(oldBlock, _)
|
||||
or
|
||||
// Any `Phi` that we propagated from the previous iteration stays in the same block.
|
||||
getOldInstruction(instr).getBlock() = oldBlock
|
||||
) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
@@ -335,6 +404,9 @@ private module Cached {
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
result = instr.(OldInstruction).getResultLanguageType()
|
||||
or
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
@@ -862,6 +934,26 @@ module DefUse {
|
||||
}
|
||||
}
|
||||
|
||||
predicate canReuseSSAForMemoryResult(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
// The previous iteration said it was reusable, so we should mark it as reusable as well.
|
||||
Alias::canReuseSSAForOldResult(oldInstruction)
|
||||
or
|
||||
// The current alias analysis says it is reusable.
|
||||
Alias::getResultMemoryLocation(oldInstruction).canReuseSSA()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
// This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
|
||||
instruction = phiInstruction(_, defLocation) and
|
||||
defLocation.canReuseSSA()
|
||||
)
|
||||
// We don't support reusing SSA for any location that could create a `Chi` instruction.
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
|
||||
@@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy
|
||||
* variable if its address never escapes and all reads and writes of that variable access the entire
|
||||
* variable using the original type of the variable.
|
||||
*/
|
||||
private predicate isVariableModeled(Allocation var) {
|
||||
predicate isVariableModeled(Allocation var) {
|
||||
not allocationEscapes(var) and
|
||||
forall(Instruction instr, AddressOperand addrOperand, IRType type |
|
||||
addrOperand = instr.getResultAddressOperand() and
|
||||
@@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA use/def chain for the specified variable can be safely reused by later
|
||||
* iterations of SSA construction. This will hold only if we modeled the variable soundly, so that
|
||||
* subsequent iterations will recompute SSA for any variable that we assumed did not escape, but
|
||||
* actually would have escaped if we had used a sound escape analysis.
|
||||
*/
|
||||
predicate canReuseSSAForVariable(IRAutomaticVariable var) {
|
||||
isVariableModeled(var) and
|
||||
not allocationEscapes(var)
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
|
||||
|
||||
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
|
||||
@@ -57,6 +68,12 @@ class MemoryLocation extends TMemoryLocation {
|
||||
final Language::LanguageType getType() { result = var.getLanguageType() }
|
||||
|
||||
final string getUniqueId() { result = var.getUniqueId() }
|
||||
|
||||
final predicate canReuseSSA() { canReuseSSAForVariable(var) }
|
||||
}
|
||||
|
||||
predicate canReuseSSAForOldResult(Instruction instr) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user