Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit deff5c3

Browse files
author
Robert Marsh
committed
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.
1 parent a9d7990 commit deff5c3

19 files changed

Lines changed: 375 additions & 22 deletions

File tree

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction {
19941994
*/
19951995
pragma[noinline]
19961996
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
1997+
1998+
/**
1999+
* Gets the input operand representing the value that flows from the specified predecessor block.
2000+
*/
2001+
final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
2002+
result = this.getAnOperand() and
2003+
result.getPredecessorBlock() = predecessorBlock
2004+
}
19972005
}
19982006

19992007
/**

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ class Operand extends TStageOperand {
3131
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
3232
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
3333
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
34-
this = phiOperand(use, def, predecessorBlock, _)
34+
this = phiOperand(use, def, predecessorBlock, _) or
35+
this = reusedPhiOperand(use, def, predecessorBlock, _)
3536
) or
3637
exists(Instruction use | this = chiOperand(use, _))
3738
}
@@ -431,7 +432,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
431432
Overlap overlap;
432433

433434
cached
434-
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
435+
PhiInputOperand() {
436+
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
437+
or
438+
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
439+
}
435440

436441
override string toString() { result = "Phi" }
437442

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
9090
or
9191
// Converting an address to a `bool` does not escape the address.
9292
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
93+
or
94+
instr instanceof CallInstruction and
95+
not exists(IREscapeAnalysisConfiguration config |
96+
config.useSoundEscapeAnalysis()
97+
)
9398
)
9499
)
95100
or

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ private import AliasConfigurationInternal
22
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
33
private import cpp
44
private import AliasAnalysis
5+
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA as UnaliasedSSA
56

67
private newtype TAllocation =
7-
TVariableAllocation(IRVariable var) or
8+
TVariableAllocation(IRVariable var) {
9+
// Only model variables that were not already handled in unaliased SSA.
10+
not UnaliasedSSA::canReuseSSAForVariable(var)
11+
} or
812
TIndirectParameterAllocation(IRAutomaticVariable var) {
913
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
1014
} or

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.internal.Overlap
33
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
44
private import semmle.code.cpp.Print
55
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
6+
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as OldSSA
67
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
78
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
89
private import semmle.code.cpp.ir.implementation.internal.OperandTag
@@ -131,6 +132,8 @@ abstract class MemoryLocation extends TMemoryLocation {
131132
* with automatic storage duration).
132133
*/
133134
predicate isAlwaysAllocatedOnStack() { none() }
135+
136+
final predicate canReuseSSA() { any() }
134137
}
135138

136139
/**
@@ -562,10 +565,19 @@ private Overlap getVariableMemoryLocationOverlap(
562565
use.getEndBitOffset())
563566
}
564567

568+
/**
569+
* Holds if the def/use information for the result of `instr` can be reused from the previous
570+
* iteration of the IR.
571+
*/
572+
predicate canReuseSSAForOldResult(Instruction instr) {
573+
OldSSA::canReuseSSAForMemoryResult(instr)
574+
}
575+
565576
bindingset[result, b]
566577
private boolean unbindBool(boolean b) { result != b.booleanNot() }
567578

568579
MemoryLocation getResultMemoryLocation(Instruction instr) {
580+
not(canReuseSSAForOldResult(instr)) and
569581
exists(MemoryAccessKind kind, boolean isMayAccess |
570582
kind = instr.getResultMemoryAccess() and
571583
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
@@ -598,6 +610,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
598610
}
599611

600612
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
613+
not(canReuseSSAForOldResult(operand.getAnyDef())) and
601614
exists(MemoryAccessKind kind, boolean isMayAccess |
602615
kind = operand.getMemoryAccess() and
603616
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and

cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,28 @@ private module Cached {
5858
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
5959
}
6060

61+
/**
62+
* Gets the block from the old IR that corresponds to `newBlock`.
63+
*/
64+
private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
65+
66+
/**
67+
* Holds if this iteration of SSA can model the def/use information for the result of
68+
* `oldInstruction`, either because alias analysis has determined a memory location for that
69+
* result, or because a previous iteration of the IR already computed that def/use information
70+
* completely.
71+
*/
72+
private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
73+
// We're modeling the result's memory location ourselves.
74+
exists(Alias::getResultMemoryLocation(oldInstruction))
75+
or
76+
// This result was already modeled by a previous iteration of SSA.
77+
Alias::canReuseSSAForOldResult(oldInstruction)
78+
}
79+
6180
cached
6281
predicate hasModeledMemoryResult(Instruction instruction) {
63-
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
82+
canModelResultForOldInstruction(getOldInstruction(instruction)) or
6483
instruction instanceof PhiInstruction or // Phis always have modeled results
6584
instruction instanceof ChiInstruction // Chis always have modeled results
6685
}
@@ -117,6 +136,30 @@ private module Cached {
117136
)
118137
}
119138

