| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.ssa; |
| |
| import com.android.dx.rop.code.BasicBlockList; |
| import com.android.dx.rop.code.Insn; |
| import com.android.dx.rop.code.PlainInsn; |
| import com.android.dx.rop.code.RegOps; |
| import com.android.dx.rop.code.RegisterSpec; |
| import com.android.dx.rop.code.RegisterSpecList; |
| import com.android.dx.rop.code.Rop; |
| import com.android.dx.rop.code.RopMethod; |
| import com.android.dx.rop.code.Rops; |
| import com.android.dx.rop.code.SourcePosition; |
| import com.android.dx.util.IntList; |
| |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| /** |
| * A method in SSA form. |
| */ |
| public final class SsaMethod { |
| /** basic blocks, indexed by block index */ |
| private ArrayList<SsaBasicBlock> blocks; |
| |
| /** Index of first executed block in method */ |
| private int entryBlockIndex; |
| |
| /** |
| * Index of exit block, which exists only in SSA form, |
| * or or {@code -1} if there is none |
| */ |
| private int exitBlockIndex; |
| |
| /** total number of registers required */ |
| private int registerCount; |
| |
| /** first register number to use for any temporary "spares" */ |
| private int spareRegisterBase; |
| |
| /** current count of spare registers used */ |
| private int borrowedSpareRegisters; |
| |
| /** really one greater than the max label */ |
| private int maxLabel; |
| |
| /** the total width, in register-units, of the method's parameters */ |
| private final int paramWidth; |
| |
| /** true if this method has no {@code this} pointer argument */ |
| private final boolean isStatic; |
| |
| /** |
| * indexed by register: the insn where said register is defined or null |
| * if undefined. null until (lazily) created. |
| */ |
| private SsaInsn[] definitionList; |
| |
| /** indexed by register: the list of all insns that use a register */ |
| private ArrayList<SsaInsn>[] useList; |
| |
| /** A version of useList with each List unmodifiable */ |
| private List<SsaInsn>[] unmodifiableUseList; |
| |
| /** |
| * "back-convert mode". Set during back-conversion when registers |
| * are about to be mapped into a non-SSA namespace. When true, |
| * use and def lists are unavailable. |
| * |
| * TODO: Remove this mode, and place the functionality elsewhere |
| */ |
| private boolean backMode; |
| |
| /** |
| * @param ropMethod rop-form method to convert from |
| * @param paramWidth the total width, in register-units, of the |
| * method's parameters |
| * @param isStatic {@code true} if this method has no {@code this} |
| * pointer argument |
| */ |
| public static SsaMethod newFromRopMethod(RopMethod ropMethod, |
| int paramWidth, boolean isStatic) { |
| SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic); |
| |
| result.convertRopToSsaBlocks(ropMethod); |
| |
| return result; |
| } |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param ropMethod {@code non-null;} the original rop-form method that |
| * this instance is based on |
| * @param paramWidth the total width, in register-units, of the |
| * method's parameters |
| * @param isStatic {@code true} if this method has no {@code this} |
| * pointer argument |
| */ |
| private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) { |
| this.paramWidth = paramWidth; |
| this.isStatic = isStatic; |
| this.backMode = false; |
| this.maxLabel = ropMethod.getBlocks().getMaxLabel(); |
| this.registerCount = ropMethod.getBlocks().getRegCount(); |
| this.spareRegisterBase = registerCount; |
| } |
| |
| /** |
| * Builds a BitSet of block indices from a basic block list and a list |
| * of labels taken from Rop form. |
| * |
| * @param blocks Rop blocks |
| * @param labelList list of rop block labels |
| * @return BitSet of block indices |
| */ |
| static BitSet bitSetFromLabelList(BasicBlockList blocks, |
| IntList labelList) { |
| BitSet result = new BitSet(blocks.size()); |
| |
| for (int i = 0, sz = labelList.size(); i < sz; i++) { |
| result.set(blocks.indexOfLabel(labelList.get(i))); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Builds an IntList of block indices from a basic block list and a list |
| * of labels taken from Rop form. |
| * |
| * @param ropBlocks Rop blocks |
| * @param labelList list of rop block labels |
| * @return IntList of block indices |
| */ |
| public static IntList indexListFromLabelList(BasicBlockList ropBlocks, |
| IntList labelList) { |
| |
| IntList result = new IntList(labelList.size()); |
| |
| for (int i = 0, sz = labelList.size(); i < sz; i++) { |
| result.add(ropBlocks.indexOfLabel(labelList.get(i))); |
| } |
| |
| return result; |
| } |
| |
| private void convertRopToSsaBlocks(RopMethod rmeth) { |
| BasicBlockList ropBlocks = rmeth.getBlocks(); |
| int sz = ropBlocks.size(); |
| |
| blocks = new ArrayList<SsaBasicBlock>(sz + 2); |
| |
| for (int i = 0; i < sz; i++) { |
| SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this); |
| blocks.add(sbb); |
| } |
| |
| // Add an no-op entry block. |
| int origEntryBlockIndex = rmeth.getBlocks() |
| .indexOfLabel(rmeth.getFirstLabel()); |
| |
| SsaBasicBlock entryBlock |
| = blocks.get(origEntryBlockIndex).insertNewPredecessor(); |
| |
| entryBlockIndex = entryBlock.getIndex(); |
| exitBlockIndex = -1; // This gets made later. |
| } |
| |
| /** |
| * Creates an exit block and attaches it to the CFG if this method |
| * exits. Methods that never exit will not have an exit block. This |
| * is called after edge-splitting and phi insertion, since the edges |
| * going into the exit block should not be considered in those steps. |
| */ |
| /*package*/ void makeExitBlock() { |
| if (exitBlockIndex >= 0) { |
| throw new RuntimeException("must be called at most once"); |
| } |
| |
| exitBlockIndex = blocks.size(); |
| SsaBasicBlock exitBlock |
| = new SsaBasicBlock(exitBlockIndex, maxLabel++, this); |
| |
| blocks.add(exitBlock); |
| |
| for (SsaBasicBlock block : blocks) { |
| block.exitBlockFixup(exitBlock); |
| } |
| |
| if (exitBlock.getPredecessors().cardinality() == 0) { |
| // In cases where there is no exit... |
| blocks.remove(exitBlockIndex); |
| exitBlockIndex = -1; |
| maxLabel--; |
| } |
| } |
| |
| /** |
| * Gets a new {@code GOTO} insn. |
| * |
| * @param block block to which this GOTO will be added |
| * (not it's destination!) |
| * @return an appropriately-constructed instance. |
| */ |
| private static SsaInsn getGoto(SsaBasicBlock block) { |
| return new NormalSsaInsn ( |
| new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO, |
| null, RegisterSpecList.EMPTY), block); |
| } |
| |
| /** |
| * Makes a new basic block for this method, which is empty besides |
| * a single {@code GOTO}. Successors and predecessors are not yet |
| * set. |
| * |
| * @return new block |
| */ |
| public SsaBasicBlock makeNewGotoBlock() { |
| int newIndex = blocks.size(); |
| SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this); |
| |
| newBlock.getInsns().add(getGoto(newBlock)); |
| blocks.add(newBlock); |
| |
| return newBlock; |
| } |
| |
| /** |
| * @return block index of first execution block |
| */ |
| public int getEntryBlockIndex() { |
| return entryBlockIndex; |
| } |
| |
| /** |
| * @return first execution block |
| */ |
| public SsaBasicBlock getEntryBlock() { |
| return blocks.get(entryBlockIndex); |
| } |
| |
| /** |
| * @return block index of exit block or {@code -1} if there is none |
| */ |
| public int getExitBlockIndex() { |
| return exitBlockIndex; |
| } |
| |
| /** |
| * @return {@code null-ok;} block of exit block or {@code null} if |
| * there is none |
| */ |
| public SsaBasicBlock getExitBlock() { |
| return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex); |
| } |
| |
| /** |
| * @param bi block index or {@code -1} for none |
| * @return rop label or {code -1} if {@code bi} was {@code -1} |
| */ |
| public int blockIndexToRopLabel(int bi) { |
| if (bi < 0) { |
| return -1; |
| } |
| return blocks.get(bi).getRopLabel(); |
| } |
| |
| /** |
| * @return count of registers used in this method |
| */ |
| public int getRegCount() { |
| return registerCount; |
| } |
| |
| /** |
| * @return the total width, in register units, of the method's |
| * parameters |
| */ |
| public int getParamWidth() { |
| return paramWidth; |
| } |
| |
| /** |
| * Returns {@code true} if this is a static method. |
| * |
| * @return {@code true} if this is a static method |
| */ |
| public boolean isStatic() { |
| return isStatic; |
| } |
| |
| /** |
| * Borrows a register to use as a temp. Used in the phi removal process. |
| * Call returnSpareRegisters() when done. |
| * |
| * @param category width (1 or 2) of the register |
| * @return register number to use |
| */ |
| public int borrowSpareRegister(int category) { |
| int result = spareRegisterBase + borrowedSpareRegisters; |
| |
| borrowedSpareRegisters += category; |
| registerCount = Math.max(registerCount, result + category); |
| |
| return result; |
| } |
| |
| /** |
| * Returns all borrowed registers. |
| */ |
| public void returnSpareRegisters() { |
| borrowedSpareRegisters = 0; |
| } |
| |
| /** |
| * @return {@code non-null;} basic block list. Do not modify. |
| */ |
| public ArrayList<SsaBasicBlock> getBlocks() { |
| return blocks; |
| } |
| |
| /** |
| * Returns the count of reachable blocks in this method: blocks that have |
| * predecessors (or are the start block) |
| * |
| * @return {@code >= 0;} number of reachable basic blocks |
| */ |
| public int getCountReachableBlocks() { |
| int ret = 0; |
| |
| for (SsaBasicBlock b : blocks) { |
| // Blocks that have been disconnected don't count. |
| if (b.isReachable()) { |
| ret++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Computes reachability for all blocks in the method. First clears old |
| * values from all blocks, then starts with the entry block and walks down |
| * the control flow graph, marking all blocks it finds as reachable. |
| */ |
| public void computeReachability() { |
| for (SsaBasicBlock block : blocks) { |
| block.setReachable(0); |
| } |
| |
| ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>(); |
| blockList.add(this.getEntryBlock()); |
| |
| while (!blockList.isEmpty()) { |
| SsaBasicBlock block = blockList.remove(0); |
| if (block.isReachable()) continue; |
| |
| block.setReachable(1); |
| BitSet succs = block.getSuccessors(); |
| for (int i = succs.nextSetBit(0); i >= 0; |
| i = succs.nextSetBit(i + 1)) { |
| blockList.add(blocks.get(i)); |
| } |
| } |
| } |
| |
| /** |
| * Remaps unversioned registers. |
| * |
| * @param mapper maps old registers to new. |
| */ |
| public void mapRegisters(RegisterMapper mapper) { |
| for (SsaBasicBlock block : getBlocks()) { |
| for (SsaInsn insn : block.getInsns()) { |
| insn.mapRegisters(mapper); |
| } |
| } |
| |
| registerCount = mapper.getNewRegisterCount(); |
| spareRegisterBase = registerCount; |
| } |
| |
| /** |
| * Returns the insn that defines the given register |
| * @param reg register in question |
| * @return insn (actual instance from code) that defined this reg or null |
| * if reg is not defined. |
| */ |
| public SsaInsn getDefinitionForRegister(int reg) { |
| if (backMode) { |
| throw new RuntimeException("No def list in back mode"); |
| } |
| |
| if (definitionList != null) { |
| return definitionList[reg]; |
| } |
| |
| definitionList = new SsaInsn[getRegCount()]; |
| |
| forEachInsn(new SsaInsn.Visitor() { |
| public void visitMoveInsn (NormalSsaInsn insn) { |
| definitionList[insn.getResult().getReg()] = insn; |
| } |
| public void visitPhiInsn (PhiInsn phi) { |
| definitionList[phi.getResult().getReg()] = phi; |
| } |
| public void visitNonMoveInsn (NormalSsaInsn insn) { |
| RegisterSpec result = insn.getResult(); |
| if (result != null) { |
| definitionList[insn.getResult().getReg()] = insn; |
| } |
| } |
| }); |
| |
| return definitionList[reg]; |
| } |
| |
| /** |
| * Builds useList and unmodifiableUseList. |
| */ |
| private void buildUseList() { |
| if (backMode) { |
| throw new RuntimeException("No use list in back mode"); |
| } |
| |
| useList = new ArrayList[registerCount]; |
| |
| for (int i = 0; i < registerCount; i++) { |
| useList[i] = new ArrayList(); |
| } |
| |
| forEachInsn(new SsaInsn.Visitor() { |
| /** {@inheritDoc} */ |
| public void visitMoveInsn (NormalSsaInsn insn) { |
| addToUses(insn); |
| } |
| /** {@inheritDoc} */ |
| public void visitPhiInsn (PhiInsn phi) { |
| addToUses(phi); |
| } |
| /** {@inheritDoc} */ |
| public void visitNonMoveInsn (NormalSsaInsn insn) { |
| addToUses(insn); |
| } |
| /** |
| * Adds specified insn to the uses list for all of its sources. |
| * @param insn {@code non-null;} insn to process |
| */ |
| private void addToUses(SsaInsn insn) { |
| RegisterSpecList rl = insn.getSources(); |
| int sz = rl.size(); |
| |
| for (int i = 0; i < sz; i++) { |
| useList[rl.get(i).getReg()].add(insn); |
| } |
| } |
| }); |
| |
| unmodifiableUseList = new List[registerCount]; |
| |
| for (int i = 0; i < registerCount; i++) { |
| unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]); |
| } |
| } |
| |
| /** |
| * Updates the use list for a single change in source register. |
| * |
| * @param insn {@code non-null;} insn being changed |
| * @param oldSource {@code null-ok;} The source that was used, if |
| * applicable |
| * @param newSource {@code non-null;} the new source being used |
| */ |
| /*package*/ void onSourceChanged(SsaInsn insn, |
| RegisterSpec oldSource, RegisterSpec newSource) { |
| if (useList == null) return; |
| |
| if (oldSource != null) { |
| int reg = oldSource.getReg(); |
| useList[reg].remove(insn); |
| } |
| |
| int reg = newSource.getReg(); |
| if (useList.length <= reg) { |
| useList = null; |
| return; |
| } |
| useList[reg].add(insn); |
| } |
| |
| /** |
| * Updates the use list for a source list change. |
| * |
| * @param insn {@code insn non-null;} insn being changed. |
| * {@code insn.getSources()} must return the new source list. |
| * @param oldSources {@code null-ok;} list of sources that were |
| * previously used |
| */ |
| /*package*/ void onSourcesChanged(SsaInsn insn, |
| RegisterSpecList oldSources) { |
| if (useList == null) return; |
| |
| if (oldSources != null) { |
| removeFromUseList(insn, oldSources); |
| } |
| |
| RegisterSpecList sources = insn.getSources(); |
| int szNew = sources.size(); |
| |
| for (int i = 0; i < szNew; i++) { |
| int reg = sources.get(i).getReg(); |
| useList[reg].add(insn); |
| } |
| } |
| |
| /** |
| * Removes a given {@code insn} from the use lists for the given |
| * {@code oldSources} (rather than the sources currently |
| * returned by insn.getSources()). |
| * |
| * @param insn {@code non-null;} insn in question |
| * @param oldSources {@code null-ok;} registers whose use lists |
| * {@code insn} should be removed form |
| */ |
| private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) { |
| if (oldSources == null) { |
| return; |
| } |
| |
| int szNew = oldSources.size(); |
| for (int i = 0; i < szNew; i++) { |
| if (!useList[oldSources.get(i).getReg()].remove(insn)) { |
| throw new RuntimeException("use not found"); |
| } |
| } |
| } |
| |
| /** |
| * Adds an insn to both the use and def lists. For use when adding |
| * a new insn to the method. |
| * |
| * @param insn {@code non-null;} insn to add |
| */ |
| /*package*/ void onInsnAdded(SsaInsn insn) { |
| onSourcesChanged(insn, null); |
| updateOneDefinition(insn, null); |
| } |
| |
| /** |
| * Removes an instruction from use and def lists. For use during |
| * instruction removal. |
| * |
| * @param insn {@code non-null;} insn to remove |
| */ |
| /*package*/ void onInsnRemoved(SsaInsn insn) { |
| if (useList != null) { |
| removeFromUseList(insn, insn.getSources()); |
| } |
| |
| RegisterSpec resultReg = insn.getResult(); |
| if (definitionList != null && resultReg != null) { |
| definitionList[resultReg.getReg()] = null; |
| } |
| } |
| |
| /** |
| * Indicates that the instruction list has changed or the SSA register |
| * count has increased, so that internal datastructures that rely on |
| * it should be rebuild. In general, the various other on* methods |
| * should be called in preference when changes occur if they are |
| * applicable. |
| */ |
| public void onInsnsChanged() { |
| // Definition list will need to be recomputed |
| definitionList = null; |
| |
| // Use list will need to be recomputed |
| useList = null; |
| unmodifiableUseList = null; |
| } |
| |
| /** |
| * Updates a single definition. |
| * |
| * @param insn {@code non-null;} insn who's result should be recorded as |
| * a definition |
| * @param oldResult {@code null-ok;} a previous result that should |
| * be no longer considered a definition by this insn |
| */ |
| /*package*/ void updateOneDefinition(SsaInsn insn, |
| RegisterSpec oldResult) { |
| if (definitionList == null) return; |
| |
| if (oldResult != null) { |
| int reg = oldResult.getReg(); |
| definitionList[reg] = null; |
| } |
| |
| RegisterSpec resultReg = insn.getResult(); |
| |
| if (resultReg != null) { |
| int reg = resultReg.getReg(); |
| |
| if (definitionList[reg] != null) { |
| throw new RuntimeException("Duplicate add of insn"); |
| } else { |
| definitionList[resultReg.getReg()] = insn; |
| } |
| } |
| } |
| |
| /** |
| * Returns the list of all source uses (not results) for a register. |
| * |
| * @param reg register in question |
| * @return unmodifiable instruction list |
| */ |
| public List<SsaInsn> getUseListForRegister(int reg) { |
| |
| if (unmodifiableUseList == null) { |
| buildUseList(); |
| } |
| |
| return unmodifiableUseList[reg]; |
| } |
| |
| /** |
| * Returns a modifiable copy of the register use list. |
| * |
| * @return modifiable copy of the use-list, indexed by register |
| */ |
| public ArrayList<SsaInsn>[] getUseListCopy() { |
| if (useList == null) { |
| buildUseList(); |
| } |
| |
| ArrayList<SsaInsn>[] useListCopy |
| = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]); |
| |
| for (int i = 0; i < registerCount; i++) { |
| useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i])); |
| } |
| |
| return useListCopy; |
| } |
| |
| /** |
| * Checks to see if the given SSA reg is ever associated with a local |
| * local variable. Each SSA reg may be associated with at most one |
| * local var. |
| * |
| * @param spec {@code non-null;} ssa reg |
| * @return true if reg is ever associated with a local |
| */ |
| public boolean isRegALocal(RegisterSpec spec) { |
| SsaInsn defn = getDefinitionForRegister(spec.getReg()); |
| |
| if (defn == null) { |
| // version 0 registers are never used as locals |
| return false; |
| } |
| |
| // Does the definition have a local associated with it? |
| if (defn.getLocalAssignment() != null) return true; |
| |
| // If not, is there a mark-local insn? |
| for (SsaInsn use : getUseListForRegister(spec.getReg())) { |
| Insn insn = use.getOriginalRopInsn(); |
| |
| if (insn != null |
| && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Sets the new register count after renaming. |
| * |
| * @param newRegCount new register count |
| */ |
| /*package*/ void setNewRegCount(int newRegCount) { |
| registerCount = newRegCount; |
| spareRegisterBase = registerCount; |
| onInsnsChanged(); |
| } |
| |
| /** |
| * Makes a new SSA register. For use after renaming has completed. |
| * |
| * @return {@code >=0;} new SSA register. |
| */ |
| public int makeNewSsaReg() { |
| int reg = registerCount++; |
| spareRegisterBase = registerCount; |
| onInsnsChanged(); |
| return reg; |
| } |
| |
| /** |
| * Visits all insns in this method. |
| * |
| * @param visitor {@code non-null;} callback interface |
| */ |
| public void forEachInsn(SsaInsn.Visitor visitor) { |
| for (SsaBasicBlock block : blocks) { |
| block.forEachInsn(visitor); |
| } |
| } |
| |
| /** |
| * Visits each phi insn in this method |
| * @param v {@code non-null;} callback. |
| * |
| */ |
| public void forEachPhiInsn(PhiInsn.Visitor v) { |
| for (SsaBasicBlock block : blocks) { |
| block.forEachPhiInsn(v); |
| } |
| } |
| |
| |
| /** |
| * Walks the basic block tree in depth-first order, calling the visitor |
| * method once for every block. This depth-first walk may be run forward |
| * from the method entry point or backwards from the method exit points. |
| * |
| * @param reverse true if this should walk backwards from the exit points |
| * @param v {@code non-null;} callback interface. {@code parent} is set |
| * unless this is the root node |
| */ |
| public void forEachBlockDepthFirst(boolean reverse, |
| SsaBasicBlock.Visitor v) { |
| BitSet visited = new BitSet(blocks.size()); |
| |
| // We push the parent first, then the child on the stack. |
| Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); |
| |
| SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock(); |
| |
| if (rootBlock == null) { |
| // in the case there's no exit block |
| return; |
| } |
| |
| stack.add(null); // Start with null parent. |
| stack.add(rootBlock); |
| |
| while (stack.size() > 0) { |
| SsaBasicBlock cur = stack.pop(); |
| SsaBasicBlock parent = stack.pop(); |
| |
| if (!visited.get(cur.getIndex())) { |
| BitSet children |
| = reverse ? cur.getPredecessors() : cur.getSuccessors(); |
| for (int i = children.nextSetBit(0); i >= 0 |
| ; i = children.nextSetBit(i + 1)) { |
| stack.add(cur); |
| stack.add(blocks.get(i)); |
| } |
| visited.set(cur.getIndex()); |
| v.visitBlock(cur, parent); |
| } |
| } |
| } |
| |
| /** |
| * Visits blocks in dom-tree order, starting at the current node. |
| * The {@code parent} parameter of the Visitor.visitBlock callback |
| * is currently always set to null. |
| * |
| * @param v {@code non-null;} callback interface |
| */ |
| public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) { |
| BitSet visited = new BitSet(getBlocks().size()); |
| Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>(); |
| |
| stack.add(getEntryBlock()); |
| |
| while (stack.size() > 0) { |
| SsaBasicBlock cur = stack.pop(); |
| ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren(); |
| |
| if (!visited.get(cur.getIndex())) { |
| // We walk the tree this way for historical reasons... |
| for (int i = curDomChildren.size() - 1; i >= 0; i--) { |
| SsaBasicBlock child = curDomChildren.get(i); |
| stack.add(child); |
| } |
| visited.set(cur.getIndex()); |
| v.visitBlock(cur, null); |
| } |
| } |
| } |
| |
| /** |
| * Deletes all insns in the set from this method. |
| * |
| * @param deletedInsns {@code non-null;} insns to delete |
| */ |
| public void deleteInsns(Set<SsaInsn> deletedInsns) { |
| for (SsaBasicBlock block : getBlocks()) { |
| ArrayList<SsaInsn> insns = block.getInsns(); |
| |
| for (int i = insns.size() - 1; i >= 0; i--) { |
| SsaInsn insn = insns.get(i); |
| |
| if (deletedInsns.contains(insn)) { |
| onInsnRemoved(insn); |
| insns.remove(i); |
| } |
| } |
| |
| // Check to see if we need to add a GOTO |
| |
| int insnsSz = insns.size(); |
| SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1); |
| |
| if (block != getExitBlock() && (insnsSz == 0 |
| || lastInsn.getOriginalRopInsn() == null |
| || lastInsn.getOriginalRopInsn().getOpcode() |
| .getBranchingness() == Rop.BRANCH_NONE)) { |
| // We managed to eat a throwable insn |
| |
| Insn gotoInsn = new PlainInsn(Rops.GOTO, |
| SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY); |
| insns.add(SsaInsn.makeFromRop(gotoInsn, block)); |
| |
| // Remove secondary successors from this block |
| BitSet succs = block.getSuccessors(); |
| for (int i = succs.nextSetBit(0); i >= 0; |
| i = succs.nextSetBit(i + 1)) { |
| if (i != block.getPrimarySuccessorIndex()) { |
| block.removeSuccessor(i); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sets "back-convert mode". Set during back-conversion when registers |
| * are about to be mapped into a non-SSA namespace. When true, |
| * use and def lists are unavailable. |
| */ |
| public void setBackMode() { |
| backMode = true; |
| useList = null; |
| definitionList = null; |
| } |
| } |