@@ -43,24 +43,82 @@ 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
+ 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
+ )
51
76
or
52
77
instr instanceof TChiInstruction
53
78
or
54
79
instr instanceof TUnreachedInstruction
55
80
}
56
81
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 )
59
117
}
60
118
61
119
cached
62
120
predicate hasModeledMemoryResult ( Instruction instruction ) {
63
- exists ( Alias :: getResultMemoryLocation ( getOldInstruction ( instruction ) ) ) or
121
+ canModelResultForOldInstruction ( getOldInstruction ( instruction ) ) or
64
122
instruction instanceof PhiInstruction or // Phis always have modeled results
65
123
instruction instanceof ChiInstruction // Chis always have modeled results
66
124
}
@@ -117,6 +175,30 @@ private module Cached {
117
175
)
118
176
}
119
177
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
+
120
202
cached
121
203
private Instruction getMemoryOperandDefinition0 (
122
204
Instruction instruction , MemoryOperandTag tag , Overlap overlap
@@ -148,6 +230,12 @@ private module Cached {
148
230
overlap instanceof MustExactlyOverlap and
149
231
exists ( MustTotallyOverlap o | exists ( getMemoryOperandDefinition0 ( instruction , tag , o ) ) )
150
232
)
233
+ or
234
+ exists ( OldIR:: NonPhiMemoryOperand oldOperand |
235
+ result = getNewDefinitionFromOldSSA ( oldOperand , overlap ) and
236
+ oldOperand .getUse ( ) = instruction and
237
+ tag = oldOperand .getOperandTag ( )
238
+ )
151
239
}
152
240
153
241
/**
@@ -214,10 +302,24 @@ private module Cached {
214
302
)
215
303
}
216
304
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
+
217
319
pragma [ noopt]
218
320
cached
219
321
Instruction getPhiOperandDefinition (
220
- PhiInstruction instr , IRBlock newPredecessorBlock , Overlap overlap
322
+ Instruction instr , IRBlock newPredecessorBlock , Overlap overlap
221
323
) {
222
324
exists (
223
325
Alias:: MemoryLocation defLocation , Alias:: MemoryLocation useLocation , OldBlock phiBlock ,
@@ -229,6 +331,8 @@ private module Cached {
229
331
result = getDefinitionOrChiInstruction ( defBlock , defOffset , defLocation , actualDefLocation ) and
230
332
overlap = Alias:: getOverlap ( actualDefLocation , useLocation )
231
333
)
334
+ or
335
+ result = getNewPhiOperandDefinitionFromOldSSA ( instr , newPredecessorBlock , overlap )
232
336
}
233
337
234
338
cached
@@ -249,7 +353,12 @@ private module Cached {
249
353
cached
250
354
Instruction getPhiInstructionBlockStart ( PhiInstruction instr ) {
251
355
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
253
362
result = getNewInstruction ( oldBlock .getFirstInstruction ( ) )
254
363
)
255
364
}
@@ -335,6 +444,9 @@ private module Cached {
335
444
result = vvar .getType ( )
336
445
)
337
446
or
447
+ instr = reusedPhiInstruction ( _) and
448
+ result = instr .( OldInstruction ) .getResultLanguageType ( )
449
+ or
338
450
instr = unreachedInstruction ( _) and result = Language:: getVoidType ( )
339
451
}
340
452
0 commit comments