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.bc2ir;
014
015import static org.jikesrvm.classloader.ClassLoaderConstants.VoidTypeCode;
016import static org.jikesrvm.compilers.opt.ir.Operators.OSR_BARRIER_opcode;
017import static org.jikesrvm.compilers.opt.ir.Operators.OSR_BARRIER;
018
019import java.lang.reflect.Constructor;
020import java.util.Collection;
021import java.util.Enumeration;
022import java.util.LinkedList;
023
024import org.jikesrvm.VM;
025import org.jikesrvm.classloader.NormalMethod;
026import org.jikesrvm.compilers.opt.OptOptions;
027import org.jikesrvm.compilers.opt.controlflow.BranchOptimizations;
028import org.jikesrvm.compilers.opt.driver.CompilerPhase;
029import org.jikesrvm.compilers.opt.ir.BasicBlock;
030import org.jikesrvm.compilers.opt.ir.IR;
031import org.jikesrvm.compilers.opt.ir.Instruction;
032import org.jikesrvm.compilers.opt.ir.OsrBarrier;
033import org.jikesrvm.compilers.opt.ir.OsrPoint;
034import org.jikesrvm.compilers.opt.ir.operand.InlinedOsrTypeInfoOperand;
035import org.jikesrvm.compilers.opt.ir.operand.Operand;
036import org.jikesrvm.compilers.opt.ir.operand.OsrTypeInfoOperand;
037
038/**
039 * A phase in the OPT compiler for construction OsrPoint instructions
040 * after inlining.
041 */
042public class OsrPointConstructor extends CompilerPhase {
043
044  @Override
045  public final boolean shouldPerform(OptOptions options) {
046    return VM.runningVM && options.OSR_GUARDED_INLINING;
047  }
048
049  @Override
050  public final String getName() {
051    return "OsrPointConstructor";
052  }
053
054  @Override
055  public Constructor<CompilerPhase> getClassConstructor() {
056    return constructor;
057  }
058
059  private static final Constructor<CompilerPhase> constructor =
060      getCompilerPhaseConstructor(OsrPointConstructor.class);
061
062  /**
063   * Need to run branch optimizations after
064   */
065  private final BranchOptimizations branchOpts;
066
067  private Collection<Instruction> osrBarriers;
068
069  private LinkedList<Instruction> osrPoints;
070
071  /**
072   * Constructor
073   */
074  public OsrPointConstructor() {
075    branchOpts = new BranchOptimizations(-1, false, false);
076  }
077
078  /**
079   * Goes through each instruction, reconstruct OsrPoint instructions.
080   */
081  @Override
082  public void perform(IR ir) {
083    // 1. collecting OsrPoint and OsrBarrier instructions
084    collectOsrPointsAndBarriers(ir);
085
086    //    new IRPrinter("before renovating osrs").perform(ir);
087
088    // 2. trace OsrBarrier for each OsrPoint, and rebuild OsrPoint
089    renovateOsrPoints(ir);
090
091    //    new IRPrinter("before removing barriers").perform(ir);
092
093    // 3. remove OsrBarriers
094    removeOsrBarriers(ir);
095
096    //    new IRPrinter("after removing barriers").perform(ir);
097
098    // 4. reconstruct CFG, cut off pieces after OsrPoint.
099    fixupCFGForOsr(ir);
100/*
101        if (VM.TraceOnStackReplacement && (0 != osrs.size())) {
102      new IRPrinter("After OsrPointConstructor").perform(ir);
103    }
104*/
105/*
106    if (VM.TraceOnStackReplacement && (0 != osrs.size())) {
107      verifyNoOsrBarriers(ir);
108    }
109*/
110    branchOpts.perform(ir);
111  }
112
113  /**
114   * Iterates over all instructions in the IR and builds a list of
115   * OsrPoint instructions and OsrBarrier instructions.
116   *
117   * @param ir the IR
118   */
119  private void collectOsrPointsAndBarriers(IR ir) {
120    osrPoints = new LinkedList<Instruction>();
121    osrBarriers = new LinkedList<Instruction>();
122
123    Enumeration<Instruction> instenum = ir.forwardInstrEnumerator();
124    while (instenum.hasMoreElements()) {
125      Instruction inst = instenum.nextElement();
126
127      if (OsrPoint.conforms(inst)) {
128        osrPoints.add(inst);
129      } else if (inst.operator() == OSR_BARRIER) {
130        osrBarriers.add(inst);
131      }
132    }
133  }
134
135  /**
136   * For each OsrPoint instruction, traces its OsrBarriers created by
137   * inlining. rebuild OsrPoint instruction to hold all necessary
138   * information to recover from inlined activation.
139   * @param ir the IR
140   */
141  private void renovateOsrPoints(IR ir) {
142
143    for (int osrIdx = 0, osrSize = osrPoints.size(); osrIdx < osrSize; osrIdx++) {
144      Instruction osr = osrPoints.get(osrIdx);
145      LinkedList<Instruction> barriers = new LinkedList<Instruction>();
146
147      // Step 1: collect barriers put before inlined method
148      //         in the order of from inner to outer
149      {
150        GenerationContext gc = ir.gc;
151        Instruction bar = gc.getOSRBarrierFromInst(osr);
152
153        if (osr.position == null) osr.position = bar.position;
154
155        adjustBCIndex(osr);
156
157        while (bar != null) {
158
159          barriers.add(bar);
160
161          // verify each barrier is clean
162          if (VM.VerifyAssertions) {
163            if (!isBarrierClean(bar)) {
164              VM.sysWriteln("Barrier " + bar + " is not clean!");
165            }
166            VM._assert(isBarrierClean(bar));
167          }
168
169          Instruction callsite = bar.position.getCallSite();
170          if (callsite != null) {
171            bar = gc.getOSRBarrierFromInst(callsite);
172
173            if (bar == null) {
174              VM.sysWrite("call site :" + callsite);
175              if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
176            }
177
178            adjustBCIndex(bar);
179          } else {
180            bar = null;
181          }
182        }
183      }
184
185      int inlineDepth = barriers.size();
186
187      if (VM.VerifyAssertions) {
188        if (inlineDepth == 0) {
189          VM.sysWriteln("Inlining depth for " + osr + " is 0!");
190        }
191        VM._assert(inlineDepth != 0);
192      }
193
194      // Step 2: make a new InlinedOsrTypeOperand from barriers
195      int[] methodids = new int[inlineDepth];
196      int[] bcindexes = new int[inlineDepth];
197      byte[][] localTypeCodes = new byte[inlineDepth][];
198      byte[][] stackTypeCodes = new byte[inlineDepth][];
199
200      int totalOperands = 0;
201      // first iteration, count the size of total locals and stack sizes
202      for (int barIdx = 0, barSize = barriers.size(); barIdx < barSize; barIdx++) {
203
204        Instruction bar = barriers.get(barIdx);
205        methodids[barIdx] = bar.position.method.getId();
206        bcindexes[barIdx] = bar.bcIndex;
207
208        OsrTypeInfoOperand typeInfo = OsrBarrier.getTypeInfo(bar);
209        localTypeCodes[barIdx] = typeInfo.localTypeCodes;
210        stackTypeCodes[barIdx] = typeInfo.stackTypeCodes;
211
212        // count the number of operand, but ignore VoidTypeCode
213        totalOperands += OsrBarrier.getNumberOfElements(bar);
214
215        /*
216        if (VM.TraceOnStackReplacement) {
217          VM.sysWriteln("OsrBarrier : "+bar.bcIndex
218                        +"@"+bar.position.method
219                        +" "+typeInfo);
220        }
221        */
222      }
223
224      // new make InlinedOsrTypeInfoOperand
225      InlinedOsrTypeInfoOperand typeInfo =
226          new InlinedOsrTypeInfoOperand(methodids, bcindexes, localTypeCodes, stackTypeCodes);
227
228      OsrPoint.mutate(osr, osr.operator(), typeInfo, totalOperands);
229
230      // Step 3: second iteration, copy operands
231      int opIndex = 0;
232      for (int barIdx = 0, barSize = barriers.size(); barIdx < barSize; barIdx++) {
233
234        Instruction bar = barriers.get(barIdx);
235        for (int elmIdx = 0, elmSize = OsrBarrier.getNumberOfElements(bar); elmIdx < elmSize; elmIdx++) {
236
237          Operand op = OsrBarrier.getElement(bar, elmIdx);
238
239          if (VM.VerifyAssertions) {
240            if (op == null) {
241              VM.sysWriteln(elmIdx + "th Operand of " + bar + " is null!");
242            }
243            VM._assert(op != null);
244          }
245
246          if (op.isRegister()) {
247            op = op.asRegister().copyU2U();
248          } else {
249            op = op.copy();
250          }
251
252          OsrPoint.setElement(osr, opIndex, op);
253          opIndex++;
254        }
255      }
256/*
257      if (VM.TraceOnStackReplacement) {
258        VM.sysWriteln("renovated OsrPoint instruction "+osr);
259        VM.sysWriteln("  position "+osr.bcIndex+"@"+osr.position.method);
260      }
261*/
262      // the last OsrBarrier should in the current method
263      if (VM.VerifyAssertions) {
264        Instruction lastBar = barriers.getLast();
265        if (ir.method != lastBar.position.method) {
266          VM.sysWriteln("The last barrier is not in the same method as osr:");
267          VM.sysWriteln(lastBar + "@" + lastBar.position.method);
268          VM.sysWriteln("current method @" + ir.method);
269        }
270        VM._assert(ir.method == lastBar.position.method);
271
272        if (opIndex != totalOperands) {
273          VM.sysWriteln("opIndex and totalOperands do not match:");
274          VM.sysWriteln("opIndex = " + opIndex);
275          VM.sysWriteln("totalOperands = " + totalOperands);
276        }
277        VM._assert(opIndex == totalOperands);
278      } // end of assertion
279    } // end of for loop
280  }
281
282  /**
283   * The OsrBarrier instruction is not in IR, so the bc index was not
284   * adjusted in OSR_AdjustBCIndex.
285   *
286   * @param barrier the OSR barrier instruction
287   */
288  private void adjustBCIndex(Instruction barrier) {
289    NormalMethod source = barrier.position.method;
290    if (source.isForOsrSpecialization()) {
291      barrier.bcIndex -= source.getOsrPrologueLength();
292    }
293  }
294
295  private void removeOsrBarriers(IR ir) {
296    for (Instruction inst : osrBarriers) {
297      inst.remove();
298    }
299    ir.gc.discardOSRBarrierInformation();
300  }
301
302  @SuppressWarnings("unused")
303  // it's a debugging tool
304  private void verifyNoOsrBarriers(IR ir) {
305    VM.sysWrite("Verifying no osr barriers");
306    Enumeration<Instruction> instenum = ir.forwardInstrEnumerator();
307    while (instenum.hasMoreElements()) {
308      Instruction inst = instenum.nextElement();
309      if (inst.getOpcode() == OSR_BARRIER_opcode) {
310        VM.sysWriteln(" NOT SANE");
311        VM.sysWriteln(inst.toString());
312        if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
313        break;
314      }
315    }
316    VM.sysWriteln(" SANE");
317  }
318
319  /**
320   * Determines if the barrier is clean by checking the number of valid operands.
321   * @param barrier the instruction to verifiy
322   * @return {@code true} if and only if the barrier is clean
323   */
324  private boolean isBarrierClean(Instruction barrier) {
325    OsrTypeInfoOperand typeInfo = OsrBarrier.getTypeInfo(barrier);
326    int totalOperands = countNonVoidTypes(typeInfo.localTypeCodes);
327    totalOperands += countNonVoidTypes(typeInfo.stackTypeCodes);
328    return (totalOperands == OsrBarrier.getNumberOfElements(barrier));
329  }
330
331  private int countNonVoidTypes(byte[] typeCodes) {
332    int count = 0;
333    for (int idx = 0, size = typeCodes.length; idx < size; idx++) {
334      if (typeCodes[idx] != VoidTypeCode) {
335        count++;
336      }
337    }
338    return count;
339  }
340
341  /**
342   * Splits each OsrPoint, and connects it to the exit point.
343   * @param ir the IR
344   */
345  private void fixupCFGForOsr(IR ir) {
346    for (int i = 0, n = osrPoints.size(); i < n; i++) {
347      Instruction osr = osrPoints.get(i);
348      BasicBlock bb = osr.getBasicBlock();
349      BasicBlock newBB = bb.segregateInstruction(osr, ir);
350      bb.recomputeNormalOut(ir);
351      newBB.recomputeNormalOut(ir);
352    }
353  }
354}