Skip to content

Commit b281102

Browse files
committed
C#: sync IR files and update for C++ SSA reuse
1 parent 5d7d26b commit b281102

File tree

10 files changed

+186
-38
lines changed

10 files changed

+186
-38
lines changed

csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll

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

58-
TPhiInstruction reusedPhiInstruction(
59-
TRawInstruction blockStartInstr) {
60-
none()
61-
}
58+
TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() }
6259

6360
class TChiInstruction = TUnaliasedSSAChiInstruction;
6461

@@ -88,8 +85,7 @@ module AliasedSSAInstructions {
8885
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
8986
}
9087

91-
TPhiInstruction reusedPhiInstruction(
92-
TRawInstruction blockStartInstr) {
88+
TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) {
9389
result = TUnaliasedSSAPhiInstruction(blockStartInstr, _)
9490
}
9591

csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ module RawOperands {
9292
none()
9393
}
9494

95+
/**
96+
* Returns the Phi operand with the specified parameters.
97+
*/
98+
TPhiOperand reusedPhiOperand(
99+
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
100+
Overlap overlap
101+
) {
102+
none()
103+
}
104+
95105
/**
96106
* Returns the Chi operand with the specified parameters.
97107
*/
@@ -123,6 +133,16 @@ module UnaliasedSSAOperands {
123133
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
124134
}
125135

136+
/**
137+
* Returns the Phi operand with the specified parameters.
138+
*/
139+
TPhiOperand reusedPhiOperand(
140+
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
141+
Unaliased::IRBlock predecessorBlock, Overlap overlap
142+
) {
143+
none()
144+
}
145+
126146
/**
127147
* Returns the Chi operand with the specified parameters.
128148
*/

csharp/ql/src/experimental/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
/**

csharp/ql/src/experimental/ir/implementation/raw/Operand.qll

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
2828
cached
2929
Operand() {
3030
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
31-
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
32-
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
31+
exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
32+
or
33+
exists(Instruction use | this = nonSSAMemoryOperand(use, _))
34+
or
3335
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
34-
this = phiOperand(use, def, predecessorBlock, _)
35-
) or
36+
this = phiOperand(use, def, predecessorBlock, _) or
37+
this = reusedPhiOperand(use, def, predecessorBlock, _)
38+
)
39+
or
3640
exists(Instruction use | this = chiOperand(use, _))
3741
}
3842

@@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
431435
Overlap overlap;
432436

433437
cached
434-
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
438+
PhiInputOperand() {
439+
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
440+
or
441+
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
442+
}
435443

436444
override string toString() { result = "Phi" }
437445

csharp/ql/src/experimental/ir/implementation/unaliased_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
/**

csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
2828
cached
2929
Operand() {
3030
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
31-
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
32-
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
31+
exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
32+
or
33+
exists(Instruction use | this = nonSSAMemoryOperand(use, _))
34+
or
3335
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
34-
this = phiOperand(use, def, predecessorBlock, _)
35-
) or
36+
this = phiOperand(use, def, predecessorBlock, _) or
37+
this = reusedPhiOperand(use, def, predecessorBlock, _)
38+
)
39+
or
3640
exists(Instruction use | this = chiOperand(use, _))
3741
}
3842

@@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
431435
Overlap overlap;
432436

433437
cached
434-
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
438+
PhiInputOperand() {
439+
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
440+
or
441+
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
442+
}
435443

436444
override string toString() { result = "Phi" }
437445

csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
9292
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
9393
or
9494
instr instanceof CallInstruction and
95-
not exists(IREscapeAnalysisConfiguration config |
96-
config.useSoundEscapeAnalysis()
97-
)
95+
not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis())
9896
)
9997
)
10098
or
@@ -331,9 +329,7 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
331329
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
332330
)
333331
or
334-
exists(Configuration::StageEscapeConfiguration config |
335-
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
336-
)
332+
Configuration::phaseNeedsSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
337333
}
338334

339335
/**

csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,4 @@ class Allocation extends IRAutomaticVariable {
1515
}
1616
}
1717

18-
class StageEscapeConfiguration extends string {
19-
StageEscapeConfiguration() {
20-
this = "StageEscapeConfiguration (unaliased_ssa)"
21-
}
22-
23-
predicate useSoundEscapeAnalysis() { any() }
24-
}
18+
predicate phaseNeedsSoundEscapeAnalysis() { any() }

csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,82 @@ private module Cached {
4343
class TStageInstruction =
4444
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
4545

46+
/**
47+
* If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
48+
* this predicate returns the `PhiInputOperand` corresponding to that predecessor block.
49+
* Otherwise, this predicate does not hold.
50+
*/
51+
private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) {
52+
result =
53+
unique(OldIR::PhiInputOperand operand |
54+
operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
55+
operand.getPredecessorBlock() instanceof OldBlock
56+
)
57+
}
58+
4659
cached
4760
predicate hasInstruction(TStageInstruction instr) {
4861
instr instanceof TRawInstruction and instr instanceof OldInstruction
4962
or
50-
instr instanceof TPhiInstruction
63+
instr = phiInstruction(_, _)
64+
or
65+
instr = reusedPhiInstruction(_) and
66+
// Check that the phi instruction is *not* degenerate, but we can't use
67+
// getDegeneratePhiOperand in the first stage with phi instyructions
68+
exists(OldIR::PhiInputOperand operand1, OldIR::PhiInputOperand operand2, OldInstruction oldInstruction |
69+
oldInstruction = instr and
70+
operand1 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
71+
operand1.getPredecessorBlock() instanceof OldBlock and
72+
operand2 = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
73+
operand2.getPredecessorBlock() instanceof OldBlock and
74+
operand1 != operand2
75+
)
5176
or
5277
instr instanceof TChiInstruction
5378
or
5479
instr instanceof TUnreachedInstruction
5580
}
5681

