summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/dex/code/RopTranslator.java
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/dx/dex/code/RopTranslator.java')
-rw-r--r--dx/src/com/android/dx/dex/code/RopTranslator.java872
1 files changed, 872 insertions, 0 deletions
diff --git a/dx/src/com/android/dx/dex/code/RopTranslator.java b/dx/src/com/android/dx/dex/code/RopTranslator.java
new file mode 100644
index 0000000..a38ea11
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopTranslator.java
@@ -0,0 +1,872 @@
+/*
+ * 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.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.rop.type.Type;
+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;} 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
+ * @return {@code non-null;} the translated version
+ */
+ public static DalvCode translate(RopMethod method, int positionInfo,
+ LocalVariableInfo locals, int paramSize) {
+ RopTranslator translator =
+ new RopTranslator(method, positionInfo, locals,
+ paramSize);
+ 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
+ */
+ private RopTranslator(RopMethod method, int positionInfo,
+ LocalVariableInfo locals, int paramSize) {
+ 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(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() {
+ 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() != DalvOps.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));
+ }
+ }
+ }
+}