/** * 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(); }
/** * Checks to see if the register is used in a bitset, taking into account its category/width. * * @param regsUsed set, indexed by register number * @param rs register to mark as used * @return true if register is fully or partially (for the case of wide registers) used. */ private static boolean checkRegUsed(BitSet regsUsed, RegisterSpec rs) { int reg = rs.getReg(); int category = rs.getCategory(); return regsUsed.get(reg) || (category == 2 ? regsUsed.get(reg + 1) : false); }
/** * 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(); } }
/** * Sets the register as used in a bitset, taking into account its category/width. * * @param regsUsed set, indexed by register number * @param rs register to mark as used */ private static void setRegsUsed(BitSet regsUsed, RegisterSpec rs) { regsUsed.set(rs.getReg()); if (rs.getCategory() > 1) { regsUsed.set(rs.getReg() + 1); } }