@@ -43,24 +43,81 @@ private module Cached {
43
43
class TStageInstruction =
44
44
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction ;
45
45
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
+
46
59
cached
47
60
predicate hasInstruction ( TStageInstruction instr ) {
48
61
instr instanceof TRawInstruction and instr instanceof OldInstruction
49
62
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
+ not exists (
69
+ unique( OldIR:: PhiInputOperand operand |
70
+ operand = instr .( OldIR:: PhiInstruction ) .getAnInputOperand ( ) and
71
+ operand .getPredecessorBlock ( ) instanceof OldBlock
72
+ )
73
+ )
51
74
or
52
75
instr instanceof TChiInstruction
53
76
or
54
77
instr instanceof TUnreachedInstruction
55
78
}
56
79
57
- private IRBlock getNewBlock ( OldBlock oldBlock ) {
58
- result .getFirstInstruction ( ) = getNewInstruction ( oldBlock .getFirstInstruction ( ) )
80
+ cached
81
+ IRBlock getNewBlock ( OldBlock oldBlock ) {
82
+ exists ( Instruction newEnd , OldIR:: Instruction oldEnd |
83
+ (
84
+ result .getLastInstruction ( ) = newEnd and
85
+ not newEnd instanceof ChiInstruction
86
+ or
87
+ newEnd = result .getLastInstruction ( ) .( ChiInstruction ) .getAPredecessor ( ) // does this work?
88
+ ) and
89
+ (
90
+ oldBlock .getLastInstruction ( ) = oldEnd and
91
+ not oldEnd instanceof OldIR:: ChiInstruction
92
+ or
93
+ oldEnd = oldBlock .getLastInstruction ( ) .( OldIR:: ChiInstruction ) .getAPredecessor ( ) // does this work?
94
+ ) and
95
+ oldEnd = getNewInstruction ( newEnd )
96
+ )
97
+ }
98
+
99
+ /**
100
+ * Gets the block from the old IR that corresponds to `newBlock`.
101
+ */
102
+ private OldBlock getOldBlock ( IRBlock newBlock ) { getNewBlock ( result ) = newBlock }
103
+
104
+ /**
105
+ * Holds if this iteration of SSA can model the def/use information for the result of
106
+ * `oldInstruction`, either because alias analysis has determined a memory location for that
107
+ * result, or because a previous iteration of the IR already computed that def/use information
108
+ * completely.
109
+ */
110
+ private predicate canModelResultForOldInstruction ( OldInstruction oldInstruction ) {
111
+ // We're modeling the result's memory location ourselves.
112
+ exists ( Alias:: getResultMemoryLocation ( oldInstruction ) )
113
+ or
114
+ // This result was already modeled by a previous iteration of SSA.
115
+ Alias:: canReuseSSAForOldResult ( oldInstruction )
59
116
}
60
117
61
118
cached
62
119
predicate hasModeledMemoryResult ( Instruction instruction ) {
63
- exists ( Alias :: getResultMemoryLocation ( getOldInstruction ( instruction ) ) ) or
120
+ canModelResultForOldInstruction ( getOldInstruction ( instruction ) ) or
64
121
instruction instanceof PhiInstruction or // Phis always have modeled results
65
122
instruction instanceof ChiInstruction // Chis always have modeled results
66
123
}
@@ -117,6 +174,32 @@ private module Cached {
117
174
)
118
175
}
119
176
177
+ /**
178
+ * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
179
+ * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
180
+ * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
181
+ * instruction that is now degenerate due all but one of its predecessor branches being
182
+ * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
183
+ * true definition.
184
+ */
185
+ private Instruction getNewDefinitionFromOldSSA ( OldIR:: MemoryOperand oldOperand , Overlap overlap ) {
186
+ exists ( Overlap originalOverlap |
187
+ originalOverlap = oldOperand .getDefinitionOverlap ( ) and
188
+ (
189
+ result = getNewInstruction ( oldOperand .getAnyDef ( ) ) and
190
+ overlap = originalOverlap
191
+ or
192
+ exists ( OldIR:: PhiInputOperand phiOperand , Overlap phiOperandOverlap |
193
+ phiOperand = getDegeneratePhiOperand ( oldOperand .getAnyDef ( ) ) and
194
+ result = getNewDefinitionFromOldSSA ( phiOperand , phiOperandOverlap ) and
195
+ overlap =
196
+ combineOverlap ( pragma [ only_bind_out ] ( phiOperandOverlap ) ,
197
+ pragma [ only_bind_out ] ( originalOverlap ) )
198
+ )
199
+ )
200
+ )
201
+ }
202
+
120
203
cached
121
204
private Instruction getMemoryOperandDefinition0 (
122
205
Instruction instruction , MemoryOperandTag tag , Overlap overlap
@@ -148,6 +231,12 @@ private module Cached {
148
231
overlap instanceof MustExactlyOverlap and
149
232
exists ( MustTotallyOverlap o | exists ( getMemoryOperandDefinition0 ( instruction , tag , o ) ) )
150
233
)
234
+ or
235
+ exists ( OldIR:: NonPhiMemoryOperand oldOperand |
236
+ result = getNewDefinitionFromOldSSA ( oldOperand , overlap ) and
237
+ oldOperand .getUse ( ) = instruction and
238
+ tag = oldOperand .getOperandTag ( )
239
+ )
151
240
}
152
241
153
242
/**
@@ -214,10 +303,24 @@ private module Cached {
214
303
)
215
304
}
216
305
306
+ /**
307
+ * Gets the new definition instruction for the operand of `instr` that flows from the block
308
+ * `newPredecessorBlock`, based on that operand's definition in the old IR.
309
+ */
310
+ private Instruction getNewPhiOperandDefinitionFromOldSSA (
311
+ Instruction instr , IRBlock newPredecessorBlock , Overlap overlap
312
+ ) {
313
+ exists ( OldIR:: PhiInstruction oldPhi , OldIR:: PhiInputOperand oldOperand |
314
+ oldPhi = getOldInstruction ( instr ) and
315
+ oldOperand = oldPhi .getInputOperand ( getOldBlock ( newPredecessorBlock ) ) and
316
+ result = getNewDefinitionFromOldSSA ( oldOperand , overlap )
317
+ )
318
+ }
319
+
217
320
pragma [ noopt]
218
321
cached
219
322
Instruction getPhiOperandDefinition (
220
- PhiInstruction instr , IRBlock newPredecessorBlock , Overlap overlap
323
+ Instruction instr , IRBlock newPredecessorBlock , Overlap overlap
221
324
) {
222
325
exists (
223
326
Alias:: MemoryLocation defLocation , Alias:: MemoryLocation useLocation , OldBlock phiBlock ,
@@ -229,6 +332,8 @@ private module Cached {
229
332
result = getDefinitionOrChiInstruction ( defBlock , defOffset , defLocation , actualDefLocation ) and
230
333
overlap = Alias:: getOverlap ( actualDefLocation , useLocation )
231
334
)
335
+ or
336
+ result = getNewPhiOperandDefinitionFromOldSSA ( instr , newPredecessorBlock , overlap )
232
337
}
233
338
234
339
cached
@@ -249,7 +354,12 @@ private module Cached {
249
354
cached
250
355
Instruction getPhiInstructionBlockStart ( PhiInstruction instr ) {
251
356
exists ( OldBlock oldBlock |
252
- instr = getPhi ( oldBlock , _) and
357
+ (
358
+ instr = getPhi ( oldBlock , _)
359
+ or
360
+ // Any `Phi` that we propagated from the previous iteration stays in the same block.
361
+ getOldInstruction ( instr ) .getBlock ( ) = oldBlock
362
+ ) and
253
363
result = getNewInstruction ( oldBlock .getFirstInstruction ( ) )
254
364
)
255
365
}
@@ -335,6 +445,9 @@ private module Cached {
335
445
result = vvar .getType ( )
336
446
)
337
447
or
448
+ instr = reusedPhiInstruction ( _) and
449
+ result = instr .( OldInstruction ) .getResultLanguageType ( )
450
+ or
338
451
instr = unreachedInstruction ( _) and result = Language:: getVoidType ( )
339
452
}
340
453
@@ -862,6 +975,26 @@ module DefUse {
862
975
}
863
976
}
864
977
978
+ predicate canReuseSSAForMemoryResult ( Instruction instruction ) {
979
+ exists ( OldInstruction oldInstruction |
980
+ oldInstruction = getOldInstruction ( instruction ) and
981
+ (
982
+ // The previous iteration said it was reusable, so we should mark it as reusable as well.
983
+ Alias:: canReuseSSAForOldResult ( oldInstruction )
984
+ or
985
+ // The current alias analysis says it is reusable.
986
+ Alias:: getResultMemoryLocation ( oldInstruction ) .canReuseSSA ( )
987
+ )
988
+ )
989
+ or
990
+ exists ( Alias:: MemoryLocation defLocation |
991
+ // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
992
+ instruction = phiInstruction ( _, defLocation ) and
993
+ defLocation .canReuseSSA ( )
994
+ )
995
+ // We don't support reusing SSA for any location that could create a `Chi` instruction.
996
+ }
997
+
865
998
/**
866
999
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
867
1000
* `DebugSSA` module, which is then imported by PrintSSA.
0 commit comments