Skip to content

Commit 542a79b

Browse files
committed
Exclude JT pattern matching under assert failure
There are several patterns to implement jump table. Need to exclude the following patterns under assert during disassemble stage: (1) nop (2) sub (3) adrp adr ldr ldr ldrh ldrh ldrh adr adr adr add add add br br br Full jump table processing for AArch64 is still disabled.
1 parent 81ae668 commit 542a79b

File tree

3 files changed

+243
-44
lines changed

3 files changed

+243
-44
lines changed

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,37 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
213213
Inst.getOpcode() == AArch64::ADDXrx64);
214214
}
215215

216+
bool isSUB(const MCInst &Inst) const override {
217+
const unsigned opcode = Inst.getOpcode();
218+
bool isSubInstr = false;
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+
isSubInstr = true;
239+
break;
240+
default:
241+
isSubInstr = false;
242+
break;
243+
}
244+
return isSubInstr;
245+
}
246+
216247
bool isLDRB(const MCInst &Inst) const {
217248
return (Inst.getOpcode() == AArch64::LDRBBpost ||
218249
Inst.getOpcode() == AArch64::LDRBBpre ||
@@ -652,6 +683,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
652683
/// # of this BB)
653684
/// br x0 # Indirect jump instruction
654685
///
686+
/// adrp + ldr pair instructions of JT
687+
/// adrp x3, :got:jump_table
688+
/// ldr x1, [x1, #value]
689+
/// ldrh w1, [x1, w3, uxtw #1]
690+
/// adr x3, 573ae4
691+
/// add x1, x3, w1, sxth #2
692+
/// br x1
693+
///
694+
/// lld/test/ELF/aarch64-adrp-ldr-got.s
695+
/// if .rodata and .text are sufficiently (<1M)
696+
/// close to each other so that the adrp + ldr pair can be relaxed to
697+
/// nop + adr.
698+
/// nop #
699+
/// adr x0, #6d8f8 # Get JT table address
700+
/// ldrh w0, [x0, w4, uxtw #1] # Loads JT entry
701+
/// adr x2, 1479b0 # Get PC first instruction for next BB
702+
/// add x0, x2, w0, sxth #2 # Finish building branch target
703+
/// # (entries in JT are relative to the end
704+
/// # of this BB)
705+
/// br x0 # Indirect jump instruction
706+
///
707+
/// sub + ldr pair instructions of JT, JT address on the stack and other BB
708+
/// sub x1, x29, #0x4, lsl #12
709+
/// ldr x1, [x1, #14352]
710+
/// ldrh w1, [x1, w3, uxtw #1]
711+
/// adr x3, 573ae4
712+
/// add x1, x3, w1, sxth #2
713+
/// br x1
655714
bool analyzeIndirectBranchFragment(
656715
const MCInst &Inst,
657716
DenseMap<const MCInst *, SmallVector<MCInst *, 4>> &UDChain,
@@ -753,45 +812,77 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
753812

754813
// Match ADD that calculates the JumpTable Base Address (not the offset)
755814
SmallVector<MCInst *, 4> &UsesLoad = UDChain[DefLoad];
756-
const MCInst *DefJTBaseAdd = UsesLoad[1];
815+
const MCInst *DefJTPageBias = UsesLoad[1];
757816
MCPhysReg From, To;
758-
if (DefJTBaseAdd == nullptr || isLoadFromStack(*DefJTBaseAdd) ||
759-
isRegToRegMove(*DefJTBaseAdd, From, To)) {
817+
JumpTable = nullptr;
818+
if (DefJTPageBias == nullptr || isLoadFromStack(*DefJTPageBias) ||
819+
isRegToRegMove(*DefJTPageBias, From, To)) {
760820
// Sometimes base address may have been defined in another basic block
761821
// (hoisted). Return with no jump table info.
762-
JumpTable = nullptr;
763822
return true;
764823
}
765824

766-
if (DefJTBaseAdd->getOpcode() == AArch64::ADR) {
825+
if (isAddXri(*DefJTPageBias)) {
826+
if (DefJTPageBias->getOperand(2).isImm())
827+
Offset = DefJTPageBias->getOperand(2).getImm();
828+
SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTPageBias];
829+
const MCInst *DefJTBasePage = UsesJTBaseAdd[1];
830+
if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) {
831+
return true;
832+
}
833+
assert(DefJTBasePage->getOpcode() == AArch64::ADRP &&
834+
"Failed to match jump table base page pattern! (2)");
835+
if (DefJTBasePage->getOperand(1).isExpr())
836+
JumpTable = DefJTBasePage->getOperand(1).getExpr();
837+
return true;
838+
} else if (isADR(*DefJTPageBias)) {
767839
// TODO: Handle the pattern where there is no adrp/add pair.
768840
// It also occurs when the binary is static.
769-
// adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
841+
// nop
842+
// *adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
770843
// ldrh w13, [x13, w12, uxtw #1]
771844
// adr x12, 0x247b30 <__gettextparse+0x5b0>
772845
// add x13, x12, w13, sxth #2
773846
// br x13
774-
errs() << "BOLT-WARNING: Failed to match indirect branch: "
775-
"nop/adr instead of adrp/add \n";
776-
return false;
777-
}
778-
779-
assert(DefJTBaseAdd->getOpcode() == AArch64::ADDXri &&
780-
"Failed to match jump table base address pattern! (1)");
847+
SmallVector<MCInst *, 4> &UsesJTNop = UDChain[DefJTPageBias];
848+
assert((UsesJTNop.size() == 1 && UsesJTNop[0] == nullptr) &&
849+
"Failed to match jump table pattern! (2)");
850+
if (DefJTPageBias->getOperand(1).isExpr()) {
851+
JumpTable = DefJTPageBias->getOperand(1).getExpr();
852+
return true;
853+
}
854+
} else if (mayLoad(*DefJTPageBias)) {
855+
if (isLoadFromStack(*DefJTPageBias))
856+
return true;
781857

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;
858+
SmallVector<MCInst *, 4> &UsesJTBase = UDChain[DefJTPageBias];
859+
const MCInst *DefJTBasePage = UsesJTBase[1];
860+
if (DefJTBasePage == nullptr)
861+
return true;
862+
if (DefJTBasePage->getOpcode() == AArch64::ADRP) {
863+
// test jmp-table-pattern-matching.s (3)
864+
if (DefJTBasePage->getOperand(1).isExpr())
865+
JumpTable = DefJTBasePage->getOperand(1).getExpr();
866+
return true;
867+
} else {
868+
// Base address may have been defined in another basic block
869+
// and sub instruction can be used to get base page address
870+
// jmp-table-pattern-matching.s (2)
871+
if (isSUB(*DefJTBasePage)) {
872+
for (const MCOperand &Operand : useOperands(*DefJTBasePage)) {
873+
if (!Operand.isReg())
874+
continue;
875+
const unsigned Reg = Operand.getReg();
876+
if (Reg == AArch64::SP || Reg == AArch64::WSP ||
877+
Reg == AArch64::FP || Reg == AArch64::W29)
878+
return true;
879+
}
880+
}
881+
}
789882
}
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;
883+
884+
assert("Failed to match jump table pattern! (4)");
885+
return false;
795886
}
796887

797888
DenseMap<const MCInst *, SmallVector<MCInst *, 4>>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
## This test checks that disassemble stage works properly
2+
## JT with indirect branch
3+
## 1) nop + adr pair instructions
4+
## 2) sub + ldr pair instructions
5+
## 3) adrp + ldr pair instructions
6+
7+
# REQUIRES: system-linux
8+
9+
# RUN: rm -rf %t && split-file %s %t
10+
11+
## Prepare binary (1)
12+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_nop_adr.s \
13+
# RUN: -o %t/jt_nop_adr.o
14+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_nop_adr.o \
15+
# RUN: -Wl,-q -Wl,-z,now, -Wl,-T,%t/within-adr-range.t -o %t/jt_nop_adr.exe
16+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_nop_adr.exe | FileCheck \
17+
# RUN: --check-prefix=JT-RELAXED %s
18+
19+
# JT-RELAXED: <_start>:
20+
# JT-RELAXED-NEXT: nop
21+
# JT-RELAXED-NEXT: adr {{.*}}x3
22+
23+
# RUN: llvm-bolt %t/jt_nop_adr.exe -o %t/jt_nop_adr.bolt 2>&1 | FileCheck %s
24+
# CHECK-NOT: Failed to match
25+
26+
## This linker script ensures that .rodata and .text are sufficiently (<1M)
27+
## close to each other so that the adrp + ldr pair can be relaxed to nop + adr.
28+
#--- within-adr-range.t
29+
SECTIONS {
30+
.rodata 0x1000: { *(.rodata) }
31+
.text 0x2000: { *(.text) }
32+
.rela.rodata : { *(.rela.rodata) }
33+
}
34+
35+
## Prepare binary (2)
36+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_sub_ldr.s \
37+
# RUN: -o %t/jt_sub_ldr.o
38+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_sub_ldr.o \
39+
# RUN: -Wl,-q -Wl,-z,now -o %t/jt_sub_ldr.exe
40+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_sub_ldr.exe | FileCheck \
41+
# RUN: --check-prefix=JT-SUB-LDR %s
42+
43+
# JT-SUB-LDR: <_start>:
44+
# JT-SUB-LDR-NEXT: sub
45+
# JT-SUB-LDR-NEXT: ldr
46+
47+
# RUN: llvm-bolt %t/jt_sub_ldr.exe -o %t/jt_sub_ldr.bolt 2>&1 | FileCheck \
48+
# RUN: --check-prefix=JT-BOLT-SUBLDR %s
49+
# JT-BOLT-SUBLDR-NOT: Failed to match
50+
51+
## Prepare binary (3)
52+
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_adrp_ldr.s \
53+
# RUN: -o %t/jt_adrp_ldr.o
54+
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_adrp_ldr.o \
55+
# RUN: -Wl,-q -Wl,-z,now -Wl,--no-relax -o %t/jt_adrp_ldr.exe
56+
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_adrp_ldr.exe | FileCheck \
57+
# RUN: --check-prefix=JT-ADRP-LDR %s
58+
59+
# JT-ADRP-LDR: <_start>:
60+
# JT-ADRP-LDR-NEXT: adrp
61+
# JT-ADRP-LDR-NEXT: ldr
62+
63+
# RUN: llvm-bolt %t/jt_adrp_ldr.exe -o %t/jt_adrp_ldr.bolt 2>&1 | FileCheck \
64+
# RUN: --check-prefix=JT-BOLT-ADRP-LDR %s
65+
# JT-BOLT-ADRP-LDR-NOT: Failed to match
66+
67+
#--- jt_nop_adr.s
68+
.globl _start
69+
.type _start, %function
70+
_start:
71+
adrp x3, :got:jump_table
72+
ldr x3, [x3, #:got_lo12:jump_table]
73+
ldrh w3, [x3, x1, lsl #1]
74+
adr x1, test2_0
75+
add x3, x1, w3, sxth #2
76+
br x3
77+
test2_0:
78+
ret
79+
test2_1:
80+
ret
81+
82+
.section .rodata,"a",@progbits
83+
jump_table:
84+
.hword (test2_0-test2_0)>>2
85+
.hword (test2_1-test2_0)>>2
86+
87+
88+
#--- jt_sub_ldr.s
89+
.globl _start
90+
.type _start, %function
91+
_start:
92+
sub x1, x29, #0x4, lsl #12
93+
ldr x1, [x1, #14352]
94+
ldrh w1, [x1, w3, uxtw #1]
95+
adr x3, test2_0
96+
add x1, x3, w1, sxth #2
97+
br x1
98+
test2_0:
99+
ret
100+
test2_1:
101+
ret
102+
103+
.section .rodata,"a",@progbits
104+
jump_table:
105+
.hword (test2_0-test2_0)>>2
106+
.hword (test2_1-test2_0)>>2
107+
108+
109+
#--- jt_adrp_ldr.s
110+
.globl _start
111+
.type _start, %function
112+
_start:
113+
adrp x3, :got:jump_table
114+
ldr x3, [x3, #:got_lo12:jump_table]
115+
ldrh w3, [x3, x1, lsl #1]
116+
adr x1, test2_0
117+
add x3, x1, w3, sxth #2
118+
br x3
119+
test2_0:
120+
ret
121+
test2_1:
122+
ret
123+
124+
.section .rodata,"a",@progbits
125+
jump_table:
126+
.hword (test2_0-test2_0)>>2
127+
.hword (test2_1-test2_0)>>2

bolt/test/AArch64/test-indirect-branch.s

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,9 @@ test1_1:
5656
test1_2:
5757
ret
5858

59-
// Pattern 2
60-
// CHECK: BOLT-WARNING: Failed to match indirect branch: nop/adr instead of adrp/add
61-
.globl test2
62-
.type test2, %function
63-
test2:
64-
nop
65-
adr x3, jump_table
66-
ldrh w3, [x3, x1, lsl #1]
67-
adr x1, test2_0
68-
add x3, x1, w3, sxth #2
69-
br x3
70-
test2_0:
71-
ret
72-
test2_1:
73-
ret
74-
7559
.section .rodata,"a",@progbits
7660
datatable:
7761
.word test1_0-datatable
7862
.word test1_1-datatable
7963
.word test1_2-datatable
8064

81-
jump_table:
82-
.hword (test2_0-test2_0)>>2
83-
.hword (test2_1-test2_0)>>2

0 commit comments

Comments
 (0)