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:
Robert Marsh
2021-03-10 14:45:46 -08:00
parent a9d7990596
commit deff5c3af1
19 changed files with 375 additions and 22 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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