• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dx.cf.code;
18 
19 import com.android.dx.rop.code.FillArrayDataInsn;
20 import com.android.dx.rop.code.Insn;
21 import com.android.dx.rop.code.PlainCstInsn;
22 import com.android.dx.rop.code.PlainInsn;
23 import com.android.dx.rop.code.RegOps;
24 import com.android.dx.rop.code.RegisterSpec;
25 import com.android.dx.rop.code.RegisterSpecList;
26 import com.android.dx.rop.code.Rop;
27 import com.android.dx.rop.code.Rops;
28 import com.android.dx.rop.code.SourcePosition;
29 import com.android.dx.rop.code.SwitchInsn;
30 import com.android.dx.rop.code.ThrowingCstInsn;
31 import com.android.dx.rop.code.ThrowingInsn;
32 import com.android.dx.rop.code.TranslationAdvice;
33 import com.android.dx.rop.cst.Constant;
34 import com.android.dx.rop.cst.CstFieldRef;
35 import com.android.dx.rop.cst.CstInteger;
36 import com.android.dx.rop.cst.CstMethodRef;
37 import com.android.dx.rop.cst.CstNat;
38 import com.android.dx.rop.cst.CstString;
39 import com.android.dx.rop.cst.CstType;
40 import com.android.dx.rop.type.Type;
41 import com.android.dx.rop.type.TypeBearer;
42 import com.android.dx.rop.type.TypeList;
43 import com.android.dx.util.IntList;
44 import 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      */
RopperMachine(Ropper ropper, ConcreteMethod method, TranslationAdvice advice)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      */
getInsns()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      */
getReturnOp()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      */
getReturnPosition()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      */
startBlock(TypeList catches)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      */
wereCatchesUsed()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      */
returns()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      */
getPrimarySuccessorIndex()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      */
getExtraBlockCount()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      */
canThrow()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      */
hasJsr()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      */
hasRet()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      */
getReturnAddress()274     public ReturnAddress getReturnAddress() {
275         return returnAddress;
276     }
277 
278     /** {@inheritDoc} */
279     @Override
run(Frame frame, int offset, int opcode)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      */
getSources(int opcode, int stackPointer)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      */
updateReturnOp(Rop op, SourcePosition pos)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      */
jopToRopOpcode(int jop, Constant cst)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