https://gcc.gnu.org/g:a87863edc04379e5d2ce67c4dc97bb0f4c046d59

commit r17-5014-ga87863edc04379e5d2ce67c4dc97bb0f4c046d59
Author: Vineet Gupta <[email protected]>
Date:   Fri Oct 9 14:15:11 2026 +0530

    bpf: report indirect calls at the call, not at the end of the function
    
    This showed up when testing the callx Pseudo-C fix.
    
    Indirect calls (out of a register) are diagnosed with -mno-callx, and
    the diagnostic output was confusing
    
      callx.c: In function 'prog':
      callx.c:12:1: error: indirect call in function, ...
         12 | }
      callx.c:12:1: error: indirect call in function, ...
    
     1. Error comes out at the closing brace of the function.
        Reason: diagnosed at the time of instruction output, when the
        location has been lost. Instead needs to be done at the time of
        expansion.
    
     2. The msg is printed twice.
        Reason: A call duplicated by a later RTL pass is reported once per
        copy rather than once per call.
    
    Fix by checking the call target in the expansion of the 'call' and
    'call_value' patterns instead, where the call still has a location,
    and return a call to address zero so the expansion can proceed.
    
    The above becomes:
    
      callx.c:11:16: error: indirect call in function, ...
         11 |         return f(3, 4);
            |                ^~~~~~~
    
    The canonical pattern of gating the call patterns themselves on
    bpf_has_callx is not an option: expanding a call cannot fail, so the call
    would be left unrecognized, turning the user error into an ICE.
    
    The test in bpf_output_call is kept as a safety net, for an indirect
    call reaching the output stage anyway.
    
    The error message no longer claims eBPF does not support indirect
    calls, and names -mno-callx instead.
    
    gcc/ChangeLog:
    
            * config/bpf/bpf-protos.h (bpf_check_call_target): New prototype.
            * config/bpf/bpf.cc (bpf_check_call_target): New function.
            (bpf_output_call): Update comment and error message.
            * config/bpf/bpf.md (call): Check the call target with
            bpf_check_call_target.
            (call_value): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/bpf/diag-indcalls.c: Expect the error at the call
            instead of at the end of the function.
            * gcc.target/bpf/diag-indcalls-2.c: New test.
    
    Signed-off-by: Vineet Gupta <[email protected]>

Diff:
---
 gcc/config/bpf/bpf-protos.h                    |  1 +
 gcc/config/bpf/bpf.cc                          | 32 ++++++++++++++++++++++++--
 gcc/config/bpf/bpf.md                          |  4 ++--
 gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c | 13 +++++++++++
 gcc/testsuite/gcc.target/bpf/diag-indcalls.c   |  4 ++--
 5 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h
index 1113cc3e6d17..eee260b799bf 100644
--- a/gcc/config/bpf/bpf-protos.h
+++ b/gcc/config/bpf/bpf-protos.h
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 /* Routines implemented in bpf.cc.  */
 
 extern HOST_WIDE_INT bpf_initial_elimination_offset (int, int);
+extern rtx bpf_check_call_target (rtx);
 extern const char *bpf_output_call (const char *templ, rtx *, int 
target_index);
 extern void bpf_target_macros (cpp_reader *);
 extern void bpf_print_operand (FILE *, rtx, int);
diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index d63341022f4d..d852e2fbd70d 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -758,11 +758,39 @@ bpf_output_destructor (rtx symbol, int priority 
ATTRIBUTE_UNUSED)
 #undef TARGET_ASM_DESTRUCTOR
 #define TARGET_ASM_DESTRUCTOR bpf_output_destructor
 
+/* Check the TARGET of a call, an RTX denoting the address of the called
+   function, and return the target to actually call.
+
+   Calling through anything but a constant address requires an indirect
+   CALL instruction, which is not available with -mno-callx.  Diagnose
+   that here, at expansion time, so the error is reported at the call
+   itself and only once per call; by the time the instruction is output
+   the location of the call is no longer available.
+
+   This function is called from the expansion of the 'call' and
+   'call_value' patterns in bpf.md.  */
+
+rtx
+bpf_check_call_target (rtx target)
+{
+  if (bpf_has_callx
+      || GET_CODE (target) == CONST_INT
+      || GET_CODE (target) == SYMBOL_REF)
+    return target;
+
+  error_at (curr_insn_location (),
+           "indirect call in function, not supported with %<-mno-callx%>");
+
+  /* Call address zero instead, so the expansion can continue.  */
+  return const0_rtx;
+}
+
 /* Return the appropriate instruction to CALL to a function.  TARGET
    is an RTX denoting the address of the called function.
 
    The main purposes of this function are:
-   - To reject indirect CALL instructions when disabled by -mno-callx.
+   - To reject indirect CALL instructions that reached this point
+     despite bpf_check_call_target, which is not expected to happen.
    - To recognize calls to kernel helper functions and emit the
      corresponding CALL N instruction.
 
@@ -796,7 +824,7 @@ bpf_output_call (const char *templ, rtx *operands, int 
target_index)
     default:
       if (!bpf_has_callx)
        {
-         error ("indirect call in function, which are not supported by eBPF");
+         error ("indirect call in function, not supported with 
%<-mno-callx%>");
          operands[target_index] = GEN_INT (0);
        }
       break;
diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md
index 15d0f95cf0d0..a34e685df127 100644
--- a/gcc/config/bpf/bpf.md
+++ b/gcc/config/bpf/bpf.md
@@ -534,7 +534,7 @@
              (use (match_operand 3 ""))])]     ;; struct_value_size_rtx
   ""
 {
-  rtx target = XEXP (operands[0], 0);
+  rtx target = bpf_check_call_target (XEXP (operands[0], 0));
   emit_call_insn (gen_call_internal (target, operands[1]));
   DONE;
 })
@@ -561,7 +561,7 @@
              (use (match_operand 3 ""))])]             ;; next_arg_reg
   ""
 {
-  rtx target = XEXP (operands[1], 0);
+  rtx target = bpf_check_call_target (XEXP (operands[1], 0));
   emit_call_insn (gen_call_value_internal (operands[0], target,
                                            operands[2]));
   DONE;
diff --git a/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c 
b/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c
new file mode 100644
index 000000000000..f0fab6c430ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/diag-indcalls-2.c
@@ -0,0 +1,13 @@
+/* Verify indirect calls are diagnosed at the call itself, and once per
+   call, also when optimizing.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-callx" } */
+
+int (*fnp) (int, int);
+
+int
+foo (void (*arg) (void))
+{
+  arg (); /* { dg-error "indirect call in function" } */
+  return (*fnp) (1, 2); /* { dg-error "indirect call in function" } */
+}
diff --git a/gcc/testsuite/gcc.target/bpf/diag-indcalls.c 
b/gcc/testsuite/gcc.target/bpf/diag-indcalls.c
index e83b03fd9987..c0fe4a8eac69 100644
--- a/gcc/testsuite/gcc.target/bpf/diag-indcalls.c
+++ b/gcc/testsuite/gcc.target/bpf/diag-indcalls.c
@@ -7,5 +7,5 @@ void (*fnp) (void);
 void
 foo ()
 {
-  (*fnp) ();
-} /* { dg-error "indirect call in function" } */
+  (*fnp) (); /* { dg-error "indirect call in function" } */
+}

Reply via email to