/** * Replaces an old successor with a new successor. This will throw RuntimeException if {@code * oldIndex} was not a successor. * * @param oldIndex index of old successor block * @param newIndex index of new successor block */ public void replaceSuccessor(int oldIndex, int newIndex) { if (oldIndex == newIndex) { return; } // Update us. successors.set(newIndex); if (primarySuccessor == oldIndex) { primarySuccessor = newIndex; } for (int i = successorList.size() - 1; i >= 0; i--) { if (successorList.get(i) == oldIndex) { successorList.set(i, newIndex); } } successors.clear(oldIndex); // Update new successor. parent.getBlocks().get(newIndex).predecessors.set(index); // Update old successor. parent.getBlocks().get(oldIndex).predecessors.clear(index); }
/** * Inserts a new empty GOTO block as a predecessor to this block. All previous predecessors will * be predecessors to the new block. * * @return {@code non-null;} an appropriately-constructed instance */ public SsaBasicBlock insertNewPredecessor() { SsaBasicBlock newPred = parent.makeNewGotoBlock(); // Update the new block. newPred.predecessors = predecessors; newPred.successors.set(index); newPred.successorList.add(index); newPred.primarySuccessor = index; // Update us. predecessors = new BitSet(parent.getBlocks().size()); predecessors.set(newPred.index); // Update our (soon-to-be) old predecessors. for (int i = newPred.predecessors.nextSetBit(0); i >= 0; i = newPred.predecessors.nextSetBit(i + 1)) { SsaBasicBlock predBlock = parent.getBlocks().get(i); predBlock.replaceSuccessor(index, newPred.index); } return newPred; }
/** * Creates a new SSA basic block from a ROP form basic block. * * @param rmeth original method * @param basicBlockIndex index this block will have * @param parent method of this block predecessor set will be updated * @return new instance */ public static SsaBasicBlock newFromRop( RopMethod rmeth, int basicBlockIndex, final SsaMethod parent) { BasicBlockList ropBlocks = rmeth.getBlocks(); BasicBlock bb = ropBlocks.get(basicBlockIndex); SsaBasicBlock result = new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent); InsnList ropInsns = bb.getInsns(); result.insns.ensureCapacity(ropInsns.size()); for (int i = 0, sz = ropInsns.size(); i < sz; i++) { result.insns.add(new NormalSsaInsn(ropInsns.get(i), result)); } result.predecessors = SsaMethod.bitSetFromLabelList(ropBlocks, rmeth.labelToPredecessors(bb.getLabel())); result.successors = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors()); result.successorList = SsaMethod.indexListFromLabelList(ropBlocks, bb.getSuccessors()); if (result.successorList.size() != 0) { int primarySuccessor = bb.getPrimarySuccessor(); result.primarySuccessor = (primarySuccessor < 0) ? -1 : ropBlocks.indexOfLabel(primarySuccessor); } return result; }
/** * Creates a new empty basic block. * * @param basicBlockIndex index this block will have * @param ropLabel original rop-form label * @param parent method of this block */ public SsaBasicBlock(final int basicBlockIndex, final int ropLabel, final SsaMethod parent) { this.parent = parent; this.index = basicBlockIndex; this.insns = new ArrayList<SsaInsn>(); this.ropLabel = ropLabel; this.predecessors = new BitSet(parent.getBlocks().size()); this.successors = new BitSet(parent.getBlocks().size()); this.successorList = new IntList(); domChildren = new ArrayList<SsaBasicBlock>(); }
/** Applies the optimization. */ private void run() { int regSz = ssaMeth.getRegCount(); ArrayList<TypedConstant> constantList = getConstsSortedByCountUse(); int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS); SsaBasicBlock start = ssaMeth.getEntryBlock(); // Constant to new register containing the constant HashMap<TypedConstant, RegisterSpec> newRegs = new HashMap<TypedConstant, RegisterSpec>(toCollect); for (int i = 0; i < toCollect; i++) { TypedConstant cst = constantList.get(i); RegisterSpec result = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst); Rop constRop = Rops.opConst(cst); if (constRop.getBranchingness() == Rop.BRANCH_NONE) { start.addInsnToHead( new PlainCstInsn( Rops.opConst(cst), SourcePosition.NO_INFO, result, RegisterSpecList.EMPTY, cst)); } else { // We need two new basic blocks along with the new insn SsaBasicBlock entryBlock = ssaMeth.getEntryBlock(); SsaBasicBlock successorBlock = entryBlock.getPrimarySuccessor(); // Insert a block containing the const insn. SsaBasicBlock constBlock = entryBlock.insertNewSuccessor(successorBlock); constBlock.replaceLastInsn( new ThrowingCstInsn( constRop, SourcePosition.NO_INFO, RegisterSpecList.EMPTY, StdTypeList.EMPTY, cst)); // Insert a block containing the move-result-pseudo insn. SsaBasicBlock resultBlock = constBlock.insertNewSuccessor(successorBlock); PlainInsn insn = new PlainInsn( Rops.opMoveResultPseudo(result.getTypeBearer()), SourcePosition.NO_INFO, result, RegisterSpecList.EMPTY); resultBlock.addInsnToHead(insn); } newRegs.put(cst, result); } updateConstUses(newRegs, regSz); }
/** * Does the extraction. * * @return {@code non-null;} the extracted information */ private LocalVariableInfo doit() { // FIXME why is this needed here? if (method.getRegCount() > 0) { for (int bi = method.getEntryBlockIndex(); bi >= 0; bi = workSet.nextSetBit(0)) { workSet.clear(bi); processBlock(bi); } } resultInfo.setImmutable(); return resultInfo; }
/** * Replaces the last insn in this block. The provided insn must have some branchingness. * * @param insn {@code non-null;} rop-form insn to add, which must branch. */ public void replaceLastInsn(Insn insn) { if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { throw new IllegalArgumentException("last insn must branch"); } SsaInsn oldInsn = insns.get(insns.size() - 1); SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); insns.set(insns.size() - 1, newInsn); parent.onInsnRemoved(oldInsn); parent.onInsnAdded(newInsn); }
/** * Constructs and inserts a new empty GOTO block {@code Z} between this block ({@code A}) and a * current successor block ({@code B}). The new block will replace B as A's successor and A as B's * predecessor. A and B will no longer be directly connected. If B is listed as a successor * multiple times, all references are replaced. * * @param other current successor (B) * @return {@code non-null;} an appropriately-constructed instance */ public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) { SsaBasicBlock newSucc = parent.makeNewGotoBlock(); if (!successors.get(other.index)) { throw new RuntimeException( "Block " + other.getRopLabelString() + " not successor of " + getRopLabelString()); } // Update the new block. newSucc.predecessors.set(this.index); newSucc.successors.set(other.index); newSucc.successorList.add(other.index); newSucc.primarySuccessor = other.index; // Update us. for (int i = successorList.size() - 1; i >= 0; i--) { if (successorList.get(i) == other.index) { successorList.set(i, newSucc.index); } } if (primarySuccessor == other.index) { primarySuccessor = newSucc.index; } successors.clear(other.index); successors.set(newSucc.index); // Update "other". other.predecessors.set(newSucc.index); other.predecessors.set(index, successors.get(other.index)); return newSucc; }
/** @return {@code null-ok;} the primary successor block or {@code null} if there is none */ public SsaBasicBlock getPrimarySuccessor() { if (primarySuccessor < 0) { return null; } else { return parent.getBlocks().get(primarySuccessor); } }
/** * Adds {@code regV} to the live-in list for this block. This is called by the liveness analyzer. * * @param regV register that is live-in for this block. */ public void addLiveIn(int regV) { if (liveIn == null) { liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); } liveIn.add(regV); }
/** @return successor list of rop labels */ public IntList getRopLabelSuccessorList() { IntList result = new IntList(successorList.size()); int sz = successorList.size(); for (int i = 0; i < sz; i++) { result.add(parent.blockIndexToRopLabel(successorList.get(i))); } return result; }
/** * Constructs an instance. This method is private. Use {@link #extract}. * * @param method {@code non-null;} the method to extract from */ private LocalVariableExtractor(SsaMethod method) { if (method == null) { throw new NullPointerException("method == null"); } ArrayList<SsaBasicBlock> blocks = method.getBlocks(); this.method = method; this.blocks = blocks; this.resultInfo = new LocalVariableInfo(method); this.workSet = new BitSet(blocks.size()); }
/** Run the literal op upgrader */ private void run() { final TranslationAdvice advice = Optimizer.getAdvice(); ssaMeth.forEachInsn( new SsaInsn.Visitor() { public void visitMoveInsn(NormalSsaInsn insn) { // do nothing } public void visitPhiInsn(PhiInsn insn) { // do nothing } public void visitNonMoveInsn(NormalSsaInsn insn) { Insn originalRopInsn = insn.getOriginalRopInsn(); Rop opcode = originalRopInsn.getOpcode(); RegisterSpecList sources = insn.getSources(); // Replace insns with constant results with const insns if (tryReplacingWithConstant(insn)) return; if (sources.size() != 2) { // We're only dealing with two-source insns here. return; } if (opcode.getBranchingness() == Rop.BRANCH_IF) { /* * An if instruction can become an if-*z instruction. */ if (isConstIntZeroOrKnownNull(sources.get(0))) { replacePlainInsn( insn, sources.withoutFirst(), RegOps.flippedIfOpcode(opcode.getOpcode()), null); } else if (isConstIntZeroOrKnownNull(sources.get(1))) { replacePlainInsn(insn, sources.withoutLast(), opcode.getOpcode(), null); } } else if (advice.hasConstantOperation(opcode, sources.get(0), sources.get(1))) { insn.upgradeToLiteral(); } else if (opcode.isCommutative() && advice.hasConstantOperation(opcode, sources.get(1), sources.get(0))) { /* * An instruction can be commuted to a literal operation */ insn.setNewSources(RegisterSpecList.make(sources.get(1), sources.get(0))); insn.upgradeToLiteral(); } } }); }
/** * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The new PlainInsn is * constructed with a new RegOp and new sources. * * <p>TODO move this somewhere else. * * @param insn {@code non-null;} an SsaInsn containing a PlainInsn * @param newSources {@code non-null;} new sources list for new insn * @param newOpcode A RegOp from {@link RegOps} * @param cst {@code null-ok;} constant for new instruction, if any */ private void replacePlainInsn( NormalSsaInsn insn, RegisterSpecList newSources, int newOpcode, Constant cst) { Insn originalRopInsn = insn.getOriginalRopInsn(); Rop newRop = Rops.ropFor(newOpcode, insn.getResult(), newSources, cst); Insn newRopInsn; if (cst == null) { newRopInsn = new PlainInsn(newRop, originalRopInsn.getPosition(), insn.getResult(), newSources); } else { newRopInsn = new PlainCstInsn( newRop, originalRopInsn.getPosition(), insn.getResult(), newSources, cst); } NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock()); List<SsaInsn> insns = insn.getBlock().getInsns(); ssaMeth.onInsnRemoved(insn); insns.set(insns.lastIndexOf(insn), newInsn); ssaMeth.onInsnAdded(newInsn); }
/** * Tries to replace an instruction with a const instruction. The given instruction must have a * constant result for it to be replaced. * * @param insn {@code non-null;} instruction to try to replace * @return true if the instruction was replaced */ private boolean tryReplacingWithConstant(NormalSsaInsn insn) { Insn originalRopInsn = insn.getOriginalRopInsn(); Rop opcode = originalRopInsn.getOpcode(); RegisterSpec result = insn.getResult(); if (result != null && !ssaMeth.isRegALocal(result) && opcode.getOpcode() != RegOps.CONST) { TypeBearer type = insn.getResult().getTypeBearer(); if (type.isConstant() && type.getBasicType() == Type.BT_INT) { // Replace the instruction with a constant replacePlainInsn(insn, RegisterSpecList.EMPTY, RegOps.CONST, (Constant) type); // Remove the source as well if this is a move-result-pseudo if (opcode.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { int pred = insn.getBlock().getPredecessors().nextSetBit(0); ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns(); NormalSsaInsn sourceInsn = (NormalSsaInsn) predInsns.get(predInsns.size() - 1); replacePlainInsn(sourceInsn, RegisterSpecList.EMPTY, RegOps.GOTO, null); } return true; } } return false; }
/** * Removes a successor from this block's successor list. * * @param oldIndex index of successor block to remove */ public void removeSuccessor(int oldIndex) { int removeIndex = 0; for (int i = successorList.size() - 1; i >= 0; i--) { if (successorList.get(i) == oldIndex) { removeIndex = i; } else { primarySuccessor = successorList.get(i); } } successorList.removeIndex(removeIndex); successors.clear(oldIndex); parent.getBlocks().get(oldIndex).predecessors.clear(index); }
/** * Adds a move instruction to the end of this basic block, just before the last instruction. If * the result of the final instruction is the source in question, then the move is placed at the * beginning of the primary successor block. This is for unversioned registers. * * @param result move destination * @param source move source */ public void addMoveToEnd(RegisterSpec result, RegisterSpec source) { if (result.getReg() == source.getReg()) { // Sometimes we end up with no-op moves. Ignore them here. return; } /* * The last Insn has to be a normal SSA insn: a phi can't branch or * return or cause an exception, etc. */ NormalSsaInsn lastInsn; lastInsn = (NormalSsaInsn) insns.get(insns.size() - 1); if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) { /* * The final insn in this block has a source or result register, and * the moves we may need to place and schedule may interfere. We * need to insert this instruction at the beginning of the primary * successor block instead. We know this is safe, because when we * edge-split earlier, we ensured that each successor has only us as * a predecessor. */ for (int i = successors.nextSetBit(0); i >= 0; i = successors.nextSetBit(i + 1)) { SsaBasicBlock succ; succ = parent.getBlocks().get(i); succ.addMoveToBeginning(result, source); } } else { /* * We can safely add a move to the end of the block just before the * last instruction, because the final insn does not assign to * anything. */ RegisterSpecList sources = RegisterSpecList.make(source); NormalSsaInsn toAdd = new NormalSsaInsn( new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources), this); insns.add(insns.size() - 1, toAdd); movesFromPhisAtEnd++; } }
/** * Inserts mark-locals if necessary when changing a register. If the definition of {@code origReg} * is associated with a local variable, then insert a mark-local for {@code newReg} just below it. * We expect the definition of {@code origReg} to ultimately be removed by the dead code * eliminator * * @param origReg {@code non-null;} original register * @param newReg {@code non-null;} new register that will replace {@code origReg} */ private void fixLocalAssignment(RegisterSpec origReg, RegisterSpec newReg) { for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) { RegisterSpec localAssignment = use.getLocalAssignment(); if (localAssignment == null) { continue; } if (use.getResult() == null) { /* * This is a mark-local. it will be updated when all uses are * updated. */ continue; } LocalItem local = localAssignment.getLocalItem(); // Un-associate original use. use.setResultLocal(null); // Now add a mark-local to the new reg immediately after. newReg = newReg.withLocalItem(local); SsaInsn newInsn = SsaInsn.makeFromRop( new PlainInsn( Rops.opMarkLocal(newReg), SourcePosition.NO_INFO, null, RegisterSpecList.make(newReg)), use.getBlock()); ArrayList<SsaInsn> insns = use.getBlock().getInsns(); insns.add(insns.indexOf(use) + 1, newInsn); } }
/** * Gets all of the collectable constant values used in this method, sorted by most used first. * Skips non-collectable consts, such as non-string object constants * * @return {@code non-null;} list of constants in most-to-least used order */ private ArrayList<TypedConstant> getConstsSortedByCountUse() { int regSz = ssaMeth.getRegCount(); final HashMap<TypedConstant, Integer> countUses = new HashMap<TypedConstant, Integer>(); /* * Each collected constant can be used by just one local (used only if * COLLECT_ONE_LOCAL is true). */ final HashSet<TypedConstant> usedByLocal = new HashSet<TypedConstant>(); // Count how many times each const value is used. for (int i = 0; i < regSz; i++) { SsaInsn insn = ssaMeth.getDefinitionForRegister(i); if (insn == null || insn.getOpcode() == null) continue; RegisterSpec result = insn.getResult(); TypeBearer typeBearer = result.getTypeBearer(); if (!typeBearer.isConstant()) continue; TypedConstant cst = (TypedConstant) typeBearer; // Find defining instruction for move-result-pseudo instructions if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { int pred = insn.getBlock().getPredecessors().nextSetBit(0); ArrayList<SsaInsn> predInsns; predInsns = ssaMeth.getBlocks().get(pred).getInsns(); insn = predInsns.get(predInsns.size() - 1); } if (insn.canThrow()) { /* * Don't move anything other than strings -- the risk of * changing where an exception is thrown is too high. */ if (!(cst instanceof CstString) || !COLLECT_STRINGS) { continue; } /* * We can't move any throwable const whose throw will be caught, * so don't count them. */ if (insn.getBlock().getSuccessors().cardinality() > 1) { continue; } } /* * TODO: Might be nice to try and figure out which local wins most * when collected. */ if (ssaMeth.isRegALocal(result)) { if (!COLLECT_ONE_LOCAL) { continue; } else { if (usedByLocal.contains(cst)) { // Count one local usage only. continue; } else { usedByLocal.add(cst); } } } Integer has = countUses.get(cst); if (has == null) { countUses.put(cst, 1); } else { countUses.put(cst, has + 1); } } // Collect constants that have been reused. ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>(); for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) { if (entry.getValue() > 1) { constantList.add(entry.getKey()); } } // Sort by use, with most used at the beginning of the list. Collections.sort( constantList, new Comparator<Constant>() { public int compare(Constant a, Constant b) { int ret; ret = countUses.get(b) - countUses.get(a); if (ret == 0) { /* * Provide sorting determinisim for constants with same * usage count. */ ret = a.compareTo(b); } return ret; } @Override public boolean equals(Object obj) { return obj == this; } }); return constantList; }
/** * Updates all uses of various consts to use the values in the newly assigned registers. * * @param newRegs {@code non-null;} mapping between constant and new reg * @param origRegCount {@code >=0;} original SSA reg count, not including newly added constant * regs */ private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs, int origRegCount) { /* * set of constants associated with a local variable; used only if * COLLECT_ONE_LOCAL is true. */ final HashSet<TypedConstant> usedByLocal = new HashSet<TypedConstant>(); final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy(); for (int i = 0; i < origRegCount; i++) { SsaInsn insn = ssaMeth.getDefinitionForRegister(i); if (insn == null) { continue; } final RegisterSpec origReg = insn.getResult(); TypeBearer typeBearer = insn.getResult().getTypeBearer(); if (!typeBearer.isConstant()) continue; TypedConstant cst = (TypedConstant) typeBearer; final RegisterSpec newReg = newRegs.get(cst); if (newReg == null) { continue; } if (ssaMeth.isRegALocal(origReg)) { if (!COLLECT_ONE_LOCAL) { continue; } else { /* * TODO: If the same local gets the same cst multiple times, * it would be nice to reuse the register. */ if (usedByLocal.contains(cst)) { continue; } else { usedByLocal.add(cst); fixLocalAssignment(origReg, newRegs.get(cst)); } } } // maps an original const register to the new collected register RegisterMapper mapper = new RegisterMapper() { @Override public int getNewRegisterCount() { return ssaMeth.getRegCount(); } @Override public RegisterSpec map(RegisterSpec registerSpec) { if (registerSpec.getReg() == origReg.getReg()) { return newReg.withLocalItem(registerSpec.getLocalItem()); } return registerSpec; } }; for (SsaInsn use : useList[origReg.getReg()]) { if (use.canThrow() && use.getBlock().getSuccessors().cardinality() > 1) { continue; } use.mapSourceRegisters(mapper); } } }
/** * Adds an insn to the head of this basic block, just after any phi insns. * * @param insn {@code non-null;} rop-form insn to add */ public void addInsnToHead(Insn insn) { SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); insns.add(getCountPhiInsns(), newInsn); parent.onInsnAdded(newInsn); }
/** * Processes a single block. * * @param blockIndex {@code >= 0;} block index of the block to process */ private void processBlock(int blockIndex) { RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(blockIndex); SsaBasicBlock block = blocks.get(blockIndex); List<SsaInsn> insns = block.getInsns(); int insnSz = insns.size(); // The exit block has no insns and no successors if (blockIndex == method.getExitBlockIndex()) { return; } /* * We may have to treat the last instruction specially: If it * can (but doesn't always) throw, and the exception can be * caught within the same method, then we need to use the * state *before* executing it to be what is merged into * exception targets. */ SsaInsn lastInsn = insns.get(insnSz - 1); boolean hasExceptionHandlers = lastInsn.getOriginalRopInsn().getCatches().size() != 0; boolean canThrowDuringLastInsn = hasExceptionHandlers && (lastInsn.getResult() != null); int freezeSecondaryStateAt = insnSz - 1; RegisterSpecSet secondaryState = primaryState; /* * Iterate over the instructions, adding information for each place * that the active variable set changes. */ for (int i = 0; i < insnSz; i++) { if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) { // Until this point, primaryState == secondaryState. primaryState.setImmutable(); primaryState = primaryState.mutableCopy(); } SsaInsn insn = insns.get(i); RegisterSpec result; result = insn.getLocalAssignment(); if (result == null) { // We may be nuking an existing local result = insn.getResult(); if (result != null && primaryState.get(result.getReg()) != null) { primaryState.remove(primaryState.get(result.getReg())); } continue; } result = result.withSimpleType(); RegisterSpec already = primaryState.get(result); /* * The equals() check ensures we only add new info if * the instruction causes a change to the set of * active variables. */ if (!result.equals(already)) { /* * If this insn represents a local moving from one register * to another, remove the association between the old register * and the local. */ RegisterSpec previous = primaryState.localItemToSpec(result.getLocalItem()); if (previous != null && (previous.getReg() != result.getReg())) { primaryState.remove(previous); } resultInfo.addAssignment(insn, result); primaryState.put(result); } } primaryState.setImmutable(); /* * Merge this state into the start state for each successor, * and update the work set where required (that is, in cases * where the start state for a block changes). */ IntList successors = block.getSuccessorList(); int succSz = successors.size(); int primarySuccessor = block.getPrimarySuccessorIndex(); for (int i = 0; i < succSz; i++) { int succ = successors.get(i); RegisterSpecSet state = (succ == primarySuccessor) ? primaryState : secondaryState; if (resultInfo.mergeStarts(succ, state)) { workSet.set(succ); } } }
/** @return rop label of primary successor */ public int getPrimarySuccessorRopLabel() { return parent.blockIndexToRopLabel(primarySuccessor); }
/** * Sorts move instructions added via {@code addMoveToEnd} during phi removal so that results don't * overwrite sources that are used. For use after all phis have been removed and all calls to * addMoveToEnd() have been made. * * <p>This is necessary because copy-propogation may have left us in a state where the same basic * block has the same register as a phi operand and a result. In this case, the register in the * phi operand always refers value before any other phis have executed. */ public void scheduleMovesFromPhis() { if (movesFromPhisAtBeginning > 1) { List<SsaInsn> toSchedule; toSchedule = insns.subList(0, movesFromPhisAtBeginning); scheduleUseBeforeAssigned(toSchedule); SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning); /* * TODO: It's actually possible that this case never happens, * because a move-exception block, having only one predecessor in * SSA form, perhaps is never on a dominance frontier. */ if (firstNonPhiMoveInsn.isMoveException()) { if (true) { /* * We've yet to observe this case, and if it can occur the * code written to handle it probably does not work. */ throw new RuntimeException("Unexpected: moves from " + "phis before move-exception"); } else { /* * A move-exception insn must be placed first in this block * We need to move it there, and deal with possible * interference. */ boolean moveExceptionInterferes = false; int moveExceptionResult = firstNonPhiMoveInsn.getResult().getReg(); /* * Does the move-exception result reg interfere with the phi * moves? */ for (SsaInsn insn : toSchedule) { if (insn.isResultReg(moveExceptionResult) || insn.isRegASource(moveExceptionResult)) { moveExceptionInterferes = true; break; } } if (!moveExceptionInterferes) { // This is the easy case. insns.remove(movesFromPhisAtBeginning); insns.add(0, firstNonPhiMoveInsn); } else { /* * We need to move the result to a spare reg and move it * back. */ RegisterSpec originalResultSpec = firstNonPhiMoveInsn.getResult(); int spareRegister = parent.borrowSpareRegister(originalResultSpec.getCategory()); // We now move it to a spare register. firstNonPhiMoveInsn.changeResultReg(spareRegister); RegisterSpec tempSpec = firstNonPhiMoveInsn.getResult(); insns.add(0, firstNonPhiMoveInsn); // And here we move it back. NormalSsaInsn toAdd = new NormalSsaInsn( new PlainInsn( Rops.opMove(tempSpec.getType()), SourcePosition.NO_INFO, originalResultSpec, RegisterSpecList.make(tempSpec)), this); /* * Place it immediately after the phi-moves, overwriting * the move-exception that was there. */ insns.set(movesFromPhisAtBeginning + 1, toAdd); } } } } if (movesFromPhisAtEnd > 1) { scheduleUseBeforeAssigned( insns.subList(insns.size() - movesFromPhisAtEnd - 1, insns.size() - 1)); } // Return registers borrowed here and in scheduleUseBeforeAssigned(). parent.returnSpareRegisters(); }
/** * Returns true if this block was last calculated to be reachable. Recalculates reachability if * value has never been computed. * * @return {@code true} if reachable */ public boolean isReachable() { if (reachable == -1) { parent.computeReachability(); } return (reachable == 1); }
/** @return true if this is the one-and-only exit block for this method */ public boolean isExitBlock() { return index == parent.getExitBlockIndex(); }
/** * Returns the set of live-out registers. Valid after register interference graph has been * generated, otherwise empty. * * @return {@code non-null;} live-out register set */ public IntSet getLiveOutRegs() { if (liveOut == null) { liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); } return liveOut; }
/** * Ensures that all move operations in this block occur such that reads of any register happen * before writes to that register. NOTE: caller is expected to returnSpareRegisters()! TODO: See * Briggs, et al "Practical Improvements to the Construction and Destruction of Static Single * Assignment Form" section 5. a) This can be done in three passes. * * @param toSchedule List of instructions. Must consist only of moves. */ private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) { BitSet regsUsedAsSources = new BitSet(parent.getRegCount()); // TODO: Get rid of this. BitSet regsUsedAsResults = new BitSet(parent.getRegCount()); int sz = toSchedule.size(); int insertPlace = 0; while (insertPlace < sz) { int oldInsertPlace = insertPlace; // Record all registers used as sources in this block. for (int i = insertPlace; i < sz; i++) { setRegsUsed(regsUsedAsSources, toSchedule.get(i).getSources().get(0)); setRegsUsed(regsUsedAsResults, toSchedule.get(i).getResult()); } /* * If there are no circular dependencies, then there exists n * instructions where n > 1 whose result is not used as a source. */ for (int i = insertPlace; i < sz; i++) { SsaInsn insn = toSchedule.get(i); /* * Move these n registers to the front, since they overwrite * nothing. */ if (!checkRegUsed(regsUsedAsSources, insn.getResult())) { Collections.swap(toSchedule, i, insertPlace++); } } /* * If we've made no progress in this iteration, there's a circular * dependency. Split it using the temp reg. */ if (oldInsertPlace == insertPlace) { SsaInsn insnToSplit = null; // Find an insn whose result is used as a source. for (int i = insertPlace; i < sz; i++) { SsaInsn insn = toSchedule.get(i); if (checkRegUsed(regsUsedAsSources, insn.getResult()) && checkRegUsed(regsUsedAsResults, insn.getSources().get(0))) { insnToSplit = insn; /* * We're going to split this insn; move it to the front. */ Collections.swap(toSchedule, insertPlace, i); break; } } // At least one insn will be set above. RegisterSpec result = insnToSplit.getResult(); RegisterSpec tempSpec = result.withReg(parent.borrowSpareRegister(result.getCategory())); NormalSsaInsn toAdd = new NormalSsaInsn( new PlainInsn( Rops.opMove(result.getType()), SourcePosition.NO_INFO, tempSpec, insnToSplit.getSources()), this); toSchedule.add(insertPlace++, toAdd); RegisterSpecList newSources = RegisterSpecList.make(tempSpec); NormalSsaInsn toReplace = new NormalSsaInsn( new PlainInsn( Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, newSources), this); toSchedule.set(insertPlace, toReplace); // The size changed. sz = toSchedule.size(); } regsUsedAsSources.clear(); regsUsedAsResults.clear(); } }