139+
/**
140+
* Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
141+
* old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
142+
* corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
143+
* instruction that is now degenerate due all but one of its predecessor branches being
144+
* unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
145+
* true definition.
146+
*/
147+
private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
148+
exists(Overlap originalOverlap |
149+
originalOverlap = oldOperand.getDefinitionOverlap() and
150+
(
151+
result = getNewInstruction(oldOperand.getAnyDef()) and
152+
overlap = originalOverlap
153+
/*or
154+
exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
155+
phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
156+
result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
157+
overlap = combineOverlap(phiOperandOverlap, originalOverlap)
158+
) */
159+
)
160+
)
161+
}
162+
120163
cached
121164
private Instruction getMemoryOperandDefinition0(
122165
Instruction instruction, MemoryOperandTag tag, Overlap overlap
@@ -148,6 +191,12 @@ private module Cached {
148191
overlap instanceof MustExactlyOverlap and
149192
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
150193
)
194+
or
195+
exists(OldIR::NonPhiMemoryOperand oldOperand |
196+
result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
197+
oldOperand.getUse() = instruction and
198+
tag = oldOperand.getOperandTag()
199+
)
151200
}
152201

153202
/**
@@ -214,10 +263,24 @@ private module Cached {
214263
)
215264
}
216265

266+
/**
267+
* Gets the new definition instruction for the operand of `instr` that flows from the block
268+
* `newPredecessorBlock`, based on that operand's definition in the old IR.
269+
*/
270+
private Instruction getNewPhiOperandDefinitionFromOldSSA(
271+
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
272+
) {
273+
exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
274+
oldPhi = getOldInstruction(instr) and
275+
oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
276+
result = getNewDefinitionFromOldSSA(oldOperand, overlap)
277+
)
278+
}
279+
217280
pragma[noopt]
218281
cached
219282
Instruction getPhiOperandDefinition(
220-
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
283+
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
221284
) {
222285
exists(
223286
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
@@ -229,6 +292,8 @@ private module Cached {
229292
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
230293
overlap = Alias::getOverlap(actualDefLocation, useLocation)
231294
)
295+
or
296+
result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
232297
}
233298

234299
cached
@@ -248,8 +313,12 @@ private module Cached {
248313

249314
cached
250315
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
251-
exists(OldBlock oldBlock |
252-
instr = getPhi(oldBlock, _) and
316+
exists(OldBlock oldBlock | (
317+
instr = getPhi(oldBlock, _)
318+
or
319+
// Any `Phi` that we propagated from the previous iteration stays in the same block.
320+
getOldInstruction(instr).getBlock() = oldBlock
321+
) and
253322
result = getNewInstruction(oldBlock.getFirstInstruction())
254323
)
255324
}
@@ -335,6 +404,9 @@ private module Cached {
335404
result = vvar.getType()
336405
)
337406
or
407+
instr = reusedPhiInstruction(_) and
408+
result = instr.(OldInstruction).getResultLanguageType()
409+
or
338410
instr = unreachedInstruction(_) and result = Language::getVoidType()
339411
}
340412

@@ -862,6 +934,26 @@ module DefUse {
862934
}
863935
}
864936

