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 Instruction(Value outValue)30 protected Instruction(Value outValue) { 31 setOutValue(outValue); 32 } 33 Instruction(Value outValue, Value inValue)34 protected Instruction(Value outValue, Value inValue) { 35 addInValue(inValue); 36 setOutValue(outValue); 37 } 38 Instruction(Value outValue, List<? extends Value> inValues)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 inValues()48 public List<Value> inValues() { 49 return inValues; 50 } 51 addInValue(Value value)52 protected void addInValue(Value value) { 53 if (value != null) { 54 inValues.add(value); 55 value.addUser(this); 56 } 57 } 58 outValue()59 public Value outValue() { 60 return outValue; 61 } 62 setOutValue(Value value)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 addDebugValue(Value value)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 clearUserInfo(Instruction instruction)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 outType()94 public final MoveType outType() { 95 return outValue.outType(); 96 } 97 buildDex(DexBuilder builder)98 public abstract void buildDex(DexBuilder builder); 99 replaceValue(Value oldValue, Value newValue)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 replaceDebugPhi(Phi phi, Value value)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 */ getBlock()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 */ setBlock(BasicBlock block)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 */ clearBlock()152 public void clearBlock() { 153 assert block != null; 154 block = null; 155 } 156 getInstructionName()157 public String getInstructionName() { 158 return getClass().getSimpleName(); 159 } 160 161 @Override toString()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 print(CfgPrinter printer)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 printLIR(CfgPrinter printer)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 getNumber()206 public int getNumber() { 207 return number; 208 } 209 setNumber(int number)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 */ identicalNonValueParts(Instruction other)220 public abstract boolean identicalNonValueParts(Instruction other); 221 compareNonValueParts(Instruction other)222 public abstract int compareNonValueParts(Instruction other); 223 identicalAfterRegisterAllocation( Value a, int aInstr, Value b, int bInstr, RegisterAllocator allocator)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 identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator)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 */ instructionTypeCanThrow()284 public boolean instructionTypeCanThrow() { 285 return false; 286 } 287 instructionInstanceCanThrow()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. */ canBeDeadCode(IRCode code, InternalOptions options)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 */ needsValueInRegister(Value value)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 */ isOutConstant()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 */ getOutConstantConstInstruction()318 public ConstInstruction getOutConstantConstInstruction() { 319 return null; 320 } 321 maxInValueRegister()322 public abstract int maxInValueRegister(); 323 maxOutValueRegister()324 public abstract int maxOutValueRegister(); 325 getDebugInfo()326 public DebugInfo getDebugInfo() { 327 return outValue == null ? null : outValue.getDebugInfo(); 328 } 329 getLocalInfo()330 public DebugLocalInfo getLocalInfo() { 331 return outValue == null ? null : outValue.getLocalInfo(); 332 } 333 getPreviousLocalValue()334 public Value getPreviousLocalValue() { 335 return outValue == null ? null : outValue.getPreviousLocalValue(); 336 } 337 getDebugValues()338 public List<Value> getDebugValues() { 339 return debugValues != null ? debugValues : ImmutableList.of(); 340 } 341 replacePreviousLocalValue(Value value)342 public void replacePreviousLocalValue(Value value) { 343 outValue.replacePreviousLocalValue(value); 344 } 345 isArrayGet()346 public boolean isArrayGet() { 347 return false; 348 } 349 asArrayGet()350 public ArrayGet asArrayGet() { 351 return null; 352 } 353 isArrayLength()354 public boolean isArrayLength() { 355 return false; 356 } 357 asArrayLength()358 public ArrayLength asArrayLength() { 359 return null; 360 } 361 isArrayPut()362 public boolean isArrayPut() { 363 return false; 364 } 365 asArrayPut()366 public ArrayPut asArrayPut() { 367 return null; 368 } 369 isArgument()370 public boolean isArgument() { 371 return false; 372 } 373 asArgument()374 public Argument asArgument() { 375 return null; 376 } 377 isArithmeticBinop()378 public boolean isArithmeticBinop() { 379 return false; 380 } 381 asArithmeticBinop()382 public ArithmeticBinop asArithmeticBinop() { 383 return null; 384 } 385 isBinop()386 public boolean isBinop() { 387 return false; 388 } 389 asBinop()390 public Binop asBinop() { 391 return null; 392 } 393 isUnop()394 public boolean isUnop() { 395 return false; 396 } 397 asUnop()398 public Unop asUnop() { 399 return null; 400 } 401 isCheckCast()402 public boolean isCheckCast() { 403 return false; 404 } 405 asCheckCast()406 public CheckCast asCheckCast() { 407 return null; 408 } 409 isConstNumber()410 public boolean isConstNumber() { 411 return false; 412 } 413 asConstNumber()414 public ConstNumber asConstNumber() { 415 return null; 416 } 417 isConstInstruction()418 public boolean isConstInstruction() { 419 return false; 420 } 421 asConstInstruction()422 public ConstInstruction asConstInstruction() { 423 return null; 424 } 425 isConstClass()426 public boolean isConstClass() { 427 return false; 428 } 429 asConstClass()430 public ConstClass asConstClass() { 431 return null; 432 } 433 isConstString()434 public boolean isConstString() { 435 return false; 436 } 437 asConstString()438 public ConstString asConstString() { 439 return null; 440 } 441 isCmp()442 public boolean isCmp() { 443 return false; 444 } 445 asCmp()446 public Cmp asCmp() { 447 return null; 448 } 449 isJumpInstruction()450 public boolean isJumpInstruction() { 451 return false; 452 } 453 asJumpInstruction()454 public JumpInstruction asJumpInstruction() { 455 return null; 456 } 457 isGoto()458 public boolean isGoto() { 459 return false; 460 } 461 asGoto()462 public Goto asGoto() { 463 return null; 464 } 465 isIf()466 public boolean isIf() { 467 return false; 468 } 469 asIf()470 public If asIf() { 471 return null; 472 } 473 isSwitch()474 public boolean isSwitch() { 475 return false; 476 } 477 asSwitch()478 public Switch asSwitch() { 479 return null; 480 } 481 isInstanceGet()482 public boolean isInstanceGet() { 483 return false; 484 } 485 asInstanceGet()486 public InstanceGet asInstanceGet() { 487 return null; 488 } 489 isInstanceOf()490 public boolean isInstanceOf() { 491 return false; 492 } 493 asInstanceOf()494 public InstanceOf asInstanceOf() { 495 return null; 496 } 497 isInstancePut()498 public boolean isInstancePut() { 499 return false; 500 } 501 asInstancePut()502 public InstancePut asInstancePut() { 503 return null; 504 } 505 isInvoke()506 public boolean isInvoke() { 507 return false; 508 } 509 asInvoke()510 public Invoke asInvoke() { 511 return null; 512 } 513 isMonitor()514 public boolean isMonitor() { 515 return false; 516 } 517 asMonitor()518 public Monitor asMonitor() { 519 return null; 520 } 521 isMove()522 public boolean isMove() { 523 return false; 524 } 525 asMove()526 public Move asMove() { 527 return null; 528 } 529 isNewArrayEmpty()530 public boolean isNewArrayEmpty() { 531 return false; 532 } 533 asNewArrayEmpty()534 public NewArrayEmpty asNewArrayEmpty() { 535 return null; 536 } 537 isNewArrayFilledData()538 public boolean isNewArrayFilledData() { 539 return false; 540 } 541 asNewArrayFilledData()542 public NewArrayFilledData asNewArrayFilledData() { 543 return null; 544 } 545 isNeg()546 public boolean isNeg() { 547 return false; 548 } 549 asNeg()550 public Neg asNeg() { 551 return null; 552 } 553 isNewInstance()554 public boolean isNewInstance() { 555 return false; 556 } 557 asNewInstance()558 public NewInstance asNewInstance() { 559 return null; 560 } 561 isNot()562 public boolean isNot() { 563 return false; 564 } 565 asNot()566 public Not asNot() { 567 return null; 568 } 569 isNumberConversion()570 public boolean isNumberConversion() { 571 return false; 572 } 573 asNumberConversion()574 public NumberConversion asNumberConversion() { 575 return null; 576 } 577 isReturn()578 public boolean isReturn() { 579 return false; 580 } 581 asReturn()582 public Return asReturn() { 583 return null; 584 } 585 isThrow()586 public boolean isThrow() { 587 return false; 588 } 589 asThrow()590 public Throw asThrow() { 591 return null; 592 } 593 isStaticGet()594 public boolean isStaticGet() { 595 return false; 596 } 597 asStaticGet()598 public StaticGet asStaticGet() { 599 return null; 600 } 601 isStaticPut()602 public boolean isStaticPut() { 603 return false; 604 } 605 asStaticPut()606 public StaticPut asStaticPut() { 607 return null; 608 } 609 isAdd()610 public boolean isAdd() { 611 return false; 612 } 613 asAdd()614 public Add asAdd() { 615 return null; 616 } 617 isSub()618 public boolean isSub() { 619 return false; 620 } 621 asSub()622 public Sub asSub() { 623 return null; 624 } 625 isMul()626 public boolean isMul() { 627 return false; 628 } 629 asMul()630 public Mul asMul() { 631 return null; 632 } 633 isDiv()634 public boolean isDiv() { 635 return false; 636 } 637 asDiv()638 public Div asDiv() { 639 return null; 640 } 641 isRem()642 public boolean isRem() { 643 return false; 644 } 645 asRem()646 public Rem asRem() { 647 return null; 648 } 649 isLogicalBinop()650 public boolean isLogicalBinop() { 651 return false; 652 } 653 asLogicalBinop()654 public LogicalBinop asLogicalBinop() { 655 return null; 656 } 657 isShl()658 public boolean isShl() { 659 return false; 660 } 661 asShl()662 public Shl asShl() { 663 return null; 664 } 665 isShr()666 public boolean isShr() { 667 return false; 668 } 669 asShr()670 public Shr asShr() { 671 return null; 672 } 673 isUshr()674 public boolean isUshr() { 675 return false; 676 } 677 asUshr()678 public Ushr asUshr() { 679 return null; 680 } 681 isAnd()682 public boolean isAnd() { 683 return false; 684 } 685 asAnd()686 public And asAnd() { 687 return null; 688 } 689 isOr()690 public boolean isOr() { 691 return false; 692 } 693 asOr()694 public Or asOr() { 695 return null; 696 } 697 isXor()698 public boolean isXor() { 699 return false; 700 } 701 asXor()702 public Xor asXor() { 703 return null; 704 } 705 isMoveException()706 public boolean isMoveException() { 707 return false; 708 } 709 asMoveException()710 public MoveException asMoveException() { 711 return null; 712 } 713 isDebugInstruction()714 public boolean isDebugInstruction() { 715 return isDebugPosition() 716 || isDebugLocalsChange() 717 || isDebugLocalWrite() 718 || isDebugLocalUninitialized(); 719 } 720 isDebugPosition()721 public boolean isDebugPosition() { 722 return false; 723 } 724 asDebugPosition()725 public DebugPosition asDebugPosition() { 726 return null; 727 } 728 isDebugLocalsChange()729 public boolean isDebugLocalsChange() { 730 return false; 731 } 732 asDebugLocalsChange()733 public DebugLocalsChange asDebugLocalsChange() { 734 return null; 735 } 736 isDebugLocalUninitialized()737 public boolean isDebugLocalUninitialized() { 738 return false; 739 } 740 asDebugLocalUninitialized()741 public DebugLocalUninitialized asDebugLocalUninitialized() { 742 return null; 743 } 744 isDebugLocalWrite()745 public boolean isDebugLocalWrite() { 746 return false; 747 } 748 asDebugLocalWrite()749 public DebugLocalWrite asDebugLocalWrite() { 750 return null; 751 } 752 isInvokeMethod()753 public boolean isInvokeMethod() { 754 return false; 755 } 756 asInvokeMethod()757 public InvokeMethod asInvokeMethod() { 758 return null; 759 } 760 isInvokeMethodWithReceiver()761 public boolean isInvokeMethodWithReceiver() { 762 return false; 763 } 764 asInvokeMethodWithReceiver()765 public InvokeMethodWithReceiver asInvokeMethodWithReceiver() { 766 return null; 767 } 768 isInvokeNewArray()769 public boolean isInvokeNewArray() { 770 return false; 771 } 772 asInvokeNewArray()773 public InvokeNewArray asInvokeNewArray() { 774 return null; 775 } 776 isInvokeCustom()777 public boolean isInvokeCustom() { 778 return false; 779 } 780 asInvokeCustom()781 public InvokeCustom asInvokeCustom() { 782 return null; 783 } 784 isInvokeDirect()785 public boolean isInvokeDirect() { 786 return false; 787 } 788 asInvokeDirect()789 public InvokeDirect asInvokeDirect() { 790 return null; 791 } 792 isInvokeInterface()793 public boolean isInvokeInterface() { 794 return false; 795 } 796 asInvokeInterface()797 public InvokeInterface asInvokeInterface() { 798 return null; 799 } 800 isInvokeStatic()801 public boolean isInvokeStatic() { 802 return false; 803 } 804 asInvokeStatic()805 public InvokeStatic asInvokeStatic() { 806 return null; 807 } 808 isInvokeSuper()809 public boolean isInvokeSuper() { 810 return false; 811 } 812 asInvokeSuper()813 public InvokeSuper asInvokeSuper() { 814 return null; 815 } 816 isInvokeVirtual()817 public boolean isInvokeVirtual() { 818 return false; 819 } 820 asInvokeVirtual()821 public InvokeVirtual asInvokeVirtual() { 822 return null; 823 } 824 isInvokePolymorphic()825 public boolean isInvokePolymorphic() { 826 return false; 827 } 828 asInvokePolymorphic()829 public InvokePolymorphic asInvokePolymorphic() { 830 return null; 831 } 832 canBeFolded()833 public boolean canBeFolded() { 834 return false; 835 } 836 fold(IRCode code)837 public ConstInstruction fold(IRCode code) { 838 throw new Unreachable("Unsupported folding for " + this); 839 } 840 841 // Returns the inlining constraint for this instruction. inliningConstraint(AppInfo info, DexType holder)842 public Constraint inliningConstraint(AppInfo info, DexType holder) { 843 return Constraint.NEVER; 844 } 845 } 846