001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.opt.ssa;
014    
015    import static org.jikesrvm.compilers.opt.driver.OptConstants.SSA_SYNTH_BCI;
016    import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
017    import static org.jikesrvm.compilers.opt.ir.Operators.PHI;
018    
019    import java.lang.reflect.Constructor;
020    import java.util.Enumeration;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Iterator;
024    import java.util.LinkedList;
025    import java.util.Stack;
026    
027    import org.jikesrvm.VM;
028    import org.jikesrvm.classloader.TypeReference;
029    import org.jikesrvm.compilers.opt.DefUse;
030    import org.jikesrvm.compilers.opt.OptOptions;
031    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
032    import org.jikesrvm.compilers.opt.controlflow.BranchOptimizations;
033    import org.jikesrvm.compilers.opt.controlflow.DominatorTree;
034    import org.jikesrvm.compilers.opt.controlflow.DominatorTreeNode;
035    import org.jikesrvm.compilers.opt.controlflow.LTDominators;
036    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
037    import org.jikesrvm.compilers.opt.ir.BasicBlock;
038    import org.jikesrvm.compilers.opt.ir.IR;
039    import org.jikesrvm.compilers.opt.ir.IRTools;
040    import org.jikesrvm.compilers.opt.ir.Instruction;
041    import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
042    import org.jikesrvm.compilers.opt.ir.Move;
043    import org.jikesrvm.compilers.opt.ir.OperandEnumeration;
044    import org.jikesrvm.compilers.opt.ir.Phi;
045    import org.jikesrvm.compilers.opt.ir.Register;
046    import org.jikesrvm.compilers.opt.ir.RegisterOperandEnumeration;
047    import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
048    import org.jikesrvm.compilers.opt.ir.operand.Operand;
049    import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
050    import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
051    import org.jikesrvm.compilers.opt.ir.operand.UnreachableOperand;
052    import org.jikesrvm.compilers.opt.liveness.LiveAnalysis;
053    import org.jikesrvm.compilers.opt.liveness.LiveSet;
054    import org.jikesrvm.compilers.opt.util.TreeNode;
055    
056    /**
057     * This compiler phase translates out of SSA form.
058     *
059     * @see SSA
060     * @see SSAOptions
061     * @see LTDominators
062     */
063    public class LeaveSSA extends CompilerPhase {
064    
065      /**
066       *  verbose debugging flag
067       */
068      static final boolean DEBUG = false;
069    
070      /**
071       * The IR to manipulate
072       */
073      private IR ir;
074    
075      private final BranchOptimizations branchOpts = new BranchOptimizations(-1, true, true);
076    
077      private boolean splitSomeBlock = false;
078    
079      private final HashSet<Instruction> globalRenameTable = new HashSet<Instruction>();
080    
081      private final HashSet<Register> globalRenamePhis = new HashSet<Register>();
082    
083      /**
084       * Should we perform this phase?
085       * @param options controlling compiler options
086       */
087      public final boolean shouldPerform(OptOptions options) {
088        return options.SSA;
089      }
090    
091      /**
092       * Constructor for this compiler phase
093       */
094      private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(LeaveSSA.class);
095    
096      /**
097       * Get a constructor object for this compiler phase
098       * @return compiler phase constructor
099       */
100      public Constructor<CompilerPhase> getClassConstructor() {
101        return constructor;
102      }
103    
104      /**
105       * Return a string name for this phase.
106       * @return "Leave SSA"
107       */
108      public final String getName() {
109        return "Leave SSA";
110      }
111    
112      /**
113       * perform the main out-of-ssa transformation
114       * @param ir the governing IR
115       */
116      public final void perform(IR ir) {
117        this.ir = ir;
118        translateFromSSA(ir);
119    
120        // reset ir.SSADictionary
121        ir.HIRInfo.dictionary = null;
122        // reset ssa options
123        ir.actualSSAOptions = null;
124    
125        branchOpts.perform(ir, true);
126    
127        ir.HIRInfo.dominatorsAreComputed = false;
128      }
129    
130      /**
131       * This class provides an abstraction over stacks of names
132       * for registers.
133       */
134      static final class VariableStacks extends HashMap<Register, Stack<Operand>> {
135        /** Support for map serialization */
136        static final long serialVersionUID = -5664504465082745314L;
137    
138        /**
139         * Get the name at the top of the stack for a particular register
140         * @param s the register in question
141         * @return the name at the top of the stack for the register
142         */
143        Operand peek(Register s) {
144          Stack<Operand> stack = get(s);
145          if (stack == null || stack.isEmpty()) {
146            return null;
147          } else {
148            return stack.peek();
149          }
150        }
151    
152        /**
153         * Pop the name at the top of the stack for a particular register
154         * @param s the register in question
155         * @return the name at the top of the stack for the register
156         */
157        Operand pop(Register s) {
158          Stack<Operand> stack = get(s);
159          if (stack == null) {
160            throw new OptimizingCompilerException(
161                "Failure in translating out of SSA form: trying to pop operand from non-existant stack");
162          } else {
163            return stack.pop();
164          }
165        }
166    
167        /**
168         * Push a name at the top of the stack for a particular register
169         * @param s the register in question
170         * @param name the name to push on the stack
171         */
172        void push(Register s, Operand name) {
173          Stack<Operand> stack = get(s);
174          if (stack == null) {
175            stack = new Stack<Operand>();
176            put(s, stack);
177          }
178          stack.push(name);
179        }
180      }
181    
182      /**
183       * An instance of this class represents a pending copy instruction
184       * to be inserted.
185       */
186      static final class Copy {
187        /**
188         * The right-hand side of the copy instruction
189         */
190        final Operand source;
191        /**
192         * The left-hand side of the copy instruction
193         */
194        final RegisterOperand destination;
195        /**
196         *  The phi instruction which generated this copy instruction
197         */
198        final Instruction phi;
199    
200        /**
201         * Create a pending copy operation for an operand of a phi instruction
202         * @param     phi   the phi instruction
203         * @param     index which operand of the instruction to copy
204         */
205        Copy(Instruction phi, int index) {
206          this.phi = phi;
207          destination = Phi.getResult(phi).asRegister();
208          source = Phi.getValue(phi, index);
209        }
210      }
211    
212      /**
213       * substitute variables renamed in control parents
214       */
215      private void performRename(BasicBlock bb, DominatorTree dom, VariableStacks s) {
216        if (DEBUG) VM.sysWriteln("performRename: " + bb);
217    
218        InstructionEnumeration e = bb.forwardRealInstrEnumerator();
219        while (e.hasMoreElements()) {
220          Instruction i = e.next();
221          OperandEnumeration ee = i.getUses();
222          while (ee.hasMoreElements()) {
223            Operand o = ee.next();
224            if (o instanceof RegisterOperand) {
225              Register r1 = ((RegisterOperand) o).getRegister();
226              if (r1.isValidation()) continue;
227              Operand r2 = s.peek(r1);
228              if (r2 != null) {
229                if (DEBUG) {
230                  VM.sysWriteln("replace operand in " + i + "(" + r2 + " for " + o);
231                }
232                i.replaceOperand(o, r2.copy());
233              }
234            }
235          }
236        }
237    
238        // record renamings required in children
239        e = bb.forwardRealInstrEnumerator();
240        while (e.hasMoreElements()) {
241          Instruction i = e.next();
242          if (globalRenameTable.contains(i)) {
243            Register original = Move.getVal(i).asRegister().getRegister();
244            RegisterOperand rename = Move.getResult(i);
245            if (DEBUG) VM.sysWriteln("record rename " + rename + " for " + original);
246            s.push(original, rename);
247          }
248        }
249    
250        // insert copies in control children
251        Enumeration<TreeNode> children = dom.getChildren(bb);
252        while (children.hasMoreElements()) {
253          BasicBlock c = ((DominatorTreeNode) children.nextElement()).getBlock();
254          performRename(c, dom, s);
255        }
256    
257        // pop renamings from this block off stack
258        e = bb.forwardRealInstrEnumerator();
259        while (e.hasMoreElements()) {
260          Instruction i = e.next();
261          if (globalRenameTable.contains(i)) {
262            Register original = Move.getVal(i).asRegister().getRegister();
263            s.pop(original);
264          }
265        }
266      }
267    
268      private boolean usedBelowCopy(BasicBlock bb, Register r) {
269        InstructionEnumeration ie = bb.reverseRealInstrEnumerator();
270        while (ie.hasMoreElements()) {
271          Instruction inst = ie.next();
272          if (inst.isBranch()) {
273            OperandEnumeration oe = inst.getUses();
274            while (oe.hasMoreElements()) {
275              Operand op = oe.next();
276              if (op.isRegister() && op.asRegister().getRegister() == r) {
277                return true;
278              }
279            }
280          } else {
281            break;
282          }
283        }
284    
285        return false;
286      }
287    
288      /**
289       * Record pending copy operations needed to insert at the end of a basic
290       * block.
291       * TODO: this procedure is getting long and ugly.  Rewrite or refactor
292       * it.
293       * @param bb the basic block to process
294       * @param live valid liveness information for the IR
295       */
296      private void scheduleCopies(BasicBlock bb, LiveAnalysis live) {
297    
298        if (DEBUG) VM.sysWrite("scheduleCopies: " + bb + "\n");
299    
300        // compute out liveness from information in LiveAnalysis
301        LiveSet out = new LiveSet();
302        for (Enumeration<BasicBlock> outBlocks = bb.getOut(); outBlocks.hasMoreElements();) {
303          BasicBlock ob = outBlocks.nextElement();
304          LiveAnalysis.BBLiveElement le = live.getLiveInfo(ob);
305          out.add(le.getIn());
306        }
307    
308        // usedByAnother represents the set of registers that appear on the
309        // left-hand side of subsequent phi nodes.  This is important, since
310        // we be careful to order copies if the same register appears as the
311        // source and dest of copies in the same basic block.
312        HashSet<Register> usedByAnother = new HashSet<Register>(4);
313    
314        // for each basic block successor b of bb, if we make a block on the
315        // critical edge bb->b, then store this critical block.
316        HashMap<BasicBlock, BasicBlock> criticalBlocks = new HashMap<BasicBlock, BasicBlock>(4);
317    
318        // For each critical basic block b in which we are inserting copies: return the
319        // mapping of registers to names implied by the copies that have
320        // already been inserted into b.
321        HashMap<BasicBlock, HashMap<Register, Register>> currentNames =
322            new HashMap<BasicBlock, HashMap<Register, Register>>(4);
323    
324        // Additionally store the current names for the current basic block bb.
325        HashMap<Register, Register> bbNames = new HashMap<Register, Register>(4);
326    
327        // copySet is a linked-list of copies we need to insert in this block.
328        final LinkedList<Copy> copySet = new LinkedList<Copy>();
329    
330        /* Worklist is actually used like a stack - should we make this an Stack ?? */
331        final LinkedList<Copy> workList = new LinkedList<Copy>();
332    
333        // collect copies required in this block.  These copies move
334        // the appropriate rval into the lval of each phi node in
335        // control children of the current block.
336        Enumeration<BasicBlock> e = bb.getOut();
337        while (e.hasMoreElements()) {
338          BasicBlock bbs = e.nextElement();
339          if (bbs.isExit()) continue;
340          for (Instruction phi = bbs.firstInstruction(); phi != bbs.lastInstruction(); phi =
341              phi.nextInstructionInCodeOrder()) {
342            if (phi.operator() != PHI) continue;
343            for (int index = 0; index < Phi.getNumberOfPreds(phi); index++) {
344              if (Phi.getPred(phi, index).block != bb) continue;
345              Operand rval = Phi.getValue(phi, index);
346              if (rval.isRegister() && Phi.getResult(phi).asRegister().getRegister() == rval.asRegister().getRegister()) {
347                continue;
348              }
349              Copy c = new Copy(phi, index);
350              copySet.add(0, c);
351              if (c.source instanceof RegisterOperand) {
352                Register r = c.source.asRegister().getRegister();
353                usedByAnother.add(r);
354              }
355            }
356          }
357        }
358        //  the copies that need to be added to this block are processed
359        //  in a worklist that ensures that copies are inserted only
360        //  after the destination register has been read by any other copy
361        //  that needs it.
362        //
363        // initialize work list with all copies whose destination is not
364        // the source for any other copy, and delete such copies from
365        // the set of needed copies.
366        for (Iterator<Copy> copySetIter = copySet.iterator(); copySetIter.hasNext();) {
367          Copy c = copySetIter.next();
368          if (!usedByAnother.contains(c.destination.getRegister())) {
369            workList.add(0, c);
370            copySetIter.remove();
371          }
372        }
373        // while there is any more work to do.
374        while (!workList.isEmpty() || !copySet.isEmpty()) {
375          // while there are copies that can be correctly inserted.
376          while (!workList.isEmpty()) {
377            Copy c = workList.remove(0);
378            Register r = c.destination.getRegister();
379            TypeReference tt = c.destination.getType();
380            if (VM.VerifyAssertions && tt == null) {
381              tt = TypeReference.Int;
382              VM.sysWrite("SSA, warning: null type in " + c.destination + "\n");
383            }
384    
385            Register rr = null;
386            if (c.source.isRegister()) rr = c.source.asRegister().getRegister();
387            boolean shouldSplitBlock =
388                !c.phi.getBasicBlock().isExceptionHandlerBasicBlock() &&
389                ((ir.options.SSA_SPLITBLOCK_TO_AVOID_RENAME && out.contains(r)) ||
390                 (rr != null && ir.options.SSA_SPLITBLOCK_FOR_LOCAL_LIVE && usedBelowCopy(bb, rr)));
391    
392            if (ir.options.SSA_SPLITBLOCK_INTO_INFREQUENT) {
393              if (!bb.getInfrequent() &&
394                  c.phi.getBasicBlock().getInfrequent() &&
395                  !c.phi.getBasicBlock().isExceptionHandlerBasicBlock()) {
396                shouldSplitBlock = true;
397              }
398            }
399    
400            // this check captures cases when the result of a phi
401            // in a control successor is live on exit of the current
402            // block.  this means it is incorrect to simply insert
403            // a copy of the destination in the current block.  so
404            // we rename the destination to a new temporary, and
405            // record the renaming so that dominator blocks get the
406            // new name.
407            if (out.contains(r) && !shouldSplitBlock) {
408              if (!globalRenamePhis.contains(r)) {
409                Register t = ir.regpool.getReg(r);
410                Instruction save = SSA.makeMoveInstruction(ir, t, r, tt);
411                if (DEBUG) {
412                  VM.sysWriteln("Inserting " + save + " before " + c.phi + " in " + c.phi.getBasicBlock());
413                }
414                c.phi.insertAfter(save);
415                globalRenamePhis.add(r);
416                globalRenameTable.add(save);
417              }
418            }
419            Instruction ci = null;
420    
421            // insert copy operation required to remove phi
422            if (c.source instanceof ConstantOperand) {
423              if (c.source instanceof UnreachableOperand) {
424                ci = null;
425              } else {
426                ci = SSA.makeMoveInstruction(ir, r, (ConstantOperand) c.source);
427              }
428            } else if (c.source instanceof RegisterOperand) {
429              if (shouldSplitBlock) {
430                if (DEBUG) VM.sysWriteln("splitting edge: " + bb + "->" + c.phi.getBasicBlock());
431                BasicBlock criticalBlock = criticalBlocks.get(c.phi.getBasicBlock());
432                if (criticalBlock == null) {
433                  criticalBlock = IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);
434                  if (c.phi.getBasicBlock().getInfrequent()) {
435                    criticalBlock.setInfrequent();
436                  }
437                  splitSomeBlock = true;
438                  criticalBlocks.put(c.phi.getBasicBlock(), criticalBlock);
439                  HashMap<Register, Register> newNames = new HashMap<Register, Register>(4);
440                  currentNames.put(criticalBlock, newNames);
441                }
442                Register sr = c.source.asRegister().getRegister();
443                HashMap<Register, Register> criticalBlockNames = currentNames.get(criticalBlock);
444                Register nameForSR = criticalBlockNames.get(sr);
445                if (nameForSR == null) {
446                  nameForSR = bbNames.get(sr);
447                  if (nameForSR == null) nameForSR = sr;
448                }
449                if (DEBUG) VM.sysWriteln("dest(r): " + r);
450                if (DEBUG) VM.sysWriteln("sr: " + sr + ", nameForSR: " + nameForSR);
451                ci = SSA.makeMoveInstruction(ir, r, nameForSR, tt);
452                criticalBlockNames.put(sr, r);
453                criticalBlock.appendInstructionRespectingTerminalBranch(ci);
454              } else {
455                Register sr = c.source.asRegister().getRegister();
456                Register nameForSR = bbNames.get(sr);
457                if (nameForSR == null) nameForSR = sr;
458                if (DEBUG) VM.sysWriteln("not splitting edge: " + bb + "->" + c.phi.getBasicBlock());
459                if (DEBUG) VM.sysWriteln("dest(r): " + r);
460                if (DEBUG) VM.sysWriteln("sr: " + sr + ", nameForSR: " + nameForSR);
461                ci = SSA.makeMoveInstruction(ir, r, nameForSR, tt);
462                bbNames.put(sr, r);
463                SSA.addAtEnd(ir, bb, ci, c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
464              }
465              // ugly hack: having already added ci; set ci to null to skip remaining code;
466              ci = null;
467            } else {
468              throw new OptimizingCompilerException("Unexpected phi operand " +
469                                                        c
470                                                            .source +
471                                                                    " encountered during SSA teardown", true);
472            }
473            if (ci != null) {
474              if (shouldSplitBlock) {
475                if (DEBUG) VM.sysWriteln("splitting edge: " + bb + "->" + c.phi.getBasicBlock());
476                BasicBlock criticalBlock = criticalBlocks.get(c.phi.getBasicBlock());
477                if (criticalBlock == null) {
478                  criticalBlock = IRTools.makeBlockOnEdge(bb, c.phi.getBasicBlock(), ir);
479                  if (c.phi.getBasicBlock().getInfrequent()) {
480                    criticalBlock.setInfrequent();
481                  }
482                  splitSomeBlock = true;
483                  criticalBlocks.put(c.phi.getBasicBlock(), criticalBlock);
484                  HashMap<Register, Register> newNames = new HashMap<Register, Register>(4);
485                  currentNames.put(criticalBlock, newNames);
486                }
487                criticalBlock.appendInstructionRespectingTerminalBranch(ci);
488              } else {
489                SSA.addAtEnd(ir, bb, ci, c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
490              }
491            }
492    
493            // source has been copied and so can now be overwritten
494            // safely.  so now add any copies _to_ the source of the
495            // current copy to the work list.
496            if (c.source instanceof RegisterOperand) {
497              Register saved = c.source.asRegister().getRegister();
498              Iterator<Copy> copySetIter = copySet.iterator();
499              while (copySetIter.hasNext()) {
500                Copy cc = copySetIter.next();
501                if (cc.destination.asRegister().getRegister() == saved) {
502                  workList.add(0, cc);
503                  copySetIter.remove();
504                }
505              }
506            }
507          }
508          // an empty work list with work remaining in the copy set
509          // implies a cycle in the dependencies amongst copies.  deal
510          // with this: break the cycle by copying the destination
511          // of an arbitrary member of the copy set into a temporary.
512          // this destination has thus been saved, and can now be
513          // safely overwritten.  so, add that copy to the work list.
514          if (!copySet.isEmpty()) {
515            Copy c = copySet.remove(0);
516            Register tt = ir.regpool.getReg(c.destination.getRegister());
517            SSA.addAtEnd(ir,
518                             bb,
519                             SSA.makeMoveInstruction(ir, tt, c.destination.getRegister(), c.destination.getType()),
520                             c.phi.getBasicBlock().isExceptionHandlerBasicBlock());
521            bbNames.put(c.destination.getRegister(), tt);
522            workList.add(0, c);
523          }
524        }
525      }
526    
527      /**
528       * Insert copy instructions into a basic block to safely translate out
529       * of SSA form.
530       *
531       * @param bb the basic block
532       * @param dom a valid dominator tree for the IR
533       * @param live valid liveness information for the IR
534       */
535      private void insertCopies(BasicBlock bb, DominatorTree dom, LiveAnalysis live) {
536        // add copies required in this block to remove phis.
537        // (record renaming required by simultaneous liveness in global tables)
538        scheduleCopies(bb, live);
539    
540        // insert copies in control children
541        Enumeration<TreeNode> children = dom.getChildren(bb);
542        while (children.hasMoreElements()) {
543          BasicBlock c = ((DominatorTreeNode) children.nextElement()).getBlock();
544          insertCopies(c, dom, live);
545        }
546      }
547    
548      /**
549       * Main driver to translate an IR out of SSA form.
550       *
551       * @param ir the IR in SSA form
552       */
553      public void translateFromSSA(IR ir) {
554        // 0. Deal with guards (validation registers)
555        unSSAGuards(ir);
556    
557        // 1. re-compute dominator tree in case of control flow changes
558        LTDominators.perform(ir, true, true);
559        DominatorTree dom = new DominatorTree(ir, true);
560    
561        // 1.5 Perform Sreedhar's naive translation from TSSA to CSSA
562        //if (ir.options.UNROLL_LOG == 0) normalizeSSA(ir);
563    
564        // 2. compute liveness
565        LiveAnalysis live = new LiveAnalysis(false,  // don't create GC maps
566                                                     true,   // skip (final) local propagation step
567                                                     // of live analysis
568                                                     false,  // don't store information at handlers
569                                                     false); // dont skip guards
570    
571        live.perform(ir);
572        // 3. initialization
573        VariableStacks s = new VariableStacks();
574        // 4. convert phi nodes into copies
575        BasicBlock b = ((DominatorTreeNode) dom.getRoot()).getBlock();
576        insertCopies(b, dom, live);
577        // 5. If necessary, recompute dominators to account for new control flow.
578        if (splitSomeBlock) {
579          LTDominators.perform(ir, true, true);
580          dom = new DominatorTree(ir, true);
581        }
582        // 6. compensate for copies required by simulataneous liveness
583        performRename(b, dom, s);
584        // 7. phis are now redundant
585        removeAllPhis(ir);
586      }
587    
588      /**
589       * Remove all phi instructions from the IR.
590       *
591       * @param ir the governing IR
592       */
593      static void removeAllPhis(IR ir) {
594        for (Instruction s = ir.firstInstructionInCodeOrder(),
595            sentinel = ir.lastInstructionInCodeOrder(),
596            nextInstr = null; s != sentinel; s = nextInstr) {
597          // cache because remove nulls next/prev fields
598          nextInstr = s.nextInstructionInCodeOrder();
599          if (Phi.conforms(s)) s.remove();
600        }
601      }
602    
603      /**
604       * Special treatment for guard registers:
605       * Remove guard-phis by evaluating operands into same register.
606       * If this target register is not unique, unite the alternatives.
607       */
608      private void unSSAGuards(IR ir) {
609        // 0. initialization
610        unSSAGuardsInit(ir);
611        // 1. Determine target registers
612        unSSAGuardsDetermineReg(ir);
613        // 2. Rename targets and remove Phis
614        unSSAGuardsFinalize(ir);
615      }
616    
617      Instruction guardPhis = null;
618    
619      /**
620       * Initialization for removal of guard phis.
621       */
622      private void unSSAGuardsInit(IR ir) {
623        guardPhis = null;
624        InstructionEnumeration e = ir.forwardInstrEnumerator();
625    
626        // visit all instructions, looking for guard phis
627    
628        while (e.hasMoreElements()) {
629          Instruction inst = e.next();
630          if (!Phi.conforms(inst)) continue;
631          Operand res = Phi.getResult(inst);
632          if (!(res instanceof RegisterOperand)) continue;
633          Register r = res.asRegister().getRegister();
634          if (!r.isValidation()) continue;
635    
636          // force all operands of Phis into registers.
637    
638          inst.scratchObject = guardPhis;
639          guardPhis = inst;
640    
641          int values = Phi.getNumberOfValues(inst);
642          for (int i = 0; i < values; ++i) {
643            Operand op = Phi.getValue(inst, i);
644            if (!(op instanceof RegisterOperand)) {
645              if (op instanceof TrueGuardOperand) {
646                BasicBlock bb = Phi.getPred(inst, i).block;
647                Instruction move = Move.create(GUARD_MOVE, res.asRegister().copyD2D(), new TrueGuardOperand());
648                move.position = ir.gc.inlineSequence;
649                move.bcIndex = SSA_SYNTH_BCI;
650                bb.appendInstructionRespectingTerminalBranchOrPEI(move);
651              } else if (op instanceof UnreachableOperand) {
652                // do nothing
653              } else {
654                if (VM.VerifyAssertions) VM._assert(false);
655              }
656            }
657          }
658        }
659    
660        // visit all guard registers, init union/find
661        for (Register r = ir.regpool.getFirstSymbolicRegister(); r != null; r = r.getNext()) {
662          if (!r.isValidation()) continue;
663          r.scratch = 1;
664          r.scratchObject = r;
665        }
666      }
667    
668      /**
669       * Determine target register for guard phi operands
670       */
671      private void unSSAGuardsDetermineReg(IR ir) {
672        Instruction inst = guardPhis;
673        while (inst != null) {
674          Register r = Phi.getResult(inst).asRegister().getRegister();
675          int values = Phi.getNumberOfValues(inst);
676          for (int i = 0; i < values; ++i) {
677            Operand op = Phi.getValue(inst, i);
678            if (op instanceof RegisterOperand) {
679              guardUnion(op.asRegister().getRegister(), r);
680            } else {
681              if (VM.VerifyAssertions) {
682                VM._assert(op instanceof TrueGuardOperand || op instanceof UnreachableOperand);
683              }
684            }
685          }
686          inst = (Instruction) inst.scratchObject;
687        }
688      }
689    
690      /**
691       * Rename registers and delete Phis.
692       */
693      private void unSSAGuardsFinalize(IR ir) {
694        DefUse.computeDU(ir);
695        for (Register r = ir.regpool.getFirstSymbolicRegister(); r != null; r = r.getNext()) {
696          if (!r.isValidation()) continue;
697          Register nreg = guardFind(r);
698          RegisterOperandEnumeration uses = DefUse.uses(r);
699          while (uses.hasMoreElements()) {
700            RegisterOperand use = uses.next();
701            use.setRegister(nreg);
702          }
703          RegisterOperandEnumeration defs = DefUse.defs(r);
704          while (defs.hasMoreElements()) {
705            RegisterOperand def = defs.next();
706            def.setRegister(nreg);
707          }
708        }
709        Instruction inst = guardPhis;
710        while (inst != null) {
711          inst.remove();
712          inst = (Instruction) inst.scratchObject;
713        }
714      }
715    
716      /**
717       * union step of union/find for guard registers during unSSA
718       */
719      private Register guardUnion(Register from, Register to) {
720        Register a = guardFind(from);
721        Register b = guardFind(to);
722        if (a == b) return a;
723        if (a.scratch == b.scratch) {
724          a.scratch++;
725          b.scratchObject = a;
726          return a;
727        }
728        if (a.scratch > b.scratch) {
729          b.scratchObject = a;
730          return a;
731        }
732        a.scratchObject = b;
733        return b;
734      }
735    
736      /**
737       * find step of union/find for guard registers during unSSA
738       */
739      private Register guardFind(Register r) {
740        Register start = r;
741        if (VM.VerifyAssertions) VM._assert(r.scratchObject != null);
742        while (r.scratchObject != r) r = (Register) r.scratchObject;
743        while (start.scratchObject != r) {
744          start.scratchObject = r;
745          start = (Register) start.scratchObject;
746        }
747        return r;
748      }
749    
750      /**
751       * Avoid potential lost copy and other associated problems by
752       * Sreedhar's naive translation from TSSA to CSSA. Guards are rather
753       * trivial to un-SSA so they have already been removed from the IR.
754       * This algorithm is very wasteful of registers so needs good
755       * coalescing.
756       * @param ir the IR to work upon
757       */
758      @SuppressWarnings("unused") // NB this was an aborted attempt to fix a bug in leave SSA
759      private static void normalizeSSA(IR ir) {
760        for (Instruction s = ir.firstInstructionInCodeOrder(),
761            sentinel = ir.lastInstructionInCodeOrder(),
762            nextInstr = null; s != sentinel; s = nextInstr) {
763          // cache so we don't process inserted instructions
764          nextInstr = s.nextInstructionInCodeOrder();
765          if (Phi.conforms(s) && !s.getBasicBlock().isExceptionHandlerBasicBlock()) {
766            // We ignore exception handler BBs as they cause problems when inserting copies
767            if (DEBUG) VM.sysWriteln("Processing " + s + " of basic block " + s.getBasicBlock());
768            // Does the phi instruction have an unreachable operand?
769            boolean hasUnreachable = false;
770            // 1. Naively copy source operands into predecessor blocks
771            for (int index = 0; index < Phi.getNumberOfPreds(s); index++) {
772              Operand op = Phi.getValue(s, index);
773              if (op.isRegister()) {
774                // Get rval
775                Register rval = op.asRegister().getRegister();
776                if (rval.isValidation()) {
777                  continue; // ignore guards
778                } else {
779                  // Make rval'
780                  Register rvalPrime = ir.regpool.getReg(rval);
781                  // Make copy instruction
782                  Instruction copy = SSA.makeMoveInstruction(ir, rvalPrime, rval, op.getType());
783                  // Insert a copy of rval to rval' in predBlock
784                  BasicBlock pred = Phi.getPred(s, index).block;
785                  pred.appendInstructionRespectingTerminalBranch(copy);
786                  if (DEBUG) VM.sysWriteln("Inserted rval copy of " + copy + " into basic block " + pred);
787                  // Rename rval to rval' in phi instruction
788                  op.asRegister().setRegister(rvalPrime);
789                }
790              } else if (op instanceof UnreachableOperand) {
791                hasUnreachable = true;
792              }
793            }
794            // 2. Naively copy the result if there were no unreachable operands
795            if (!hasUnreachable) {
796              Operand op = Phi.getResult(s);
797              if (!op.isRegister()) {
798                // ignore heap operands
799              } else {
800                // Get lval
801                Register lval = op.asRegister().getRegister();
802                // Make lval'
803                Register lvalPrime = ir.regpool.getReg(lval);
804                // Make copy instruction
805                Instruction copy = SSA.makeMoveInstruction(ir, lval, lvalPrime, op.getType());
806                // Insert a copy of lval' to lval after phi instruction
807                s.insertAfter(copy);
808                // Rename lval to lval' in phi instruction
809                op.asRegister().setRegister(lvalPrime);
810                if (DEBUG) VM.sysWriteln("Inserted lval copy of " + copy + " after " + s);
811              }
812            }
813          }
814        }
815      }
816    }