937+
predicate canReuseSSAForMemoryResult(Instruction instruction) {
938+
exists(OldInstruction oldInstruction |
939+
oldInstruction = getOldInstruction(instruction) and
940+
(
941+
// The previous iteration said it was reusable, so we should mark it as reusable as well.
942+
Alias::canReuseSSAForOldResult(oldInstruction)
943+
or
944+
// The current alias analysis says it is reusable.
945+
Alias::getResultMemoryLocation(oldInstruction).canReuseSSA()
946+
)
947+
)
948+
or
949+
exists(Alias::MemoryLocation defLocation |
950+
// This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
951+
instruction = phiInstruction(_, defLocation) and
952+
defLocation.canReuseSSA()
953+
)
954+
// We don't support reusing SSA for any location that could create a `Chi` instruction.
955+
}
956+
865957
/**
866958
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
867959
* `DebugSSA` module, which is then imported by PrintSSA.

cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ module UnaliasedSSAInstructions {
5555
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
5656
}
5757

58+
TRawInstruction reusedPhiInstruction(
59+
TRawInstruction blockStartInstr) {
60+
none()
61+
}
62+
5863
class TChiInstruction = TUnaliasedSSAChiInstruction;
5964

6065
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
@@ -75,14 +80,19 @@ module UnaliasedSSAInstructions {
7580
* a class alias.
7681
*/
7782
module AliasedSSAInstructions {
78-
class TPhiInstruction = TAliasedSSAPhiInstruction;
83+
class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction;
7984

8085
TPhiInstruction phiInstruction(
8186
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
8287
) {
8388
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
8489
}
8590

91+
TPhiInstruction reusedPhiInstruction(
92+
TRawInstruction blockStartInstr) {
93+
result = TUnaliasedSSAPhiInstruction(blockStartInstr, _)
94+
}
95+
8696
class TChiInstruction = TAliasedSSAChiInstruction;
8797

8898
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {

cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private module Shared {
8484
}
8585
}
8686

87-
/**
87+
/**
8888
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
8989
* raw IR stage.
9090
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
@@ -109,6 +109,12 @@ module RawOperands {
109109
none()
110110
}
111111

112+
TPhiOperand reusedPhiOperand(
113+
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
114+
Overlap overlap
115+
) {
116+
none()
117+
}
112118
/**
113119
* Returns the Chi operand with the specified parameters.
114120
*/
@@ -140,6 +146,12 @@ module UnaliasedSSAOperands {
140146
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
141147
}
142148

149+
TPhiOperand reusedPhiOperand(
150+
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
151+
Unaliased::IRBlock predecessorBlock, Overlap overlap
152+
) {
153+
none()
154+
}
143155
/**
144156
* Returns the Chi operand with the specified parameters.
145157
*/
@@ -155,7 +167,7 @@ module UnaliasedSSAOperands {
155167
module AliasedSSAOperands {
156168
import Shared
157169

158-
class TPhiOperand = Internal::TAliasedPhiOperand;
170+
class TPhiOperand = Internal::TAliasedPhiOperand or Internal::TUnaliasedPhiOperand;
159171

160172
class TChiOperand = Internal::TAliasedChiOperand;
161173

@@ -165,12 +177,24 @@ module AliasedSSAOperands {
165177
* Returns the Phi operand with the specified parameters.
166178
*/
167179
TPhiOperand phiOperand(
168-
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
180+
Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
169181
Aliased::IRBlock predecessorBlock, Overlap overlap
170182
) {
171183
result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
172184
}
173185

186+
/**
187+
* Returns the Phi operand with the specified parameters.
188+
*/
189+
TPhiOperand reusedPhiOperand(
190+
Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
191+
Aliased::IRBlock predecessorBlock, Overlap overlap
192+
) {
193+
exists(Unaliased::IRBlock oldBlock |
194+
predecessorBlock.getFirstInstruction() = oldBlock.getFirstInstruction() and
195+
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, oldBlock, overlap)
196+
)
197+
}
174198
/**
175199
* Returns the Chi operand with the specified parameters.
176200
*/

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,14 @@ class PhiInstruction extends Instruction {
19941994
*/
19951995
pragma[noinline]
19961996
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
1997+
1998+
/**
1999+
* Gets the input operand representing the value that flows from the specified predecessor block.
2000+
*/
2001+
final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
2002+
result = this.getAnOperand() and
2003+
result.getPredecessorBlock() = predecessorBlock
2004+
}
19972005
}
19982006

19992007
/**

0 commit comments

Comments
 (0)