/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.bc2ir; import static org.jikesrvm.classloader.ClassLoaderConstants.VoidTypeCode; import static org.jikesrvm.compilers.opt.ir.Operators.OSR_BARRIER_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.OSR_BARRIER; import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Enumeration; import java.util.LinkedList; import org.jikesrvm.VM; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.controlflow.BranchOptimizations; import org.jikesrvm.compilers.opt.driver.CompilerPhase; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.OsrBarrier; import org.jikesrvm.compilers.opt.ir.OsrPoint; import org.jikesrvm.compilers.opt.ir.operand.InlinedOsrTypeInfoOperand; import org.jikesrvm.compilers.opt.ir.operand.Operand; import org.jikesrvm.compilers.opt.ir.operand.OsrTypeInfoOperand; /** * A phase in the OPT compiler for construction OsrPoint instructions * after inlining. */ public class OsrPointConstructor extends CompilerPhase { @Override public final boolean shouldPerform(OptOptions options) { return VM.runningVM && options.OSR_GUARDED_INLINING; } @Override public final String getName() { return "OsrPointConstructor"; } @Override public Constructor<CompilerPhase> getClassConstructor() { return constructor; } private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(OsrPointConstructor.class); /** * Need to run branch optimizations after */ private final BranchOptimizations branchOpts; private Collection<Instruction> osrBarriers; private LinkedList<Instruction> osrPoints; /** * Constructor */ public OsrPointConstructor() { branchOpts = new BranchOptimizations(-1, false, false); } /** * Goes through each instruction, reconstruct OsrPoint instructions. */ @Override public void perform(IR ir) { // 1. collecting OsrPoint and OsrBarrier instructions collectOsrPointsAndBarriers(ir); // new IRPrinter("before renovating osrs").perform(ir); // 2. trace OsrBarrier for each OsrPoint, and rebuild OsrPoint renovateOsrPoints(ir); // new IRPrinter("before removing barriers").perform(ir); // 3. remove OsrBarriers removeOsrBarriers(ir); // new IRPrinter("after removing barriers").perform(ir); // 4. reconstruct CFG, cut off pieces after OsrPoint. fixupCFGForOsr(ir); /* if (VM.TraceOnStackReplacement && (0 != osrs.size())) { new IRPrinter("After OsrPointConstructor").perform(ir); } */ /* if (VM.TraceOnStackReplacement && (0 != osrs.size())) { verifyNoOsrBarriers(ir); } */ branchOpts.perform(ir); } /** * Iterates over all instructions in the IR and builds a list of * OsrPoint instructions and OsrBarrier instructions. * * @param ir the IR */ private void collectOsrPointsAndBarriers(IR ir) { osrPoints = new LinkedList<Instruction>(); osrBarriers = new LinkedList<Instruction>(); Enumeration<Instruction> instenum = ir.forwardInstrEnumerator(); while (instenum.hasMoreElements()) { Instruction inst = instenum.nextElement(); if (OsrPoint.conforms(inst)) { osrPoints.add(inst); } else if (inst.operator() == OSR_BARRIER) { osrBarriers.add(inst); } } } /** * For each OsrPoint instruction, traces its OsrBarriers created by * inlining. rebuild OsrPoint instruction to hold all necessary * information to recover from inlined activation. * @param ir the IR */ private void renovateOsrPoints(IR ir) { for (int osrIdx = 0, osrSize = osrPoints.size(); osrIdx < osrSize; osrIdx++) { Instruction osr = osrPoints.get(osrIdx); LinkedList<Instruction> barriers = new LinkedList<Instruction>(); // Step 1: collect barriers put before inlined method // in the order of from inner to outer { GenerationContext gc = ir.getGc(); Instruction bar = gc.getOSRBarrierFromInst(osr); if (osr.position() == null) osr.setPosition(bar.position()); adjustBCIndex(osr); while (bar != null) { barriers.add(bar); // verify each barrier is clean if (VM.VerifyAssertions) { if (!isBarrierClean(bar)) { VM.sysWriteln("Barrier " + bar + " is not clean!"); } VM._assert(isBarrierClean(bar)); } Instruction callsite = bar.position().getCallSite(); if (callsite != null) { bar = gc.getOSRBarrierFromInst(callsite); if (bar == null) { VM.sysWrite("call site :" + callsite); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } adjustBCIndex(bar); } else { bar = null; } } } int inlineDepth = barriers.size(); if (VM.VerifyAssertions) { if (inlineDepth == 0) { VM.sysWriteln("Inlining depth for " + osr + " is 0!"); } VM._assert(inlineDepth != 0); } // Step 2: make a new InlinedOsrTypeOperand from barriers int[] methodids = new int[inlineDepth]; int[] bcindexes = new int[inlineDepth]; byte[][] localTypeCodes = new byte[inlineDepth][]; byte[][] stackTypeCodes = new byte[inlineDepth][]; int totalOperands = 0; // first iteration, count the size of total locals and stack sizes for (int barIdx = 0, barSize = barriers.size(); barIdx < barSize; barIdx++) { Instruction bar = barriers.get(barIdx); methodids[barIdx] = bar.position().method.getId(); bcindexes[barIdx] = bar.getBytecodeIndex(); OsrTypeInfoOperand typeInfo = OsrBarrier.getTypeInfo(bar); localTypeCodes[barIdx] = typeInfo.localTypeCodes; stackTypeCodes[barIdx] = typeInfo.stackTypeCodes; // count the number of operand, but ignore VoidTypeCode totalOperands += OsrBarrier.getNumberOfElements(bar); /* if (VM.TraceOnStackReplacement) { VM.sysWriteln("OsrBarrier : "+bar.bcIndex +"@"+bar.position.method +" "+typeInfo); } */ } // new make InlinedOsrTypeInfoOperand InlinedOsrTypeInfoOperand typeInfo = new InlinedOsrTypeInfoOperand(methodids, bcindexes, localTypeCodes, stackTypeCodes); OsrPoint.mutate(osr, osr.operator(), typeInfo, totalOperands); // Step 3: second iteration, copy operands int opIndex = 0; for (int barIdx = 0, barSize = barriers.size(); barIdx < barSize; barIdx++) { Instruction bar = barriers.get(barIdx); for (int elmIdx = 0, elmSize = OsrBarrier.getNumberOfElements(bar); elmIdx < elmSize; elmIdx++) { Operand op = OsrBarrier.getElement(bar, elmIdx); if (VM.VerifyAssertions) { if (op == null) { VM.sysWriteln(elmIdx + "th Operand of " + bar + " is null!"); } VM._assert(op != null); } if (op.isRegister()) { op = op.asRegister().copyU2U(); } else { op = op.copy(); } OsrPoint.setElement(osr, opIndex, op); opIndex++; } } /* if (VM.TraceOnStackReplacement) { VM.sysWriteln("renovated OsrPoint instruction "+osr); VM.sysWriteln(" position "+osr.bcIndex+"@"+osr.position.method); } */ // the last OsrBarrier should in the current method if (VM.VerifyAssertions) { Instruction lastBar = barriers.getLast(); if (ir.method != lastBar.position().method) { VM.sysWriteln("The last barrier is not in the same method as osr:"); VM.sysWriteln(lastBar + "@" + lastBar.position().method); VM.sysWriteln("current method @" + ir.method); } VM._assert(ir.method == lastBar.position().method); if (opIndex != totalOperands) { VM.sysWriteln("opIndex and totalOperands do not match:"); VM.sysWriteln("opIndex = " + opIndex); VM.sysWriteln("totalOperands = " + totalOperands); } VM._assert(opIndex == totalOperands); } // end of assertion } // end of for loop } /** * The OsrBarrier instruction is not in IR, so the bc index was not * adjusted in OSR_AdjustBCIndex. * * @param barrier the OSR barrier instruction */ private void adjustBCIndex(Instruction barrier) { NormalMethod source = barrier.position().method; if (source.isForOsrSpecialization()) { barrier.adjustBytecodeIndex(-source.getOsrPrologueLength()); } } private void removeOsrBarriers(IR ir) { for (Instruction inst : osrBarriers) { inst.remove(); } ir.getGc().discardOSRBarrierInformation(); } @SuppressWarnings("unused") // it's a debugging tool private void verifyNoOsrBarriers(IR ir) { VM.sysWrite("Verifying no osr barriers"); Enumeration<Instruction> instenum = ir.forwardInstrEnumerator(); while (instenum.hasMoreElements()) { Instruction inst = instenum.nextElement(); if (inst.getOpcode() == OSR_BARRIER_opcode) { VM.sysWriteln(" NOT SANE"); VM.sysWriteln(inst.toString()); if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); break; } } VM.sysWriteln(" SANE"); } /** * Determines if the barrier is clean by checking the number of valid operands. * @param barrier the instruction to verifiy * @return {@code true} if and only if the barrier is clean */ private boolean isBarrierClean(Instruction barrier) { OsrTypeInfoOperand typeInfo = OsrBarrier.getTypeInfo(barrier); int totalOperands = countNonVoidTypes(typeInfo.localTypeCodes); totalOperands += countNonVoidTypes(typeInfo.stackTypeCodes); return (totalOperands == OsrBarrier.getNumberOfElements(barrier)); } private int countNonVoidTypes(byte[] typeCodes) { int count = 0; for (int idx = 0, size = typeCodes.length; idx < size; idx++) { if (typeCodes[idx] != VoidTypeCode) { count++; } } return count; } /** * Splits each OsrPoint, and connects it to the exit point. * @param ir the IR */ private void fixupCFGForOsr(IR ir) { for (int i = 0, n = osrPoints.size(); i < n; i++) { Instruction osr = osrPoints.get(i); BasicBlock bb = osr.getBasicBlock(); BasicBlock newBB = bb.segregateInstruction(osr, ir); bb.recomputeNormalOut(ir); newBB.recomputeNormalOut(ir); } } }