/* * 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.RegisterSpec; import com.android.dx.rop.code.RopMethod; import com.android.dx.util.IntIterator; import java.util.ArrayList; import java.util.BitSet; /** * Converts ROP methods to SSA Methods */ public class SsaConverter { public static final boolean DEBUG = false; /** * Returns an SSA representation, edge-split and with phi * functions placed. * * @param rmeth input * @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 * @return output in SSA form */ public static SsaMethod convertToSsaMethod(RopMethod rmeth, int paramWidth, boolean isStatic) { SsaMethod result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); edgeSplit(result); LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); placePhiFunctions(result, localInfo); new SsaRenamer(result).run(); /* * The exit block, added here, is not considered for edge splitting * or phi placement since no actual control flows to it. */ result.makeExitBlock(); return result; } /** * Returns an SSA represention with only the edge-splitter run. * * @param rmeth method to process * @param paramWidth width of all arguments in the method * @param isStatic {@code true} if this method has no {@code this} * pointer argument * @return an SSA represention with only the edge-splitter run */ public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth, boolean isStatic) { SsaMethod result; result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); edgeSplit(result); return result; } /** * Returns an SSA represention with only the steps through the * phi placement run. * * @param rmeth method to process * @param paramWidth width of all arguments in the method * @param isStatic {@code true} if this method has no {@code this} * pointer argument * @return an SSA represention with only the edge-splitter run */ public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth, boolean isStatic) { SsaMethod result; result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); edgeSplit(result); LocalVariableInfo localInfo = LocalVariableExtractor.extract(result); placePhiFunctions(result, localInfo); return result; } /** * See Appel section 19.1: * * Converts CFG into "edge-split" form, such that each node either a * unique successor or unique predecessor.<p> * * In addition, the SSA form we use enforces a further constraint, * requiring each block with a final instruction that returns a * value to have a primary successor that has no other * predecessor. This ensures move statements can always be * inserted correctly when phi statements are removed. * * @param result method to process */ private static void edgeSplit(SsaMethod result) { edgeSplitPredecessors(result); edgeSplitMoveExceptionsAndResults(result); edgeSplitSuccessors(result); } /** * Inserts Z nodes as new predecessors for every node that has multiple * successors and multiple predecessors. * * @param result {@code non-null;} method to process */ private static void edgeSplitPredecessors(SsaMethod result) { ArrayList<SsaBasicBlock> blocks = result.getBlocks(); /* * New blocks are added to the end of the block list during * this iteration. */ for (int i = blocks.size() - 1; i >= 0; i-- ) { SsaBasicBlock block = blocks.get(i); if (nodeNeedsUniquePredecessor(block)) { block.insertNewPredecessor(); } } } /** * @param block {@code non-null;} block in question * @return {@code true} if this node needs to have a unique * predecessor created for it */ private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) { /* * Any block with that has both multiple successors and multiple * predecessors needs a new predecessor node. */ int countPredecessors = block.getPredecessors().cardinality(); int countSuccessors = block.getSuccessors().cardinality(); return (countPredecessors > 1 && countSuccessors > 1); } /** * In ROP form, move-exception must occur as the first insn in a block * immediately succeeding the insn that could thrown an exception. * We may need room to insert move insns later, so make sure to split * any block that starts with a move-exception such that there is a * unique move-exception block for each predecessor. * * @param ssaMeth method to process */ private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) { ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks(); /* * New blocks are added to the end of the block list during * this iteration. */ for (int i = blocks.size() - 1; i >= 0; i-- ) { SsaBasicBlock block = blocks.get(i); /* * Any block that starts with a move-exception and has more than * one predecessor... */ if (!block.isExitBlock() && block.getPredecessors().cardinality() > 1 && block.getInsns().get(0).isMoveException()) { // block.getPredecessors() is changed in the loop below. BitSet preds = (BitSet)block.getPredecessors().clone(); for (int j = preds.nextSetBit(0); j >= 0; j = preds.nextSetBit(j + 1)) { SsaBasicBlock predecessor = blocks.get(j); SsaBasicBlock zNode = predecessor.insertNewSuccessor(block); /* * Make sure to place the move-exception as the * first insn. */ zNode.getInsns().add(0, block.getInsns().get(0).clone()); } // Remove the move-exception from the original block. block.getInsns().remove(0); } } } /** * Inserts Z nodes for every node that needs a new * successor. * * @param result {@code non-null;} method to process */ private static void edgeSplitSuccessors(SsaMethod result) { ArrayList<SsaBasicBlock> blocks = result.getBlocks(); /* * New blocks are added to the end of the block list during * this iteration. */ for (int i = blocks.size() - 1; i >= 0; i-- ) { SsaBasicBlock block = blocks.get(i); // Successors list is modified in loop below. BitSet successors = (BitSet)block.getSuccessors().clone(); for (int j = successors.nextSetBit(0); j >= 0; j = successors.nextSetBit(j+1)) { SsaBasicBlock succ = blocks.get(j); if (needsNewSuccessor(block, succ)) { block.insertNewSuccessor(succ); } } } } /** * Returns {@code true} if block and successor need a Z-node * between them. Presently, this is {@code true} if the final * instruction has any sources or results and the current * successor block has more than one predecessor. * * @param block predecessor node * @param succ successor node * @return {@code true} if a Z node is needed */ private static boolean needsNewSuccessor(SsaBasicBlock block, SsaBasicBlock succ) { ArrayList<SsaInsn> insns = block.getInsns(); SsaInsn lastInsn = insns.get(insns.size() - 1); return ((lastInsn.getResult() != null) || (lastInsn.getSources().size() > 0)) && succ.getPredecessors().cardinality() > 1; } /** * See Appel algorithm 19.6: * * Place Phi functions in appropriate locations. * * @param ssaMeth {@code non-null;} method to process. * Modifications are made in-place. * @param localInfo {@code non-null;} local variable info, used * when placing phis */ private static void placePhiFunctions (SsaMethod ssaMeth, LocalVariableInfo localInfo) { ArrayList<SsaBasicBlock> ssaBlocks; int regCount; int blockCount; ssaBlocks = ssaMeth.getBlocks(); blockCount = ssaBlocks.size(); regCount = ssaMeth.getRegCount(); DomFront df = new DomFront(ssaMeth); DomFront.DomInfo[] domInfos = df.run(); // Bit set of registers vs block index "definition sites" BitSet[] defsites = new BitSet[regCount]; // Bit set of registers vs block index "phi placement sites" BitSet[] phisites = new BitSet[regCount]; for (int i = 0; i < regCount; i++) { defsites[i] = new BitSet(blockCount); phisites[i] = new BitSet(blockCount); } /* * For each register, build a set of all basic blocks where * containing an assignment to that register. */ for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) { SsaBasicBlock b = ssaBlocks.get(bi); for (SsaInsn insn : b.getInsns()) { RegisterSpec rs = insn.getResult(); if (rs != null) { defsites[rs.getReg()].set(bi); } } } if (DEBUG) { System.out.println("defsites"); for (int i = 0; i < regCount; i++) { StringBuilder sb = new StringBuilder(); sb.append('v').append(i).append(": "); sb.append(defsites[i].toString()); System.out.println(sb); } } BitSet worklist; /* * For each register, compute all locations for phi placement * based on dominance-frontier algorithm. */ for (int reg = 0, s = ssaMeth.getRegCount() ; reg < s ; reg++ ) { int workBlockIndex; /* Worklist set starts out with each node where reg is assigned. */ worklist = (BitSet) (defsites[reg].clone()); while (0 <= (workBlockIndex = worklist.nextSetBit(0))) { worklist.clear(workBlockIndex); IntIterator dfIterator = domInfos[workBlockIndex].dominanceFrontiers.iterator(); while (dfIterator.hasNext()) { int dfBlockIndex = dfIterator.next(); if (!phisites[reg].get(dfBlockIndex)) { phisites[reg].set(dfBlockIndex); RegisterSpec rs = localInfo.getStarts(dfBlockIndex).get(reg); if (rs == null) { ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(reg); } else { ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs); } if (!defsites[reg].get(dfBlockIndex)) { worklist.set(dfBlockIndex); } } } } } if (DEBUG) { System.out.println("phisites"); for (int i = 0; i < regCount; i++) { StringBuilder sb = new StringBuilder(); sb.append('v').append(i).append(": "); sb.append(phisites[i].toString()); System.out.println(sb); } } } }