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;
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 CstString("newInstance"),
61                                    new CstString("(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(opcode == ByteOps.ISTORE);
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 firstType = sources.get(0).getTypeBearer();
521            TypeBearer lastType = sources.get(1).getTypeBearer();
522
523            if ((lastType.isConstant() || firstType.isConstant()) &&
524                 advice.hasConstantOperation(rop, sources.get(0),
525                                             sources.get(1))) {
526
527                if (lastType.isConstant()) {
528                    /*
529                     * The target architecture has an instruction that can
530                     * build in the constant found in the second argument,
531                     * so pull it out of the sources and just use it as a
532                     * constant here.
533                     */
534                    cst = (Constant) lastType;
535                    sources = sources.withoutLast();
536
537                    // For subtraction, change to addition and invert constant
538                    if (rop.getOpcode() == RegOps.SUB) {
539                        ropOpcode = RegOps.ADD;
540                        CstInteger cstInt = (CstInteger) lastType;
541                        cst = CstInteger.make(-cstInt.getValue());
542                    }
543                } else {
544                    /*
545                     * The target architecture has an instruction that can
546                     * build in the constant found in the first argument,
547                     * so pull it out of the sources and just use it as a
548                     * constant here.
549                     */
550                    cst = (Constant) firstType;
551                    sources = sources.withoutFirst();
552                }
553
554                rop = Rops.ropFor(ropOpcode, destType, sources, cst);
555            }
556        }
557
558        SwitchList cases = getAuxCases();
559        ArrayList<Constant> initValues = getInitValues();
560        boolean canThrow = rop.canThrow();
561
562        blockCanThrow |= canThrow;
563
564        if (cases != null) {
565            if (cases.size() == 0) {
566                // It's a default-only switch statement. It can happen!
567                insn = new PlainInsn(Rops.GOTO, pos, null,
568                                     RegisterSpecList.EMPTY);
569                primarySuccessorIndex = 0;
570            } else {
571                IntList values = cases.getValues();
572                insn = new SwitchInsn(rop, pos, dest, sources, values);
573                primarySuccessorIndex = values.size();
574            }
575        } else if (ropOpcode == RegOps.RETURN) {
576            /*
577             * Returns get turned into the combination of a move (if
578             * non-void and if the return doesn't already mention
579             * register 0) and a goto (to the return block).
580             */
581            if (sources.size() != 0) {
582                RegisterSpec source = sources.get(0);
583                TypeBearer type = source.getTypeBearer();
584                if (source.getReg() != 0) {
585                    insns.add(new PlainInsn(Rops.opMove(type), pos,
586                                            RegisterSpec.make(0, type),
587                                            source));
588                }
589            }
590            insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
591            primarySuccessorIndex = 0;
592            updateReturnOp(rop, pos);
593            returns = true;
594        } else if (cst != null) {
595            if (canThrow) {
596                insn =
597                    new ThrowingCstInsn(rop, pos, sources, catches, cst);
598                catchesUsed = true;
599                primarySuccessorIndex = catches.size();
600            } else {
601                insn = new PlainCstInsn(rop, pos, dest, sources, cst);
602            }
603        } else if (canThrow) {
604            insn = new ThrowingInsn(rop, pos, sources, catches);
605            catchesUsed = true;
606            if (opcode == ByteOps.ATHROW) {
607                /*
608                 * The op athrow is the only one where it's possible
609                 * to have non-empty successors and yet not have a
610                 * primary successor.
611                 */
612                primarySuccessorIndex = -1;
613            } else {
614                primarySuccessorIndex = catches.size();
615            }
616        } else {
617            insn = new PlainInsn(rop, pos, dest, sources);
618        }
619
620        insns.add(insn);
621
622        if (moveResult != null) {
623            insns.add(moveResult);
624        }
625
626        /*
627         * If initValues is non-null, it means that the parser has
628         * seen a group of compatible constant initialization
629         * bytecodes that are applied to the current newarray. The
630         * action we take here is to convert these initialization
631         * bytecodes into a single fill-array-data ROP which lays out
632         * all the constant values in a table.
633         */
634        if (initValues != null) {
635            extraBlockCount++;
636            insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
637                    RegisterSpecList.make(moveResult.getResult()), initValues,
638                    cst);
639            insns.add(insn);
640        }
641    }
642
643    /**
644     * Helper for {@link #run}, which gets the list of sources for the.
645     * instruction.
646     *
647     * @param opcode the opcode being translated
648     * @param stackPointer {@code >= 0;} the stack pointer after the
649     * instruction's arguments have been popped
650     * @return {@code non-null;} the sources
651     */
652    private RegisterSpecList getSources(int opcode, int stackPointer) {
653        int count = argCount();
654
655        if (count == 0) {
656            // We get an easy out if there aren't any sources.
657            return RegisterSpecList.EMPTY;
658        }
659
660        int localIndex = getLocalIndex();
661        RegisterSpecList sources;
662
663        if (localIndex >= 0) {
664            // The instruction is operating on a local variable.
665            sources = new RegisterSpecList(1);
666            sources.set(0, RegisterSpec.make(localIndex, arg(0)));
667        } else {
668            sources = new RegisterSpecList(count);
669            int regAt = stackPointer;
670            for (int i = 0; i < count; i++) {
671                RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
672                sources.set(i, spec);
673                regAt += spec.getCategory();
674            }
675
676            switch (opcode) {
677                case ByteOps.IASTORE: {
678                    /*
679                     * The Java argument order for array stores is
680                     * (array, index, value), but the rop argument
681                     * order is (value, array, index). The following
682                     * code gets the right arguments in the right
683                     * places.
684                     */
685                    if (count != 3) {
686                        throw new RuntimeException("shouldn't happen");
687                    }
688                    RegisterSpec array = sources.get(0);
689                    RegisterSpec index = sources.get(1);
690                    RegisterSpec value = sources.get(2);
691                    sources.set(0, value);
692                    sources.set(1, array);
693                    sources.set(2, index);
694                    break;
695                }
696                case ByteOps.PUTFIELD: {
697                    /*
698                     * Similar to above: The Java argument order for
699                     * putfield is (object, value), but the rop
700                     * argument order is (value, object).
701                     */
702                    if (count != 2) {
703                        throw new RuntimeException("shouldn't happen");
704                    }
705                    RegisterSpec obj = sources.get(0);
706                    RegisterSpec value = sources.get(1);
707                    sources.set(0, value);
708                    sources.set(1, obj);
709                    break;
710                }
711            }
712        }
713
714        sources.setImmutable();
715        return sources;
716    }
717
718    /**
719     * Sets or updates the information about the return block.
720     *
721     * @param op {@code non-null;} the opcode to use
722     * @param pos {@code non-null;} the position to use
723     */
724    private void updateReturnOp(Rop op, SourcePosition pos) {
725        if (op == null) {
726            throw new NullPointerException("op == null");
727        }
728
729        if (pos == null) {
730            throw new NullPointerException("pos == null");
731        }
732
733        if (returnOp == null) {
734            returnOp = op;
735            returnPosition = pos;
736        } else {
737            if (returnOp != op) {
738                throw new SimException("return op mismatch: " + op + ", " +
739                                       returnOp);
740            }
741
742            if (pos.getLine() > returnPosition.getLine()) {
743                // Pick the largest line number to be the "canonical" return.
744                returnPosition = pos;
745            }
746        }
747    }
748
749    /**
750     * Gets the register opcode for the given Java opcode.
751     *
752     * @param jop {@code >= 0;} the Java opcode
753     * @param cst {@code null-ok;} the constant argument, if any
754     * @return {@code >= 0;} the corresponding register opcode
755     */
756    private int jopToRopOpcode(int jop, Constant cst) {
757        switch (jop) {
758            case ByteOps.POP:
759            case ByteOps.POP2:
760            case ByteOps.DUP:
761            case ByteOps.DUP_X1:
762            case ByteOps.DUP_X2:
763            case ByteOps.DUP2:
764            case ByteOps.DUP2_X1:
765            case ByteOps.DUP2_X2:
766            case ByteOps.SWAP:
767            case ByteOps.JSR:
768            case ByteOps.RET:
769            case ByteOps.MULTIANEWARRAY: {
770                // These need to be taken care of specially.
771                break;
772            }
773            case ByteOps.NOP: {
774                return RegOps.NOP;
775            }
776            case ByteOps.LDC:
777            case ByteOps.LDC2_W: {
778                return RegOps.CONST;
779            }
780            case ByteOps.ILOAD:
781            case ByteOps.ISTORE: {
782                return RegOps.MOVE;
783            }
784            case ByteOps.IALOAD: {
785                return RegOps.AGET;
786            }
787            case ByteOps.IASTORE: {
788                return RegOps.APUT;
789            }
790            case ByteOps.IADD:
791            case ByteOps.IINC: {
792                return RegOps.ADD;
793            }
794            case ByteOps.ISUB: {
795                return RegOps.SUB;
796            }
797            case ByteOps.IMUL: {
798                return RegOps.MUL;
799            }
800            case ByteOps.IDIV: {
801                return RegOps.DIV;
802            }
803            case ByteOps.IREM: {
804                return RegOps.REM;
805            }
806            case ByteOps.INEG: {
807                return RegOps.NEG;
808            }
809            case ByteOps.ISHL: {
810                return RegOps.SHL;
811            }
812            case ByteOps.ISHR: {
813                return RegOps.SHR;
814            }
815            case ByteOps.IUSHR: {
816                return RegOps.USHR;
817            }
818            case ByteOps.IAND: {
819                return RegOps.AND;
820            }
821            case ByteOps.IOR: {
822                return RegOps.OR;
823            }
824            case ByteOps.IXOR: {
825                return RegOps.XOR;
826            }
827            case ByteOps.I2L:
828            case ByteOps.I2F:
829            case ByteOps.I2D:
830            case ByteOps.L2I:
831            case ByteOps.L2F:
832            case ByteOps.L2D:
833            case ByteOps.F2I:
834            case ByteOps.F2L:
835            case ByteOps.F2D:
836            case ByteOps.D2I:
837            case ByteOps.D2L:
838            case ByteOps.D2F: {
839                return RegOps.CONV;
840            }
841            case ByteOps.I2B: {
842                return RegOps.TO_BYTE;
843            }
844            case ByteOps.I2C: {
845                return RegOps.TO_CHAR;
846            }
847            case ByteOps.I2S: {
848                return RegOps.TO_SHORT;
849            }
850            case ByteOps.LCMP:
851            case ByteOps.FCMPL:
852            case ByteOps.DCMPL: {
853                return RegOps.CMPL;
854            }
855            case ByteOps.FCMPG:
856            case ByteOps.DCMPG: {
857                return RegOps.CMPG;
858            }
859            case ByteOps.IFEQ:
860            case ByteOps.IF_ICMPEQ:
861            case ByteOps.IF_ACMPEQ:
862            case ByteOps.IFNULL: {
863                return RegOps.IF_EQ;
864            }
865            case ByteOps.IFNE:
866            case ByteOps.IF_ICMPNE:
867            case ByteOps.IF_ACMPNE:
868            case ByteOps.IFNONNULL: {
869                return RegOps.IF_NE;
870            }
871            case ByteOps.IFLT:
872            case ByteOps.IF_ICMPLT: {
873                return RegOps.IF_LT;
874            }
875            case ByteOps.IFGE:
876            case ByteOps.IF_ICMPGE: {
877                return RegOps.IF_GE;
878            }
879            case ByteOps.IFGT:
880            case ByteOps.IF_ICMPGT: {
881                return RegOps.IF_GT;
882            }
883            case ByteOps.IFLE:
884            case ByteOps.IF_ICMPLE: {
885                return RegOps.IF_LE;
886            }
887            case ByteOps.GOTO: {
888                return RegOps.GOTO;
889            }
890            case ByteOps.LOOKUPSWITCH: {
891                return RegOps.SWITCH;
892            }
893            case ByteOps.IRETURN:
894            case ByteOps.RETURN: {
895                return RegOps.RETURN;
896            }
897            case ByteOps.GETSTATIC: {
898                return RegOps.GET_STATIC;
899            }
900            case ByteOps.PUTSTATIC: {
901                return RegOps.PUT_STATIC;
902            }
903            case ByteOps.GETFIELD: {
904                return RegOps.GET_FIELD;
905            }
906            case ByteOps.PUTFIELD: {
907                return RegOps.PUT_FIELD;
908            }
909            case ByteOps.INVOKEVIRTUAL: {
910                return RegOps.INVOKE_VIRTUAL;
911            }
912            case ByteOps.INVOKESPECIAL: {
913                /*
914                 * Determine whether the opcode should be
915                 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
916                 * on "invokespecial" as well as section 4.8.2 (7th
917                 * bullet point) for the gory details.
918                 */
919                CstMethodRef ref = (CstMethodRef) cst;
920                if (ref.isInstanceInit() ||
921                    (ref.getDefiningClass() == method.getDefiningClass()) ||
922                    !method.getAccSuper()) {
923                    return RegOps.INVOKE_DIRECT;
924                }
925                return RegOps.INVOKE_SUPER;
926            }
927            case ByteOps.INVOKESTATIC: {
928                return RegOps.INVOKE_STATIC;
929            }
930            case ByteOps.INVOKEINTERFACE: {
931                return RegOps.INVOKE_INTERFACE;
932            }
933            case ByteOps.NEW: {
934                return RegOps.NEW_INSTANCE;
935            }
936            case ByteOps.NEWARRAY:
937            case ByteOps.ANEWARRAY: {
938                return RegOps.NEW_ARRAY;
939            }
940            case ByteOps.ARRAYLENGTH: {
941                return RegOps.ARRAY_LENGTH;
942            }
943            case ByteOps.ATHROW: {
944                return RegOps.THROW;
945            }
946            case ByteOps.CHECKCAST: {
947                return RegOps.CHECK_CAST;
948            }
949            case ByteOps.INSTANCEOF: {
950                return RegOps.INSTANCE_OF;
951            }
952            case ByteOps.MONITORENTER: {
953                return RegOps.MONITOR_ENTER;
954            }
955            case ByteOps.MONITOREXIT: {
956                return RegOps.MONITOR_EXIT;
957            }
958        }
959
960        throw new RuntimeException("shouldn't happen");
961    }
962}
963