RopperMachine.java revision 99409883d9c4c0ffb49b070ce307bb33a9dfe9f1
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 true if a RET has ben encountered since the last call to
263     * startBlock()
264     */
265    public boolean hasRet() {
266        return returnAddress != null;
267    }
268
269    /**
270     * @return {@code null-ok;} return address of a ret instruction if encountered
271     * since last call to startBlock(). null if no ret instruction encountered.
272     */
273    public ReturnAddress getReturnAddress() {
274        return returnAddress;
275    }
276
277    /** {@inheritDoc} */
278    @Override
279    public void run(Frame frame, int offset, int opcode) {
280        /*
281         * This is the stack pointer after the opcode's arguments have been
282         * popped.
283         */
284        int stackPointer = maxLocals + frame.getStack().size();
285
286        // The sources have to be retrieved before super.run() gets called.
287        RegisterSpecList sources = getSources(opcode, stackPointer);
288        int sourceCount = sources.size();
289
290        super.run(frame, offset, opcode);
291
292        SourcePosition pos = method.makeSourcePosistion(offset);
293        RegisterSpec localTarget = getLocalTarget();
294        int destCount = resultCount();
295        RegisterSpec dest;
296
297        if (destCount == 0) {
298            dest = null;
299            switch (opcode) {
300                case ByteOps.POP:
301                case ByteOps.POP2: {
302                    // These simply don't appear in the rop form.
303                    return;
304                }
305            }
306        } else if (localTarget != null) {
307            dest = localTarget;
308        } else if (destCount == 1) {
309            dest = RegisterSpec.make(stackPointer, result(0));
310        } else {
311            /*
312             * This clause only ever applies to the stack manipulation
313             * ops that have results (that is, dup* and swap but not
314             * pop*).
315             *
316             * What we do is first move all the source registers into
317             * the "temporary stack" area defined for the method, and
318             * then move stuff back down onto the main "stack" in the
319             * arrangement specified by the stack op pattern.
320             *
321             * Note: This code ends up emitting a lot of what will
322             * turn out to be superfluous moves (e.g., moving back and
323             * forth to the same local when doing a dup); however,
324             * that makes this code a bit easier (and goodness knows
325             * it doesn't need any extra complexity), and all the SSA
326             * stuff is going to want to deal with this sort of
327             * superfluous assignment anyway, so it should be a wash
328             * in the end.
329             */
330            int scratchAt = ropper.getFirstTempStackReg();
331            RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
332
333            for (int i = 0; i < sourceCount; i++) {
334                RegisterSpec src = sources.get(i);
335                TypeBearer type = src.getTypeBearer();
336                RegisterSpec scratch = src.withReg(scratchAt);
337                insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
338                scratchRegs[i] = scratch;
339                scratchAt += src.getCategory();
340            }
341
342            for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
343                int which = (pattern & 0x0f) - 1;
344                RegisterSpec scratch = scratchRegs[which];
345                TypeBearer type = scratch.getTypeBearer();
346                insns.add(new PlainInsn(Rops.opMove(type), pos,
347                                        scratch.withReg(stackPointer),
348                                        scratch));
349                stackPointer += type.getType().getCategory();
350            }
351            return;
352        }
353
354        TypeBearer destType = (dest != null) ? dest : Type.VOID;
355        Constant cst = getAuxCst();
356        int ropOpcode;
357        Rop rop;
358        Insn insn;
359
360        if (opcode == ByteOps.MULTIANEWARRAY) {
361            blockCanThrow = true;
362
363            // Add the extra instructions for handling multianewarray.
364
365            extraBlockCount = 6;
366
367            /*
368             * Add an array constructor for the int[] containing all the
369             * dimensions.
370             */
371            RegisterSpec dimsReg =
372                RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
373            rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
374            insn = new ThrowingCstInsn(rop, pos, sources, catches,
375                    CstType.INT_ARRAY);
376            insns.add(insn);
377
378            // Add a move-result for the new-filled-array
379            rop = Rops.opMoveResult(Type.INT_ARRAY);
380            insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
381            insns.add(insn);
382
383            /*
384             * Add a const-class instruction for the specified array
385             * class.
386             */
387
388            /*
389             * Remove as many dimensions from the originally specified
390             * class as are given in the explicit list of dimensions,
391             * so as to pass the right component class to the standard
392             * Java library array constructor.
393             */
394            Type componentType = ((CstType) cst).getClassType();
395            for (int i = 0; i < sourceCount; i++) {
396                componentType = componentType.getComponentType();
397            }
398
399            RegisterSpec classReg =
400                RegisterSpec.make(dest.getReg(), Type.CLASS);
401
402            if (componentType.isPrimitive()) {
403                /*
404                 * The component type is primitive (e.g., int as opposed
405                 * to Integer), so we have to fetch the corresponding
406                 * TYPE class.
407                 */
408                CstFieldRef typeField =
409                    CstFieldRef.forPrimitiveType(componentType);
410                insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos,
411                                           RegisterSpecList.EMPTY,
412                                           catches, typeField);
413            } else {
414                /*
415                 * The component type is an object type, so just make a
416                 * normal class reference.
417                 */
418                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
419                                           RegisterSpecList.EMPTY, catches,
420                                           new CstType(componentType));
421            }
422
423            insns.add(insn);
424
425            // Add a move-result-pseudo for the get-static or const
426            rop = Rops.opMoveResultPseudo(classReg.getType());
427            insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
428            insns.add(insn);
429
430            /*
431             * Add a call to the "multianewarray method," that is,
432             * Array.newInstance(class, dims). Note: The result type
433             * of newInstance() is Object, which is why the last
434             * instruction in this sequence is a cast to the right
435             * type for the original instruction.
436             */
437
438            RegisterSpec objectReg =
439                RegisterSpec.make(dest.getReg(), Type.OBJECT);
440
441            insn = new ThrowingCstInsn(
442                    Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()),
443                    pos, RegisterSpecList.make(classReg, dimsReg),
444                    catches, MULTIANEWARRAY_METHOD);
445            insns.add(insn);
446
447            // Add a move-result
448            rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype()
449                    .getReturnType());
450            insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
451            insns.add(insn);
452
453            /*
454             * And finally, set up for the remainder of this method to
455             * add an appropriate cast.
456             */
457
458            opcode = ByteOps.CHECKCAST;
459            sources = RegisterSpecList.make(objectReg);
460
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
478        rop = Rops.ropFor(ropOpcode, destType, sources, cst);
479
480        Insn moveResult = null;
481        if (dest != null && rop.isCallLike()) {
482            // We're going to want to have a move-result in the next basic block
483            extraBlockCount++;
484
485            moveResult = new PlainInsn(
486                    Rops.opMoveResult(((CstMethodRef) cst).getPrototype()
487                    .getReturnType()), pos, dest, RegisterSpecList.EMPTY);
488
489            dest = null;
490        } else if (dest != null && rop.canThrow()) {
491            // We're going to want to have a move-result-pseudo
492            // in the next basic block
493            extraBlockCount++;
494
495            moveResult = new PlainInsn(
496                    Rops.opMoveResultPseudo(dest.getTypeBearer()),
497                    pos, dest, RegisterSpecList.EMPTY);
498
499            dest = null;
500        }
501        if (ropOpcode == RegOps.NEW_ARRAY) {
502            /*
503             * In the original bytecode, this was either a primitive
504             * array constructor "newarray" or an object array
505             * constructor "anewarray". In the former case, there is
506             * no explicit constant, and in the latter, the constant
507             * is for the element type and not the array type. The rop
508             * instruction form for both of these is supposed to be
509             * the resulting array type, so we initialize / alter
510             * "cst" here, accordingly. Conveniently enough, the rop
511             * opcode already gets constructed with the proper array
512             * type.
513             */
514            cst = CstType.intern(rop.getResult());
515        } else if ((cst == null) && (sourceCount == 2)) {
516            TypeBearer lastType = sources.get(1).getTypeBearer();
517
518            if (lastType.isConstant()
519                    && advice.hasConstantOperation(rop,
520                    sources.get(0), sources.get(1))) {
521                /*
522                 * The target architecture has an instruction that can
523                 * build in the constant found in the second argument,
524                 * so pull it out of the sources and just use it as a
525                 * constant here.
526                 */
527                cst = (Constant) lastType;
528                sources = sources.withoutLast();
529                rop = Rops.ropFor(ropOpcode, destType, sources, cst);
530            }
531        }
532
533        SwitchList cases = getAuxCases();
534        ArrayList<Constant> initValues = getInitValues();
535        boolean canThrow = rop.canThrow();
536
537        blockCanThrow |= canThrow;
538
539        if (cases != null) {
540            if (cases.size() == 0) {
541                // It's a default-only switch statement. It can happen!
542                insn = new PlainInsn(Rops.GOTO, pos, null,
543                                     RegisterSpecList.EMPTY);
544                primarySuccessorIndex = 0;
545            } else {
546                IntList values = cases.getValues();
547                insn = new SwitchInsn(rop, pos, dest, sources, values);
548                primarySuccessorIndex = values.size();
549            }
550        } else if (ropOpcode == RegOps.RETURN) {
551            /*
552             * Returns get turned into the combination of a move (if
553             * non-void and if the return doesn't already mention
554             * register 0) and a goto (to the return block).
555             */
556            if (sources.size() != 0) {
557                RegisterSpec source = sources.get(0);
558                TypeBearer type = source.getTypeBearer();
559                if (source.getReg() != 0) {
560                    insns.add(new PlainInsn(Rops.opMove(type), pos,
561                                            RegisterSpec.make(0, type),
562                                            source));
563                }
564            }
565            insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
566            primarySuccessorIndex = 0;
567            updateReturnOp(rop, pos);
568            returns = true;
569        } else if (cst != null) {
570            if (canThrow) {
571                insn =
572                    new ThrowingCstInsn(rop, pos, sources, catches, cst);
573                catchesUsed = true;
574                primarySuccessorIndex = catches.size();
575            } else {
576                insn = new PlainCstInsn(rop, pos, dest, sources, cst);
577            }
578        } else if (canThrow) {
579            insn = new ThrowingInsn(rop, pos, sources, catches);
580            catchesUsed = true;
581            if (opcode == ByteOps.ATHROW) {
582                /*
583                 * The op athrow is the only one where it's possible
584                 * to have non-empty successors and yet not have a
585                 * primary successor.
586                 */
587                primarySuccessorIndex = -1;
588            } else {
589                primarySuccessorIndex = catches.size();
590            }
591        } else {
592            insn = new PlainInsn(rop, pos, dest, sources);
593        }
594
595        insns.add(insn);
596
597        if (moveResult != null) {
598            insns.add(moveResult);
599        }
600
601        /*
602         * If initValues is non-null, it means that the parser has seen a group
603         * of compatible constant initialization bytecodes that are applied to
604         * the current newarray. The action we take here is to convert these
605         * initialization bytecodes into a single fill-array-data ROP which lays
606         * out all the constant values in a table.
607         */
608        if (initValues != null) {
609            extraBlockCount++;
610            insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
611                    RegisterSpecList.make(moveResult.getResult()), initValues,
612                    cst);
613            insns.add(insn);
614        }
615    }
616
617    /**
618     * Helper for {@link #run}, which gets the list of sources for the.
619     * instruction.
620     *
621     * @param opcode the opcode being translated
622     * @param stackPointer {@code >= 0;} the stack pointer after the instruction's
623     * arguments have been popped
624     * @return {@code non-null;} the sources
625     */
626    private RegisterSpecList getSources(int opcode, int stackPointer) {
627        int count = argCount();
628
629        if (count == 0) {
630            // We get an easy out if there aren't any sources.
631            return RegisterSpecList.EMPTY;
632        }
633
634        int localIndex = getLocalIndex();
635        RegisterSpecList sources;
636
637        if (localIndex >= 0) {
638            // The instruction is operating on a local variable.
639            sources = new RegisterSpecList(1);
640            sources.set(0, RegisterSpec.make(localIndex, arg(0)));
641        } else {
642            sources = new RegisterSpecList(count);
643            int regAt = stackPointer;
644            for (int i = 0; i < count; i++) {
645                RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
646                sources.set(i, spec);
647                regAt += spec.getCategory();
648            }
649
650            switch (opcode) {
651                case ByteOps.IASTORE: {
652                    /*
653                     * The Java argument order for array stores is
654                     * (array, index, value), but the rop argument
655                     * order is (value, array, index). The following
656                     * code gets the right arguments in the right
657                     * places.
658                     */
659                    if (count != 3) {
660                        throw new RuntimeException("shouldn't happen");
661                    }
662                    RegisterSpec array = sources.get(0);
663                    RegisterSpec index = sources.get(1);
664                    RegisterSpec value = sources.get(2);
665                    sources.set(0, value);
666                    sources.set(1, array);
667                    sources.set(2, index);
668                    break;
669                }
670                case ByteOps.PUTFIELD: {
671                    /*
672                     * Similar to above: The Java argument order for
673                     * putfield is (object, value), but the rop
674                     * argument order is (value, object).
675                     */
676                    if (count != 2) {
677                        throw new RuntimeException("shouldn't happen");
678                    }
679                    RegisterSpec obj = sources.get(0);
680                    RegisterSpec value = sources.get(1);
681                    sources.set(0, value);
682                    sources.set(1, obj);
683                    break;
684                }
685            }
686        }
687
688        sources.setImmutable();
689        return sources;
690    }
691
692    /**
693     * Sets or updates the information about the return block.
694     *
695     * @param op {@code non-null;} the opcode to use
696     * @param pos {@code non-null;} the position to use
697     */
698    private void updateReturnOp(Rop op, SourcePosition pos) {
699        if (op == null) {
700            throw new NullPointerException("op == null");
701        }
702
703        if (pos == null) {
704            throw new NullPointerException("pos == null");
705        }
706
707        if (returnOp == null) {
708            returnOp = op;
709            returnPosition = pos;
710        } else {
711            if (returnOp != op) {
712                throw new SimException("return op mismatch: " + op + ", " +
713                                       returnOp);
714            }
715
716            if (pos.getLine() > returnPosition.getLine()) {
717                // Pick the largest line number to be the "canonical" return.
718                returnPosition = pos;
719            }
720        }
721    }
722
723    /**
724     * Gets the register opcode for the given Java opcode.
725     *
726     * @param jop {@code >= 0;} the Java opcode
727     * @param cst {@code null-ok;} the constant argument, if any
728     * @return {@code >= 0;} the corresponding register opcode
729     */
730    private int jopToRopOpcode(int jop, Constant cst) {
731        switch (jop) {
732            case ByteOps.POP:
733            case ByteOps.POP2:
734            case ByteOps.DUP:
735            case ByteOps.DUP_X1:
736            case ByteOps.DUP_X2:
737            case ByteOps.DUP2:
738            case ByteOps.DUP2_X1:
739            case ByteOps.DUP2_X2:
740            case ByteOps.SWAP:
741            case ByteOps.JSR:
742            case ByteOps.RET:
743            case ByteOps.MULTIANEWARRAY: {
744                // These need to be taken care of specially.
745                break;
746            }
747            case ByteOps.NOP: {
748                return RegOps.NOP;
749            }
750            case ByteOps.LDC:
751            case ByteOps.LDC2_W: {
752                return RegOps.CONST;
753            }
754            case ByteOps.ILOAD:
755            case ByteOps.ISTORE: {
756                return RegOps.MOVE;
757            }
758            case ByteOps.IALOAD: {
759                return RegOps.AGET;
760            }
761            case ByteOps.IASTORE: {
762                return RegOps.APUT;
763            }
764            case ByteOps.IADD:
765            case ByteOps.IINC: {
766                return RegOps.ADD;
767            }
768            case ByteOps.ISUB: {
769                return RegOps.SUB;
770            }
771            case ByteOps.IMUL: {
772                return RegOps.MUL;
773            }
774            case ByteOps.IDIV: {
775                return RegOps.DIV;
776            }
777            case ByteOps.IREM: {
778                return RegOps.REM;
779            }
780            case ByteOps.INEG: {
781                return RegOps.NEG;
782            }
783            case ByteOps.ISHL: {
784                return RegOps.SHL;
785            }
786            case ByteOps.ISHR: {
787                return RegOps.SHR;
788            }
789            case ByteOps.IUSHR: {
790                return RegOps.USHR;
791            }
792            case ByteOps.IAND: {
793                return RegOps.AND;
794            }
795            case ByteOps.IOR: {
796                return RegOps.OR;
797            }
798            case ByteOps.IXOR: {
799                return RegOps.XOR;
800            }
801            case ByteOps.I2L:
802            case ByteOps.I2F:
803            case ByteOps.I2D:
804            case ByteOps.L2I:
805            case ByteOps.L2F:
806            case ByteOps.L2D:
807            case ByteOps.F2I:
808            case ByteOps.F2L:
809            case ByteOps.F2D:
810            case ByteOps.D2I:
811            case ByteOps.D2L:
812            case ByteOps.D2F: {
813                return RegOps.CONV;
814            }
815            case ByteOps.I2B: {
816                return RegOps.TO_BYTE;
817            }
818            case ByteOps.I2C: {
819                return RegOps.TO_CHAR;
820            }
821            case ByteOps.I2S: {
822                return RegOps.TO_SHORT;
823            }
824            case ByteOps.LCMP:
825            case ByteOps.FCMPL:
826            case ByteOps.DCMPL: {
827                return RegOps.CMPL;
828            }
829            case ByteOps.FCMPG:
830            case ByteOps.DCMPG: {
831                return RegOps.CMPG;
832            }
833            case ByteOps.IFEQ:
834            case ByteOps.IF_ICMPEQ:
835            case ByteOps.IF_ACMPEQ:
836            case ByteOps.IFNULL: {
837                return RegOps.IF_EQ;
838            }
839            case ByteOps.IFNE:
840            case ByteOps.IF_ICMPNE:
841            case ByteOps.IF_ACMPNE:
842            case ByteOps.IFNONNULL: {
843                return RegOps.IF_NE;
844            }
845            case ByteOps.IFLT:
846            case ByteOps.IF_ICMPLT: {
847                return RegOps.IF_LT;
848            }
849            case ByteOps.IFGE:
850            case ByteOps.IF_ICMPGE: {
851                return RegOps.IF_GE;
852            }
853            case ByteOps.IFGT:
854            case ByteOps.IF_ICMPGT: {
855                return RegOps.IF_GT;
856            }
857            case ByteOps.IFLE:
858            case ByteOps.IF_ICMPLE: {
859                return RegOps.IF_LE;
860            }
861            case ByteOps.GOTO: {
862                return RegOps.GOTO;
863            }
864            case ByteOps.LOOKUPSWITCH: {
865                return RegOps.SWITCH;
866            }
867            case ByteOps.IRETURN:
868            case ByteOps.RETURN: {
869                return RegOps.RETURN;
870            }
871            case ByteOps.GETSTATIC: {
872                return RegOps.GET_STATIC;
873            }
874            case ByteOps.PUTSTATIC: {
875                return RegOps.PUT_STATIC;
876            }
877            case ByteOps.GETFIELD: {
878                return RegOps.GET_FIELD;
879            }
880            case ByteOps.PUTFIELD: {
881                return RegOps.PUT_FIELD;
882            }
883            case ByteOps.INVOKEVIRTUAL: {
884                return RegOps.INVOKE_VIRTUAL;
885            }
886            case ByteOps.INVOKESPECIAL: {
887                /*
888                 * Determine whether the opcode should be
889                 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
890                 * on "invokespecial" as well as section 4.8.2 (7th
891                 * bullet point) for the gory details.
892                 */
893                CstMethodRef ref = (CstMethodRef) cst;
894                if (ref.isInstanceInit() ||
895                    (ref.getDefiningClass() == method.getDefiningClass()) ||
896                    !method.getAccSuper()) {
897                    return RegOps.INVOKE_DIRECT;
898                }
899                return RegOps.INVOKE_SUPER;
900            }
901            case ByteOps.INVOKESTATIC: {
902                return RegOps.INVOKE_STATIC;
903            }
904            case ByteOps.INVOKEINTERFACE: {
905                return RegOps.INVOKE_INTERFACE;
906            }
907            case ByteOps.NEW: {
908                return RegOps.NEW_INSTANCE;
909            }
910            case ByteOps.NEWARRAY:
911            case ByteOps.ANEWARRAY: {
912                return RegOps.NEW_ARRAY;
913            }
914            case ByteOps.ARRAYLENGTH: {
915                return RegOps.ARRAY_LENGTH;
916            }
917            case ByteOps.ATHROW: {
918                return RegOps.THROW;
919            }
920            case ByteOps.CHECKCAST: {
921                return RegOps.CHECK_CAST;
922            }
923            case ByteOps.INSTANCEOF: {
924                return RegOps.INSTANCE_OF;
925            }
926            case ByteOps.MONITORENTER: {
927                return RegOps.MONITOR_ENTER;
928            }
929            case ByteOps.MONITOREXIT: {
930                return RegOps.MONITOR_EXIT;
931            }
932        }
933
934        throw new RuntimeException("shouldn't happen");
935    }
936}
937