| /* |
| * 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.dex.code; |
| |
| import com.android.dx.dex.DexOptions; |
| import com.android.dx.io.Opcodes; |
| import com.android.dx.rop.code.BasicBlock; |
| import com.android.dx.rop.code.BasicBlockList; |
| import com.android.dx.rop.code.FillArrayDataInsn; |
| import com.android.dx.rop.code.Insn; |
| import com.android.dx.rop.code.LocalVariableInfo; |
| import com.android.dx.rop.code.PlainCstInsn; |
| 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.RegisterSpecSet; |
| import com.android.dx.rop.code.Rop; |
| import com.android.dx.rop.code.RopMethod; |
| import com.android.dx.rop.code.SourcePosition; |
| import com.android.dx.rop.code.SwitchInsn; |
| import com.android.dx.rop.code.ThrowingCstInsn; |
| import com.android.dx.rop.code.ThrowingInsn; |
| import com.android.dx.rop.cst.Constant; |
| import com.android.dx.rop.cst.CstInteger; |
| import com.android.dx.util.Bits; |
| import com.android.dx.util.IntList; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Translator from {@link RopMethod} to {@link DalvCode}. The {@link |
| * #translate} method is the thing to call on this class. |
| */ |
| public final class RopTranslator { |
| /** {@code non-null;} options for dex output */ |
| private final DexOptions dexOptions; |
| |
| /** {@code non-null;} method to translate */ |
| private final RopMethod method; |
| |
| /** |
| * how much position info to preserve; one of the static |
| * constants in {@link PositionList} |
| */ |
| private final int positionInfo; |
| |
| /** {@code null-ok;} local variable info to use */ |
| private final LocalVariableInfo locals; |
| |
| /** {@code non-null;} container for all the address objects for the method */ |
| private final BlockAddresses addresses; |
| |
| /** {@code non-null;} list of output instructions in-progress */ |
| private final OutputCollector output; |
| |
| /** {@code non-null;} visitor to use during translation */ |
| private final TranslationVisitor translationVisitor; |
| |
| /** {@code >= 0;} register count for the method */ |
| private final int regCount; |
| |
| /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */ |
| private int[] order; |
| |
| /** size, in register units, of all the parameters to this method */ |
| private final int paramSize; |
| |
| /** |
| * true if the parameters to this method happen to be in proper order |
| * at the end of the frame (as the optimizer emits them) |
| */ |
| private boolean paramsAreInOrder; |
| |
| /** |
| * Translates a {@link RopMethod}. This may modify the given |
| * input. |
| * |
| * @param method {@code non-null;} the original method |
| * @param positionInfo how much position info to preserve; one of the |
| * static constants in {@link PositionList} |
| * @param locals {@code null-ok;} local variable information to use |
| * @param paramSize size, in register units, of all the parameters to |
| * this method |
| * @param dexOptions {@code non-null;} options for dex output |
| * @return {@code non-null;} the translated version |
| */ |
| public static DalvCode translate(RopMethod method, int positionInfo, |
| LocalVariableInfo locals, int paramSize, DexOptions dexOptions) { |
| RopTranslator translator = |
| new RopTranslator(method, positionInfo, locals, paramSize, dexOptions); |
| return translator.translateAndGetResult(); |
| } |
| |
| /** |
| * Constructs an instance. This method is private. Use {@link #translate}. |
| * |
| * @param method {@code non-null;} the original method |
| * @param positionInfo how much position info to preserve; one of the |
| * static constants in {@link PositionList} |
| * @param locals {@code null-ok;} local variable information to use |
| * @param paramSize size, in register units, of all the parameters to |
| * this method |
| * @param dexOptions {@code non-null;} options for dex output |
| */ |
| private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals, |
| int paramSize, DexOptions dexOptions) { |
| this.dexOptions = dexOptions; |
| this.method = method; |
| this.positionInfo = positionInfo; |
| this.locals = locals; |
| this.addresses = new BlockAddresses(method); |
| this.paramSize = paramSize; |
| this.order = null; |
| this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize); |
| |
| BasicBlockList blocks = method.getBlocks(); |
| int bsz = blocks.size(); |
| |
| /* |
| * Max possible instructions includes three code address |
| * objects per basic block (to the first and last instruction, |
| * and just past the end of the block), and the possibility of |
| * an extra goto at the end of each basic block. |
| */ |
| int maxInsns = (bsz * 3) + blocks.getInstructionCount(); |
| |
| if (locals != null) { |
| /* |
| * If we're tracking locals, then there's could be another |
| * extra instruction per block (for the locals state at the |
| * start of the block) as well as one for each interblock |
| * local introduction. |
| */ |
| maxInsns += bsz + locals.getAssignmentCount(); |
| } |
| |
| /* |
| * If params are not in order, we will need register space |
| * for them before this is all over... |
| */ |
| this.regCount = blocks.getRegCount() |
| + (paramsAreInOrder ? 0 : this.paramSize); |
| |
| this.output = new OutputCollector(dexOptions, maxInsns, bsz * 3, regCount); |
| |
| if (locals != null) { |
| this.translationVisitor = |
| new LocalVariableAwareTranslationVisitor(output, locals); |
| } else { |
| this.translationVisitor = new TranslationVisitor(output); |
| } |
| } |
| |
| /** |
| * Checks to see if the move-param instructions that occur in this |
| * method happen to slot the params in an order at the top of the |
| * stack frame that matches dalvik's calling conventions. This will |
| * alway result in "true" for methods that have run through the |
| * SSA optimizer. |
| * |
| * @param paramSize size, in register units, of all the parameters |
| * to this method |
| */ |
| private static boolean calculateParamsAreInOrder(RopMethod method, |
| final int paramSize) { |
| final boolean[] paramsAreInOrder = { true }; |
| final int initialRegCount = method.getBlocks().getRegCount(); |
| |
| /* |
| * We almost could just check the first block here, but the |
| * {@code cf} layer will put in a second move-param in a |
| * subsequent block in the case of synchronized methods. |
| */ |
| method.getBlocks().forEachInsn(new Insn.BaseVisitor() { |
| @Override |
| public void visitPlainCstInsn(PlainCstInsn insn) { |
| if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) { |
| int param = |
| ((CstInteger) insn.getConstant()).getValue(); |
| |
| paramsAreInOrder[0] = paramsAreInOrder[0] |
| && ((initialRegCount - paramSize + param) |
| == insn.getResult().getReg()); |
| } |
| } |
| }); |
| |
| return paramsAreInOrder[0]; |
| } |
| |
| /** |
| * Does the translation and returns the result. |
| * |
| * @return {@code non-null;} the result |
| */ |
| private DalvCode translateAndGetResult() { |
| pickOrder(); |
| outputInstructions(); |
| |
| StdCatchBuilder catches = |
| new StdCatchBuilder(method, order, addresses); |
| |
| return new DalvCode(positionInfo, output.getFinisher(), catches); |
| } |
| |
| /** |
| * Performs initial creation of output instructions based on the |
| * original blocks. |
| */ |
| private void outputInstructions() { |
| BasicBlockList blocks = method.getBlocks(); |
| int[] order = this.order; |
| int len = order.length; |
| |
| // Process the blocks in output order. |
| for (int i = 0; i < len; i++) { |
| int nextI = i + 1; |
| int nextLabel = (nextI == order.length) ? -1 : order[nextI]; |
| outputBlock(blocks.labelToBlock(order[i]), nextLabel); |
| } |
| } |
| |
| /** |
| * Helper for {@link #outputInstructions}, which does the processing |
| * and output of one block. |
| * |
| * @param block {@code non-null;} the block to process and output |
| * @param nextLabel {@code >= -1;} the next block that will be processed, or |
| * {@code -1} if there is no next block |
| */ |
| private void outputBlock(BasicBlock block, int nextLabel) { |
| // Append the code address for this block. |
| CodeAddress startAddress = addresses.getStart(block); |
| output.add(startAddress); |
| |
| // Append the local variable state for the block. |
| if (locals != null) { |
| RegisterSpecSet starts = locals.getStarts(block); |
| output.add(new LocalSnapshot(startAddress.getPosition(), |
| starts)); |
| } |
| |
| /* |
| * Choose and append an output instruction for each original |
| * instruction. |
| */ |
| translationVisitor.setBlock(block, addresses.getLast(block)); |
| block.getInsns().forEach(translationVisitor); |
| |
| // Insert the block end code address. |
| output.add(addresses.getEnd(block)); |
| |
| // Set up for end-of-block activities. |
| |
| int succ = block.getPrimarySuccessor(); |
| Insn lastInsn = block.getLastInsn(); |
| |
| /* |
| * Check for (and possibly correct for) a non-optimal choice of |
| * which block will get output next. |
| */ |
| |
| if ((succ >= 0) && (succ != nextLabel)) { |
| /* |
| * The block has a "primary successor" and that primary |
| * successor isn't the next block to be output. |
| */ |
| Rop lastRop = lastInsn.getOpcode(); |
| if ((lastRop.getBranchingness() == Rop.BRANCH_IF) && |
| (block.getSecondarySuccessor() == nextLabel)) { |
| /* |
| * The block ends with an "if" of some sort, and its |
| * secondary successor (the "then") is in fact the |
| * next block to output. So, reverse the sense of |
| * the test, so that we can just emit the next block |
| * without an interstitial goto. |
| */ |
| output.reverseBranch(1, addresses.getStart(succ)); |
| } else { |
| /* |
| * Our only recourse is to add a goto here to get the |
| * flow to be correct. |
| */ |
| TargetInsn insn = |
| new TargetInsn(Dops.GOTO, lastInsn.getPosition(), |
| RegisterSpecList.EMPTY, |
| addresses.getStart(succ)); |
| output.add(insn); |
| } |
| } |
| } |
| |
| /** |
| * Picks an order for the blocks by doing "trace" analysis. |
| */ |
| private void pickOrder() { |
| BasicBlockList blocks = method.getBlocks(); |
| int sz = blocks.size(); |
| int maxLabel = blocks.getMaxLabel(); |
| int[] workSet = Bits.makeBitSet(maxLabel); |
| int[] tracebackSet = Bits.makeBitSet(maxLabel); |
| |
| for (int i = 0; i < sz; i++) { |
| BasicBlock one = blocks.get(i); |
| Bits.set(workSet, one.getLabel()); |
| } |
| |
| int[] order = new int[sz]; |
| int at = 0; |
| |
| /* |
| * Starting with the designated "first label" (that is, the |
| * first block of the method), add that label to the order, |
| * and then pick its first as-yet unordered successor to |
| * immediately follow it, giving top priority to the primary |
| * (aka default) successor (if any). Keep following successors |
| * until the trace runs out of possibilities. Then, continue |
| * by finding an unordered chain containing the first as-yet |
| * unordered block, and adding it to the order, and so on. |
| */ |
| for (int label = method.getFirstLabel(); |
| label != -1; |
| label = Bits.findFirst(workSet, 0)) { |
| |
| /* |
| * Attempt to trace backward from the chosen block to an |
| * as-yet unordered predecessor which lists the chosen |
| * block as its primary successor, and so on, until we |
| * fail to find such an unordered predecessor. Start the |
| * trace with that block. Note that the first block in the |
| * method has no predecessors, so in that case this loop |
| * will simply terminate with zero iterations and without |
| * picking a new starter block. |
| */ |
| traceBack: |
| for (;;) { |
| IntList preds = method.labelToPredecessors(label); |
| int psz = preds.size(); |
| |
| for (int i = 0; i < psz; i++) { |
| int predLabel = preds.get(i); |
| |
| if (Bits.get(tracebackSet, predLabel)) { |
| /* |
| * We found a predecessor loop; stop tracing back |
| * from here. |
| */ |
| break; |
| } |
| |
| if (!Bits.get(workSet, predLabel)) { |
| // This one's already ordered. |
| continue; |
| } |
| |
| BasicBlock pred = blocks.labelToBlock(predLabel); |
| if (pred.getPrimarySuccessor() == label) { |
| // Found one! |
| label = predLabel; |
| Bits.set(tracebackSet, label); |
| continue traceBack; |
| } |
| } |
| |
| // Failed to find a better block to start the trace. |
| break; |
| } |
| |
| /* |
| * Trace a path from the chosen block to one of its |
| * unordered successors (hopefully the primary), and so |
| * on, until we run out of unordered successors. |
| */ |
| while (label != -1) { |
| Bits.clear(workSet, label); |
| Bits.clear(tracebackSet, label); |
| order[at] = label; |
| at++; |
| |
| BasicBlock one = blocks.labelToBlock(label); |
| BasicBlock preferredBlock = blocks.preferredSuccessorOf(one); |
| |
| if (preferredBlock == null) { |
| break; |
| } |
| |
| int preferred = preferredBlock.getLabel(); |
| int primary = one.getPrimarySuccessor(); |
| |
| if (Bits.get(workSet, preferred)) { |
| /* |
| * Order the current block's preferred successor |
| * next, as it has yet to be scheduled. |
| */ |
| label = preferred; |
| } else if ((primary != preferred) && (primary >= 0) |
| && Bits.get(workSet, primary)) { |
| /* |
| * The primary is available, so use that. |
| */ |
| label = primary; |
| } else { |
| /* |
| * There's no obvious candidate, so pick the first |
| * one that's available, if any. |
| */ |
| IntList successors = one.getSuccessors(); |
| int ssz = successors.size(); |
| label = -1; |
| for (int i = 0; i < ssz; i++) { |
| int candidate = successors.get(i); |
| if (Bits.get(workSet, candidate)) { |
| label = candidate; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (at != sz) { |
| // There was a duplicate block label. |
| throw new RuntimeException("shouldn't happen"); |
| } |
| |
| this.order = order; |
| } |
| |
| /** |
| * Gets the complete register list (result and sources) out of a |
| * given rop instruction. For insns that are commutative, have |
| * two register sources, and have a source equal to the result, |
| * place that source first. |
| * |
| * @param insn {@code non-null;} instruction in question |
| * @return {@code non-null;} the instruction's complete register list |
| */ |
| private static RegisterSpecList getRegs(Insn insn) { |
| return getRegs(insn, insn.getResult()); |
| } |
| |
| /** |
| * Gets the complete register list (result and sources) out of a |
| * given rop instruction. For insns that are commutative, have |
| * two register sources, and have a source equal to the result, |
| * place that source first. |
| * |
| * @param insn {@code non-null;} instruction in question |
| * @param resultReg {@code null-ok;} the real result to use (ignore the insn's) |
| * @return {@code non-null;} the instruction's complete register list |
| */ |
| private static RegisterSpecList getRegs(Insn insn, |
| RegisterSpec resultReg) { |
| RegisterSpecList regs = insn.getSources(); |
| |
| if (insn.getOpcode().isCommutative() |
| && (regs.size() == 2) |
| && (resultReg.getReg() == regs.get(1).getReg())) { |
| |
| /* |
| * For commutative ops which have two register sources, |
| * if the second source is the same register as the result, |
| * swap the sources so that an opcode of form 12x can be selected |
| * instead of one of form 23x |
| */ |
| |
| regs = RegisterSpecList.make(regs.get(1), regs.get(0)); |
| } |
| |
| if (resultReg == null) { |
| return regs; |
| } |
| |
| return regs.withFirst(resultReg); |
| } |
| |
| /** |
| * Instruction visitor class for doing the instruction translation per se. |
| */ |
| private class TranslationVisitor implements Insn.Visitor { |
| /** {@code non-null;} list of output instructions in-progress */ |
| private final OutputCollector output; |
| |
| /** {@code non-null;} basic block being worked on */ |
| private BasicBlock block; |
| |
| /** |
| * {@code null-ok;} code address for the salient last instruction of the |
| * block (used before switches and throwing instructions) |
| */ |
| private CodeAddress lastAddress; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param output {@code non-null;} destination for instruction output |
| */ |
| public TranslationVisitor(OutputCollector output) { |
| this.output = output; |
| } |
| |
| /** |
| * Sets the block currently being worked on. |
| * |
| * @param block {@code non-null;} the block |
| * @param lastAddress {@code non-null;} code address for the salient |
| * last instruction of the block |
| */ |
| public void setBlock(BasicBlock block, CodeAddress lastAddress) { |
| this.block = block; |
| this.lastAddress = lastAddress; |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitPlainInsn(PlainInsn insn) { |
| Rop rop = insn.getOpcode(); |
| if (rop.getOpcode() == RegOps.MARK_LOCAL) { |
| /* |
| * Ignore these. They're dealt with by |
| * the LocalVariableAwareTranslationVisitor |
| */ |
| return; |
| } |
| if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { |
| // These get skipped |
| return; |
| } |
| |
| SourcePosition pos = insn.getPosition(); |
| Dop opcode = RopToDop.dopFor(insn); |
| DalvInsn di; |
| |
| switch (rop.getBranchingness()) { |
| case Rop.BRANCH_NONE: |
| case Rop.BRANCH_RETURN: |
| case Rop.BRANCH_THROW: { |
| di = new SimpleInsn(opcode, pos, getRegs(insn)); |
| break; |
| } |
| case Rop.BRANCH_GOTO: { |
| /* |
| * Code in the main translation loop will emit a |
| * goto if necessary (if the branch isn't to the |
| * immediately subsequent block). |
| */ |
| return; |
| } |
| case Rop.BRANCH_IF: { |
| int target = block.getSuccessors().get(1); |
| di = new TargetInsn(opcode, pos, getRegs(insn), |
| addresses.getStart(target)); |
| break; |
| } |
| default: { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| } |
| |
| addOutput(di); |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitPlainCstInsn(PlainCstInsn insn) { |
| SourcePosition pos = insn.getPosition(); |
| Dop opcode = RopToDop.dopFor(insn); |
| Rop rop = insn.getOpcode(); |
| int ropOpcode = rop.getOpcode(); |
| DalvInsn di; |
| |
| if (rop.getBranchingness() != Rop.BRANCH_NONE) { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| |
| if (ropOpcode == RegOps.MOVE_PARAM) { |
| if (!paramsAreInOrder) { |
| /* |
| * Parameters are not in order at the top of the reg space. |
| * We need to add moves. |
| */ |
| |
| RegisterSpec dest = insn.getResult(); |
| int param = |
| ((CstInteger) insn.getConstant()).getValue(); |
| RegisterSpec source = |
| RegisterSpec.make(regCount - paramSize + param, |
| dest.getType()); |
| di = new SimpleInsn(opcode, pos, |
| RegisterSpecList.make(dest, source)); |
| addOutput(di); |
| } |
| } else { |
| // No moves required for the parameters |
| RegisterSpecList regs = getRegs(insn); |
| di = new CstInsn(opcode, pos, regs, insn.getConstant()); |
| addOutput(di); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitSwitchInsn(SwitchInsn insn) { |
| SourcePosition pos = insn.getPosition(); |
| IntList cases = insn.getCases(); |
| IntList successors = block.getSuccessors(); |
| int casesSz = cases.size(); |
| int succSz = successors.size(); |
| int primarySuccessor = block.getPrimarySuccessor(); |
| |
| /* |
| * Check the assumptions that the number of cases is one |
| * less than the number of successors and that the last |
| * successor in the list is the primary (in this case, the |
| * default). This test is here to guard against forgetting |
| * to change this code if the way switch instructions are |
| * constructed also gets changed. |
| */ |
| if ((casesSz != (succSz - 1)) || |
| (primarySuccessor != successors.get(casesSz))) { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| |
| CodeAddress[] switchTargets = new CodeAddress[casesSz]; |
| |
| for (int i = 0; i < casesSz; i++) { |
| int label = successors.get(i); |
| switchTargets[i] = addresses.getStart(label); |
| } |
| |
| CodeAddress dataAddress = new CodeAddress(pos); |
| SwitchData dataInsn = |
| new SwitchData(pos, lastAddress, cases, switchTargets); |
| Dop opcode = dataInsn.isPacked() ? |
| Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH; |
| TargetInsn switchInsn = |
| new TargetInsn(opcode, pos, getRegs(insn), dataAddress); |
| |
| addOutput(lastAddress); |
| addOutput(switchInsn); |
| |
| addOutputSuffix(new OddSpacer(pos)); |
| addOutputSuffix(dataAddress); |
| addOutputSuffix(dataInsn); |
| } |
| |
| /** |
| * Looks forward to the current block's primary successor, returning |
| * the RegisterSpec of the result of the move-result-pseudo at the |
| * top of that block or null if none. |
| * |
| * @return {@code null-ok;} result of move-result-pseudo at the beginning of |
| * primary successor |
| */ |
| private RegisterSpec getNextMoveResultPseudo() |
| { |
| int label = block.getPrimarySuccessor(); |
| |
| if (label < 0) { |
| return null; |
| } |
| |
| Insn insn |
| = method.getBlocks().labelToBlock(label).getInsns().get(0); |
| |
| if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) { |
| return null; |
| } else { |
| return insn.getResult(); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitThrowingCstInsn(ThrowingCstInsn insn) { |
| SourcePosition pos = insn.getPosition(); |
| Dop opcode = RopToDop.dopFor(insn); |
| Rop rop = insn.getOpcode(); |
| Constant cst = insn.getConstant(); |
| |
| if (rop.getBranchingness() != Rop.BRANCH_THROW) { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| |
| addOutput(lastAddress); |
| |
| if (rop.isCallLike()) { |
| RegisterSpecList regs = insn.getSources(); |
| DalvInsn di = new CstInsn(opcode, pos, regs, cst); |
| |
| addOutput(di); |
| } else { |
| RegisterSpec realResult = getNextMoveResultPseudo(); |
| |
| RegisterSpecList regs = getRegs(insn, realResult); |
| DalvInsn di; |
| |
| boolean hasResult = opcode.hasResult() |
| || (rop.getOpcode() == RegOps.CHECK_CAST); |
| |
| if (hasResult != (realResult != null)) { |
| throw new RuntimeException( |
| "Insn with result/move-result-pseudo mismatch " + |
| insn); |
| } |
| |
| if ((rop.getOpcode() == RegOps.NEW_ARRAY) && |
| (opcode.getOpcode() != Opcodes.NEW_ARRAY)) { |
| /* |
| * It's a type-specific new-array-<primitive>, and |
| * so it should be turned into a SimpleInsn (no |
| * constant ref as it's implicit). |
| */ |
| di = new SimpleInsn(opcode, pos, regs); |
| } else { |
| /* |
| * This is the general case for constant-bearing |
| * instructions. |
| */ |
| di = new CstInsn(opcode, pos, regs, cst); |
| } |
| |
| addOutput(di); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitThrowingInsn(ThrowingInsn insn) { |
| SourcePosition pos = insn.getPosition(); |
| Dop opcode = RopToDop.dopFor(insn); |
| Rop rop = insn.getOpcode(); |
| RegisterSpec realResult; |
| |
| if (rop.getBranchingness() != Rop.BRANCH_THROW) { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| |
| realResult = getNextMoveResultPseudo(); |
| |
| if (opcode.hasResult() != (realResult != null)) { |
| throw new RuntimeException( |
| "Insn with result/move-result-pseudo mismatch" + insn); |
| } |
| |
| addOutput(lastAddress); |
| |
| DalvInsn di = new SimpleInsn(opcode, pos, |
| getRegs(insn, realResult)); |
| |
| addOutput(di); |
| } |
| |
| /** {@inheritDoc} */ |
| public void visitFillArrayDataInsn(FillArrayDataInsn insn) { |
| SourcePosition pos = insn.getPosition(); |
| Constant cst = insn.getConstant(); |
| ArrayList<Constant> values = insn.getInitValues(); |
| Rop rop = insn.getOpcode(); |
| |
| if (rop.getBranchingness() != Rop.BRANCH_NONE) { |
| throw new RuntimeException("shouldn't happen"); |
| } |
| CodeAddress dataAddress = new CodeAddress(pos); |
| ArrayData dataInsn = |
| new ArrayData(pos, lastAddress, values, cst); |
| |
| TargetInsn fillArrayDataInsn = |
| new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn), |
| dataAddress); |
| |
| addOutput(lastAddress); |
| addOutput(fillArrayDataInsn); |
| |
| addOutputSuffix(new OddSpacer(pos)); |
| addOutputSuffix(dataAddress); |
| addOutputSuffix(dataInsn); |
| } |
| |
| /** |
| * Adds to the output. |
| * |
| * @param insn {@code non-null;} instruction to add |
| */ |
| protected void addOutput(DalvInsn insn) { |
| output.add(insn); |
| } |
| |
| /** |
| * Adds to the output suffix. |
| * |
| * @param insn {@code non-null;} instruction to add |
| */ |
| protected void addOutputSuffix(DalvInsn insn) { |
| output.addSuffix(insn); |
| } |
| } |
| |
| /** |
| * Instruction visitor class for doing instruction translation with |
| * local variable tracking |
| */ |
| private class LocalVariableAwareTranslationVisitor |
| extends TranslationVisitor { |
| /** {@code non-null;} local variable info */ |
| private LocalVariableInfo locals; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param output {@code non-null;} destination for instruction output |
| * @param locals {@code non-null;} the local variable info |
| */ |
| public LocalVariableAwareTranslationVisitor(OutputCollector output, |
| LocalVariableInfo locals) { |
| super(output); |
| this.locals = locals; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void visitPlainInsn(PlainInsn insn) { |
| super.visitPlainInsn(insn); |
| addIntroductionIfNecessary(insn); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void visitPlainCstInsn(PlainCstInsn insn) { |
| super.visitPlainCstInsn(insn); |
| addIntroductionIfNecessary(insn); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void visitSwitchInsn(SwitchInsn insn) { |
| super.visitSwitchInsn(insn); |
| addIntroductionIfNecessary(insn); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void visitThrowingCstInsn(ThrowingCstInsn insn) { |
| super.visitThrowingCstInsn(insn); |
| addIntroductionIfNecessary(insn); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void visitThrowingInsn(ThrowingInsn insn) { |
| super.visitThrowingInsn(insn); |
| addIntroductionIfNecessary(insn); |
| } |
| |
| /** |
| * Adds a {@link LocalStart} to the output if the given |
| * instruction in fact introduces a local variable. |
| * |
| * @param insn {@code non-null;} instruction in question |
| */ |
| public void addIntroductionIfNecessary(Insn insn) { |
| RegisterSpec spec = locals.getAssignment(insn); |
| |
| if (spec != null) { |
| addOutput(new LocalStart(insn.getPosition(), spec)); |
| } |
| } |
| } |
| } |