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 */
013package org.jikesrvm.compilers.opt.ssa;
014
015import static org.jikesrvm.compilers.opt.ir.Operators.PI;
016
017import java.lang.reflect.Constructor;
018import java.util.Enumeration;
019
020import org.jikesrvm.VM;
021import org.jikesrvm.compilers.opt.OptOptions;
022import org.jikesrvm.compilers.opt.OptimizingCompilerException;
023import org.jikesrvm.compilers.opt.driver.CompilerPhase;
024import org.jikesrvm.compilers.opt.ir.BasicBlock;
025import org.jikesrvm.compilers.opt.ir.BoundsCheck;
026import org.jikesrvm.compilers.opt.ir.GuardedUnary;
027import org.jikesrvm.compilers.opt.ir.IR;
028import org.jikesrvm.compilers.opt.ir.IRTools;
029import org.jikesrvm.compilers.opt.ir.IfCmp;
030import org.jikesrvm.compilers.opt.ir.InlineGuard;
031import org.jikesrvm.compilers.opt.ir.Instruction;
032import org.jikesrvm.compilers.opt.ir.Move;
033import org.jikesrvm.compilers.opt.ir.NullCheck;
034import org.jikesrvm.compilers.opt.ir.Operator;
035import org.jikesrvm.compilers.opt.ir.TypeCheck;
036import org.jikesrvm.compilers.opt.ir.operand.Operand;
037import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
038
039/**
040 * This pass inserts PI nodes (Effectively copies)
041 * on branch edges, to introduce new names for analysis
042 */
043public final class PiNodes extends CompilerPhase {
044
045  /**
046   * Should we insert PI nodes for array references after bounds-checks
047   * and null-checks?
048   * <p>TODO: if this is false, then null-check elimination
049   * will be ineffective.
050   * <p>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   */
071  @Override
072  public boolean shouldPerform(OptOptions options) {
073    return 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  @Override
087  public Constructor<CompilerPhase> getClassConstructor() {
088    return constructor;
089  }
090
091  /**
092   * A String representation of this phase
093   * @return a string representation
094   */
095  @Override
096  public String getName() {
097    return "Pi Nodes " + insertion;
098  }
099
100  /**
101   * Should we print the IR either before or after this phase?
102   * @param options controlling compiler options
103   * @param before control for the query
104   */
105  @Override
106  public boolean printingEnabled(OptOptions options, boolean before) {
107    return false;
108  }
109
110  /**
111   * Create the phase.
112   *
113   * @param insert If true, we insert PI nodes,  If false, we remove them.
114   */
115  public PiNodes(boolean insert) {
116    this.insertion = insert;
117    this.typeChecks = false;
118  }
119
120  /**
121   * Create the phase.
122   *
123   * @param insert If true, we insert PI nodes,  If false, we remove them.
124   * @param typeChecks If true, we insert PI nodes only for type checks.
125   */
126  public PiNodes(boolean insert, boolean typeChecks) {
127    super(new Object[]{insert, typeChecks});
128    this.insertion = insert;
129    this.typeChecks = typeChecks;
130  }
131
132  @Override
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    Enumeration<Instruction> e = ir.forwardInstrEnumerator();
158    while (e.hasMoreElements()) {
159      Instruction instr = e.nextElement();
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        Enumeration<BasicBlock> outBB = thisbb.getNormalOut();
170        BasicBlock out1 = outBB.nextElement();
171        BasicBlock new1 = IRTools.makeBlockOnEdge(thisbb, out1, ir);
172        BasicBlock out2 = outBB.nextElement();
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 (Enumeration<Instruction> e = ir.forwardInstrEnumerator(); e.hasMoreElements();) {
366      Instruction s = e.nextElement();
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   * TODO Convert to JavaDoc and add missing tags.
380   * <p>
381   * Get the instruction a Pi node is linked to.
382   * <strong>PRECONDITION: </strong> register lists computed and valid.
383   */
384  public static Instruction getGenerator(Instruction def) {
385    if (def.operator() != PI) {
386      throw new OptimizingCompilerException("Not a PI Node!");
387    }
388    Operand g = GuardedUnary.getGuard(def);
389    Instruction link = g.asRegister().getRegister().defList.instruction;
390    return link;
391  }
392
393  /*
394   * TODO Convert to JavaDoc and add missing tags.
395   * <p>
396   * Is an instruction a Pi node linked to the <em>not taken</em> edge of
397   * a conditional branch instruction?
398   */
399  public static boolean isNotTakenPi(Instruction def) {
400    if (def.operator() != PI) {
401      return false;
402    }
403    Operand g = GuardedUnary.getGuard(def);
404    return g.asRegister().isNotTaken();
405  }
406
407  /*
408   * TODO Convert to JavaDoc and add missing tags.
409   * <p>
410   * Is an instruction a Pi node linked to the <em>taken</em> edge of
411   * a conditional branch instruction?
412   */
413  public static boolean isTakenPi(Instruction def) {
414    if (def.operator() != PI) {
415      return false;
416    }
417    Operand g = GuardedUnary.getGuard(def);
418    return g.asRegister().isTaken();
419  }
420
421  /*
422   * TODO Convert to JavaDoc and add missing tags.
423   * <p>
424   * Is an instruction a Pi node linked to a bounds-check?
425   */
426  public static boolean isBoundsCheckPi(Instruction def) {
427    if (def.operator() != PI) {
428      return false;
429    }
430    Operand g = GuardedUnary.getGuard(def);
431    return g.asRegister().isBoundsCheck();
432  }
433
434  /*
435   * TODO Convert to JavaDoc and add missing tags.
436   * <p>
437   * Is an instruction a Pi node linked to a null-check?
438   */
439  public static boolean isNullCheckPi(Instruction def) {
440    if (def.operator() != PI) {
441      return false;
442    }
443    Operand g = GuardedUnary.getGuard(def);
444    return g.asRegister().isNullCheck();
445  }
446}