RopperMachine.java revision d24414a5d1780ce25179f3467b228f9a53863fb4
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.cf.code;
18
19import com.android.dx.rop.code.FillArrayDataInsn;
20import com.android.dx.rop.code.Insn;
21import com.android.dx.rop.code.PlainCstInsn;
22import com.android.dx.rop.code.PlainInsn;
23import com.android.dx.rop.code.RegOps;
24import com.android.dx.rop.code.RegisterSpec;
25import com.android.dx.rop.code.RegisterSpecList;
26import com.android.dx.rop.code.Rop;
27import com.android.dx.rop.code.Rops;
28import com.android.dx.rop.code.SourcePosition;
29import com.android.dx.rop.code.SwitchInsn;
30import com.android.dx.rop.code.ThrowingCstInsn;
31import com.android.dx.rop.code.ThrowingInsn;
32import com.android.dx.rop.code.TranslationAdvice;
33import com.android.dx.rop.cst.Constant;
34import com.android.dx.rop.cst.CstFieldRef;
35import com.android.dx.rop.cst.CstMethodRef;
36import com.android.dx.rop.cst.CstNat;
37import com.android.dx.rop.cst.CstType;
38import com.android.dx.rop.cst.CstUtf8;
39import com.android.dx.rop.type.Type;
40import com.android.dx.rop.type.TypeBearer;
41import com.android.dx.rop.type.TypeList;
42import com.android.dx.util.IntList;
43
44import java.util.ArrayList;
45
46/**
47 * Machine implementation for use by {@link Ropper}.
48 */
49/*package*/ final class RopperMachine extends ValueAwareMachine {
50    /** {@code non-null;} array reflection class */
51    private static final CstType ARRAY_REFLECT_TYPE =
52        new CstType(Type.internClassName("java/lang/reflect/Array"));
53
54    /**
55     * {@code non-null;} method constant for use in converting
56     * {@code multianewarray} instructions
57     */
58    private static final CstMethodRef MULTIANEWARRAY_METHOD =
59        new CstMethodRef(ARRAY_REFLECT_TYPE,
60                         new CstNat(new CstUtf8("newInstance"),
61                                    new CstUtf8("(Ljava/lang/Class;[I)" +
62                                                "Ljava/lang/Object;")));
63
64    /** {@code non-null;} {@link Ropper} controlling this instance */
65    private final Ropper ropper;
66
67    /** {@code non-null;} method being converted */
68    private final ConcreteMethod method;
69
70    /** {@code non-null;} translation advice */
71    private final TranslationAdvice advice;
72
73    /** max locals of the method */
74    private final int maxLocals;
75
76    /** {@code non-null;} instructions for the rop basic block in-progress */
77    private final ArrayList<Insn> insns;
78
79    /** {@code non-null;} catches for the block currently being processed */
80    private TypeList catches;
81
82    /** whether the catches have been used in an instruction */
83    private boolean catchesUsed;
84
85    /** whether the block contains a {@code return} */
86    private boolean returns;
87
88    /** primary successor index */
89    private int primarySuccessorIndex;
90
91    /** {@code >= 0;} number of extra basic blocks required */
92    private int extraBlockCount;
93
94    /** true if last processed block ends with a jsr or jsr_W*/
95    private boolean hasJsr;
96
97    /** true if an exception can be thrown by the last block processed */
98    private boolean blockCanThrow;
99
100    /**
101     * If non-null, the ReturnAddress that was used by the terminating ret
102     * instruction. If null, there was no ret instruction encountered.
103     */
104
105    private ReturnAddress returnAddress;
106
107    /**
108     * {@code null-ok;} the appropriate {@code return} op or {@code null}
109     * if it is not yet known
110     */
111    private Rop returnOp;
112
113    /**
114     * {@code null-ok;} the source position for the return block or {@code null}
115     * if it is not yet known
116     */
117    private SourcePosition returnPosition;
118
119    /**
120     * Constructs an instance.
121     *
122     * @param ropper {@code non-null;} ropper controlling this instance
123     * @param method {@code non-null;} method being converted
124     * @param advice {@code non-null;} translation advice to use
125     */
126    public RopperMachine(Ropper ropper, ConcreteMethod method,
127            TranslationAdvice advice) {
128        super(method.getEffectiveDescriptor());
129
130        if (ropper == null) {
131            throw new NullPointerException("ropper == null");
132        }
133
134        if (advice == null) {
135            throw new NullPointerException("advice == null");
136        }
137
138        this.ropper = ropper;
139        this.method = method;
140        this.advice = advice;
141        this.maxLocals = method.getMaxLocals();
142        this.insns = new ArrayList<Insn>(25);
143        this.catches = null;
144        this.catchesUsed = false;
145        this.returns = false;
146        this.primarySuccessorIndex = -1;
147        this.extraBlockCount = 0;
148        this.blockCanThrow = false;
149        this.returnOp = null;
150        this.returnPosition = null;
151    }
152
153    /**
154     * Gets the instructions array. It is shared and gets modified by
155     * subsequent calls to this instance.
156     *
157     * @return {@code non-null;} the instructions array
158     */
159    public ArrayList<Insn> getInsns() {
160        return insns;
161    }
162
163    /**
164     * Gets the return opcode encountered, if any.
165     *
166     * @return {@code null-ok;} the return opcode
167     */
168    public Rop getReturnOp() {
169        return returnOp;
170    }
171
172    /**
173     * Gets the return position, if known.
174     *
175     * @return {@code null-ok;} the return position
176     */
177    public SourcePosition getReturnPosition() {
178        return returnPosition;
179    }
180
181    /**
182     * Gets ready to start working on a new block. This will clear the
183     * {@link #insns} list, set {@link #catches}, reset whether it has
184     * been used, reset whether the block contains a
185     * {@code return}, and reset {@link #primarySuccessorIndex}.
186     */
187    public void startBlock(TypeList catches) {
188        this.catches = catches;
189
190        insns.clear();
191        catchesUsed = false;
192        returns = false;
193        primarySuccessorIndex = 0;
194        extraBlockCount = 0;
195        blockCanThrow = false;
196        hasJsr = false;
197        returnAddress = null;
198    }
199
200    /**
201     * Gets whether {@link #catches} was used. This indicates that the
202     * last instruction in the block is one of the ones that can throw.
203     *
204     * @return whether {@code catches} has been used
205     */
206    public boolean wereCatchesUsed() {
207        return catchesUsed;
208    }
209
210    /**
211     * Gets whether the block just processed ended with a
212     * {@code return}.
213     *
214     * @return whether the block returns
215     */
216    public boolean returns() {
217        return returns;
218    }
219
220    /**
221     * Gets the primary successor index. This is the index into the
222     * successors list where the primary may be found or
223     * {@code -1} if there are successors but no primary
224     * successor. This may return something other than
225     * {@code -1} in the case of an instruction with no
226     * successors at all (primary or otherwise).
227     *
228     * @return {@code >= -1;} the primary successor index
229     */
230    public int getPrimarySuccessorIndex() {
231        return primarySuccessorIndex;
232    }
233
234    /**
235     * Gets how many extra blocks will be needed to represent the
236     * block currently being translated. Each extra block should consist
237     * of one instruction from the end of the original block.
238     *
239     * @return {@code >= 0;} the number of extra blocks needed
240     */
241    public int getExtraBlockCount() {
242        return extraBlockCount;
243    }
244
245    /**
246     * @return true if at least one of the insn processed since the last
247     * call to startBlock() can throw.
248     */
249    public boolean canThrow() {
250        return blockCanThrow;
251    }
252
253    /**
254     * @return true if a JSR has ben encountered since the last call to
255     * startBlock()
256     */
257    public boolean hasJsr() {
258        return hasJsr;
259    }
260
261    /**
262     * @return {@code true} if a {@code ret} has ben encountered since
263     * the last call to {@code startBlock()}
264     */
265    public boolean hasRet() {
266        return returnAddress != null;
267    }
268
269    /**
270     * @return {@code null-ok;} return address of a {@code ret}
271     * instruction if encountered since last call to startBlock().
272     * {@code null} if no ret instruction encountered.
273     */
274    public ReturnAddress getReturnAddress() {
275        return returnAddress;
276    }
277
278    /** {@inheritDoc} */
279    @Override
280    public void run(Frame frame, int offset, int opcode) {
281        /*
282         * This is the stack pointer after the opcode's arguments have been
283         * popped.
284         */
285        int stackPointer = maxLocals + frame.getStack().size();
286
287        // The sources have to be retrieved before super.run() gets called.
288        RegisterSpecList sources = getSources(opcode, stackPointer);
289        int sourceCount = sources.size();
290
291        super.run(frame, offset, opcode);
292
293        SourcePosition pos = method.makeSourcePosistion(offset);
294        RegisterSpec localTarget = getLocalTarget();
295        int destCount = resultCount();
296        RegisterSpec dest;
297
298        if (destCount == 0) {
299            dest = null;
300            switch (opcode) {
301                case ByteOps.POP:
302                case ByteOps.POP2: {
303                    // These simply don't appear in the rop form.
304                    return;
305                }
306            }
307        } else if (localTarget != null) {
308            dest = localTarget;
309        } else if (destCount == 1) {
310            dest = RegisterSpec.make(stackPointer, result(0));
311        } else {
312            /*
313             * This clause only ever applies to the stack manipulation
314             * ops that have results (that is, dup* and swap but not
315             * pop*).
316             *
317             * What we do is first move all the source registers into
318             * the "temporary stack" area defined for the method, and
319             * then move stuff back down onto the main "stack" in the
320             * arrangement specified by the stack op pattern.
321             *
322             * Note: This code ends up emitting a lot of what will
323             * turn out to be superfluous moves (e.g., moving back and
324             * forth to the same local when doing a dup); however,
325             * that makes this code a bit easier (and goodness knows
326             * it doesn't need any extra complexity), and all the SSA
327             * stuff is going to want to deal with this sort of
328             * superfluous assignment anyway, so it should be a wash
329             * in the end.
330             */
331            int scratchAt = ropper.getFirstTempStackReg();
332            RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
333
334            for (int i = 0; i < sourceCount; i++) {
335                RegisterSpec src = sources.get(i);
336                TypeBearer type = src.getTypeBearer();
337                RegisterSpec scratch = src.withReg(scratchAt);
338                insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
339                scratchRegs[i] = scratch;
340                scratchAt += src.getCategory();
341            }
342
343            for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
344                int which = (pattern & 0x0f) - 1;
345                RegisterSpec scratch = scratchRegs[which];
346                TypeBearer type = scratch.getTypeBearer();
347                insns.add(new PlainInsn(Rops.opMove(type), pos,
348                                        scratch.withReg(stackPointer),
349                                        scratch));
350                stackPointer += type.getType().getCategory();
351            }
352            return;
353        }
354
355        TypeBearer destType = (dest != null) ? dest : Type.VOID;
356        Constant cst = getAuxCst();
357        int ropOpcode;
358        Rop rop;
359        Insn insn;
360
361        if (opcode == ByteOps.MULTIANEWARRAY) {
362            blockCanThrow = true;
363
364            // Add the extra instructions for handling multianewarray.
365
366            extraBlockCount = 6;
367
368            /*
369             * Add an array constructor for the int[] containing all the
370             * dimensions.
371             */
372            RegisterSpec dimsReg =
373                RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
374            rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
375            insn = new ThrowingCstInsn(rop, pos, sources, catches,
376                    CstType.INT_ARRAY);
377            insns.add(insn);
378
379            // Add a move-result for the new-filled-array
380            rop = Rops.opMoveResult(Type.INT_ARRAY);
381            insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
382            insns.add(insn);
383
384            /*
385             * Add a const-class instruction for the specified array
386             * class.
387             */
388
389            /*
390             * Remove as many dimensions from the originally specified
391             * class as are given in the explicit list of dimensions,
392             * so as to pass the right component class to the standard
393             * Java library array constructor.
394             */
395            Type componentType = ((CstType) cst).getClassType();
396            for (int i = 0; i < sourceCount; i++) {
397                componentType = componentType.getComponentType();
398            }
399
400            RegisterSpec classReg =
401                RegisterSpec.make(dest.getReg(), Type.CLASS);
402
403            if (componentType.isPrimitive()) {
404                /*
405                 * The component type is primitive (e.g., int as opposed
406                 * to Integer), so we have to fetch the corresponding
407                 * TYPE class.
408                 */
409                CstFieldRef typeField =
410                    CstFieldRef.forPrimitiveType(componentType);
411                insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos,
412                                           RegisterSpecList.EMPTY,
413                                           catches, typeField);
414            } else {
415                /*
416                 * The component type is an object type, so just make a
417                 * normal class reference.
418                 */
419                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
420                                           RegisterSpecList.EMPTY, catches,
421                                           new CstType(componentType));
422            }
423
424            insns.add(insn);
425
426            // Add a move-result-pseudo for the get-static or const
427            rop = Rops.opMoveResultPseudo(classReg.getType());
428            insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
429            insns.add(insn);
430
431            /*
432             * Add a call to the "multianewarray method," that is,
433             * Array.newInstance(class, dims). Note: The result type
434             * of newInstance() is Object, which is why the last
435             * instruction in this sequence is a cast to the right
436             * type for the original instruction.
437             */
438
439            RegisterSpec objectReg =
440                RegisterSpec.make(dest.getReg(), Type.OBJECT);
441
442            insn = new ThrowingCstInsn(
443                    Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()),
444                    pos, RegisterSpecList.make(classReg, dimsReg),
445                    catches, MULTIANEWARRAY_METHOD);
446            insns.add(insn);
447
448            // Add a move-result.
449            rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype()
450                    .getReturnType());
451            insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
452            insns.add(insn);
453
454            /*
455             * And finally, set up for the remainder of this method to
456             * add an appropriate cast.
457             */
458
459            opcode = ByteOps.CHECKCAST;
460            sources = RegisterSpecList.make(objectReg);
461        } else if (opcode == ByteOps.JSR) {
462            // JSR has no Rop instruction
463            hasJsr = true;
464            return;
465        } else if (opcode == ByteOps.RET) {
466            try {
467                returnAddress = (ReturnAddress)arg(0);
468            } catch (ClassCastException ex) {
469                throw new RuntimeException(
470                        "Argument to RET was not a ReturnAddress", ex);
471            }
472            // RET has no Rop instruction.
473            return;
474        }
475
476        ropOpcode = jopToRopOpcode(opcode, cst);
477        rop = Rops.ropFor(ropOpcode, destType, sources, cst);
478
479        Insn moveResult = null;
480        if (dest != null && rop.isCallLike()) {
481            /*
482             * We're going to want to have a move-result in the next
483             * basic block.
484             */
485            extraBlockCount++;
486
487            moveResult = new PlainInsn(
488                    Rops.opMoveResult(((CstMethodRef) cst).getPrototype()
489                    .getReturnType()), pos, dest, RegisterSpecList.EMPTY);
490
491            dest = null;
492        } else if (dest != null && rop.canThrow()) {
493            /*
494             * We're going to want to have a move-result-pseudo in the
495             * next basic block.
496             */
497            extraBlockCount++;
498
499            moveResult = new PlainInsn(
500                    Rops.opMoveResultPseudo(dest.getTypeBearer()),
501                    pos, dest, RegisterSpecList.EMPTY);
502
503            dest = null;
504        }
505        if (ropOpcode == RegOps.NEW_ARRAY) {
506            /*
507             * In the original bytecode, this was either a primitive
508             * array constructor "newarray" or an object array
509             * constructor "anewarray". In the former case, there is
510             * no explicit constant, and in the latter, the constant
511             * is for the element type and not the array type. The rop
512             * instruction form for both of these is supposed to be
513             * the resulting array type, so we initialize / alter
514             * "cst" here, accordingly. Conveniently enough, the rop
515             * opcode already gets constructed with the proper array
516             * type.
517             */
518            cst = CstType.intern(rop.getResult());
519        } else if ((cst == null) && (sourceCount == 2)) {
520            TypeBearer lastType = sources.get(1).getTypeBearer();
521
522            if (lastType.isConstant()
523                    && advice.hasConstantOperation(rop,
524                    sources.get(0), sources.get(1))) {
525                /*
526                 * The target architecture has an instruction that can
527                 * build in the constant found in the second argument,
528                 * so pull it out of the sources and just use it as a
529                 * constant here.
530                 */
531                cst = (Constant) lastType;
532                sources = sources.withoutLast();
533                rop = Rops.ropFor(ropOpcode, destType, sources, cst);
534            }
535        }
536
537        SwitchList cases = getAuxCases();
538        ArrayList<Constant> initValues = getInitValues();
539        boolean canThrow = rop.canThrow();
540
541        blockCanThrow |= canThrow;
542
543        if (cases != null) {
544            if (cases.size() == 0) {
545                // It's a default-only switch statement. It can happen!
546                insn = new PlainInsn(Rops.GOTO, pos, null,
547                                     RegisterSpecList.EMPTY);
548                primarySuccessorIndex = 0;
549            } else {
550                IntList values = cases.getValues();
551                insn = new SwitchInsn(rop, pos, dest, sources, values);
552                primarySuccessorIndex = values.size();
553            }
554        } else if (ropOpcode == RegOps.RETURN) {
555            /*
556             * Returns get turned into the combination of a move (if
557             * non-void and if the return doesn't already mention
558             * register 0) and a goto (to the return block).
559             */
560            if (sources.size() != 0) {
561                RegisterSpec source = sources.get(0);
562                TypeBearer type = source.getTypeBearer();
563                if (source.getReg() != 0) {
564                    insns.add(new PlainInsn(Rops.opMove(type), pos,
565                                            RegisterSpec.make(0, type),
566                                            source));
567                }
568            }
569            insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
570            primarySuccessorIndex = 0;
571            updateReturnOp(rop, pos);
572            returns = true;
573        } else if (cst != null) {
574            if (canThrow) {
575                insn =
576                    new ThrowingCstInsn(rop, pos, sources, catches, cst);
577                catchesUsed = true;
578                primarySuccessorIndex = catches.size();
579            } else {
580                insn = new PlainCstInsn(rop, pos, dest, sources, cst);
581            }
582        } else if (canThrow) {
583            insn = new ThrowingInsn(rop, pos, sources, catches);
584            catchesUsed = true;
585            if (opcode == ByteOps.ATHROW) {
586                /*
587                 * The op athrow is the only one where it's possible
588                 * to have non-empty successors and yet not have a
589                 * primary successor.
590                 */
591                primarySuccessorIndex = -1;
592            } else {
593                primarySuccessorIndex = catches.size();
594            }
595        } else {
596            insn = new PlainInsn(rop, pos, dest, sources);
597        }
598
599        insns.add(insn);
600
601        if (moveResult != null) {
602            insns.add(moveResult);
603        }
604
605        /*
606         * If initValues is non-null, it means that the parser has
607         * seen a group of compatible constant initialization
608         * bytecodes that are applied to the current newarray. The
609         * action we take here is to convert these initialization
610         * bytecodes into a single fill-array-data ROP which lays out
611         * all the constant values in a table.
612         */
613        if (initValues != null) {
614            extraBlockCount++;
615            insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
616                    RegisterSpecList.make(moveResult.getResult()), initValues,
617                    cst);
618            insns.add(insn);
619        }
620    }
621
622    /**
623     * Helper for {@link #run}, which gets the list of sources for the.
624     * instruction.
625     *
626     * @param opcode the opcode being translated
627     * @param stackPointer {@code >= 0;} the stack pointer after the
628     * instruction's arguments have been popped
629     * @return {@code non-null;} the sources
630     */
631    private RegisterSpecList getSources(int opcode, int stackPointer) {
632        int count = argCount();
633
634        if (count == 0) {
635            // We get an easy out if there aren't any sources.
636            return RegisterSpecList.EMPTY;
637        }
638
639        int localIndex = getLocalIndex();
640        RegisterSpecList sources;
641
642        if (localIndex >= 0) {
643            // The instruction is operating on a local variable.
644            sources = new RegisterSpecList(1);
645            sources.set(0, RegisterSpec.make(localIndex, arg(0)));
646        } else {
647            sources = new RegisterSpecList(count);
648            int regAt = stackPointer;
649            for (int i = 0; i < count; i++) {
650                RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
651                sources.set(i, spec);
652                regAt += spec.getCategory();
653            }
654
655            switch (opcode) {
656                case ByteOps.IASTORE: {
657                    /*
658                     * The Java argument order for array stores is
659                     * (array, index, value), but the rop argument
660                     * order is (value, array, index). The following
661                     * code gets the right arguments in the right
662                     * places.
663                     */
664                    if (count != 3) {
665                        throw new RuntimeException("shouldn't happen");
666                    }
667                    RegisterSpec array = sources.get(0);
668                    RegisterSpec index = sources.get(1);
669                    RegisterSpec value = sources.get(2);
670                    sources.set(0, value);
671                    sources.set(1, array);
672                    sources.set(2, index);
673                    break;
674                }
675                case ByteOps.PUTFIELD: {
676                    /*
677                     * Similar to above: The Java argument order for
678                     * putfield is (object, value), but the rop
679                     * argument order is (value, object).
680                     */
681                    if (count != 2) {
682                        throw new RuntimeException("shouldn't happen");
683                    }
684                    RegisterSpec obj = sources.get(0);
685                    RegisterSpec value = sources.get(1);
686                    sources.set(0, value);
687                    sources.set(1, obj);
688                    break;
689                }
690            }
691        }
692
693        sources.setImmutable();
694        return sources;
695    }
696
697    /**
698     * Sets or updates the information about the return block.
699     *
700     * @param op {@code non-null;} the opcode to use
701     * @param pos {@code non-null;} the position to use
702     */
703    private void updateReturnOp(Rop op, SourcePosition pos) {
704        if (op == null) {
705            throw new NullPointerException("op == null");
706        }
707
708        if (pos == null) {
709            throw new NullPointerException("pos == null");
710        }
711
712        if (returnOp == null) {
713            returnOp = op;
714            returnPosition = pos;
715        } else {
716            if (returnOp != op) {
717                throw new SimException("return op mismatch: " + op + ", " +
718                                       returnOp);
719            }
720
721            if (pos.getLine() > returnPosition.getLine()) {
722                // Pick the largest line number to be the "canonical" return.
723                returnPosition = pos;
724            }
725        }
726    }
727
728    /**
729     * Gets the register opcode for the given Java opcode.
730     *
731     * @param jop {@code >= 0;} the Java opcode
732     * @param cst {@code null-ok;} the constant argument, if any
733     * @return {@code >= 0;} the corresponding register opcode
734     */
735    private int jopToRopOpcode(int jop, Constant cst) {
736        switch (jop) {
737            case ByteOps.POP:
738            case ByteOps.POP2:
739            case ByteOps.DUP:
740            case ByteOps.DUP_X1:
741            case ByteOps.DUP_X2:
742            case ByteOps.DUP2:
743            case ByteOps.DUP2_X1:
744            case ByteOps.DUP2_X2:
745            case ByteOps.SWAP:
746            case ByteOps.JSR:
747            case ByteOps.RET:
748            case ByteOps.MULTIANEWARRAY: {
749                // These need to be taken care of specially.
750                break;
751            }
752            case ByteOps.NOP: {
753                return RegOps.NOP;
754            }
755            case ByteOps.LDC:
756            case ByteOps.LDC2_W: {
757                return RegOps.CONST;
758            }
759            case ByteOps.ILOAD:
760            case ByteOps.ISTORE: {
761                return RegOps.MOVE;
762            }
763            case ByteOps.IALOAD: {
764                return RegOps.AGET;
765            }
766            case ByteOps.IASTORE: {
767                return RegOps.APUT;
768            }
769            case ByteOps.IADD:
770            case ByteOps.IINC: {
771                return RegOps.ADD;
772            }
773            case ByteOps.ISUB: {
774                return RegOps.SUB;
775            }
776            case ByteOps.IMUL: {
777                return RegOps.MUL;
778            }
779            case ByteOps.IDIV: {
780                return RegOps.DIV;
781            }
782            case ByteOps.IREM: {
783                return RegOps.REM;
784            }
785            case ByteOps.INEG: {
786                return RegOps.NEG;
787            }
788            case ByteOps.ISHL: {
789                return RegOps.SHL;
790            }
791            case ByteOps.ISHR: {
792                return RegOps.SHR;
793            }
794            case ByteOps.IUSHR: {
795                return RegOps.USHR;
796            }
797            case ByteOps.IAND: {
798                return RegOps.AND;
799            }
800            case ByteOps.IOR: {
801                return RegOps.OR;
802            }
803            case ByteOps.IXOR: {
804                return RegOps.XOR;
805            }
806            case ByteOps.I2L:
807            case ByteOps.I2F:
808            case ByteOps.I2D:
809            case ByteOps.L2I:
810            case ByteOps.L2F:
811            case ByteOps.L2D:
812            case ByteOps.F2I:
813            case ByteOps.F2L:
814            case ByteOps.F2D:
815            case ByteOps.D2I:
816            case ByteOps.D2L:
817            case ByteOps.D2F: {
818                return RegOps.CONV;
819            }
820            case ByteOps.I2B: {
821                return RegOps.TO_BYTE;
822            }
823            case ByteOps.I2C: {
824                return RegOps.TO_CHAR;
825            }
826            case ByteOps.I2S: {
827                return RegOps.TO_SHORT;
828            }
829            case ByteOps.LCMP:
830            case ByteOps.FCMPL:
831            case ByteOps.DCMPL: {
832                return RegOps.CMPL;
833            }
834            case ByteOps.FCMPG:
835            case ByteOps.DCMPG: {
836                return RegOps.CMPG;
837            }
838            case ByteOps.IFEQ:
839            case ByteOps.IF_ICMPEQ:
840            case ByteOps.IF_ACMPEQ:
841            case ByteOps.IFNULL: {
842                return RegOps.IF_EQ;
843            }
844            case ByteOps.IFNE:
845            case ByteOps.IF_ICMPNE:
846            case ByteOps.IF_ACMPNE:
847            case ByteOps.IFNONNULL: {
848                return RegOps.IF_NE;
849            }
850            case ByteOps.IFLT:
851            case ByteOps.IF_ICMPLT: {
852                return RegOps.IF_LT;
853            }
854            case ByteOps.IFGE:
855            case ByteOps.IF_ICMPGE: {
856                return RegOps.IF_GE;
857            }
858            case ByteOps.IFGT:
859            case ByteOps.IF_ICMPGT: {
860                return RegOps.IF_GT;
861            }
862            case ByteOps.IFLE:
863            case ByteOps.IF_ICMPLE: {
864                return RegOps.IF_LE;
865            }
866            case ByteOps.GOTO: {
867                return RegOps.GOTO;
868            }
869            case ByteOps.LOOKUPSWITCH: {
870                return RegOps.SWITCH;
871            }
872            case ByteOps.IRETURN:
873            case ByteOps.RETURN: {
874                return RegOps.RETURN;
875            }
876            case ByteOps.GETSTATIC: {
877                return RegOps.GET_STATIC;
878            }
879            case ByteOps.PUTSTATIC: {
880                return RegOps.PUT_STATIC;
881            }
882            case ByteOps.GETFIELD: {
883                return RegOps.GET_FIELD;
884            }
885            case ByteOps.PUTFIELD: {
886                return RegOps.PUT_FIELD;
887            }
888            case ByteOps.INVOKEVIRTUAL: {
889                return RegOps.INVOKE_VIRTUAL;
890            }
891            case ByteOps.INVOKESPECIAL: {
892                /*
893                 * Determine whether the opcode should be
894                 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
895                 * on "invokespecial" as well as section 4.8.2 (7th
896                 * bullet point) for the gory details.
897                 */
898                CstMethodRef ref = (CstMethodRef) cst;
899                if (ref.isInstanceInit() ||
900                    (ref.getDefiningClass() == method.getDefiningClass()) ||
901                    !method.getAccSuper()) {
902                    return RegOps.INVOKE_DIRECT;
903                }
904                return RegOps.INVOKE_SUPER;
905            }
906            case ByteOps.INVOKESTATIC: {
907                return RegOps.INVOKE_STATIC;
908            }
909            case ByteOps.INVOKEINTERFACE: {
910                return RegOps.INVOKE_INTERFACE;
911            }
912            case ByteOps.NEW: {
913                return RegOps.NEW_INSTANCE;
914            }
915            case ByteOps.NEWARRAY:
916            case ByteOps.ANEWARRAY: {
917                return RegOps.NEW_ARRAY;
918            }
919            case ByteOps.ARRAYLENGTH: {
920                return RegOps.ARRAY_LENGTH;
921            }
922            case ByteOps.ATHROW: {
923                return RegOps.THROW;
924            }
925            case ByteOps.CHECKCAST: {
926                return RegOps.CHECK_CAST;
927            }
928            case ByteOps.INSTANCEOF: {
929                return RegOps.INSTANCE_OF;
930            }
931            case ByteOps.MONITORENTER: {
932                return RegOps.MONITOR_ENTER;
933            }
934            case ByteOps.MONITOREXIT: {
935                return RegOps.MONITOR_EXIT;
936            }
937        }
938
939        throw new RuntimeException("shouldn't happen");
940    }
941}
942