/** * Adds a move instruction after the phi insn block. * * @param result move destination * @param source move source */ public void addMoveToBeginning(RegisterSpec result, RegisterSpec source) { if (result.getReg() == source.getReg()) { // Sometimes we end up with no-op moves. Ignore them here. return; } RegisterSpecList sources = RegisterSpecList.make(source); NormalSsaInsn toAdd = new NormalSsaInsn( new PlainInsn(Rops.opMove(result.getType()), SourcePosition.NO_INFO, result, sources), this); insns.add(getCountPhiInsns(), toAdd); movesFromPhisAtBeginning++; }
/** * 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++; } }
/** {@inheritDoc} */ @Override public boolean isCompatible(DalvInsn insn) { if (!(insn instanceof CstInsn)) { return false; } RegisterSpecList regs = insn.getRegisters(); RegisterSpec reg; switch (regs.size()) { case 1: { reg = regs.get(0); break; } case 2: { /* * This format is allowed for ops that are effectively * 2-arg but where the two args are identical. */ reg = regs.get(0); if (reg.getReg() != regs.get(1).getReg()) { return false; } break; } default: { return false; } } if (!unsignedFitsInByte(reg.getReg())) { return false; } CstInsn ci = (CstInsn) insn; int cpi = ci.getIndex(); if (!unsignedFitsInShort(cpi)) { return false; } Constant cst = ci.getConstant(); return (cst instanceof CstType) || (cst instanceof CstFieldRef) || (cst instanceof CstString); }
/** * 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); } }
/** * 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); } } }
/** * 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); }
/** * 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); } }
/** * 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); } } }