@@ -43,24 +43,81 @@ 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+ not exists (
69+ unique( OldIR:: PhiInputOperand operand |
70+ operand = instr .( OldIR:: PhiInstruction ) .getAnInputOperand ( ) and
71+ operand .getPredecessorBlock ( ) instanceof OldBlock
72+ )
73+ )
5174 or
5275 instr instanceof TChiInstruction
5376 or
5477 instr instanceof TUnreachedInstruction
5578 }
5679
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 )
59116 }
60117
61118 cached
62119 predicate hasModeledMemoryResult ( Instruction instruction ) {
63- exists ( Alias :: getResultMemoryLocation ( getOldInstruction ( instruction ) ) ) or
120+ canModelResultForOldInstruction ( getOldInstruction ( instruction ) ) or
64121 instruction instanceof PhiInstruction or // Phis always have modeled results
65122 instruction instanceof ChiInstruction // Chis always have modeled results
66123 }
@@ -117,6 +174,32 @@ private module Cached {
117174 )
118175 }
119176
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+
120203 cached
121204 private Instruction getMemoryOperandDefinition0 (
122205 Instruction instruction , MemoryOperandTag tag , Overlap overlap
@@ -148,6 +231,12 @@ private module Cached {
148231 overlap instanceof MustExactlyOverlap and
149232 exists ( MustTotallyOverlap o | exists ( getMemoryOperandDefinition0 ( instruction , tag , o ) ) )
150233 )
234+ or
235+ exists ( OldIR:: NonPhiMemoryOperand oldOperand |
236+ result = getNewDefinitionFromOldSSA ( oldOperand , overlap ) and
237+ oldOperand .getUse ( ) = instruction and
238+ tag = oldOperand .getOperandTag ( )
239+ )
151240 }
152241
153242 /**
@@ -214,10 +303,24 @@ private module Cached {
214303 )
215304 }
216305
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+
217320 pragma [ noopt]
218321 cached
219322 Instruction getPhiOperandDefinition (
220- PhiInstruction instr , IRBlock newPredecessorBlock , Overlap overlap
323+ Instruction instr , IRBlock newPredecessorBlock , Overlap overlap
221324 ) {
222325 exists (
223326 Alias:: MemoryLocation defLocation , Alias:: MemoryLocation useLocation , OldBlock phiBlock ,
@@ -229,6 +332,8 @@ private module Cached {
229332 result = getDefinitionOrChiInstruction ( defBlock , defOffset , defLocation , actualDefLocation ) and
230333 overlap = Alias:: getOverlap ( actualDefLocation , useLocation )
231334 )
335+ or
336+ result = getNewPhiOperandDefinitionFromOldSSA ( instr , newPredecessorBlock , overlap )
232337 }
233338
234339 cached
@@ -249,7 +354,12 @@ private module Cached {
249354 cached
250355 Instruction getPhiInstructionBlockStart ( PhiInstruction instr ) {
251356 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
253363 result = getNewInstruction ( oldBlock .getFirstInstruction ( ) )
254364 )
255365 }
@@ -335,6 +445,9 @@ private module Cached {
335445 result = vvar .getType ( )
336446 )
337447 or
448+ instr = reusedPhiInstruction ( _) and
449+ result = instr .( OldInstruction ) .getResultLanguageType ( )
450+ or
338451 instr = unreachedInstruction ( _) and result = Language:: getVoidType ( )
339452 }
340453
@@ -862,6 +975,26 @@ module DefUse {
862975 }
863976}
864977
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+
865998/**
866999 * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
8671000 * `DebugSSA` module, which is then imported by PrintSSA.
0 commit comments