|
18 | 18 | #include "bolt/Core/BinaryBasicBlock.h" |
19 | 19 | #include "bolt/Core/BinaryFunction.h" |
20 | 20 | #include "bolt/Core/MCPlusBuilder.h" |
| 21 | +#include "bolt/Utils/CommandLineOpts.h" |
21 | 22 | #include "llvm/BinaryFormat/ELF.h" |
22 | 23 | #include "llvm/MC/MCContext.h" |
23 | 24 | #include "llvm/MC/MCFixupKindInfo.h" |
@@ -213,6 +214,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { |
213 | 214 | Inst.getOpcode() == AArch64::ADDXrx64); |
214 | 215 | } |
215 | 216 |
|
| 217 | + bool isSUB(const MCInst &Inst) const override { |
| 218 | + const unsigned opcode = Inst.getOpcode(); |
| 219 | + switch (opcode) { |
| 220 | + case AArch64::SUBSWri: |
| 221 | + case AArch64::SUBSWrr: |
| 222 | + case AArch64::SUBSWrs: |
| 223 | + case AArch64::SUBSWrx: |
| 224 | + case AArch64::SUBSXri: |
| 225 | + case AArch64::SUBSXrr: |
| 226 | + case AArch64::SUBSXrs: |
| 227 | + case AArch64::SUBSXrx: |
| 228 | + case AArch64::SUBSXrx64: |
| 229 | + case AArch64::SUBWri: |
| 230 | + case AArch64::SUBWrr: |
| 231 | + case AArch64::SUBWrs: |
| 232 | + case AArch64::SUBWrx: |
| 233 | + case AArch64::SUBXri: |
| 234 | + case AArch64::SUBXrr: |
| 235 | + case AArch64::SUBXrs: |
| 236 | + case AArch64::SUBXrx: |
| 237 | + case AArch64::SUBXrx64: |
| 238 | + return true; |
| 239 | + default: |
| 240 | + return false; |
| 241 | + } |
| 242 | + llvm_unreachable(""); |
| 243 | + } |
| 244 | + |
216 | 245 | bool isLDRB(const MCInst &Inst) const { |
217 | 246 | return (Inst.getOpcode() == AArch64::LDRBBpost || |
218 | 247 | Inst.getOpcode() == AArch64::LDRBBpre || |
@@ -652,13 +681,45 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { |
652 | 681 | /// # of this BB) |
653 | 682 | /// br x0 # Indirect jump instruction |
654 | 683 | /// |
| 684 | + /// adrp + ldr pair instructions of JT |
| 685 | + /// adrp x3, :got:jump_table |
| 686 | + /// ldr x1, [x1, #value] |
| 687 | + /// ldrh w1, [x1, w3, uxtw #1] |
| 688 | + /// adr x3, 573ae4 |
| 689 | + /// add x1, x3, w1, sxth #2 |
| 690 | + /// br x1 |
| 691 | + /// |
| 692 | + /// lld/test/ELF/aarch64-adrp-ldr-got.s |
| 693 | + /// if .rodata and .text are sufficiently (<1M) |
| 694 | + /// close to each other so that the adrp + ldr pair can be relaxed to |
| 695 | + /// nop + adr. |
| 696 | + /// nop # |
| 697 | + /// adr x0, #6d8f8 # Get JT table address |
| 698 | + /// ldrh w0, [x0, w4, uxtw #1] # Loads JT entry |
| 699 | + /// adr x2, 1479b0 # Get PC first instruction for next BB |
| 700 | + /// add x0, x2, w0, sxth #2 # Finish building branch target |
| 701 | + /// # (entries in JT are relative to the end |
| 702 | + /// # of this BB) |
| 703 | + /// br x0 # Indirect jump instruction |
| 704 | + /// |
| 705 | + /// sub + ldr pair instructions of JT, JT address on the stack and other BB |
| 706 | + /// sub x1, x29, #0x4, lsl #12 |
| 707 | + /// ldr x1, [x1, #14352] |
| 708 | + /// ldrh w1, [x1, w3, uxtw #1] |
| 709 | + /// adr x3, 573ae4 |
| 710 | + /// add x1, x3, w1, sxth #2 |
| 711 | + /// br x1 |
655 | 712 | bool analyzeIndirectBranchFragment( |
656 | 713 | const MCInst &Inst, |
657 | 714 | DenseMap<const MCInst *, SmallVector<MCInst *, 4>> &UDChain, |
658 | 715 | const MCExpr *&JumpTable, int64_t &Offset, int64_t &ScaleValue, |
659 | 716 | MCInst *&PCRelBase) const { |
660 | | - // Expect AArch64 BR |
661 | | - assert(Inst.getOpcode() == AArch64::BR && "Unexpected opcode"); |
| 717 | + |
| 718 | + if (Inst.getOpcode() != AArch64::BR) { |
| 719 | + if (opts::Verbosity > 1) |
| 720 | + outs() << "BOLT-WARNING: Unexpected opcode\n"; |
| 721 | + return false; |
| 722 | + } |
662 | 723 |
|
663 | 724 | // Match the indirect branch pattern for aarch64 |
664 | 725 | SmallVector<MCInst *, 4> &UsesRoot = UDChain[&Inst]; |
@@ -697,8 +758,12 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { |
697 | 758 | // Parsed as ADDXrs reg:x8 reg:x8 reg:x12 imm:0 |
698 | 759 | return false; |
699 | 760 | } |
700 | | - assert(DefAdd->getOpcode() == AArch64::ADDXrx && |
701 | | - "Failed to match indirect branch!"); |
| 761 | + |
| 762 | + if (DefAdd->getOpcode() != AArch64::ADDXrx) { |
| 763 | + if (opts::Verbosity > 1) |
| 764 | + outs() << "BOLT-WARNING: Failed to match indirect branch!\n"; |
| 765 | + return false; |
| 766 | + } |
702 | 767 |
|
703 | 768 | // Validate ADD operands |
704 | 769 | int64_t OperandExtension = DefAdd->getOperand(3).getImm(); |
@@ -737,61 +802,129 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { |
737 | 802 | // This was observed in the wild in HHVM code (dispatchImpl). |
738 | 803 | return false; |
739 | 804 | } |
| 805 | + |
740 | 806 | MCInst *DefBaseAddr = UsesAdd[1]; |
741 | | - assert(DefBaseAddr->getOpcode() == AArch64::ADR && |
742 | | - "Failed to match indirect branch pattern! (fragment 3)"); |
| 807 | + if (DefBaseAddr->getOpcode() != AArch64::ADR) { |
| 808 | + if (opts::Verbosity > 1) |
| 809 | + outs() << "BOLT-WARNING: " |
| 810 | + << "Failed to match indirect branch pattern! (fragment 3)\n"; |
| 811 | + return false; |
| 812 | + } |
743 | 813 |
|
744 | 814 | PCRelBase = DefBaseAddr; |
745 | 815 | // Match LOAD to load the jump table (relative) target |
746 | 816 | const MCInst *DefLoad = UsesAdd[2]; |
747 | | - assert(mayLoad(*DefLoad) && |
748 | | - "Failed to match indirect branch load pattern! (1)"); |
749 | | - assert((ScaleValue != 1LL || isLDRB(*DefLoad)) && |
750 | | - "Failed to match indirect branch load pattern! (2)"); |
751 | | - assert((ScaleValue != 2LL || isLDRH(*DefLoad)) && |
752 | | - "Failed to match indirect branch load pattern! (3)"); |
| 817 | + if (!mayLoad(*DefLoad)) { |
| 818 | + if (opts::Verbosity > 1) |
| 819 | + outs() << "BOLT-WARNING: " |
| 820 | + << "Failed to match indirect branch load pattern! (1)\n"; |
| 821 | + return false; |
| 822 | + } |
| 823 | + |
| 824 | + if (isLDRB(*DefLoad) && ScaleValue != 1LL) { |
| 825 | + if (opts::Verbosity > 1) |
| 826 | + outs() << "BOLT-WARNING: " |
| 827 | + << "Failed to match indirect branch load pattern! (2)\n"; |
| 828 | + return false; |
| 829 | + } else if (isLDRH(*DefLoad) && ScaleValue != 2LL) { |
| 830 | + if (opts::Verbosity > 1) |
| 831 | + outs() << "BOLT-WARNING: " |
| 832 | + << "Failed to match indirect branch load pattern! (3)\n"; |
| 833 | + return false; |
| 834 | + } else if (isLDRW(*DefLoad) && ScaleValue != 4LL) { |
| 835 | + if (opts::Verbosity > 1) |
| 836 | + outs() << "BOLT-WARNING: " |
| 837 | + << "Failed to match indirect branch load pattern! (4)\n"; |
| 838 | + return false; |
| 839 | + } |
753 | 840 |
|
754 | 841 | // Match ADD that calculates the JumpTable Base Address (not the offset) |
755 | 842 | SmallVector<MCInst *, 4> &UsesLoad = UDChain[DefLoad]; |
756 | | - const MCInst *DefJTBaseAdd = UsesLoad[1]; |
| 843 | + const MCInst *DefJTPageBias = UsesLoad[1]; |
757 | 844 | MCPhysReg From, To; |
758 | | - if (DefJTBaseAdd == nullptr || isLoadFromStack(*DefJTBaseAdd) || |
759 | | - isRegToRegMove(*DefJTBaseAdd, From, To)) { |
| 845 | + JumpTable = nullptr; |
| 846 | + if (DefJTPageBias == nullptr || isLoadFromStack(*DefJTPageBias) || |
| 847 | + isRegToRegMove(*DefJTPageBias, From, To)) { |
760 | 848 | // Sometimes base address may have been defined in another basic block |
761 | 849 | // (hoisted). Return with no jump table info. |
762 | | - JumpTable = nullptr; |
763 | 850 | return true; |
764 | 851 | } |
765 | 852 |
|
766 | | - if (DefJTBaseAdd->getOpcode() == AArch64::ADR) { |
| 853 | + if (isAddXri(*DefJTPageBias)) { |
| 854 | + if (DefJTPageBias->getOperand(2).isImm()) |
| 855 | + Offset = DefJTPageBias->getOperand(2).getImm(); |
| 856 | + SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTPageBias]; |
| 857 | + const MCInst *DefJTBasePage = UsesJTBaseAdd[1]; |
| 858 | + if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) { |
| 859 | + return true; |
| 860 | + } |
| 861 | + |
| 862 | + if (DefJTBasePage->getOpcode() != AArch64::ADRP) { |
| 863 | + if (opts::Verbosity > 1) |
| 864 | + outs() << "BOLT-WARNING: " |
| 865 | + << "Failed to match jump table base page pattern! (2)\n"; |
| 866 | + return false; |
| 867 | + } |
| 868 | + |
| 869 | + if (DefJTBasePage->getOperand(1).isExpr()) |
| 870 | + JumpTable = DefJTBasePage->getOperand(1).getExpr(); |
| 871 | + return true; |
| 872 | + } else if (isADR(*DefJTPageBias)) { |
767 | 873 | // TODO: Handle the pattern where there is no adrp/add pair. |
768 | 874 | // It also occurs when the binary is static. |
769 | | - // adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50> |
| 875 | + // nop |
| 876 | + // *adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50> |
770 | 877 | // ldrh w13, [x13, w12, uxtw #1] |
771 | 878 | // adr x12, 0x247b30 <__gettextparse+0x5b0> |
772 | 879 | // add x13, x12, w13, sxth #2 |
773 | 880 | // br x13 |
774 | | - errs() << "BOLT-WARNING: Failed to match indirect branch: " |
775 | | - "nop/adr instead of adrp/add \n"; |
776 | | - return false; |
777 | | - } |
| 881 | + SmallVector<MCInst *, 4> &UsesJTNop = UDChain[DefJTPageBias]; |
| 882 | + if (UsesJTNop.size() == 1 && UsesJTNop[0] != nullptr) { |
| 883 | + if (opts::Verbosity > 1) |
| 884 | + outs() << "BOLT-WARNING: " |
| 885 | + << "Failed to match jump table pattern! (2)\n"; |
| 886 | + return false; |
| 887 | + } |
778 | 888 |
|
779 | | - assert(DefJTBaseAdd->getOpcode() == AArch64::ADDXri && |
780 | | - "Failed to match jump table base address pattern! (1)"); |
| 889 | + if (DefJTPageBias->getOperand(1).isExpr()) { |
| 890 | + JumpTable = DefJTPageBias->getOperand(1).getExpr(); |
| 891 | + return true; |
| 892 | + } |
| 893 | + } else if (mayLoad(*DefJTPageBias)) { |
| 894 | + if (isLoadFromStack(*DefJTPageBias)) |
| 895 | + return true; |
781 | 896 |
|
782 | | - if (DefJTBaseAdd->getOperand(2).isImm()) |
783 | | - Offset = DefJTBaseAdd->getOperand(2).getImm(); |
784 | | - SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTBaseAdd]; |
785 | | - const MCInst *DefJTBasePage = UsesJTBaseAdd[1]; |
786 | | - if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) { |
787 | | - JumpTable = nullptr; |
788 | | - return true; |
| 897 | + SmallVector<MCInst *, 4> &UsesJTBase = UDChain[DefJTPageBias]; |
| 898 | + const MCInst *DefJTBasePage = UsesJTBase[1]; |
| 899 | + if (DefJTBasePage == nullptr) |
| 900 | + return true; |
| 901 | + if (DefJTBasePage->getOpcode() == AArch64::ADRP) { |
| 902 | + // test jmp-table-pattern-matching.s (3) |
| 903 | + if (DefJTBasePage->getOperand(1).isExpr()) |
| 904 | + JumpTable = DefJTBasePage->getOperand(1).getExpr(); |
| 905 | + return true; |
| 906 | + } else { |
| 907 | + // Base address may have been defined in another basic block |
| 908 | + // and sub instruction can be used to get base page address |
| 909 | + // jmp-table-pattern-matching.s (2) |
| 910 | + if (isSUB(*DefJTBasePage)) { |
| 911 | + for (const MCOperand &Operand : useOperands(*DefJTBasePage)) { |
| 912 | + if (!Operand.isReg()) |
| 913 | + continue; |
| 914 | + const unsigned Reg = Operand.getReg(); |
| 915 | + if (Reg == AArch64::SP || Reg == AArch64::WSP || |
| 916 | + Reg == AArch64::FP || Reg == AArch64::W29) |
| 917 | + return true; |
| 918 | + } |
| 919 | + } |
| 920 | + } |
789 | 921 | } |
790 | | - assert(DefJTBasePage->getOpcode() == AArch64::ADRP && |
791 | | - "Failed to match jump table base page pattern! (2)"); |
792 | | - if (DefJTBasePage->getOperand(1).isExpr()) |
793 | | - JumpTable = DefJTBasePage->getOperand(1).getExpr(); |
794 | | - return true; |
| 922 | + |
| 923 | + if (opts::Verbosity > 1) |
| 924 | + outs() << "BOLT-WARNING: " |
| 925 | + << "Failed to match jump table pattern! (4)\n"; |
| 926 | + |
| 927 | + return false; |
795 | 928 | } |
796 | 929 |
|
797 | 930 | DenseMap<const MCInst *, SmallVector<MCInst *, 4>> |
|
0 commit comments