[llvm] [BPF] Support jump-table style callx (PR #159798)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 19 08:43:23 PDT 2025


https://github.com/yonghong-song created https://github.com/llvm/llvm-project/pull/159798

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, at 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, at 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, at object  # @BPF.__const.result.ops
        .section        .calltables,"a", at 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, at object                 # @BPF.ops
        .section        .calltables,"aw", at 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, at object                 # @BPF.ops
        .section        .calltables,"a", at 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.

>From 4609b1d2675a3cc445029ef8aba33078ef428122 Mon Sep 17 00:00:00 2001
From: Yonghong Song <yonghong.song at linux.dev>
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, at 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, at 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, at object  # @BPF.__const.result.ops
        .section        .calltables,"a", at 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, at object                 # @BPF.ops
        .section        .calltables,"aw", at 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, at object                 # @BPF.ops
        .section        .calltables,"a", at 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 | 36 +++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
index b202b20291aff..9c54f4068d8ac 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,47 @@ bool BPFCheckAndAdjustIR::insertASpaceCasts(Module &M) {
   return Changed;
 }
 
+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<Constant>(Global.getInitializer());
+    if (!CV)
+      continue;
+    ConstantArray *CA = dyn_cast<ConstantArray>(CV);
+    if (!CA)
+      continue;
+
+    for (unsigned i = 1, e = CA->getNumOperands(); i != e; ++i) {
+      if (!dyn_cast<Function>(CA->getOperand(i)))
+        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;
 }
 



More information about the llvm-commits mailing list