|
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