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