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