57-
private IRBlock getNewBlock(OldBlock oldBlock) {
58-
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
82+
cached IRBlock getNewBlock(OldBlock oldBlock) {
83+
exists(Instruction newEnd, OldIR::Instruction oldEnd |
84+
(
85+
result.getLastInstruction() = newEnd and
86+
not newEnd instanceof ChiInstruction
87+
or
88+
newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work?
89+
) and
90+
(
91+
oldBlock.getLastInstruction() = oldEnd and
92+
not oldEnd instanceof OldIR::ChiInstruction
93+
or
94+
oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work?
95+
) and
96+
oldEnd = getNewInstruction(newEnd)
97+
)
98+
}
99+
100+
/**
101+
* Gets the block from the old IR that corresponds to `newBlock`.
102+
*/
103+
private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
104+
105+
/**
106+
* Holds if this iteration of SSA can model the def/use information for the result of
107+
* `oldInstruction`, either because alias analysis has determined a memory location for that
108+
* result, or because a previous iteration of the IR already computed that def/use information
109+
* completely.
110+
*/
111+
private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
112+
// We're modeling the result's memory location ourselves.
113+
exists(Alias::getResultMemoryLocation(oldInstruction))
114+
or
115+
// This result was already modeled by a previous iteration of SSA.
116+
Alias::canReuseSSAForOldResult(oldInstruction)
59117
}
60118

61119
cached
62120
predicate hasModeledMemoryResult(Instruction instruction) {
63-
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
121+
canModelResultForOldInstruction(getOldInstruction(instruction)) or
64122
instruction instanceof PhiInstruction or // Phis always have modeled results
65123
instruction instanceof ChiInstruction // Chis always have modeled results
66124
}
@@ -117,6 +175,30 @@ private module Cached {
117175
)
118176
}
119177

178+
/**
179+
* Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
180+
* old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
181+
* corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
182+
* instruction that is now degenerate due all but one of its predecessor branches being
183+
* unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
184+
* true definition.
185+
*/
186+
private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
187+
exists(Overlap originalOverlap |
188+
originalOverlap = oldOperand.getDefinitionOverlap() and
189+
(
190+
result = getNewInstruction(oldOperand.getAnyDef()) and
191+
overlap = originalOverlap
192+
or
193+
exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
194+
phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
195+
result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
196+
overlap = combineOverlap(phiOperandOverlap, originalOverlap)
197+
)
198+
)
199+
)
200+
}
201+
120202
cached
121203
private Instruction getMemoryOperandDefinition0(
122204
Instruction instruction, MemoryOperandTag tag, Overlap overlap
@@ -148,6 +230,12 @@ private module Cached {
148230
overlap instanceof MustExactlyOverlap and
149231
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
150232
)
233+
or
234+
exists(OldIR::NonPhiMemoryOperand oldOperand |
235+
result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
236+
oldOperand.getUse() = instruction and
237+
tag = oldOperand.getOperandTag()
238+
)
151239
}
152240

153241
/**
@@ -214,10 +302,24 @@ private module Cached {
214302
)
215303
}
216304

305+
/**
306+
* Gets the new definition instruction for the operand of `instr` that flows from the block
307+
* `newPredecessorBlock`, based on that operand's definition in the old IR.
308+
*/
309+
private Instruction getNewPhiOperandDefinitionFromOldSSA(
310+
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
311+
) {
312+
exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
313+
oldPhi = getOldInstruction(instr) and
314+
oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
315+
result = getNewDefinitionFromOldSSA(oldOperand, overlap)
316+
)
317+
}
318+
217319
pragma[noopt]
218320
cached
219321
Instruction getPhiOperandDefinition(
220-
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
322+
Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
221323
) {
222324
exists(
223325
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
@@ -229,6 +331,8 @@ private module Cached {
229331
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
230332
overlap = Alias::getOverlap(actualDefLocation, useLocation)
231333
)
334+
or
335+
result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
232336
}
233337

234338
cached
@@ -249,7 +353,12 @@ private module Cached {
249353
cached
250354
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
251355
exists(OldBlock oldBlock |
252-
instr = getPhi(oldBlock, _) and
356+
(
357+
instr = getPhi(oldBlock, _)
358+
or
359+
// Any `Phi` that we propagated from the previous iteration stays in the same block.
360+
getOldInstruction(instr).getBlock() = oldBlock
361+
) and
253362
result = getNewInstruction(oldBlock.getFirstInstruction())
254363
)
255364
}
@@ -335,6 +444,9 @@ private module Cached {
335444
result = vvar.getType()
336445
)
337446
or
447+
instr = reusedPhiInstruction(_) and
448+
result = instr.(OldInstruction).getResultLanguageType()
449+
or
338450
instr = unreachedInstruction(_) and result = Language::getVoidType()
339451
}
340452

csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ class MemoryLocation extends TMemoryLocation {
7272
final predicate canReuseSSA() { canReuseSSAForVariable(var) }
7373
}
7474

75-
predicate canReuseSSAForOldResult(Instruction instr) {
76-
none()
77-
}
75+
predicate canReuseSSAForOldResult(Instruction instr) { none() }
7876

7977
/**
8078
* Represents a set of `MemoryLocation`s that cannot overlap with

0 commit comments

Comments
 (0)