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