mirror of
https://github.com/github/codeql.git
synced 2026-04-23 07:45:17 +02:00
C#: sync IR files and update for C++ SSA reuse
This commit is contained in:
@@ -55,10 +55,7 @@ module UnaliasedSSAInstructions {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TPhiInstruction reusedPhiInstruction(
|
||||
TRawInstruction blockStartInstr) {
|
||||
none()
|
||||
}
|
||||
TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() }
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
@@ -88,8 +85,7 @@ module AliasedSSAInstructions {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TPhiInstruction reusedPhiInstruction(
|
||||
TRawInstruction blockStartInstr) {
|
||||
TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, _)
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,16 @@ module RawOperands {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
|
||||
Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
@@ -123,6 +133,16 @@ module UnaliasedSSAOperands {
|
||||
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
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, _)
|
||||
) or
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
@@ -431,7 +435,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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
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, _)
|
||||
) or
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
@@ -431,7 +435,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" }
|
||||
|
||||
|
||||
@@ -92,9 +92,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
|
||||
or
|
||||
instr instanceof CallInstruction and
|
||||
not exists(IREscapeAnalysisConfiguration config |
|
||||
config.useSoundEscapeAnalysis()
|
||||
)
|
||||
not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis())
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -331,9 +329,7 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
or
|
||||
exists(Configuration::StageEscapeConfiguration config |
|
||||
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
)
|
||||
Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,10 +15,4 @@ class Allocation extends IRAutomaticVariable {
|
||||
}
|
||||
}
|
||||
|
||||
class StageEscapeConfiguration extends string {
|
||||
StageEscapeConfiguration() {
|
||||
this = "StageEscapeConfiguration (unaliased_ssa)"
|
||||
}
|
||||
|
||||
predicate useSoundEscapeAnalysis() { any() }
|
||||
}
|
||||
predicate phaseNeedsSoundEscapeAnalysis() { any() }
|
||||
|
||||
@@ -43,24 +43,82 @@ private module Cached {
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
/**
|
||||
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
|
||||
* this predicate returns the `PhiInputOperand` corresponding to that predecessor block.
|
||||
* Otherwise, this predicate does not hold.
|
||||
*/
|
||||
private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) {
|
||||
result =
|
||||
unique(OldIR::PhiInputOperand operand |
|
||||
operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
|
||||
operand.getPredecessorBlock() instanceof OldBlock
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
instr = phiInstruction(_, _)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
// Check that the phi instruction is *not* degenerate, but we can't use
|
||||
// getDegeneratePhiOperand in the first stage with phi instyructions
|
||||
exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction |
|
||||
oldInstruction = instr and
|
||||
operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
|
||||
operand1.getPredecessorBlock() instanceof OldBlock and
|
||||
operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
|
||||
operand2.getPredecessorBlock() instanceof OldBlock and
|
||||
operand1 != operand2
|
||||
)
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
cached IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
exists(Instruction newEnd, OldIR::Instruction oldEnd |
|
||||
(
|
||||
result.getLastInstruction() = newEnd and
|
||||
not newEnd instanceof ChiInstruction
|
||||
or
|
||||
newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work?
|
||||
) and
|
||||
(
|
||||
oldBlock.getLastInstruction() = oldEnd and
|
||||
not oldEnd instanceof OldIR::ChiInstruction
|
||||
or
|
||||
oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work?
|
||||
) and
|
||||
oldEnd = getNewInstruction(newEnd)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +175,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 +230,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 +302,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 +331,8 @@ private module Cached {
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
)
|
||||
or
|
||||
result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -249,7 +353,12 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = getPhi(oldBlock, _) and
|
||||
(
|
||||
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 +444,9 @@ private module Cached {
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
instr = reusedPhiInstruction(_) and
|
||||
result = instr.(OldInstruction).getResultLanguageType()
|
||||
or
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
|
||||
@@ -72,9 +72,7 @@ class MemoryLocation extends TMemoryLocation {
|
||||
final predicate canReuseSSA() { canReuseSSAForVariable(var) }
|
||||
}
|
||||
|
||||
predicate canReuseSSAForOldResult(Instruction instr) {
|
||||
none()
|
||||
}
|
||||
predicate canReuseSSAForOldResult(Instruction instr) { none() }
|
||||
|
||||
/**
|
||||
* Represents a set of `MemoryLocation`s that cannot overlap with
|
||||
|
||||
Reference in New Issue
Block a user