/* * 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.back; import com.android.dx.ssa.SsaMethod; import com.android.dx.ssa.SsaBasicBlock; import com.android.dx.ssa.SsaInsn; import com.android.dx.ssa.PhiInsn; import com.android.dx.rop.code.RegisterSpec; import java.util.BitSet; import java.util.List; import java.util.ArrayList; /** * From Appel "Modern Compiler Implementation in Java" algorithm 19.17 * Calculate the live ranges for register {@code reg}.

* * v = regV

* s = insn

* M = visitedBlocks

*/ public class LivenessAnalyzer { /** * {@code non-null;} index by basic block indexed set of basic blocks * that have already been visited. "M" as written in the original Appel * algorithm. */ private final BitSet visitedBlocks; /** * {@code non-null;} set of blocks remaing to visit as "live out as block" */ private final BitSet liveOutBlocks; /** * {@code >=0;} SSA register currently being analyzed. * "v" in the original Appel algorithm. */ private final int regV; /** method to process */ private final SsaMethod ssaMeth; /** interference graph being updated */ private final InterferenceGraph interference; /** block "n" in Appel 19.17 */ private SsaBasicBlock blockN; /** index of statement {@code s} in {@code blockN} */ private int statementIndex; /** the next function to call */ private NextFunction nextFunction; /** constants for {@link #nextFunction} */ private static enum NextFunction { LIVE_IN_AT_STATEMENT, LIVE_OUT_AT_STATEMENT, LIVE_OUT_AT_BLOCK, DONE; } /** * Runs register liveness algorithm for a method, updating the * live in/out information in {@code SsaBasicBlock} instances and * returning an interference graph. * * @param ssaMeth {@code non-null;} method to process * @return {@code non-null;} interference graph indexed by SSA * registers in both directions */ public static InterferenceGraph constructInterferenceGraph( SsaMethod ssaMeth) { int szRegs = ssaMeth.getRegCount(); InterferenceGraph interference = new InterferenceGraph(szRegs); for (int i = 0; i < szRegs; i++) { new LivenessAnalyzer(ssaMeth, i, interference).run(); } coInterferePhis(ssaMeth, interference); return interference; } /** * Makes liveness analyzer instance for specific register. * * @param ssaMeth {@code non-null;} method to process * @param reg register whose liveness to analyze * @param interference {@code non-null;} indexed by SSA reg in * both dimensions; graph to update * */ private LivenessAnalyzer(SsaMethod ssaMeth, int reg, InterferenceGraph interference) { int blocksSz = ssaMeth.getBlocks().size(); this.ssaMeth = ssaMeth; this.regV = reg; visitedBlocks = new BitSet(blocksSz); liveOutBlocks = new BitSet(blocksSz); this.interference = interference; } /** * The algorithm in Appel is presented in partial tail-recursion * form. Obviously, that's not efficient in java, so this function * serves as the dispatcher instead. */ private void handleTailRecursion() { while (nextFunction != NextFunction.DONE) { switch (nextFunction) { case LIVE_IN_AT_STATEMENT: nextFunction = NextFunction.DONE; liveInAtStatement(); break; case LIVE_OUT_AT_STATEMENT: nextFunction = NextFunction.DONE; liveOutAtStatement(); break; case LIVE_OUT_AT_BLOCK: nextFunction = NextFunction.DONE; liveOutAtBlock(); break; default: } } } /** * From Appel algorithm 19.17. */ public void run() { List useList = ssaMeth.getUseListForRegister(regV); for (SsaInsn insn : useList) { nextFunction = NextFunction.DONE; if (insn instanceof PhiInsn) { // If s is a phi-function with V as it's ith argument. PhiInsn phi = (PhiInsn) insn; for (SsaBasicBlock pred : phi.predBlocksForReg(regV, ssaMeth)) { blockN = pred; nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; handleTailRecursion(); } } else { blockN = insn.getBlock(); statementIndex = blockN.getInsns().indexOf(insn); if (statementIndex < 0) { throw new RuntimeException( "insn not found in it's own block"); } nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; handleTailRecursion(); } } int nextLiveOutBlock; while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) { blockN = ssaMeth.getBlocks().get(nextLiveOutBlock); liveOutBlocks.clear(nextLiveOutBlock); nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; handleTailRecursion(); } } /** * "v is live-out at n." */ private void liveOutAtBlock() { if (! visitedBlocks.get(blockN.getIndex())) { visitedBlocks.set(blockN.getIndex()); blockN.addLiveOut(regV); ArrayList insns; insns = blockN.getInsns(); // Live out at last statement in blockN statementIndex = insns.size() - 1; nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } } /** * "v is live-in at s." */ private void liveInAtStatement() { // if s is the first statement in block N if (statementIndex == 0) { // v is live-in at n blockN.addLiveIn(regV); BitSet preds = blockN.getPredecessors(); liveOutBlocks.or(preds); } else { // Let s' be the statement preceeding s statementIndex -= 1; nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } } /** * "v is live-out at s." */ private void liveOutAtStatement() { SsaInsn statement = blockN.getInsns().get(statementIndex); RegisterSpec rs = statement.getResult(); if (!statement.isResultReg(regV)) { if (rs != null) { interference.add(regV, rs.getReg()); } nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; } } /** * Ensures that all the phi result registers for all the phis in the * same basic block interfere with each other. This is needed since * the dead code remover has allowed through "dead-end phis" whose * results are not used except as local assignments. Without this step, * a the result of a dead-end phi might be assigned the same register * as the result of another phi, and the phi removal move scheduler may * generate moves that over-write the live result. * * @param ssaMeth {@code non-null;} method to pricess * @param interference {@code non-null;} interference graph */ private static void coInterferePhis(SsaMethod ssaMeth, InterferenceGraph interference) { for (SsaBasicBlock b : ssaMeth.getBlocks()) { List phis = b.getPhiInsns(); int szPhis = phis.size(); for (int i = 0; i < szPhis; i++) { for (int j = 0; j < szPhis; j++) { if (i == j) { continue; } interference.add(phis.get(i).getResult().getReg(), phis.get(j).getResult().getReg()); } } } } }