Skip to content

Commit

Permalink
Exclude JT pattern matching under assert failure
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Moksyakov Alexey authored and yavtuk committed Jan 9, 2025
1 parent 81ae668 commit 542a79b
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 44 deletions.
141 changes: 116 additions & 25 deletions bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
Inst.getOpcode() == AArch64::ADDXrx64);
}

bool isSUB(const MCInst &Inst) const override {
const unsigned opcode = Inst.getOpcode();
bool isSubInstr = false;
switch (opcode) {
case AArch64::SUBSWri:
case AArch64::SUBSWrr:
case AArch64::SUBSWrs:
case AArch64::SUBSWrx:
case AArch64::SUBSXri:
case AArch64::SUBSXrr:
case AArch64::SUBSXrs:
case AArch64::SUBSXrx:
case AArch64::SUBSXrx64:
case AArch64::SUBWri:
case AArch64::SUBWrr:
case AArch64::SUBWrs:
case AArch64::SUBWrx:
case AArch64::SUBXri:
case AArch64::SUBXrr:
case AArch64::SUBXrs:
case AArch64::SUBXrx:
case AArch64::SUBXrx64:
isSubInstr = true;
break;
default:
isSubInstr = false;
break;
}
return isSubInstr;
}

bool isLDRB(const MCInst &Inst) const {
return (Inst.getOpcode() == AArch64::LDRBBpost ||
Inst.getOpcode() == AArch64::LDRBBpre ||
Expand Down Expand Up @@ -652,6 +683,34 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
/// # of this BB)
/// br x0 # Indirect jump instruction
///
/// adrp + ldr pair instructions of JT
/// adrp x3, :got:jump_table
/// ldr x1, [x1, #value]
/// ldrh w1, [x1, w3, uxtw #1]
/// adr x3, 573ae4
/// add x1, x3, w1, sxth #2
/// br x1
///
/// lld/test/ELF/aarch64-adrp-ldr-got.s
/// if .rodata and .text are sufficiently (<1M)
/// close to each other so that the adrp + ldr pair can be relaxed to
/// nop + adr.
/// nop #
/// adr x0, #6d8f8 # Get JT table address
/// ldrh w0, [x0, w4, uxtw #1] # Loads JT entry
/// adr x2, 1479b0 # Get PC first instruction for next BB
/// add x0, x2, w0, sxth #2 # Finish building branch target
/// # (entries in JT are relative to the end
/// # of this BB)
/// br x0 # Indirect jump instruction
///
/// sub + ldr pair instructions of JT, JT address on the stack and other BB
/// sub x1, x29, #0x4, lsl #12
/// ldr x1, [x1, #14352]
/// ldrh w1, [x1, w3, uxtw #1]
/// adr x3, 573ae4
/// add x1, x3, w1, sxth #2
/// br x1
bool analyzeIndirectBranchFragment(
const MCInst &Inst,
DenseMap<const MCInst *, SmallVector<MCInst *, 4>> &UDChain,
Expand Down Expand Up @@ -753,45 +812,77 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {

// Match ADD that calculates the JumpTable Base Address (not the offset)
SmallVector<MCInst *, 4> &UsesLoad = UDChain[DefLoad];
const MCInst *DefJTBaseAdd = UsesLoad[1];
const MCInst *DefJTPageBias = UsesLoad[1];
MCPhysReg From, To;
if (DefJTBaseAdd == nullptr || isLoadFromStack(*DefJTBaseAdd) ||
isRegToRegMove(*DefJTBaseAdd, From, To)) {
JumpTable = nullptr;
if (DefJTPageBias == nullptr || isLoadFromStack(*DefJTPageBias) ||
isRegToRegMove(*DefJTPageBias, From, To)) {
// Sometimes base address may have been defined in another basic block
// (hoisted). Return with no jump table info.
JumpTable = nullptr;
return true;
}

if (DefJTBaseAdd->getOpcode() == AArch64::ADR) {
if (isAddXri(*DefJTPageBias)) {
if (DefJTPageBias->getOperand(2).isImm())
Offset = DefJTPageBias->getOperand(2).getImm();
SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTPageBias];
const MCInst *DefJTBasePage = UsesJTBaseAdd[1];
if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) {
return true;
}
assert(DefJTBasePage->getOpcode() == AArch64::ADRP &&
"Failed to match jump table base page pattern! (2)");
if (DefJTBasePage->getOperand(1).isExpr())
JumpTable = DefJTBasePage->getOperand(1).getExpr();
return true;
} else if (isADR(*DefJTPageBias)) {
// TODO: Handle the pattern where there is no adrp/add pair.
// It also occurs when the binary is static.
// adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
// nop
// *adr x13, 0x215a18 <_nl_value_type_LC_COLLATE+0x50>
// ldrh w13, [x13, w12, uxtw #1]
// adr x12, 0x247b30 <__gettextparse+0x5b0>
// add x13, x12, w13, sxth #2
// br x13
errs() << "BOLT-WARNING: Failed to match indirect branch: "
"nop/adr instead of adrp/add \n";
return false;
}

assert(DefJTBaseAdd->getOpcode() == AArch64::ADDXri &&
"Failed to match jump table base address pattern! (1)");
SmallVector<MCInst *, 4> &UsesJTNop = UDChain[DefJTPageBias];
assert((UsesJTNop.size() == 1 && UsesJTNop[0] == nullptr) &&
"Failed to match jump table pattern! (2)");
if (DefJTPageBias->getOperand(1).isExpr()) {
JumpTable = DefJTPageBias->getOperand(1).getExpr();
return true;
}
} else if (mayLoad(*DefJTPageBias)) {
if (isLoadFromStack(*DefJTPageBias))
return true;

if (DefJTBaseAdd->getOperand(2).isImm())
Offset = DefJTBaseAdd->getOperand(2).getImm();
SmallVector<MCInst *, 4> &UsesJTBaseAdd = UDChain[DefJTBaseAdd];
const MCInst *DefJTBasePage = UsesJTBaseAdd[1];
if (DefJTBasePage == nullptr || isLoadFromStack(*DefJTBasePage)) {
JumpTable = nullptr;
return true;
SmallVector<MCInst *, 4> &UsesJTBase = UDChain[DefJTPageBias];
const MCInst *DefJTBasePage = UsesJTBase[1];
if (DefJTBasePage == nullptr)
return true;
if (DefJTBasePage->getOpcode() == AArch64::ADRP) {
// test jmp-table-pattern-matching.s (3)
if (DefJTBasePage->getOperand(1).isExpr())
JumpTable = DefJTBasePage->getOperand(1).getExpr();
return true;
} else {
// Base address may have been defined in another basic block
// and sub instruction can be used to get base page address
// jmp-table-pattern-matching.s (2)
if (isSUB(*DefJTBasePage)) {
for (const MCOperand &Operand : useOperands(*DefJTBasePage)) {
if (!Operand.isReg())
continue;
const unsigned Reg = Operand.getReg();
if (Reg == AArch64::SP || Reg == AArch64::WSP ||
Reg == AArch64::FP || Reg == AArch64::W29)
return true;
}
}
}
}
assert(DefJTBasePage->getOpcode() == AArch64::ADRP &&
"Failed to match jump table base page pattern! (2)");
if (DefJTBasePage->getOperand(1).isExpr())
JumpTable = DefJTBasePage->getOperand(1).getExpr();
return true;

assert("Failed to match jump table pattern! (4)");
return false;
}

DenseMap<const MCInst *, SmallVector<MCInst *, 4>>
Expand Down
127 changes: 127 additions & 0 deletions bolt/test/AArch64/jmp-table-pattern-matching.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
## This test checks that disassemble stage works properly
## JT with indirect branch
## 1) nop + adr pair instructions
## 2) sub + ldr pair instructions
## 3) adrp + ldr pair instructions

# REQUIRES: system-linux

# RUN: rm -rf %t && split-file %s %t

## Prepare binary (1)
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_nop_adr.s \
# RUN: -o %t/jt_nop_adr.o
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_nop_adr.o \
# RUN: -Wl,-q -Wl,-z,now, -Wl,-T,%t/within-adr-range.t -o %t/jt_nop_adr.exe
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_nop_adr.exe | FileCheck \
# RUN: --check-prefix=JT-RELAXED %s

# JT-RELAXED: <_start>:
# JT-RELAXED-NEXT: nop
# JT-RELAXED-NEXT: adr {{.*}}x3

# RUN: llvm-bolt %t/jt_nop_adr.exe -o %t/jt_nop_adr.bolt 2>&1 | FileCheck %s
# CHECK-NOT: Failed to match

## This linker script ensures that .rodata and .text are sufficiently (<1M)
## close to each other so that the adrp + ldr pair can be relaxed to nop + adr.
#--- within-adr-range.t
SECTIONS {
.rodata 0x1000: { *(.rodata) }
.text 0x2000: { *(.text) }
.rela.rodata : { *(.rela.rodata) }
}

## Prepare binary (2)
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_sub_ldr.s \
# RUN: -o %t/jt_sub_ldr.o
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_sub_ldr.o \
# RUN: -Wl,-q -Wl,-z,now -o %t/jt_sub_ldr.exe
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_sub_ldr.exe | FileCheck \
# RUN: --check-prefix=JT-SUB-LDR %s

# JT-SUB-LDR: <_start>:
# JT-SUB-LDR-NEXT: sub
# JT-SUB-LDR-NEXT: ldr

# RUN: llvm-bolt %t/jt_sub_ldr.exe -o %t/jt_sub_ldr.bolt 2>&1 | FileCheck \
# RUN: --check-prefix=JT-BOLT-SUBLDR %s
# JT-BOLT-SUBLDR-NOT: Failed to match

## Prepare binary (3)
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %t/jt_adrp_ldr.s \
# RUN: -o %t/jt_adrp_ldr.o
# RUN: %clang %cflags --target=aarch64-unknown-linux %t/jt_adrp_ldr.o \
# RUN: -Wl,-q -Wl,-z,now -Wl,--no-relax -o %t/jt_adrp_ldr.exe
# RUN: llvm-objdump --no-show-raw-insn -d %t/jt_adrp_ldr.exe | FileCheck \
# RUN: --check-prefix=JT-ADRP-LDR %s

# JT-ADRP-LDR: <_start>:
# JT-ADRP-LDR-NEXT: adrp
# JT-ADRP-LDR-NEXT: ldr

# RUN: llvm-bolt %t/jt_adrp_ldr.exe -o %t/jt_adrp_ldr.bolt 2>&1 | FileCheck \
# RUN: --check-prefix=JT-BOLT-ADRP-LDR %s
# JT-BOLT-ADRP-LDR-NOT: Failed to match

#--- jt_nop_adr.s
.globl _start
.type _start, %function
_start:
adrp x3, :got:jump_table
ldr x3, [x3, #:got_lo12:jump_table]
ldrh w3, [x3, x1, lsl #1]
adr x1, test2_0
add x3, x1, w3, sxth #2
br x3
test2_0:
ret
test2_1:
ret

.section .rodata,"a",@progbits
jump_table:
.hword (test2_0-test2_0)>>2
.hword (test2_1-test2_0)>>2


#--- jt_sub_ldr.s
.globl _start
.type _start, %function
_start:
sub x1, x29, #0x4, lsl #12
ldr x1, [x1, #14352]
ldrh w1, [x1, w3, uxtw #1]
adr x3, test2_0
add x1, x3, w1, sxth #2
br x1
test2_0:
ret
test2_1:
ret

.section .rodata,"a",@progbits
jump_table:
.hword (test2_0-test2_0)>>2
.hword (test2_1-test2_0)>>2


#--- jt_adrp_ldr.s
.globl _start
.type _start, %function
_start:
adrp x3, :got:jump_table
ldr x3, [x3, #:got_lo12:jump_table]
ldrh w3, [x3, x1, lsl #1]
adr x1, test2_0
add x3, x1, w3, sxth #2
br x3
test2_0:
ret
test2_1:
ret

.section .rodata,"a",@progbits
jump_table:
.hword (test2_0-test2_0)>>2
.hword (test2_1-test2_0)>>2
19 changes: 0 additions & 19 deletions bolt/test/AArch64/test-indirect-branch.s
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,9 @@ test1_1:
test1_2:
ret

// Pattern 2
// CHECK: BOLT-WARNING: Failed to match indirect branch: nop/adr instead of adrp/add
.globl test2
.type test2, %function
test2:
nop
adr x3, jump_table
ldrh w3, [x3, x1, lsl #1]
adr x1, test2_0
add x3, x1, w3, sxth #2
br x3
test2_0:
ret
test2_1:
ret

.section .rodata,"a",@progbits
datatable:
.word test1_0-datatable
.word test1_1-datatable
.word test1_2-datatable

jump_table:
.hword (test2_0-test2_0)>>2
.hword (test2_1-test2_0)>>2

0 comments on commit 542a79b

Please sign in to comment.