/* * 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.ssa; import static org.jikesrvm.compilers.opt.ir.Operators.*; import java.lang.reflect.Constructor; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import org.jikesrvm.VM; import org.jikesrvm.compilers.opt.DefUse; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.controlflow.DominatorInfo; import org.jikesrvm.compilers.opt.controlflow.DominatorTree; import org.jikesrvm.compilers.opt.controlflow.Dominators; import org.jikesrvm.compilers.opt.controlflow.DominatorsPhase; import org.jikesrvm.compilers.opt.driver.CompilerPhase; import org.jikesrvm.compilers.opt.ir.AStore; import org.jikesrvm.compilers.opt.ir.BBend; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock; import org.jikesrvm.compilers.opt.ir.GuardResultCarrier; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.Label; import org.jikesrvm.compilers.opt.ir.LocationCarrier; import org.jikesrvm.compilers.opt.ir.Operator; import org.jikesrvm.compilers.opt.ir.Phi; import org.jikesrvm.compilers.opt.ir.PutField; import org.jikesrvm.compilers.opt.ir.PutStatic; import org.jikesrvm.compilers.opt.ir.Register; import org.jikesrvm.compilers.opt.ir.ResultCarrier; import org.jikesrvm.compilers.opt.ir.operand.BasicBlockOperand; import org.jikesrvm.compilers.opt.ir.operand.HeapOperand; import org.jikesrvm.compilers.opt.ir.operand.LocationOperand; import org.jikesrvm.compilers.opt.ir.operand.Operand; import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; import org.jikesrvm.compilers.opt.liveness.LiveAnalysis; import org.jikesrvm.compilers.opt.util.Queue; /** * This class does loop invariant code movement. It is a subphase of {@link GCP} (global code placement). */ public class LICM extends CompilerPhase { /** Generate debug output? */ private static final boolean DEBUG = false; /** Generate verbose debug output? */ private static boolean VERBOSE = false; private Map<Instruction, Integer> instructionNumbers; /** * Constructor for this compiler phase */ private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(LICM.class); /** * Get a constructor object for this compiler phase * @return compiler phase constructor */ @Override public Constructor<CompilerPhase> getClassConstructor() { return constructor; } /** * Execute loop invariant code motion on the given IR. */ @Override public void perform(IR ir) { this.ir = ir; if (DEBUG && ir.hasReachableExceptionHandlers()) { VM.sysWriteln("] " + ir.method); (new LiveAnalysis(false, false, true, false)).perform(ir); Enumeration<BasicBlock> e = ir.getBasicBlocks(); while (e.hasMoreElements()) { BasicBlock b = e.nextElement(); if (b instanceof ExceptionHandlerBasicBlock) { VM.sysWriteln("] " + b + ": " + ((ExceptionHandlerBasicBlock) b).getLiveSet()); } } } if (ir.hasReachableExceptionHandlers() || GCP.tooBig(ir)) { resetLandingPads(); return; } VERBOSE = ir.options.DEBUG_GCP; if (VERBOSE && ir.options.hasMETHOD_TO_PRINT()) { VERBOSE = ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString()); if (!VERBOSE) { resetLandingPads(); return; } } if (VERBOSE) VM.sysWriteln("] " + ir.method); initialize(ir); if (VERBOSE) SSA.printInstructions(ir); Instruction inst = ir.firstInstructionInCodeOrder(); while (inst != null) { Instruction next = inst.nextInstructionInCodeOrder(); if (DEBUG) System.out.println("scheduleEarly: " + inst); scheduleEarly(inst); inst = next; } inst = ir.lastInstructionInCodeOrder(); while (inst != null) { Instruction next = inst.prevInstructionInCodeOrder(); scheduleLate(inst); inst = next; } resetLandingPads(); if (DEBUG) SSA.printInstructions(ir); ir.actualSSAOptions.setScalarValid(false); } /** * @return the name of the phase */ @Override public String getName() { return "LICM"; } /** * @return <code>true</code> if SSA-based global code placement is being * performed */ @Override public boolean shouldPerform(OptOptions options) { return options.SSA_GCP; } //------------------------- Implementation ------------------------- /** * @param inst the instruction that might be moved * @param ir the governing iR * @return whether it is safe to move the given instruction, depending on * the properties of the IR (is it in heapSSA form or not?) */ public static boolean shouldMove(Instruction inst, IR ir) { if ((inst.isAllocation()) || inst.isDynamicLinkingPoint() || inst.getOpcode() >= ARCH_INDEPENDENT_END_opcode) { return false; } if (ir.isNotHIR() && ((inst.isPEI()) || inst.isThrow() || inst.isImplicitLoad() || inst.isImplicitStore())) { return false; } switch (inst.getOpcode()) { case INT_MOVE_opcode: case LONG_MOVE_opcode: case INT_COND_MOVE_opcode: case LONG_COND_MOVE_opcode: case FLOAT_COND_MOVE_opcode: case DOUBLE_COND_MOVE_opcode: case REF_COND_MOVE_opcode: case PUTSTATIC_opcode: case PUTFIELD_opcode: case GETSTATIC_opcode: case GETFIELD_opcode: case INT_ALOAD_opcode: case LONG_ALOAD_opcode: case FLOAT_ALOAD_opcode: case DOUBLE_ALOAD_opcode: case REF_ALOAD_opcode: case BYTE_ALOAD_opcode: case UBYTE_ALOAD_opcode: case SHORT_ALOAD_opcode: case USHORT_ALOAD_opcode: case INT_ASTORE_opcode: case LONG_ASTORE_opcode: case FLOAT_ASTORE_opcode: case DOUBLE_ASTORE_opcode: case REF_ASTORE_opcode: case BYTE_ASTORE_opcode: case SHORT_ASTORE_opcode: case CHECKCAST_opcode: case CHECKCAST_NOTNULL_opcode: case CHECKCAST_UNRESOLVED_opcode: case MUST_IMPLEMENT_INTERFACE_opcode: case INSTANCEOF_opcode: case INSTANCEOF_NOTNULL_opcode: case INSTANCEOF_UNRESOLVED_opcode: case PI_opcode: case FLOAT_MOVE_opcode: case DOUBLE_MOVE_opcode: case REF_MOVE_opcode: case GUARD_MOVE_opcode: case GUARD_COMBINE_opcode: case TRAP_IF_opcode: case REF_ADD_opcode: case INT_ADD_opcode: case LONG_ADD_opcode: case FLOAT_ADD_opcode: case DOUBLE_ADD_opcode: case REF_SUB_opcode: case INT_SUB_opcode: case LONG_SUB_opcode: case FLOAT_SUB_opcode: case DOUBLE_SUB_opcode: case INT_MUL_opcode: case LONG_MUL_opcode: case FLOAT_MUL_opcode: case DOUBLE_MUL_opcode: case INT_DIV_opcode: case LONG_DIV_opcode: case FLOAT_DIV_opcode: case DOUBLE_DIV_opcode: case INT_REM_opcode: case LONG_REM_opcode: case FLOAT_REM_opcode: case DOUBLE_REM_opcode: case INT_NEG_opcode: case LONG_NEG_opcode: case FLOAT_NEG_opcode: case DOUBLE_NEG_opcode: case REF_SHL_opcode: case INT_SHL_opcode: case LONG_SHL_opcode: case REF_SHR_opcode: case INT_SHR_opcode: case LONG_SHR_opcode: case REF_USHR_opcode: case INT_USHR_opcode: case LONG_USHR_opcode: case REF_AND_opcode: case INT_AND_opcode: case LONG_AND_opcode: case REF_OR_opcode: case INT_OR_opcode: case LONG_OR_opcode: case REF_XOR_opcode: case INT_XOR_opcode: case REF_NOT_opcode: case INT_NOT_opcode: case LONG_NOT_opcode: case LONG_XOR_opcode: case INT_2LONG_opcode: case INT_2FLOAT_opcode: case INT_2DOUBLE_opcode: case INT_2ADDRSigExt_opcode: case INT_2ADDRZerExt_opcode: case LONG_2ADDR_opcode: case ADDR_2INT_opcode: case ADDR_2LONG_opcode: case LONG_2INT_opcode: case LONG_2FLOAT_opcode: case LONG_2DOUBLE_opcode: case FLOAT_2INT_opcode: case FLOAT_2LONG_opcode: case FLOAT_2DOUBLE_opcode: case DOUBLE_2INT_opcode: case DOUBLE_2LONG_opcode: case DOUBLE_2FLOAT_opcode: case INT_2BYTE_opcode: case INT_2USHORT_opcode: case INT_2SHORT_opcode: case LONG_CMP_opcode: case FLOAT_CMPL_opcode: case FLOAT_CMPG_opcode: case DOUBLE_CMPL_opcode: case DOUBLE_CMPG_opcode: case NULL_CHECK_opcode: case BOUNDS_CHECK_opcode: case INT_ZERO_CHECK_opcode: case LONG_ZERO_CHECK_opcode: case OBJARRAY_STORE_CHECK_opcode: case OBJARRAY_STORE_CHECK_NOTNULL_opcode: case BOOLEAN_NOT_opcode: case BOOLEAN_CMP_INT_opcode: case BOOLEAN_CMP_ADDR_opcode: case FLOAT_AS_INT_BITS_opcode: case INT_BITS_AS_FLOAT_opcode: case DOUBLE_AS_LONG_BITS_opcode: case LONG_BITS_AS_DOUBLE_opcode: case ARRAYLENGTH_opcode: case GET_OBJ_TIB_opcode: case GET_CLASS_TIB_opcode: case GET_TYPE_FROM_TIB_opcode: case GET_SUPERCLASS_IDS_FROM_TIB_opcode: case GET_DOES_IMPLEMENT_FROM_TIB_opcode: case GET_ARRAY_ELEMENT_TIB_FROM_TIB_opcode: return !(GCP.usesOrDefsPhysicalRegisterOrAddressType(inst)); } return false; } /** * Schedule this instruction as early as possible * @param inst the instruction to schedule * @return the instruction that serves as the new lower bound (exclusive) * for further scheduling */ private Instruction scheduleEarly(Instruction inst) { Instruction _earlyPos; if (getState(inst) >= early) return getEarlyPos(inst); setState(inst, early); setEarlyPos(inst, inst); // already on outer level? //if (ir.HIRInfo.LoopStructureTree.getLoopNestDepth(getBlock(inst)) == 0) // return inst; if (ir.options.FREQ_FOCUS_EFFORT && getOrigBlock(inst).getInfrequent()) { return inst; } // explicitly INCLUDE instructions if (!shouldMove(inst, ir)) { return inst; } // dependencies via scalar operands _earlyPos = scheduleScalarDefsEarly(inst.getUses(), ir.firstInstructionInCodeOrder(), inst); if (VM.VerifyAssertions) VM._assert(_earlyPos != null); // memory dependencies if (ir.isHIR()) { _earlyPos = scheduleHeapDefsEarly(ssad.getHeapUses(inst), _earlyPos, inst); if (VM.VerifyAssertions) VM._assert(_earlyPos != null); } /* don't put memory stores or PEIs on speculative path */ if ((inst.isPEI() && !ir.options.SSA_LICM_IGNORE_PEI) || inst.isImplicitStore()) { while (!postDominates(getBlock(inst), getBlock(_earlyPos), ir)) { _earlyPos = dominanceSuccessor(_earlyPos, inst); } } setEarlyPos(inst, _earlyPos); if (DEBUG && getBlock(_earlyPos) != getBlock(inst)) { VM.sysWriteln("new earlyBlock: " + getBlock(_earlyPos) + " for " + getBlock(inst) + ": " + inst); } setBlock(inst, getBlock(_earlyPos)); return _earlyPos; } BasicBlock scheduleLate(Instruction inst) { if (DEBUG) VM.sysWriteln("Schedule Late: " + inst); BasicBlock lateBlock = null; int _state = getState(inst); if (_state == late || _state == done) return getBlock(inst); setState(inst, late); if (ir.options.FREQ_FOCUS_EFFORT) { BasicBlock _origBlock = getOrigBlock(inst); if (_origBlock.getInfrequent()) { return _origBlock; } } // explicitly INCLUDE instructions if (!shouldMove(inst, ir)) { return getOrigBlock(inst); } // dependencies via scalar operands lateBlock = scheduleScalarUsesLate(inst, lateBlock); if (DEBUG) VM.sysWriteln("lateBlock1: " + lateBlock + " for " + inst); // dependencies via heap operands if (ir.isHIR()) { lateBlock = scheduleHeapUsesLate(inst, lateBlock); if (DEBUG) VM.sysWriteln("lateBlock2: " + lateBlock + " for " + inst); } // if there are no uses, this instruction is dead. if (lateBlock == null) { if (VERBOSE) VM.sysWriteln("deleting " + inst); inst.remove(); } else { if (DEBUG && lateBlock != getOrigBlock(inst)) { VM.sysWriteln("new lateBlock: " + lateBlock + " for " + getOrigBlock(inst) + ": " + inst); } BasicBlock to = upto(getEarlyPos(inst), lateBlock, inst); if (to == null) { lateBlock = getOrigBlock(inst); } else { if (VM.VerifyAssertions) VM._assert(getState(inst) != done); lateBlock = to; if (getOrigBlock(inst) != to) move(inst, to); } } setState(inst, done); setBlock(inst, lateBlock); return lateBlock; } /** * return `a's successor on the path from `a' to `b' in the dominator * tree. `a' must dominate `b' and `a' and `b' must belong to * different blocks. * * @param a an instruction matching the constraints from the description * @param b another instruction matching the constraints from the description * @return the dominance successor of a */ private Instruction dominanceSuccessor(Instruction a, Instruction b) { BasicBlock aBlock = getBlock(a); BasicBlock bBlock = getBlock(b); if (VM.VerifyAssertions) { VM._assert(aBlock != bBlock && dominator.dominates(aBlock, bBlock)); } BasicBlock last = null; while (bBlock != aBlock) { last = bBlock; bBlock = dominator.getParent(bBlock); } return last.firstInstruction(); } /** * Compares two instructions according to their depth in the dominator tree * and return the one with the greatest depth. * * @param a an instruction * @param b another instruction * @return the instruction with the greatest depth in the dominator * tree */ private Instruction maxDominatorDepth(Instruction a, Instruction b) { BasicBlock aBlock = getBlock(a); BasicBlock bBlock = getBlock(b); int aDomDepth = dominator.depth(aBlock); int bDomDepth = dominator.depth(bBlock); if (aDomDepth > bDomDepth) return a; if (aDomDepth < bDomDepth) return b; if (VM.VerifyAssertions) VM._assert(aBlock == bBlock); // if an instruction depends on a branch, it can not be placed in // this block. Make sure we record this fact. We use this // information in upto() return a.isBranch() ? a : b; } private BasicBlock commonDominator(BasicBlock a, BasicBlock b) { //VM.sysWrite ("CD: "+a+", "+b); if (a == null) return b; if (b == null) return a; while (a != b) { int aDomDepth = dominator.depth(a); int bDomDepth = dominator.depth(b); if (aDomDepth >= bDomDepth) a = dominator.getParent(a); if (bDomDepth >= aDomDepth) b = dominator.getParent(b); } //VM.sysWriteln (" = " + a); return a; } /** * Schedules an instruction as early as possible, * but behind the definitions in e and behind earlyPos * * @param e the definitions that must have been occurred before the new * position of the instruction * @param earlyPos the instruction that serves as a lower bound (exclusive) * for the position * @param inst the instruction to schedule * @return the instruction that serves as the new lower bound (exclusive) * for further scheduling */ private Instruction scheduleScalarDefsEarly(Enumeration<Operand> e, Instruction earlyPos, Instruction inst) { while (e.hasMoreElements()) { Operand op = e.nextElement(); Instruction def = definingInstruction(op); scheduleEarly(def); if (def.isBranch()) def = dominanceSuccessor(def, inst); earlyPos = maxDominatorDepth(def, earlyPos); } return earlyPos; } /** * Schedules an instruction as early as possible, * but behind the definitions of op[i] and behind earlyPos. * * @param op the definitions that must have been occurred before the new * position of the instruction * @param earlyPos the instruction that serves as a lower bound (exclusive) * for the position * @param me the instruction to schedule * @return the instruction that serves as the new lower bound (exclusive) * for further scheduling */ Instruction scheduleHeapDefsEarly(HeapOperand<?>[] op, Instruction earlyPos, Instruction me) { if (op == null) return earlyPos; for (HeapOperand<?> anOp : op) { Instruction def = definingInstruction(anOp); // if (me.isImplicitLoad() || me.isImplicitStore()) // def = _getRealDef(def, me) // ; // else if (me.isPEI()) // def = _getRealExceptionDef(def) // ; if (VM.VerifyAssertions) VM._assert(def != null); earlyPos = maxDominatorDepth(scheduleEarly(def), earlyPos); } return earlyPos; } BasicBlock useBlock(Instruction use, Operand op) { //VM.sysWriteln ("UseBlock: " + use); BasicBlock res = scheduleLate(use); if (res != null && Phi.conforms(use)) { int i; for (i = Phi.getNumberOfValues(use) - 1; i >= 0; --i) { if (Phi.getValue(use, i) == op) { res = Phi.getPred(use, i).block; break; } } if (VM.VerifyAssertions) VM._assert(i >= 0); } return res; } private BasicBlock scheduleScalarUsesLate(Instruction inst, BasicBlock lateBlock) { Operand resOp = getResult(inst); if (resOp == null || !(resOp instanceof RegisterOperand)) { return lateBlock; } Register res = ((RegisterOperand) resOp).getRegister(); Enumeration<RegisterOperand> e = DefUse.uses(res); while (e.hasMoreElements()) { Operand op = e.nextElement(); Instruction use = op.instruction; BasicBlock _block = useBlock(use, op); lateBlock = commonDominator(_block, lateBlock); } return lateBlock; } BasicBlock scheduleHeapUsesLate(Instruction inst, BasicBlock lateBlock) { //VM.sysWriteln (" scheduleHeapUsesLate"); Operand[] defs = ssad.getHeapDefs(inst); if (defs == null) return lateBlock; //VM.sysWriteln (" defs: " + defs.length); for (Operand def : defs) { @SuppressWarnings("unchecked") // Cast to generic HeapOperand HeapOperand<Object> dhop = (HeapOperand) def; HeapVariable<Object> H = dhop.value; if (DEBUG) VM.sysWriteln("H: " + H); Iterator<HeapOperand<Object>> it = ssad.iterateHeapUses(H); //VM.sysWriteln (" H: " + H + " (" + ssad.getNumberOfUses (H) + ")"); while (it.hasNext()) { HeapOperand<Object> uhop = it.next(); //VM.sysWriteln (" uhop: " + uhop): Instruction use = uhop.instruction; //VM.sysWriteln ("use: " + use); BasicBlock _block = useBlock(use, uhop); lateBlock = commonDominator(_block, lateBlock); } } return lateBlock; } /** * @param op the operand that's being defined * @return the instruction that defines the operand. */ Instruction definingInstruction(Operand op) { if (op instanceof HeapOperand) { @SuppressWarnings("unchecked") // Cast to generic HeapOperand HeapOperand<Object> hop = (HeapOperand) op; HeapVariable<Object> H = hop.value; HeapOperand<Object> defiOp = ssad.getUniqueDef(H); // Variable may be defined by caller, so depends on method entry if (defiOp == null || defiOp.instruction == null) { return ir.firstInstructionInCodeOrder(); } else { //VM.sysWriteln ("def of " + op + " is " + defiOp.instruction); return defiOp.instruction; } } else if (op instanceof RegisterOperand) { Register reg = ((RegisterOperand) op).getRegister(); Enumeration<RegisterOperand> defs = DefUse.defs(reg); if (!defs.hasMoreElements()) { // params have no def return ir.firstInstructionInCodeOrder(); } else { Instruction def = defs.nextElement().instruction; // we are in SSA, so there is at most one definition. if (VM.VerifyAssertions) VM._assert(!defs.hasMoreElements()); //if (defs.hasMoreElements()) { // VM.sysWriteln("GCP: multiple defs: " + reg); // return null; //} return def; } } else { // some constant return ir.firstInstructionInCodeOrder(); } } /** * @param inst an instruction * @return the result operand of the instruction */ Operand getResult(Instruction inst) { if (ResultCarrier.conforms(inst)) { return ResultCarrier.getResult(inst); } if (GuardResultCarrier.conforms(inst)) { return GuardResultCarrier.getGuardResult(inst); } if (Phi.conforms(inst)) { return Phi.getResult(inst); } return null; } /** * Visits the blocks between the late and the early position along * their path in the dominator tree. * * @param earlyPos the early position * @param lateBlock the late position * @param inst the instruction to schedule * @return the block with the smallest execution costs */ BasicBlock upto(Instruction earlyPos, BasicBlock lateBlock, Instruction inst) { BasicBlock _origBlock = getOrigBlock(inst); BasicBlock actBlock = lateBlock; BasicBlock bestBlock = lateBlock; BasicBlock earlyBlock = getBlock(earlyPos); if (VM.VerifyAssertions) { if (!dominator.dominates(earlyBlock.getNumber(), _origBlock.getNumber()) || !dominator.dominates(earlyBlock.getNumber(), lateBlock.getNumber())) { SSA.printInstructions(ir); VM.sysWriteln("> " + earlyBlock.getNumber() + ", " + _origBlock.getNumber() + ", " + lateBlock.getNumber()); VM.sysWriteln(inst.toString()); } VM._assert(dominator.dominates(earlyBlock.getNumber(), _origBlock.getNumber())); VM._assert(dominator.dominates(earlyBlock.getNumber(), lateBlock.getNumber())); } for (; ;) { /* is the actual block better (less frequent) than the so far best block? */ if (frequency(actBlock) < frequency(bestBlock)) { if (DEBUG) { VM.sysWriteln("going from " + frequency(_origBlock) + " to " + frequency(actBlock)); } bestBlock = actBlock; } /* all candidates checked? */ if (actBlock == earlyBlock) { break; } /* walk up the dominator tree for next candidate*/ actBlock = dominator.getParent(actBlock); } if (bestBlock == _origBlock) return null; if (DEBUG) VM.sysWriteln("best Block: " + bestBlock); return bestBlock; } /** * Determines how expensive it is to place an instruction in this block. * * @param b a basic block * @return the block's execution frequency */ final float frequency(BasicBlock b) { return b.getExecutionFrequency(); } void move(Instruction inst, BasicBlock to) { BasicBlock _origBlock = getOrigBlock(inst); Instruction cand = null; /* find a position within bestBlock */ if (dominator.dominates(_origBlock.getNumber(), to.getNumber())) { // moved down, so insert in from Instruction last = null; Enumeration<Instruction> e = to.forwardInstrEnumerator(); while (e.hasMoreElements()) { cand = e.nextElement(); if (DEBUG) VM.sysWriteln(cand.toString());; // skip labels, phis, and yieldpoints if (!Label.conforms(cand) && !cand.isYieldPoint() && !Phi.conforms(cand)) { break; } last = cand; } cand = last; } else { // moved up, so insert at end of block Enumeration<Instruction> e = to.reverseInstrEnumerator(); while (e.hasMoreElements()) { cand = e.nextElement(); if (DEBUG) VM.sysWriteln(cand.toString()); // skip branches and newly placed insts if (!BBend.conforms(cand) && !cand.isBranch() && !relocated.contains(cand)) { break; } } if (DEBUG) VM.sysWriteln("Adding to relocated: " + inst); relocated.add(inst); } if (DEBUG && moved.add(inst.operator())) { VM.sysWriteln("m(" + (ir.isLIR() ? "l" : "h") + ") " + inst.operator()); } if (VERBOSE) { VM.sysWrite(ir.isLIR() ? "%" : "#"); VM.sysWriteln(" moving " + inst + " from " + _origBlock + " to " + to + "\n" + "behind " + cand); } inst.remove(); cand.insertAfter(inst); } //------------------------------------------------------------ // some helper methods //------------------------------------------------------------ /** * does a post dominate b? * @param a the possible post-dominator * @param b the possibly post-dominated block * @param ir the IR that contains the blocks * @return whether the first block post-dominates the second block */ boolean postDominates(BasicBlock a, BasicBlock b, IR ir) { boolean res; if (a == b) { return true; } //VM.sysWrite ("does " + a + " postdominate " + b + "?: "); DominatorInfo info = ir.getDominators().getDominatorInfo(b); res = info.isDominatedBy(a); //VM.sysWriteln (res ? "yes" : "no"); return res; } BasicBlock getBlock(Instruction inst) { return block[instructionNumbers.get(inst)]; } void setBlock(Instruction inst, BasicBlock b) { block[instructionNumbers.get(inst)] = b; } Instruction getEarlyPos(Instruction inst) { return earlyPos[instructionNumbers.get(inst)]; } void setEarlyPos(Instruction inst, Instruction pos) { earlyPos[instructionNumbers.get(inst)] = pos; } BasicBlock getOrigBlock(Instruction inst) { return origBlock[instructionNumbers.get(inst)]; } void setOrigBlock(Instruction inst, BasicBlock b) { origBlock[instructionNumbers.get(inst)] = b; } /** * In what state (initial, early, late, done) is this instruction * @param inst the instruction to check * @return the instruction's state */ int getState(Instruction inst) { return state[instructionNumbers.get(inst)]; } /** * Set the state (initial, early, late, done) of the instruction * @param inst the instruction * @param s the state */ void setState(Instruction inst, int s) { state[instructionNumbers.get(inst)] = s; } //------------------------------------------------------------ // initialization //------------------------------------------------------------ void initialize(IR ir) { this.ir = ir; relocated = new HashSet<Instruction>(); // Note: the following unfactors the CFG new DominatorsPhase(true).perform(ir); Dominators dominators = new Dominators(); dominators.computeApproxPostdominators(ir); ir.setDominators(dominators); dominator = ir.HIRInfo.dominatorTree; if (DEBUG) VM.sysWriteln(dominator.toString()); instructionNumbers = ir.numberInstructionsViaMap(); int instructions = instructionNumbers.size(); ssad = ir.HIRInfo.dictionary; DefUse.computeDU(ir); ssad.recomputeArrayDU(); // also number implicit heap phis Enumeration<BasicBlock> e = ir.getBasicBlocks(); while (e.hasMoreElements()) { BasicBlock b = e.nextElement(); Iterator<Instruction> pe = ssad.getHeapPhiInstructions(b); while (pe.hasNext()) { Instruction inst = pe.next(); instructionNumbers.put(inst, Integer.valueOf(instructions++)); } } state = new int[instructions]; origBlock = new BasicBlock[instructions]; block = new BasicBlock[instructions]; earlyPos = new Instruction[instructions]; e = ir.getBasicBlocks(); while (e.hasMoreElements()) { BasicBlock b = e.nextElement(); Enumeration<Instruction> ie = ssad.getAllInstructions(b); while (ie.hasMoreElements()) { Instruction inst = ie.nextElement(); setBlock(inst, b); setOrigBlock(inst, b); setState(inst, initial); } } if (ir.isHIR()) { e = ir.getBasicBlocks(); while (e.hasMoreElements()) { BasicBlock b = e.nextElement(); if (ir.options.FREQ_FOCUS_EFFORT && b.getInfrequent()) continue; Enumeration<Instruction> ie = ssad.getAllInstructions(b); while (ie.hasMoreElements()) { Instruction inst = ie.nextElement(); while (simplify(inst, b)) ; } } ssad.recomputeArrayDU(); } } //------------------------------------------------------------ // private state //------------------------------------------------------------ private static final int initial = 0; private static final int early = 1; private static final int late = 2; private static final int done = 3; private HashSet<Instruction> relocated; private int[] state; private BasicBlock[] block; private BasicBlock[] origBlock; private Instruction[] earlyPos; private SSADictionary ssad; private DominatorTree dominator; private IR ir; private final HashSet<Operator> moved = DEBUG ? new HashSet<Operator>() : null; private boolean simplify(Instruction inst, BasicBlock block) { if (!Phi.conforms(inst)) return false; // no phi //if (Phi.getNumberOfValues (inst) != 2) return false; // want exactly 2 inputs //VM.sysWriteln ("Simplify " + inst); Operand resOp = Phi.getResult(inst); if (!(resOp instanceof HeapOperand)) { //VM.sysWriteln (" no heap op result"); return false; // scalar phi } int xidx = -1; Instruction x = null; for (int i = Phi.getNumberOfValues(inst) - 1; i >= 0; --i) { Instruction in = definingInstruction(Phi.getValue(inst, i)); if (getOrigBlock(in) != getOrigBlock(inst) && dominator.dominates(getOrigBlock(in), getOrigBlock(inst))) { if (xidx != -1) return false; xidx = i; x = in; } else if (!dominator.dominates(getOrigBlock(inst), getOrigBlock(in))) { return false; } } if (x == null) return false; replaceUses(inst, (HeapOperand<?>) Phi.getValue(inst, xidx), Phi.getPred(inst, xidx), true); @SuppressWarnings("unchecked") // Cast to generic HeapOperand HeapOperand<Object> hop = (HeapOperand) resOp; if (hop.value.isExceptionHeapType()) return false; /* check that inside the loop, the heap variable is only used/defed by simple, non-volatile loads or only by stores if so, replace uses of inst (a memory phi) with its dominating input */ int type = checkLoop(inst, hop, xidx, block); if (type == CL_LOADS_ONLY || type == CL_STORES_ONLY || type == CL_NONE) { replaceUses(inst, (HeapOperand<?>) Phi.getValue(inst, xidx), Phi.getPred(inst, xidx), false); } return false; } static final int CL_NONE = 0; static final int CL_LOADS_ONLY = 1; static final int CL_STORES_ONLY = 2; static final int CL_LOADS_AND_STORES = 3; static final int CL_COMPLEX = 4; /* * check that inside the loop, the heap variable is only used/defed * by simple, non-volatile loads/stores<p> * * returns one of: * CL_LOADS_ONLY, CL_STORES_ONLY, CL_LOADS_AND_STORES, CL_COMPLEX */ @SuppressWarnings("unused") // useful for debugging private int _checkLoop(Instruction inst, HeapOperand<?> hop, int xidx) { for (int i = Phi.getNumberOfValues(inst) - 1; i >= 0; --i) { if (i == xidx) continue; Instruction y = definingInstruction(Phi.getValue(inst, i)); while (y != inst) { //VM.sysWriteln (" y: " + y); if (y.isImplicitStore() || y.isPEI() || !LocationCarrier.conforms(y)) { return CL_COMPLEX; } // check for access to volatile field LocationOperand loc = LocationCarrier.getLocation(y); if (loc == null || loc.mayBeVolatile()) { //VM.sysWriteln (" no loc or volatile field"); return CL_COMPLEX; } for (HeapOperand<?> op : ssad.getHeapUses(y)) { if (op.value.isExceptionHeapType()) continue; if (op.getHeapType() != hop.getHeapType()) return CL_COMPLEX; y = definingInstruction(op); } } } return CL_LOADS_ONLY; } /* * TODO document xidx parameter and turn this comment into proper JavaDoc. * <p> * Checks that inside the loop, the heap variable is only used/defed * by simple, non-volatile loads/stores * * @param inst a phi instruction * @param hop the result operand of the phi instruction * @param xidx * @param block the block that contains the phi instruction * * @return one of {@link #CL_LOADS_ONLY}, {@link #CL_STORES_ONLY}, * {@link #CL_LOADS_AND_STORES}, {@link #CL_COMPLEX} */ private int checkLoop(Instruction inst, HeapOperand<Object> hop, int xidx, BasicBlock block) { HashSet<Instruction> seen = new HashSet<Instruction>(); Queue<Instruction> workList = new Queue<Instruction>(); int _state = CL_NONE; int instUses = 0; seen.add(inst); for (int i = Phi.getNumberOfValues(inst) - 1; i >= 0; --i) { if (i == xidx) continue; Instruction y = definingInstruction(Phi.getValue(inst, i)); if (y == inst) instUses++; if (!(seen.contains(y))) { seen.add(y); workList.insert(y); } } while (!(workList.isEmpty())) { Instruction y = workList.remove(); if (Phi.conforms(y)) { for (int i = Phi.getNumberOfValues(y) - 1; i >= 0; --i) { Instruction z = definingInstruction(Phi.getValue(y, i)); if (z == inst) instUses++; if (!(seen.contains(z))) { seen.add(z); workList.insert(z); } } } else if ((y.isPEI()) || !LocationCarrier.conforms(y) || y.operator().isAcquire() || y.operator().isRelease()) { return CL_COMPLEX; } else { // check for access to volatile field LocationOperand loc = LocationCarrier.getLocation(y); if (loc == null || loc.mayBeVolatile()) { //VM.sysWriteln (" no loc or volatile field"); return CL_COMPLEX; } if (y.isImplicitStore()) { // only accept loop-invariant stores // conservatively estimate loop-invariance by header domination if (!inVariantLocation(y, block)) return CL_COMPLEX; _state |= CL_STORES_ONLY; } else { _state |= CL_LOADS_ONLY; } for (HeapOperand<?> op : ssad.getHeapUses(y)) { if (op.value.isExceptionHeapType()) continue; if (op.getHeapType() != hop.getHeapType()) return CL_COMPLEX; y = definingInstruction(op); if (y == inst) instUses++; if (!(seen.contains(y))) { seen.add(y); workList.insert(y); } } } } if (_state == CL_STORES_ONLY && ssad.getNumberOfUses(hop.value) != instUses) { return CL_COMPLEX; } return _state; } private boolean inVariantLocation(Instruction inst, BasicBlock block) { if (PutStatic.conforms(inst)) return true; if (PutField.conforms(inst)) { return useDominates(PutField.getRef(inst), block); } if (AStore.conforms(inst)) { return ((useDominates(AStore.getArray(inst), block)) && useDominates(AStore.getIndex(inst), block)); } if (VM.VerifyAssertions) { String msg = "inst does not conform to PutStatic, Putfield or AStore: " + inst; VM._assert(VM.NOT_REACHED, msg); } return false; } private boolean useDominates(Operand op, BasicBlock block) { if (!(op instanceof RegisterOperand)) return true; Instruction inst = definingInstruction(op); BasicBlock b = getOrigBlock(inst); return b != block && dominator.dominates(b, block); } /* * In the consumers of `inst', replace uses of `inst's result * with uses of `replacement' */ private boolean replaceUses(Instruction inst, HeapOperand<?> replacement, BasicBlockOperand replacementBlock, boolean onlyPEIs) { if (VM.VerifyAssertions) VM._assert(Phi.conforms(inst)); boolean changed = false; @SuppressWarnings("unchecked") // Cast to generic HeapOperand HeapOperand<Object> hop = (HeapOperand) Phi.getResult(inst); HeapVariable<Object> H = hop.value; Iterator<HeapOperand<Object>> it = ssad.iterateHeapUses(H); while (it.hasNext()) { hop = it.next(); Instruction user = hop.instruction; if (onlyPEIs && !user.isPEI()) continue; if (Phi.conforms(user)) { for (int i = 0; i < Phi.getNumberOfValues(user); i++) { if (Phi.getValue(user, i) == hop) { Phi.setValue(user, i, replacement.copy()); Phi.setPred(user, i, (BasicBlockOperand) replacementBlock.copy()); } } changed |= replacement.value != H; } else { HeapOperand<?>[] uses = ssad.getHeapUses(user); for (int i = uses.length - 1; i >= 0; --i) { if (uses[i].value == H) { changed |= replacement.value != H; uses[i] = replacement.copy(); uses[i].setInstruction(user); } } } if (DEBUG && changed) { VM.sysWriteln(" changing dependency of " + user + "\n" + "from " + H + " to " + replacement); } } if (!onlyPEIs) { for (int i = Phi.getNumberOfValues(inst) - 1; i >= 0; --i) { Phi.setValue(inst, i, replacement.copy()); } } return changed; } private void resetLandingPads() { Enumeration<BasicBlock> e = ir.getBasicBlocks(); while (e.hasMoreElements()) e.nextElement().clearLandingPad(); } }