From 535e5f4b81accc4c1fc45b0d707d86582a1f8fe2 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Thu, 4 Sep 2025 10:02:56 -0700 Subject: [PATCH] [BPF] Support jump-table style callx The following is an example: ``` typedef int (*op_t)(int, int); __attribute__((section("_add"))) static int add(int a, int b) { return a + b; } __attribute__((section("_mul"))) static int mul(int a, int b) { return a * b; } __attribute__((noinline)) static int apply(op_t *ops, int index, int a, int b) { // indirect call via function pointer return ops[index](a, b); } #ifdef STATIC static op_t ops[] = { add, mul, add, add, mul, mul }; #endif #ifdef GLOBAL op_t ops[] = { add, mul, add, add, mul, mul }; #endif int result(int i, int j) { #ifdef PRIVATE op_t ops[] = { add, mul, add, add, mul, mul }; #endif int x = 2, y = 3; int r1 = apply(ops, 0, x, y); int r2 = apply(ops, 4, x, y); return r1 + r2; } ``` Compilation for three different modes: ``` clang --target=bpf -DPRIVATE -O2 -S t.c -o t.s.private clang --target=bpf -DSTATIC -O2 -S t.c -o t.s.static clang --target=bpf -DGLOBAL -O2 -S t.c -o t.s.global ``` The assembly for the above three different modes. For example, for PRIVATE mode: ``` .text .globl result # -- Begin function result .p2align 3 .type result,@function result: # @result # %bb.0: r1 = BPF.__const.result.ops ll w2 = 0 call apply w6 = w0 r1 = BPF.__const.result.ops ll w2 = 4 call apply w0 += w6 exit .Lfunc_end0: .size result, .Lfunc_end0-result ... .text .p2align 3 # -- Begin function apply .type apply,@function apply: # @apply # %bb.0: r2 = w2 r2 <<= 3 r1 += r2 r3 = *(u64 *)(r1 + 0) w1 = 2 w2 = 3 callx r3 exit .Lfunc_end3: .size apply, .Lfunc_end3-apply ... .type BPF.__const.result.ops,@object # @BPF.__const.result.ops .section .calltables,"a",@progbits .p2align 3, 0x0 BPF.__const.result.ops: .quad add .quad mul .quad add .quad add .quad mul .quad mul .size BPF.__const.result.ops, 48 ``` STATIC and GLOBAL modes are similar except the callx table. For GLOBAL: ``` .type BPF.ops,@object # @BPF.ops .section .calltables,"aw",@progbits .globl BPF.ops .p2align 3, 0x0 BPF.ops: .quad add .quad mul .quad add .quad add .quad mul .quad mul .size BPF.ops, 48 ``` For STATIC: ``` .type BPF.ops,@object # @BPF.ops .section .calltables,"a",@progbits .p2align 3, 0x0 BPF.ops: .quad add .quad mul .quad add .quad add .quad mul .quad mul .size BPF.ops, 48 ``` Will add selftests after the implementation is validated in kernel. --- llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp index b202b20291aff..4d6cabe1eed8d 100644 --- a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp +++ b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp @@ -55,6 +55,7 @@ class BPFCheckAndAdjustIR final : public ModulePass { bool sinkMinMax(Module &M); bool removeGEPBuiltins(Module &M); bool insertASpaceCasts(Module &M); + bool renameCallTableGlobal(Module &M); }; } // End anonymous namespace @@ -527,12 +528,53 @@ bool BPFCheckAndAdjustIR::insertASpaceCasts(Module &M) { return Changed; } +static bool isAllFunctionInCA(ConstantArray *CA) { + for (unsigned i = 1, e = CA->getNumOperands(); i != e; ++i) { + if (!dyn_cast(CA->getOperand(i))) + return false; + } + return true; +} + +bool BPFCheckAndAdjustIR::renameCallTableGlobal(Module &M) { + bool Changed = false; + for (GlobalVariable &Global : M.globals()) { + if (Global.getLinkage() != GlobalValue::PrivateLinkage && + Global.getLinkage() != GlobalValue::InternalLinkage && + Global.getLinkage() != GlobalValue::ExternalLinkage) + continue; + if (Global.getLinkage() == GlobalValue::PrivateLinkage && + !Global.isConstant()) + continue; + if (!Global.hasInitializer()) + continue; + + Constant *CV = dyn_cast(Global.getInitializer()); + if (!CV) + continue; + ConstantArray *CA = dyn_cast(CV); + if (!CA) + continue; + if (!isAllFunctionInCA(CA)) + continue; + + Global.setName("BPF." + Global.getName()); + if (Global.getLinkage() == GlobalValue::PrivateLinkage) + Global.setLinkage(GlobalValue::LinkageTypes::InternalLinkage); + Global.setSection(".calltables"); + Changed = true; + } + + return Changed; +} + bool BPFCheckAndAdjustIR::adjustIR(Module &M) { bool Changed = removePassThroughBuiltin(M); Changed = removeCompareBuiltin(M) || Changed; Changed = sinkMinMax(M) || Changed; Changed = removeGEPBuiltins(M) || Changed; Changed = insertASpaceCasts(M) || Changed; + Changed = renameCallTableGlobal(M) || Changed; return Changed; }