1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.ir.code; 5 6 import com.android.tools.r8.errors.Unreachable; 7 import com.android.tools.r8.graph.AppInfo; 8 import com.android.tools.r8.graph.DebugLocalInfo; 9 import com.android.tools.r8.graph.DexType; 10 import com.android.tools.r8.ir.code.Value.DebugInfo; 11 import com.android.tools.r8.ir.conversion.DexBuilder; 12 import com.android.tools.r8.ir.optimize.Inliner.Constraint; 13 import com.android.tools.r8.ir.regalloc.RegisterAllocator; 14 import com.android.tools.r8.utils.CfgPrinter; 15 import com.android.tools.r8.utils.InternalOptions; 16 import com.android.tools.r8.utils.StringUtils; 17 import com.android.tools.r8.utils.StringUtils.BraceType; 18 import com.google.common.collect.ImmutableList; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 public abstract class Instruction { 23 24 protected Value outValue = null; 25 protected final List<Value> inValues = new ArrayList<>(); 26 private BasicBlock block = null; 27 private int number = -1; 28 private List<Value> debugValues = null; 29 30 protected Instruction(Value outValue) { 31 setOutValue(outValue); 32 } 33 34 protected Instruction(Value outValue, Value inValue) { 35 addInValue(inValue); 36 setOutValue(outValue); 37 } 38 39 protected Instruction(Value outValue, List<? extends Value> inValues) { 40 if (inValues != null) { 41 for (Value v : inValues) { 42 addInValue(v); 43 } 44 } 45 setOutValue(outValue); 46 } 47 48 public List<Value> inValues() { 49 return inValues; 50 } 51 52 protected void addInValue(Value value) { 53 if (value != null) { 54 inValues.add(value); 55 value.addUser(this); 56 } 57 } 58 59 public Value outValue() { 60 return outValue; 61 } 62 63 public void setOutValue(Value value) { 64 assert outValue == null || !outValue.hasUsersInfo() || outValue.numberOfAllUsers() == 0; 65 outValue = value; 66 if (outValue != null) { 67 outValue.definition = this; 68 Value previousLocalValue = getPreviousLocalValue(); 69 if (previousLocalValue != null) { 70 previousLocalValue.addDebugUser(this); 71 } 72 } 73 } 74 75 public void addDebugValue(Value value) { 76 assert value.getLocalInfo() != null; 77 if (debugValues == null) { 78 debugValues = new ArrayList<>(); 79 } 80 debugValues.add(value); 81 value.addDebugUser(this); 82 } 83 84 public static void clearUserInfo(Instruction instruction) { 85 if (instruction.outValue != null) { 86 instruction.outValue.clearUsersInfo(); 87 } 88 instruction.inValues.forEach(Value::clearUsersInfo); 89 if (instruction.debugValues != null) { 90 instruction.debugValues.forEach(Value::clearUsersInfo); 91 } 92 } 93 94 public final MoveType outType() { 95 return outValue.outType(); 96 } 97 98 public abstract void buildDex(DexBuilder builder); 99 100 public void replaceValue(Value oldValue, Value newValue) { 101 for (int i = 0; i < inValues.size(); i++) { 102 if (oldValue == inValues.get(i)) { 103 inValues.set(i, newValue); 104 newValue.addUser(this); 105 oldValue.removeUser(this); 106 } 107 } 108 } 109 110 public void replaceDebugPhi(Phi phi, Value value) { 111 if (debugValues != null) { 112 for (int i = 0; i < debugValues.size(); i++) { 113 if (phi == debugValues.get(i)) { 114 if (value.getLocalInfo() == null) { 115 debugValues.remove(i); 116 } else { 117 debugValues.set(i, value); 118 value.addDebugUser(this); 119 } 120 } 121 } 122 } 123 if (phi == getPreviousLocalValue()) { 124 if (value.getDebugInfo() == null) { 125 replacePreviousLocalValue(null); 126 } else { 127 replacePreviousLocalValue(value); 128 value.addDebugUser(this); 129 } 130 } 131 } 132 133 /** 134 * Returns the basic block containing this instruction. 135 */ 136 public BasicBlock getBlock() { 137 assert block != null; 138 return block; 139 } 140 141 /** 142 * Set the basic block of this instruction. See IRBuilder. 143 */ 144 public void setBlock(BasicBlock block) { 145 assert block != null; 146 this.block = block; 147 } 148 149 /** 150 * Clear the basic block of this instruction. Use when removing an instruction from a block. 151 */ 152 public void clearBlock() { 153 assert block != null; 154 block = null; 155 } 156 157 public String getInstructionName() { 158 return getClass().getSimpleName(); 159 } 160 161 @Override 162 public String toString() { 163 StringBuilder builder = new StringBuilder(); 164 builder.append(getInstructionName()); 165 for (int i = builder.length(); i < 20; i++) { 166 builder.append(" "); 167 } 168 builder.append(" "); 169 if (outValue != null) { 170 builder.append(outValue); 171 builder.append(" <- "); 172 } 173 if (!inValues.isEmpty()) { 174 StringUtils.append(builder, inValues, ", ", BraceType.NONE); 175 } 176 return builder.toString(); 177 } 178 179 public void print(CfgPrinter printer) { 180 int uses = 0; 181 String value; 182 if (outValue == null) { 183 value = printer.makeUnusedValue(); 184 } else { 185 if (outValue.hasUsersInfo()) { 186 uses = outValue.uniqueUsers().size() + outValue.uniquePhiUsers().size(); 187 } 188 value = "v" + outValue.getNumber(); 189 } 190 printer 191 .print(0) // bci 192 .sp().append(uses) // use 193 .sp().append(value) // tid 194 .sp().append(getClass().getSimpleName()); 195 for (Value in : inValues) { 196 printer.append(" v").append(in.getNumber()); 197 } 198 } 199 200 public void printLIR(CfgPrinter printer) { 201 // TODO(ager): Improve the instruction printing. Use different name for values so that the 202 // HIR and LIR values are not confused in the c1 visualizer. 203 printer.print(number).sp().append(toString()); 204 } 205 206 public int getNumber() { 207 return number; 208 } 209 210 public void setNumber(int number) { 211 assert number != -1; 212 this.number = number; 213 } 214 215 /** 216 * Compare equality of two class-equivalent instructions modulo their values. 217 * 218 * <p>It is a precondition to this method that this.getClass() == other.getClass(). 219 */ 220 public abstract boolean identicalNonValueParts(Instruction other); 221 222 public abstract int compareNonValueParts(Instruction other); 223 224 private boolean identicalAfterRegisterAllocation( 225 Value a, int aInstr, Value b, int bInstr, RegisterAllocator allocator) { 226 if (a.needsRegister() != b.needsRegister()) { 227 return false; 228 } 229 if (a.needsRegister()) { 230 if (allocator.getRegisterForValue(a, aInstr) != allocator.getRegisterForValue(b, bInstr)) { 231 return false; 232 } 233 } else { 234 ConstNumber aNum = a.getConstInstruction().asConstNumber(); 235 ConstNumber bNum = b.getConstInstruction().asConstNumber(); 236 if (!aNum.identicalNonValueParts(bNum)) { 237 return false; 238 } 239 } 240 if (a.outType() != b.outType()) { 241 return false; 242 } 243 return true; 244 } 245 246 public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) { 247 if (other.getClass() != getClass()) { 248 return false; 249 } 250 if (!identicalNonValueParts(other)) { 251 return false; 252 } 253 if (isInvokeDirect() && !asInvokeDirect().sameConstructorReceiverValue(other.asInvoke())) { 254 return false; 255 } 256 if (outValue != null) { 257 if (other.outValue == null) { 258 return false; 259 } 260 if (!identicalAfterRegisterAllocation( 261 outValue, getNumber(), other.outValue, other.getNumber(), allocator)) { 262 return false; 263 } 264 } else if (other.outValue != null) { 265 return false; 266 } 267 // Check that all input values have the same type and allocated registers. 268 if (inValues.size() != other.inValues.size()) { 269 return false; 270 } 271 for (int j = 0; j < inValues.size(); j++) { 272 Value in0 = inValues.get(j); 273 Value in1 = other.inValues.get(j); 274 if (!identicalAfterRegisterAllocation(in0, getNumber(), in1, other.getNumber(), allocator)) { 275 return false; 276 } 277 } 278 return true; 279 } 280 281 /** 282 * Returns true if this instruction may throw an exception. 283 */ 284 public boolean instructionTypeCanThrow() { 285 return false; 286 } 287 288 public boolean instructionInstanceCanThrow() { 289 return instructionTypeCanThrow(); 290 } 291 292 /** Returns true is this instruction can be treated as dead code if its outputs are not used. */ 293 public boolean canBeDeadCode(IRCode code, InternalOptions options) { 294 return !instructionInstanceCanThrow(); 295 } 296 297 /** 298 * Returns true if this instruction need this value in a register. 299 */ 300 public boolean needsValueInRegister(Value value) { 301 return true; 302 } 303 304 /** 305 * Returns true if the out value of this instruction is a constant. 306 * 307 * @return whether the out value of this instruction is a constant. 308 */ 309 public boolean isOutConstant() { 310 return false; 311 } 312 313 /** 314 * Returns the ConstInstruction defining the constant out value if the out value is constant. 315 * 316 * @return ConstInstruction or null. 317 */ 318 public ConstInstruction getOutConstantConstInstruction() { 319 return null; 320 } 321 322 public abstract int maxInValueRegister(); 323 324 public abstract int maxOutValueRegister(); 325 326 public DebugInfo getDebugInfo() { 327 return outValue == null ? null : outValue.getDebugInfo(); 328 } 329 330 public DebugLocalInfo getLocalInfo() { 331 return outValue == null ? null : outValue.getLocalInfo(); 332 } 333 334 public Value getPreviousLocalValue() { 335 return outValue == null ? null : outValue.getPreviousLocalValue(); 336 } 337 338 public List<Value> getDebugValues() { 339 return debugValues != null ? debugValues : ImmutableList.of(); 340 } 341 342 public void replacePreviousLocalValue(Value value) { 343 outValue.replacePreviousLocalValue(value); 344 } 345 346 public boolean isArrayGet() { 347 return false; 348 } 349 350 public ArrayGet asArrayGet() { 351 return null; 352 } 353 354 public boolean isArrayLength() { 355 return false; 356 } 357 358 public ArrayLength asArrayLength() { 359 return null; 360 } 361 362 public boolean isArrayPut() { 363 return false; 364 } 365 366 public ArrayPut asArrayPut() { 367 return null; 368 } 369 370 public boolean isArgument() { 371 return false; 372 } 373 374 public Argument asArgument() { 375 return null; 376 } 377 378 public boolean isArithmeticBinop() { 379 return false; 380 } 381 382 public ArithmeticBinop asArithmeticBinop() { 383 return null; 384 } 385 386 public boolean isBinop() { 387 return false; 388 } 389 390 public Binop asBinop() { 391 return null; 392 } 393 394 public boolean isUnop() { 395 return false; 396 } 397 398 public Unop asUnop() { 399 return null; 400 } 401 402 public boolean isCheckCast() { 403 return false; 404 } 405 406 public CheckCast asCheckCast() { 407 return null; 408 } 409 410 public boolean isConstNumber() { 411 return false; 412 } 413 414 public ConstNumber asConstNumber() { 415 return null; 416 } 417 418 public boolean isConstInstruction() { 419 return false; 420 } 421 422 public ConstInstruction asConstInstruction() { 423 return null; 424 } 425 426 public boolean isConstClass() { 427 return false; 428 } 429 430 public ConstClass asConstClass() { 431 return null; 432 } 433 434 public boolean isConstString() { 435 return false; 436 } 437 438 public ConstString asConstString() { 439 return null; 440 } 441 442 public boolean isCmp() { 443 return false; 444 } 445 446 public Cmp asCmp() { 447 return null; 448 } 449 450 public boolean isJumpInstruction() { 451 return false; 452 } 453 454 public JumpInstruction asJumpInstruction() { 455 return null; 456 } 457 458 public boolean isGoto() { 459 return false; 460 } 461 462 public Goto asGoto() { 463 return null; 464 } 465 466 public boolean isIf() { 467 return false; 468 } 469 470 public If asIf() { 471 return null; 472 } 473 474 public boolean isSwitch() { 475 return false; 476 } 477 478 public Switch asSwitch() { 479 return null; 480 } 481 482 public boolean isInstanceGet() { 483 return false; 484 } 485 486 public InstanceGet asInstanceGet() { 487 return null; 488 } 489 490 public boolean isInstanceOf() { 491 return false; 492 } 493 494 public InstanceOf asInstanceOf() { 495 return null; 496 } 497 498 public boolean isInstancePut() { 499 return false; 500 } 501 502 public InstancePut asInstancePut() { 503 return null; 504 } 505 506 public boolean isInvoke() { 507 return false; 508 } 509 510 public Invoke asInvoke() { 511 return null; 512 } 513 514 public boolean isMonitor() { 515 return false; 516 } 517 518 public Monitor asMonitor() { 519 return null; 520 } 521 522 public boolean isMove() { 523 return false; 524 } 525 526 public Move asMove() { 527 return null; 528 } 529 530 public boolean isNewArrayEmpty() { 531 return false; 532 } 533 534 public NewArrayEmpty asNewArrayEmpty() { 535 return null; 536 } 537 538 public boolean isNewArrayFilledData() { 539 return false; 540 } 541 542 public NewArrayFilledData asNewArrayFilledData() { 543 return null; 544 } 545 546 public boolean isNeg() { 547 return false; 548 } 549 550 public Neg asNeg() { 551 return null; 552 } 553 554 public boolean isNewInstance() { 555 return false; 556 } 557 558 public NewInstance asNewInstance() { 559 return null; 560 } 561 562 public boolean isNot() { 563 return false; 564 } 565 566 public Not asNot() { 567 return null; 568 } 569 570 public boolean isNumberConversion() { 571 return false; 572 } 573 574 public NumberConversion asNumberConversion() { 575 return null; 576 } 577 578 public boolean isReturn() { 579 return false; 580 } 581 582 public Return asReturn() { 583 return null; 584 } 585 586 public boolean isThrow() { 587 return false; 588 } 589 590 public Throw asThrow() { 591 return null; 592 } 593 594 public boolean isStaticGet() { 595 return false; 596 } 597 598 public StaticGet asStaticGet() { 599 return null; 600 } 601 602 public boolean isStaticPut() { 603 return false; 604 } 605 606 public StaticPut asStaticPut() { 607 return null; 608 } 609 610 public boolean isAdd() { 611 return false; 612 } 613 614 public Add asAdd() { 615 return null; 616 } 617 618 public boolean isSub() { 619 return false; 620 } 621 622 public Sub asSub() { 623 return null; 624 } 625 626 public boolean isMul() { 627 return false; 628 } 629 630 public Mul asMul() { 631 return null; 632 } 633 634 public boolean isDiv() { 635 return false; 636 } 637 638 public Div asDiv() { 639 return null; 640 } 641 642 public boolean isRem() { 643 return false; 644 } 645 646 public Rem asRem() { 647 return null; 648 } 649 650 public boolean isLogicalBinop() { 651 return false; 652 } 653 654 public LogicalBinop asLogicalBinop() { 655 return null; 656 } 657 658 public boolean isShl() { 659 return false; 660 } 661 662 public Shl asShl() { 663 return null; 664 } 665 666 public boolean isShr() { 667 return false; 668 } 669 670 public Shr asShr() { 671 return null; 672 } 673 674 public boolean isUshr() { 675 return false; 676 } 677 678 public Ushr asUshr() { 679 return null; 680 } 681 682 public boolean isAnd() { 683 return false; 684 } 685 686 public And asAnd() { 687 return null; 688 } 689 690 public boolean isOr() { 691 return false; 692 } 693 694 public Or asOr() { 695 return null; 696 } 697 698 public boolean isXor() { 699 return false; 700 } 701 702 public Xor asXor() { 703 return null; 704 } 705 706 public boolean isMoveException() { 707 return false; 708 } 709 710 public MoveException asMoveException() { 711 return null; 712 } 713 714 public boolean isDebugInstruction() { 715 return isDebugPosition() 716 || isDebugLocalsChange() 717 || isDebugLocalWrite() 718 || isDebugLocalUninitialized(); 719 } 720 721 public boolean isDebugPosition() { 722 return false; 723 } 724 725 public DebugPosition asDebugPosition() { 726 return null; 727 } 728 729 public boolean isDebugLocalsChange() { 730 return false; 731 } 732 733 public DebugLocalsChange asDebugLocalsChange() { 734 return null; 735 } 736 737 public boolean isDebugLocalUninitialized() { 738 return false; 739 } 740 741 public DebugLocalUninitialized asDebugLocalUninitialized() { 742 return null; 743 } 744 745 public boolean isDebugLocalWrite() { 746 return false; 747 } 748 749 public DebugLocalWrite asDebugLocalWrite() { 750 return null; 751 } 752 753 public boolean isInvokeMethod() { 754 return false; 755 } 756 757 public InvokeMethod asInvokeMethod() { 758 return null; 759 } 760 761 public boolean isInvokeMethodWithReceiver() { 762 return false; 763 } 764 765 public InvokeMethodWithReceiver asInvokeMethodWithReceiver() { 766 return null; 767 } 768 769 public boolean isInvokeNewArray() { 770 return false; 771 } 772 773 public InvokeNewArray asInvokeNewArray() { 774 return null; 775 } 776 777 public boolean isInvokeCustom() { 778 return false; 779 } 780 781 public InvokeCustom asInvokeCustom() { 782 return null; 783 } 784 785 public boolean isInvokeDirect() { 786 return false; 787 } 788 789 public InvokeDirect asInvokeDirect() { 790 return null; 791 } 792 793 public boolean isInvokeInterface() { 794 return false; 795 } 796 797 public InvokeInterface asInvokeInterface() { 798 return null; 799 } 800 801 public boolean isInvokeStatic() { 802 return false; 803 } 804 805 public InvokeStatic asInvokeStatic() { 806 return null; 807 } 808 809 public boolean isInvokeSuper() { 810 return false; 811 } 812 813 public InvokeSuper asInvokeSuper() { 814 return null; 815 } 816 817 public boolean isInvokeVirtual() { 818 return false; 819 } 820 821 public InvokeVirtual asInvokeVirtual() { 822 return null; 823 } 824 825 public boolean isInvokePolymorphic() { 826 return false; 827 } 828 829 public InvokePolymorphic asInvokePolymorphic() { 830 return null; 831 } 832 833 public boolean canBeFolded() { 834 return false; 835 } 836 837 public ConstInstruction fold(IRCode code) { 838 throw new Unreachable("Unsupported folding for " + this); 839 } 840 841 // Returns the inlining constraint for this instruction. 842 public Constraint inliningConstraint(AppInfo info, DexType holder) { 843 return Constraint.NEVER; 844 } 845 } 846