| /* |
| * 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.RegisterSpecSet; |
| import com.android.dx.rop.code.RegisterSpec; |
| import com.android.dx.util.IntList; |
| |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.List; |
| |
| /** |
| * Code to figure out which local variables are active at which points in |
| * a method. Stolen and retrofitted from |
| * com.android.dx.rop.code.LocalVariableExtractor |
| * |
| * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in, |
| * converted, and adapted through edge-splitting. |
| */ |
| public class LocalVariableExtractor { |
| /** {@code non-null;} method being extracted from */ |
| private final SsaMethod method; |
| |
| /** {@code non-null;} block list for the method */ |
| private final ArrayList<SsaBasicBlock> blocks; |
| |
| /** {@code non-null;} result in-progress */ |
| private final LocalVariableInfo resultInfo; |
| |
| /** {@code non-null;} work set indicating blocks needing to be processed */ |
| private final BitSet workSet; |
| |
| /** |
| * Extracts out all the local variable information from the given method. |
| * |
| * @param method {@code non-null;} the method to extract from |
| * @return {@code non-null;} the extracted information |
| */ |
| public static LocalVariableInfo extract(SsaMethod method) { |
| LocalVariableExtractor lve = new LocalVariableExtractor(method); |
| return lve.doit(); |
| } |
| |
| /** |
| * Constructs an instance. This method is private. Use {@link #extract}. |
| * |
| * @param method {@code non-null;} the method to extract from |
| */ |
| private LocalVariableExtractor(SsaMethod method) { |
| if (method == null) { |
| throw new NullPointerException("method == null"); |
| } |
| |
| ArrayList<SsaBasicBlock> blocks = method.getBlocks(); |
| |
| this.method = method; |
| this.blocks = blocks; |
| this.resultInfo = new LocalVariableInfo(method); |
| this.workSet = new BitSet(blocks.size()); |
| } |
| |
| /** |
| * Does the extraction. |
| * |
| * @return {@code non-null;} the extracted information |
| */ |
| private LocalVariableInfo doit() { |
| |
| //FIXME why is this needed here? |
| if (method.getRegCount() > 0 ) { |
| for (int bi = method.getEntryBlockIndex(); |
| bi >= 0; |
| bi = workSet.nextSetBit(0)) { |
| workSet.clear(bi); |
| processBlock(bi); |
| } |
| } |
| |
| resultInfo.setImmutable(); |
| return resultInfo; |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| } |
| } |