https://gcc.gnu.org/g:9e15bb5fc2efd1514d597866af1ca39fe0d19de8
commit r17-5012-g9e15bb5fc2efd1514d597866af1ca39fe0d19de8 Author: Vineet Gupta <[email protected]> Date: Fri Oct 9 14:14:52 2026 +0530 bpf: spell xBPF indirect calls `callx' in the pseudo-C dialect An indirect call is spelled `callx rN' in the pseudo-C dialect however the call patterns emitted a single `call\t%N' template for both dialects, so a function pointer call built with -mxbpf could not be assembled because Pseudo-C is the default: $ bpf-unknown-none-gcc -mxbpf -O2 -c callx.c Error: unexpected register name `r0' in instruction `call r0' The only existing test for this, xbpf-indirect-call-1.c, passes -masm=normal and so did not catch it. A direct call is spelled `call' in both dialects, so the template cannot simply be turned into an alternation. Instead split the call target constraint, which already distinguishes a constant call address (S) from a register, into two alternatives and give each its own template. gcc/ChangeLog: * config/bpf/bpf.md (call_internal): Split the call target constraint into separate alternatives for a constant address and for a register, and use the pseudo-C spelling `callx' for the latter. (call_value_internal): Likewise. * config/bpf/bpf.cc (bpf_output_call): Update comment. gcc/testsuite/ChangeLog: * gcc.target/bpf/xbpf-indirect-call-2.c: New test. Signed-off-by: Vineet Gupta <[email protected]> Diff: --- gcc/config/bpf/bpf.cc | 2 +- gcc/config/bpf/bpf.md | 16 +++++++++++---- .../gcc.target/bpf/xbpf-indirect-call-2.c | 23 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc index 6bd714d50534..b7227f1c135c 100644 --- a/gcc/config/bpf/bpf.cc +++ b/gcc/config/bpf/bpf.cc @@ -760,7 +760,7 @@ bpf_output_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) The main purposes of this function are: - To reject indirect CALL instructions, which are not supported by - eBPF. + eBPF, unless targeting xBPF. - To recognize calls to kernel helper functions and emit the corresponding CALL N instruction. diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md index a2bceb8998d7..a577b494133f 100644 --- a/gcc/config/bpf/bpf.md +++ b/gcc/config/bpf/bpf.md @@ -539,13 +539,19 @@ DONE; }) +;; Emit a direct call for a constant address (S), or an indirect call +;; for a register (r), the latter only generated when targeting xBPF. +;; Indirect calls are spelled `callx rN' in the pseudo-C dialect but +;; `call %rN' in the normal one. (define_insn "call_internal" - [(call (mem:DI (match_operand:DI 0 "call_operand" "Sr")) + [(call (mem:DI (match_operand:DI 0 "call_operand" "S,r")) (match_operand:SI 1 "general_operand" ""))] ;; operands[2] is next_arg_register ;; operands[3] is struct_value_size_rtx. "" - { return bpf_output_call ("call\t%0", operands, 0); } + "@ + *return bpf_output_call (\"call\t%0\", operands, 0); + *return bpf_output_call (\"{call\t%0|callx\t%0}\", operands, 0);" [(set_attr "type" "jmp")]) (define_expand "call_value" @@ -563,12 +569,14 @@ (define_insn "call_value_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:DI (match_operand:DI 1 "call_operand" "Sr")) + (call (mem:DI (match_operand:DI 1 "call_operand" "S,r")) (match_operand:SI 2 "general_operand" "")))] ;; operands[3] is next_arg_register ;; operands[4] is struct_value_size_rtx. "" - { return bpf_output_call ("call\t%1", operands, 1); } + "@ + *return bpf_output_call (\"call\t%1\", operands, 1); + *return bpf_output_call (\"{call\t%1|callx\t%1}\", operands, 1);" [(set_attr "type" "jmp")]) (define_insn "sibcall" diff --git a/gcc/testsuite/gcc.target/bpf/xbpf-indirect-call-2.c b/gcc/testsuite/gcc.target/bpf/xbpf-indirect-call-2.c new file mode 100644 index 000000000000..8e64eca4aa6f --- /dev/null +++ b/gcc/testsuite/gcc.target/bpf/xbpf-indirect-call-2.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-mxbpf -masm=pseudoc" } */ + +/* GCC should generate an indirect call instruction (callx REG) when + targeting xBPF in the pseudo-C assembly dialect. The normal dialect + spelling (call %REG) is not accepted by the assembler in pseudo-C. */ + +void +foo () +{ + ; +} + +void +bar() +{ + void (*funp) () = &foo; + + (*funp) (); +} + +/* { dg-final { scan-assembler "callx\tr" } } */ +/* { dg-final { scan-assembler-not "call\tr" } } */
