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.regalloc.ia32;
014    
015    import java.util.Enumeration;
016    
017    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
018    import org.jikesrvm.compilers.opt.ir.BasicBlock;
019    import org.jikesrvm.compilers.opt.ir.IR;
020    import org.jikesrvm.compilers.opt.ir.Instruction;
021    import org.jikesrvm.compilers.opt.ir.MIR_LowTableSwitch;
022    import org.jikesrvm.compilers.opt.ir.Operators;
023    import org.jikesrvm.compilers.opt.ir.Register;
024    import org.jikesrvm.compilers.opt.ir.ia32.PhysicalRegisterTools;
025    import org.jikesrvm.compilers.opt.ir.operand.Operand;
026    import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
027    
028    /**
029     * This class splits live ranges for certain special cases to ensure
030     * correctness during IA32 register allocation.
031     */
032    public class MIRSplitRanges extends CompilerPhase implements Operators {
033    
034      /**
035       * Return this instance of this phase. This phase contains no
036       * per-compilation instance fields.
037       * @param ir not used
038       * @return this
039       */
040      @Override
041      public CompilerPhase newExecution(IR ir) {
042        return this;
043      }
044    
045      /**
046       * Return the name of this phase
047       * @return "Live Range Splitting"
048       */
049      @Override
050      public final String getName() {
051        return "MIR Range Splitting";
052      }
053    
054      /**
055       * The main method.<p>
056       *
057       * We split live ranges for registers around PEIs which have catch
058       * blocks.  Suppose we have a
059       * PEI s which uses a symbolic register r1.  We must ensure that after
060       * register allocation, r1 is NOT assigned to a scratch location in s,
061       * since this would mess up code in the catch block that uses r1.<p>
062       *
063       * So, instead, we introduce a new temporary r2 which holds the value of
064       * r1.  The live range for r2 spans only the instruction s.  Later, we
065       * will ensure that r2 is never spilled.<p>
066       *
067       * TODO: This could be implemented more efficiently.
068       *
069       * @param ir the governing IR
070       */
071      @Override
072      public final void perform(IR ir) {
073    
074        java.util.HashMap<Register, Register> newMap = new java.util.HashMap<Register, Register>(5);
075    
076        for (Enumeration<BasicBlock> be = ir.getBasicBlocks(); be.hasMoreElements();) {
077          BasicBlock bb = be.nextElement();
078          for (Enumeration<Instruction> ie = bb.forwardInstrEnumerator(); ie.hasMoreElements();) {
079            Instruction s = ie.nextElement();;
080    
081            // clear the cache of register assignments
082            newMap.clear();
083    
084            // Split live ranges at PEIs and a few special cases to
085            // make sure we can pin values that must be in registers.
086            // NOTE: Any operator that is an IA32 special case that must have
087            //       a particular operand in a register must be mentioned both
088            //       here and in RegisterRestrictions!
089            if (s.isPEI() && s.operator != IR_PROLOGUE) {
090              if (bb.hasApplicableExceptionalOut(s) || !RegisterRestrictions.SCRATCH_IN_PEI) {
091                splitAllLiveRanges(s, newMap, ir, false);
092              }
093            }
094    
095            // handle special cases for IA32
096            //  (1) Some operands must be in registers
097            switch (s.getOpcode()) {
098              case MIR_LOWTABLESWITCH_opcode: {
099                RegisterOperand rOp = MIR_LowTableSwitch.getIndex(s);
100                RegisterOperand temp = findOrCreateTemp(rOp, newMap, ir);
101                // NOTE: Index as marked as a DU because LowTableSwitch is
102                //       going to destroy the value in the register.
103                //       By construction (see ConvertToLowLevelIR), no one will
104                //       ever read the value computed by a LowTableSwitch.
105                //       Therefore, don't insert a move instruction after the
106                //       LowTableSwitch (which would cause IR verification
107                //       problems anyways, since LowTableSwitch is a branch).
108                insertMoveBefore(temp, rOp.copyRO(), s); // move r into 'temp' before s
109                rOp.setRegister(temp.getRegister());
110              }
111              break;
112            }
113          }
114        }
115      }
116    
117      /**
118       * Split the live ranges of all register operands of an instruction
119       * @param s      the instruction to process
120       * @param newMap a mapping from symbolics to temporaries
121       * @param ir  the containing IR
122       * @param rootOnly only consider root operands?
123       */
124      private static void splitAllLiveRanges(Instruction s, java.util.HashMap<Register, Register> newMap,
125                                             IR ir, boolean rootOnly) {
126        // walk over each USE
127        for (Enumeration<Operand> u = rootOnly ? s.getRootUses() : s.getUses(); u.hasMoreElements();) {
128          Operand use = u.nextElement();
129          if (use.isRegister()) {
130            RegisterOperand rUse = use.asRegister();
131            RegisterOperand temp = findOrCreateTemp(rUse, newMap, ir);
132            // move 'use' into 'temp' before s
133            insertMoveBefore(temp, rUse.copyRO(), s);
134          }
135        }
136        // walk over each DEF (by defintion defs == root defs)
137        for (Enumeration<Operand> d = s.getDefs(); d.hasMoreElements();) {
138          Operand def = d.nextElement();
139          if (def.isRegister()) {
140            RegisterOperand rDef = def.asRegister();
141            RegisterOperand temp = findOrCreateTemp(rDef, newMap, ir);
142            // move 'temp' into 'r' after s
143            insertMoveAfter(rDef.copyRO(), temp, s);
144          }
145        }
146        // Now go back and replace the registers.
147        for (Enumeration<Operand> ops = rootOnly ? s.getRootOperands() : s.getOperands(); ops.hasMoreElements();) {
148          Operand op = ops.nextElement();
149          if (op.isRegister()) {
150            RegisterOperand rOp = op.asRegister();
151            Register r = rOp.getRegister();
152            Register newR = newMap.get(r);
153            if (newR != null) {
154              rOp.setRegister(newR);
155            }
156          }
157        }
158      }
159    
160      /**
161       * Find or create a temporary register to cache a symbolic register.
162       *
163       * @param rOp the symbolic register
164       * @param map a mapping from symbolics to temporaries
165       * @param ir the governing IR
166       */
167      private static RegisterOperand findOrCreateTemp(RegisterOperand rOp,
168                                                          java.util.HashMap<Register, Register> map, IR ir) {
169        Register tReg = map.get(rOp.getRegister());
170        if (tReg == null) {
171          RegisterOperand tOp = ir.regpool.makeTemp(rOp.getType());
172          map.put(rOp.getRegister(), tOp.getRegister());
173          return tOp;
174        } else {
175          return new RegisterOperand(tReg, rOp.getType());
176        }
177      }
178    
179      /**
180       * Insert an instruction to move r1 into r2 before instruction s
181       */
182      private static void insertMoveBefore(RegisterOperand r2, RegisterOperand r1, Instruction s) {
183        Instruction m = PhysicalRegisterTools.makeMoveInstruction(r2, r1);
184        s.insertBefore(m);
185      }
186    
187      /**
188       * Insert an instruction to move r1 into r2 after instruction s
189       */
190      private static void insertMoveAfter(RegisterOperand r2, RegisterOperand r1, Instruction s) {
191        Instruction m = PhysicalRegisterTools.makeMoveInstruction(r2, r1);
192        s.insertAfter(m);
193      }
194    }