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.ir.Operators.PI;
016    
017    import java.lang.reflect.Constructor;
018    
019    import org.jikesrvm.VM;
020    import org.jikesrvm.compilers.opt.OptOptions;
021    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
022    import org.jikesrvm.compilers.opt.driver.CompilerPhase;
023    import org.jikesrvm.compilers.opt.ir.BasicBlock;
024    import org.jikesrvm.compilers.opt.ir.BasicBlockEnumeration;
025    import org.jikesrvm.compilers.opt.ir.BoundsCheck;
026    import org.jikesrvm.compilers.opt.ir.GuardedUnary;
027    import org.jikesrvm.compilers.opt.ir.IR;
028    import org.jikesrvm.compilers.opt.ir.IRTools;
029    import org.jikesrvm.compilers.opt.ir.IfCmp;
030    import org.jikesrvm.compilers.opt.ir.InlineGuard;
031    import org.jikesrvm.compilers.opt.ir.Instruction;
032    import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
033    import org.jikesrvm.compilers.opt.ir.Move;
034    import org.jikesrvm.compilers.opt.ir.NullCheck;
035    import org.jikesrvm.compilers.opt.ir.Operator;
036    import org.jikesrvm.compilers.opt.ir.TypeCheck;
037    import org.jikesrvm.compilers.opt.ir.operand.Operand;
038    import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
039    
040    /**
041     *
042     * This pass inserts PI nodes (Effectively copies)
043     * on branch edges, to introduce new names for analysis
044     */
045    public final class PiNodes extends CompilerPhase {
046    
047      /**
048       * Should we insert PI nodes for array references after bounds-checks
049       * and null-checks?  TODO: if this is false, then null-check elimination
050       * will be ineffective.  TODO: prove that null-check elimination is
051       * sound before turning this on again.
052       */
053      static final boolean CHECK_REF_PI = false;
054    
055      /**
056       * Should we insert (true) or delete (false) PI nodes?
057       */
058      final boolean insertion;
059    
060      /**
061       * Are we adding pi nodes for type checks only?  This is for GNOSYS
062       * analysis right now.
063       */
064      final boolean typeChecks;
065    
066      /**
067       * Should this phase be performed?
068       * Only perform this when we are doing an SSA-based optimization
069       * that can benefit from PI nodes.
070       * @param options controlling compiler options
071       */
072      public boolean shouldPerform(OptOptions options) {
073        return options.SSA_GLOBAL_BOUNDS_CHECK || typeChecks;
074      }
075    
076      /**
077       * Constructor for this compiler phase
078       */
079      private static final Constructor<CompilerPhase> constructor =
080          getCompilerPhaseConstructor(PiNodes.class, new Class[]{Boolean.TYPE, Boolean.TYPE});
081    
082      /**
083       * Get a constructor object for this compiler phase
084       * @return compiler phase constructor
085       */
086      public Constructor<CompilerPhase> getClassConstructor() {
087        return constructor;
088      }
089    
090      /**
091       * A String representation of this phase
092       * @return a string representation
093       */
094      public String getName() {
095        return "Pi Nodes " + insertion;
096      }
097    
098      /**
099       * Should we print the IR either before or after this phase?
100       * @param options controlling compiler options
101       * @param before control for the query
102       */
103      public boolean printingEnabled(OptOptions options, boolean before) {
104        return false;
105      }
106    
107      /**
108       * Create the phase.
109       *
110       * @param insert If true, we insert PI nodes,  If false, we remove them.
111       */
112      public PiNodes(boolean insert) {
113        this.insertion = insert;
114        this.typeChecks = false;
115      }
116    
117      /**
118       * Create the phase.
119       *
120       * @param insert If true, we insert PI nodes,  If false, we remove them.
121       * @param typeChecks If true, we insert PI nodes only for type checks.
122       */
123      public PiNodes(boolean insert, boolean typeChecks) {
124        super(new Object[]{insert, typeChecks});
125        this.insertion = insert;
126        this.typeChecks = typeChecks;
127      }
128    
129      /**
130       * Perform the transformation.
131       * @param ir the IR to optimize
132       */
133      public void perform(IR ir) {
134        if (insertion) {
135          if (!typeChecks) {
136            insertPiIfNodes(ir);
137            insertPiBcNodes(ir);
138            insertPiNullCheckNodes(ir);
139          } else {
140            insertPiCheckCastNodes(ir);
141          }
142          // invalidate SSA state
143          ir.actualSSAOptions = null;
144        } else {
145          cleanUp(ir);
146        }
147      }
148    
149      /**
150       *  Insert PI nodes corresponding to compare operations.
151       *  Pi-nodes are represented as dummy assignments with a single
152       *  argument inserted along each outedge of the conditional.
153       *
154       *  @param ir the governing IR
155       */
156      private void insertPiIfNodes(IR ir) {
157        InstructionEnumeration e = ir.forwardInstrEnumerator();
158        while(e.hasMoreElements()) {
159          Instruction instr = e.next();
160          // TODO: what other compareops generate useful assertions?
161          if (IfCmp.conforms(instr) || InlineGuard.conforms(instr)) {
162    
163            BasicBlock thisbb = instr.getBasicBlock();
164            // only handle the "normal" case
165            if (thisbb.getNumberOfNormalOut() != 2) {
166              continue;
167            }
168            // insert new basic blocks on each edge out of thisbb
169            BasicBlockEnumeration outBB = thisbb.getNormalOut();
170            BasicBlock out1 = outBB.next();
171            BasicBlock new1 = IRTools.makeBlockOnEdge(thisbb, out1, ir);
172            BasicBlock out2 = outBB.next();
173            BasicBlock new2 = IRTools.makeBlockOnEdge(thisbb, out2, ir);
174    
175            // For these types of IfCmp's, the Pi Node is not actually
176            // needed yet.  For now the only functionality needed is the
177            // blocks made on the outgoing edges.
178            if (InlineGuard.conforms(instr)) continue;
179    
180            RegisterOperand ifGuard = IfCmp.getGuardResult(instr);
181    
182            if (VM.VerifyAssertions) {
183              VM._assert(ifGuard != null);
184            }
185            // get compared variables
186            Operand a = IfCmp.getVal1(instr);
187            Operand b = IfCmp.getVal2(instr);
188            // determine which block is "taken" on the branch
189            BasicBlock takenBlock = IfCmp.getTarget(instr).target.getBasicBlock();
190            boolean new1IsTaken = false;
191            if (takenBlock == new1) {
192              new1IsTaken = true;
193            }
194    
195            // insert the PI-node instructions for a and b
196            if (a.isRegister() &&
197                !a.asRegister().getRegister().isPhysical() &&
198                (a.asRegister().getRegister().isInteger() || a.asRegister().getRegister().isAddress())) {
199              // insert pi-nodes only for variables, not constants
200              Instruction s = GuardedUnary.create(PI, (RegisterOperand) a.copy(), a.copy(), null);
201              RegisterOperand sGuard = (RegisterOperand) ifGuard.copy();
202              if (new1IsTaken) {
203                sGuard.setTaken();
204              } else {
205                sGuard.setNotTaken();
206              }
207              GuardedUnary.setGuard(s, sGuard);
208              new1.prependInstruction(s);
209              s = s.copyWithoutLinks();
210              sGuard = (RegisterOperand) ifGuard.copy();
211              if (new1IsTaken) {
212                sGuard.setNotTaken();
213              } else {
214                sGuard.setTaken();
215              }
216              GuardedUnary.setGuard(s, sGuard);
217              new2.prependInstruction(s);
218            }
219            if (b.isRegister() &&
220                !b.asRegister().getRegister().isPhysical() &&
221                (b.asRegister().getRegister().isInteger() || b.asRegister().getRegister().isAddress())) {
222              Instruction s = GuardedUnary.create(PI, (RegisterOperand) b.copy(), b.copy(), null);
223              RegisterOperand sGuard = (RegisterOperand) ifGuard.copy();
224              if (new1IsTaken) {
225                sGuard.setTaken();
226              } else {
227                sGuard.setNotTaken();
228              }
229              GuardedUnary.setGuard(s, sGuard);
230              new1.prependInstruction(s);
231              s = s.copyWithoutLinks();
232              sGuard = (RegisterOperand) ifGuard.copy();
233              if (new1IsTaken) {
234                sGuard.setNotTaken();
235              } else {
236                sGuard.setTaken();
237              }
238              GuardedUnary.setGuard(s, sGuard);
239              new2.prependInstruction(s);
240            }
241          }
242        }
243      }
244    
245      /**
246       * Insert Pi nodes for boundchecks.
247       *
248       * <p>Each boundcheck Arr, Index will be followed by
249       * <pre> PI Index, Index </pre>
250       *
251       * @param ir the governing IR
252       */
253      private void insertPiBcNodes(IR ir) {
254        Instruction nextInst = null;
255        // for each instruction in the IR
256        for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) {
257          // can't use iterator, since we modify instruction stream
258          nextInst = instr.nextInstructionInCodeOrder();
259          if (BoundsCheck.conforms(instr)) {
260            // Create a pi node for the index.
261            Operand index = BoundsCheck.getIndex(instr);
262            // create the instruction and insert it
263            if (index.isRegister() && !index.asRegister().getRegister().isPhysical()) {
264              Instruction s = GuardedUnary.create(PI, (RegisterOperand) index.copy(), index.copy(), null);
265              RegisterOperand sGuard = (RegisterOperand) BoundsCheck.getGuardResult(instr).copy();
266              sGuard.setBoundsCheck();
267              GuardedUnary.setGuard(s, sGuard);
268              instr.insertAfter(s);
269            }
270            if (CHECK_REF_PI) {
271              // Create a pi node for the array.
272              Operand array = BoundsCheck.getRef(instr);
273              // create the instruction and insert it
274              if (array.isRegister() && !array.asRegister().getRegister().isPhysical()) {
275                Instruction s = GuardedUnary.create(PI, (RegisterOperand) array.copy(), array.copy(), null);
276                RegisterOperand sGuard = (RegisterOperand) BoundsCheck.getGuardResult(instr).copy();
277                sGuard.setBoundsCheck();
278                GuardedUnary.setGuard(s, sGuard);
279                instr.insertAfter(s);
280              }
281            }
282          }
283        }
284      }
285    
286      /**
287       * Insert Pi nodes for null check operations.
288       *
289       * <p>Each checkcast obj will be followed by
290       * <pre> PI obj, obj </pre>
291       *
292       * @param ir the governing IR
293       */
294      private void insertPiNullCheckNodes(IR ir) {
295        if (!CHECK_REF_PI) return;
296        Instruction nextInst = null;
297        // for each instruction in the IR
298        for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) {
299          // can't use iterator, since we modify instruction stream
300          nextInst = instr.nextInstructionInCodeOrder();
301          if (NullCheck.conforms(instr)) {
302            // get compared variables
303            Operand obj = NullCheck.getRef(instr);
304            // create the instruction and insert it
305            if (obj.isRegister()) {
306              RegisterOperand lval = (RegisterOperand) obj.copy();
307              Instruction s = GuardedUnary.create(PI, lval, obj.copy(), null);
308              RegisterOperand sGuard = (RegisterOperand) NullCheck.getGuardResult(instr).copy();
309              sGuard.setNullCheck();
310              GuardedUnary.setGuard(s, sGuard);
311              instr.insertAfter(s);
312            }
313          }
314        }
315      }
316    
317      /**
318       * Insert Pi nodes for checkcast operations.
319       *
320       * <p>Each checkcast obj will be followed by
321       * <pre> ref_move obj, obj </pre>
322       *
323       * @param ir the governing IR
324       */
325      private void insertPiCheckCastNodes(IR ir) {
326        Instruction nextInst = null;
327        // for each instruction in the IR
328        for (Instruction instr = ir.firstInstructionInCodeOrder(); instr != null; instr = nextInst) {
329          // can't use iterator, since we modify instruction stream
330          nextInst = instr.nextInstructionInCodeOrder();
331          if (TypeCheck.conforms(instr)) {
332            // get compared variables
333            Operand obj = TypeCheck.getRef(instr);
334            // create the instruction and insert it
335            if (obj.isRegister()) {
336              RegisterOperand lval = (RegisterOperand) obj.copy();
337              lval.clearDeclaredType();
338              if (lval.getType().isLoaded() && lval.getType().isClassType() && lval.getType().peekType().asClass().isFinal()) {
339                lval.setPreciseType(TypeCheck.getType(instr).getTypeRef());
340              } else {
341                lval.clearPreciseType();
342                lval.setType(TypeCheck.getType(instr).getTypeRef());
343              }
344              Instruction s = GuardedUnary.create(PI, lval, obj.copy(), null);
345              s.position = instr.position;
346              s.bcIndex = instr.bcIndex;
347              Operand iGuard = TypeCheck.getGuard(instr);
348              if (iGuard != null) {
349                Operand sGuard = iGuard.copy();
350                GuardedUnary.setGuard(s, sGuard);
351              }
352              instr.insertAfter(s);
353            }
354          }
355        }
356      }
357    
358      /**
359       * Change all PI nodes to INT_MOVE instructions
360       * <p> Side effect: invalidates SSA state
361       *
362       * @param ir the governing IR
363       */
364      static void cleanUp(IR ir) {
365        for (InstructionEnumeration e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
366          Instruction s = e.next();
367          if (s.operator == PI) {
368            RegisterOperand result = GuardedUnary.getResult(s);
369            Operator mv = IRTools.getMoveOp(result.getType());
370            Operand val = GuardedUnary.getVal(s);
371            Move.mutate(s, mv, result, val);
372          }
373        }
374        // invalidate SSA state
375        ir.actualSSAOptions = null;
376      }
377    
378      /**
379       * Get the instruction a Pi node is linked to.
380       * <strong>PRECONDITION: </strong> register lists computed and valid.
381       */
382      public static Instruction getGenerator(Instruction def) {
383        if (def.operator != PI) {
384          throw new OptimizingCompilerException("Not a PI Node!");
385        }
386        Operand g = GuardedUnary.getGuard(def);
387        Instruction link = g.asRegister().getRegister().defList.instruction;
388        return link;
389      }
390    
391      /**
392       * Is an instruction a Pi node linked to the <em>not taken</em> edge of
393       * a conditional branch instruction?
394       */
395      public static boolean isNotTakenPi(Instruction def) {
396        if (def.operator != PI) {
397          return false;
398        }
399        Operand g = GuardedUnary.getGuard(def);
400        return g.asRegister().isNotTaken();
401      }
402    
403      /**
404       * Is an instruction a Pi node linked to the <em>taken</em> edge of
405       * a conditional branch instruction?
406       */
407      public static boolean isTakenPi(Instruction def) {
408        if (def.operator != PI) {
409          return false;
410        }
411        Operand g = GuardedUnary.getGuard(def);
412        return g.asRegister().isTaken();
413      }
414    
415      /**
416       * Is an instruction a Pi node linked to a bounds-check?
417       */
418      public static boolean isBoundsCheckPi(Instruction def) {
419        if (def.operator != PI) {
420          return false;
421        }
422        Operand g = GuardedUnary.getGuard(def);
423        return g.asRegister().isBoundsCheck();
424      }
425    
426      /**
427       * Is an instruction a Pi node linked to a null-check?
428       */
429      public static boolean isNullCheckPi(Instruction def) {
430        if (def.operator != PI) {
431          return false;
432        }
433        Operand g = GuardedUnary.getGuard(def);
434        return g.asRegister().isNullCheck();
435      }
436    }