Skip to content

Commit db86168

Browse files
author
Yonghong Song
committed
[RFC][BPF] Support Jump Table
NOTE: We probably need cpu v5 or other flags to enable this feature. We can add it later when necessary. This patch adds jump table support. A new insn 'gotox <reg>' is added to allow goto through a register. The register represents the address in the current section. The function is a concrete example with bpf selftest progs/user_ringbuf_success.c. Compilation command line to generate .s file: ============================================= clang -g -Wall -Werror -D__TARGET_ARCH_x86 -mlittle-endian \ -I/home/yhs/work/bpf-next/tools/testing/selftests/bpf/tools/include \ -I/home/yhs/work/bpf-next/tools/testing/selftests/bpf \ -I/home/yhs/work/bpf-next/tools/include/uapi \ -I/home/yhs/work/bpf-next/tools/testing/selftests/usr/include -std=gnu11 \ -fno-strict-aliasing -Wno-compare-distinct-pointer-types \ -idirafter /home/yhs/work/llvm-project/llvm/build.21/Release/lib/clang/21/include \ -idirafter /usr/local/include -idirafter /usr/include \ -DENABLE_ATOMICS_TESTS -O2 -S progs/user_ringbuf_success.c \ -o /home/yhs/work/bpf-next/tools/testing/selftests/bpf/user_ringbuf_success.bpf.o.s \ --target=bpf -mcpu=v3 The related assembly: read_protocol_msg: ... r3 <<= 3 r1 = .LJTI1_0 ll r1 += r3 r1 = *(u64 *)(r1 + 0) gotox r1 LBB1_4: r1 = *(u64 *)(r0 + 8) goto LBB1_5 LBB1_7: r1 = *(u64 *)(r0 + 8) goto LBB1_8 LBB1_9: w1 = *(u32 *)(r0 + 8) r1 <<= 32 r1 s>>= 32 r2 = kern_mutated ll r3 = *(u64 *)(r2 + 0) r3 *= r1 *(u64 *)(r2 + 0) = r3 goto LBB1_11 LBB1_6: w1 = *(u32 *)(r0 + 8) r1 <<= 32 r1 s>>= 32 LBB1_5: ... .section .rodata,"a",@progbits .p2align 3, 0x0 .LJTI1_0: .quad LBB1_4 .quad LBB1_6 .quad LBB1_7 .quad LBB1_9 ... publish_next_kern_msg: ... r6 <<= 3 r1 = .LJTI6_0 ll r1 += r6 r1 = *(u64 *)(r1 + 0) gotox r1 LBB6_3: ... LBB6_5: ... LBB6_6: ... LBB6_4: ... .section .rodata,"a",@progbits .p2align 3, 0x0 .LJTI6_0: .quad LBB6_3 .quad LBB6_4 .quad LBB6_5 .quad LBB6_6 You can see in the above .LJTI1_0 and .LJTI6_0 are actually jump table targets and these two jump tables are used in insns so they can get proper jump table target with gotox insn. Now let us look at sections in .o file ======================================= For example, [ 6] .rodata PROGBITS 0000000000000000 000740 0000d6 00 A 0 0 8 [ 7] .rel.rodata REL 0000000000000000 003860 000080 10 I 39 6 8 [ 8] .llvm_jump_table_sizes LLVM_JT_SIZES 0000000000000000 000816 000010 00 0 0 1 [ 9] .rel.llvm_jump_table_sizes REL 0000000000000000 0038e0 000010 10 I 39 8 8 ... [14] .llvm_jump_table_sizes LLVM_JT_SIZES 0000000000000000 000958 000010 00 0 0 1 [15] .rel.llvm_jump_table_sizes REL 0000000000000000 003970 000010 10 I 39 14 8 With llvm-readelf dump section 8 and 14: $ llvm-readelf -x 8 user_ringbuf_success.bpf.o Hex dump of section '.llvm_jump_table_sizes': 0x00000000 00000000 00000000 04000000 00000000 ................ $ llvm-readelf -x 14 user_ringbuf_success.bpf.o Hex dump of section '.llvm_jump_table_sizes': 0x00000000 20000000 00000000 04000000 00000000 ............... You can see. There are two jump tables: jump table 1: offset 0, size 4 (4 labels) jump table 2: offset 0x20, size 4 (4 labels) Check sections 9 and 15, we can find the corresponding section: Relocation section '.rel.llvm_jump_table_sizes' at offset 0x38e0 contains 1 entries: Offset Info Type Symbol's Value Symbol's Name 0000000000000000 0000000a00000002 R_BPF_64_ABS64 0000000000000000 .rodata Relocation section '.rel.llvm_jump_table_sizes' at offset 0x3970 contains 1 entries: Offset Info Type Symbol's Value Symbol's Name 0000000000000000 0000000a00000002 R_BPF_64_ABS64 0000000000000000 .rodata and confirmed that the relocation is against '.rodata'. Dump .rodata section: 0x00000000 a8000000 00000000 10010000 00000000 ................ 0x00000010 b8000000 00000000 c8000000 00000000 ................ 0x00000020 28040000 00000000 00050000 00000000 (............... 0x00000030 70040000 00000000 b8040000 00000000 p............... 0x00000040 44726169 6e207265 7475726e 65643a20 Drain returned: So we can get two jump tables: .rodata offset 0, # of lables 4: 0x00000000 a8000000 00000000 10010000 00000000 ................ 0x00000010 b8000000 00000000 c8000000 00000000 ................ .rodata offset 0x200, # of lables 4: 0x00000020 28040000 00000000 00050000 00000000 (............... 0x00000030 70040000 00000000 b8040000 00000000 p............... This way, you just need to scan related code section. As long as it matches one of jump tables (.rodata relocation, offset also matching), you do not need to care about gotox at all in libbpf. An option -bpf-min-jump-table-entries is implemented to control the minimum number of entries to use a jump table on BPF. The default value 4, but it can be changed with the following clang option clang ... -mllvm -bpf-min-jump-table-entries=6 where the number of jump table cases needs to be >= 6 in order to use jump table.
1 parent 8063bd1 commit db86168

File tree

10 files changed

+115
-3
lines changed

10 files changed

+115
-3
lines changed

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/CodeGen/StackMaps.h"
2727
#include "llvm/DebugInfo/CodeView/CodeView.h"
2828
#include "llvm/IR/InlineAsm.h"
29+
#include "llvm/Support/CommandLine.h"
2930
#include "llvm/Support/Compiler.h"
3031
#include "llvm/Support/ErrorHandling.h"
3132
#include <cstdint>
@@ -34,6 +35,7 @@
3435
#include <vector>
3536

3637
namespace llvm {
38+
extern cl::opt<bool> EmitJumpTableSizesSection;
3739

3840
class AddrLabelMap;
3941
class AsmPrinterHandler;

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ static cl::opt<bool> BBAddrMapSkipEmitBBEntries(
168168
"unnecessary for some PGOAnalysisMap features."),
169169
cl::Hidden, cl::init(false));
170170

171-
static cl::opt<bool> EmitJumpTableSizesSection(
171+
cl::opt<bool> llvm::EmitJumpTableSizesSection(
172172
"emit-jump-table-sizes-section",
173173
cl::desc("Emit a section containing jump table addresses and sizes"),
174174
cl::Hidden, cl::init(false));

llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ struct BPFOperand : public MCParsedAsmOperand {
232232
.Case("callx", true)
233233
.Case("goto", true)
234234
.Case("gotol", true)
235+
.Case("gotox", true)
235236
.Case("may_goto", true)
236237
.Case("*", true)
237238
.Case("exit", true)

llvm/lib/Target/BPF/BPFAsmPrinter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class BPFAsmPrinter : public AsmPrinter {
5757
} // namespace
5858

5959
bool BPFAsmPrinter::doInitialization(Module &M) {
60+
EmitJumpTableSizesSection = true;
6061
AsmPrinter::doInitialization(M);
6162

6263
// Only emit BTF when debuginfo available.

llvm/lib/Target/BPF/BPFISelLowering.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ static cl::opt<bool> BPFExpandMemcpyInOrder("bpf-expand-memcpy-in-order",
3838
cl::Hidden, cl::init(false),
3939
cl::desc("Expand memcpy into load/store pairs in order"));
4040

41+
static cl::opt<unsigned> BPFMinimumJumpTableEntries(
42+
"bpf-min-jump-table-entries", cl::init(4), cl::Hidden,
43+
cl::desc("Set minimum number of entries to use a jump table on BPF"));
44+
4145
static void fail(const SDLoc &DL, SelectionDAG &DAG, const Twine &Msg,
4246
SDValue Val = {}) {
4347
std::string Str;
@@ -67,12 +71,13 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
6771

6872
setOperationAction(ISD::BR_CC, MVT::i64, Custom);
6973
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
70-
setOperationAction(ISD::BRIND, MVT::Other, Expand);
7174
setOperationAction(ISD::BRCOND, MVT::Other, Expand);
7275

7376
setOperationAction(ISD::TRAP, MVT::Other, Custom);
7477

75-
setOperationAction({ISD::GlobalAddress, ISD::ConstantPool}, MVT::i64, Custom);
78+
setOperationAction({ISD::GlobalAddress, ISD::ConstantPool, ISD::JumpTable,
79+
ISD::BlockAddress},
80+
MVT::i64, Custom);
7681

7782
setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Custom);
7883
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
@@ -159,6 +164,7 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
159164

160165
setBooleanContents(ZeroOrOneBooleanContent);
161166
setMaxAtomicSizeInBitsSupported(64);
167+
setMinimumJumpTableEntries(BPFMinimumJumpTableEntries);
162168

163169
// Function alignments
164170
setMinFunctionAlignment(Align(8));
@@ -316,10 +322,14 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
316322
report_fatal_error("unimplemented opcode: " + Twine(Op.getOpcode()));
317323
case ISD::BR_CC:
318324
return LowerBR_CC(Op, DAG);
325+
case ISD::JumpTable:
326+
return LowerJumpTable(Op, DAG);
319327
case ISD::GlobalAddress:
320328
return LowerGlobalAddress(Op, DAG);
321329
case ISD::ConstantPool:
322330
return LowerConstantPool(Op, DAG);
331+
case ISD::BlockAddress:
332+
return LowerBlockAddress(Op, DAG);
323333
case ISD::SELECT_CC:
324334
return LowerSELECT_CC(Op, DAG);
325335
case ISD::SDIV:
@@ -780,6 +790,11 @@ SDValue BPFTargetLowering::LowerTRAP(SDValue Op, SelectionDAG &DAG) const {
780790
return LowerCall(CLI, InVals);
781791
}
782792

793+
SDValue BPFTargetLowering::LowerJumpTable(SDValue Op, SelectionDAG &DAG) const {
794+
JumpTableSDNode *N = cast<JumpTableSDNode>(Op);
795+
return getAddr(N, DAG);
796+
}
797+
783798
const char *BPFTargetLowering::getTargetNodeName(unsigned Opcode) const {
784799
switch ((BPFISD::NodeType)Opcode) {
785800
case BPFISD::FIRST_NUMBER:
@@ -811,6 +826,17 @@ static SDValue getTargetNode(ConstantPoolSDNode *N, const SDLoc &DL, EVT Ty,
811826
N->getOffset(), Flags);
812827
}
813828

829+
static SDValue getTargetNode(BlockAddressSDNode *N, const SDLoc &DL, EVT Ty,
830+
SelectionDAG &DAG, unsigned Flags) {
831+
return DAG.getTargetBlockAddress(N->getBlockAddress(), Ty, N->getOffset(),
832+
Flags);
833+
}
834+
835+
static SDValue getTargetNode(JumpTableSDNode *N, const SDLoc &DL, EVT Ty,
836+
SelectionDAG &DAG, unsigned Flags) {
837+
return DAG.getTargetJumpTable(N->getIndex(), Ty, Flags);
838+
}
839+
814840
template <class NodeTy>
815841
SDValue BPFTargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG,
816842
unsigned Flags) const {
@@ -837,6 +863,12 @@ SDValue BPFTargetLowering::LowerConstantPool(SDValue Op,
837863
return getAddr(N, DAG);
838864
}
839865

866+
SDValue BPFTargetLowering::LowerBlockAddress(SDValue Op,
867+
SelectionDAG &DAG) const {
868+
BlockAddressSDNode *N = cast<BlockAddressSDNode>(Op);
869+
return getAddr(N, DAG);
870+
}
871+
840872
unsigned
841873
BPFTargetLowering::EmitSubregExt(MachineInstr &MI, MachineBasicBlock *BB,
842874
unsigned Reg, bool isSigned) const {

llvm/lib/Target/BPF/BPFISelLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class BPFTargetLowering : public TargetLowering {
8181
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
8282
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
8383
SDValue LowerTRAP(SDValue Op, SelectionDAG &DAG) const;
84+
SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
85+
SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const;
8486

8587
template <class NodeTy>
8688
SDValue getAddr(NodeTy *N, SelectionDAG &DAG, unsigned Flags = 0) const;

llvm/lib/Target/BPF/BPFInstrInfo.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ bool BPFInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
181181
if (!isUnpredicatedTerminator(*I))
182182
break;
183183

184+
// If a JX insn, we're done.
185+
if (I->getOpcode() == BPF::JX)
186+
break;
187+
184188
// A terminator that isn't a branch can't easily be handled
185189
// by this analysis.
186190
if (!I->isBranch())
@@ -259,3 +263,40 @@ unsigned BPFInstrInfo::removeBranch(MachineBasicBlock &MBB,
259263

260264
return Count;
261265
}
266+
267+
int BPFInstrInfo::getJumpTableIndex(const MachineInstr &MI) const {
268+
// The pattern looks like:
269+
// %0 = LD_imm64 %jump-table.0 ; load jump-table address
270+
// %1 = ADD_rr %0, $another_reg ; address + offset
271+
// %2 = LDD %1, 0 ; load the actual label
272+
// JX %2
273+
const MachineFunction &MF = *MI.getParent()->getParent();
274+
const MachineRegisterInfo &MRI = MF.getRegInfo();
275+
276+
Register Reg = MI.getOperand(0).getReg();
277+
if (!Reg.isVirtual())
278+
return -1;
279+
MachineInstr *Ldd = MRI.getUniqueVRegDef(Reg);
280+
if (Ldd == nullptr || Ldd->getOpcode() != BPF::LDD)
281+
return -1;
282+
283+
Reg = Ldd->getOperand(1).getReg();
284+
if (!Reg.isVirtual())
285+
return -1;
286+
MachineInstr *Add = MRI.getUniqueVRegDef(Reg);
287+
if (Add == nullptr || Add->getOpcode() != BPF::ADD_rr)
288+
return -1;
289+
290+
Reg = Add->getOperand(1).getReg();
291+
if (!Reg.isVirtual())
292+
return -1;
293+
MachineInstr *LDimm64 = MRI.getUniqueVRegDef(Reg);
294+
if (LDimm64 == nullptr || LDimm64->getOpcode() != BPF::LD_imm64)
295+
return -1;
296+
297+
const MachineOperand &MO = LDimm64->getOperand(1);
298+
if (!MO.isJTI())
299+
return -1;
300+
301+
return MO.getIndex();
302+
}

llvm/lib/Target/BPF/BPFInstrInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class BPFInstrInfo : public BPFGenInstrInfo {
5858
MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond,
5959
const DebugLoc &DL,
6060
int *BytesAdded = nullptr) const override;
61+
62+
int getJumpTableIndex(const MachineInstr &MI) const override;
63+
6164
private:
6265
void expandMEMCPY(MachineBasicBlock::iterator) const;
6366

llvm/lib/Target/BPF/BPFInstrInfo.td

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,15 @@ class TYPE_LD_ST<bits<3> mode, bits<2> size,
183183
let Inst{60-59} = size;
184184
}
185185

186+
// For indirect jump
187+
class TYPE_IND_JMP<bits<4> op, bits<1> srctype,
188+
dag outs, dag ins, string asmstr, list<dag> pattern>
189+
: InstBPF<outs, ins, asmstr, pattern> {
190+
191+
let Inst{63-60} = op;
192+
let Inst{59} = srctype;
193+
}
194+
186195
// jump instructions
187196
class JMP_RR<BPFJumpOp Opc, string OpcodeStr, PatLeaf Cond>
188197
: TYPE_ALU_JMP<Opc.Value, BPF_X.Value,
@@ -216,6 +225,18 @@ class JMP_RI<BPFJumpOp Opc, string OpcodeStr, PatLeaf Cond>
216225
let BPFClass = BPF_JMP;
217226
}
218227

228+
class JMP_IND<BPFJumpOp Opc, string OpcodeStr, list<dag> Pattern>
229+
: TYPE_ALU_JMP<Opc.Value, BPF_X.Value,
230+
(outs),
231+
(ins GPR:$dst),
232+
!strconcat(OpcodeStr, " $dst"),
233+
Pattern> {
234+
bits<4> dst;
235+
236+
let Inst{51-48} = dst;
237+
let BPFClass = BPF_JMP;
238+
}
239+
219240
class JMP_JCOND<BPFJumpOp Opc, string OpcodeStr, list<dag> Pattern>
220241
: TYPE_ALU_JMP<Opc.Value, BPF_K.Value,
221242
(outs),
@@ -281,6 +302,10 @@ defm JSLT : J<BPF_JSLT, "s<", BPF_CC_LT, BPF_CC_LT_32>;
281302
defm JSLE : J<BPF_JSLE, "s<=", BPF_CC_LE, BPF_CC_LE_32>;
282303
defm JSET : J<BPF_JSET, "&", NoCond, NoCond>;
283304
def JCOND : JMP_JCOND<BPF_JCOND, "may_goto", []>;
305+
306+
let isIndirectBranch = 1 in {
307+
def JX : JMP_IND<BPF_JA, "gotox", [(brind i64:$dst)]>;
308+
}
284309
}
285310

286311
// ALU instructions
@@ -851,6 +876,8 @@ let usesCustomInserter = 1, isCodeGenOnly = 1 in {
851876
// load 64-bit global addr into register
852877
def : Pat<(BPFWrapper tglobaladdr:$in), (LD_imm64 tglobaladdr:$in)>;
853878
def : Pat<(BPFWrapper tconstpool:$in), (LD_imm64 tconstpool:$in)>;
879+
def : Pat<(BPFWrapper tblockaddress:$in), (LD_imm64 tblockaddress:$in)>;
880+
def : Pat<(BPFWrapper tjumptable:$in), (LD_imm64 tjumptable:$in)>;
854881

855882
// 0xffffFFFF doesn't fit into simm32, optimize common case
856883
def : Pat<(i64 (and (i64 GPR:$src), 0xffffFFFF)),

llvm/lib/Target/BPF/BPFMCInstLower.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ void BPFMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
7777
case MachineOperand::MO_ConstantPoolIndex:
7878
MCOp = LowerSymbolOperand(MO, Printer.GetCPISymbol(MO.getIndex()));
7979
break;
80+
case MachineOperand::MO_JumpTableIndex:
81+
MCOp = LowerSymbolOperand(MO, Printer.GetJTISymbol(MO.getIndex()));
82+
break;
8083
}
8184

8285
OutMI.addOperand(MCOp);

0 commit comments

Comments
 (0)