We were reserving one of the hard registers in BPF in order to
implement dynamic stack allocation: alloca and VLAs. However, there is
kernel code that has inline assembly that requires all the non-fixed
registers to be available for register allocation.

This patch:

1. Liberates r9 that is now available for register allocation.

2. Adds a check to GCC so it errors out if the user tries to do
   dynamic stack allocation.  A couple of tests are added for this.

3. Changes xbpf so it no longer saves and restores callee-saved
   registers.  A couple of tests for this have been removed.

4. Adds bpf-*-* to the list of targets that do not support alloca in
   target-support.exp.

Tested in host x86_64-linux-gnu and target bpf-unknown-none.

gcc/ChangeLog

        * config/bpf/bpf.md (allocate_stack): Define.
        * config/bpf/bpf.h (FIRST_PSEUDO_REGISTER): Make room for fake
        stack pointer register.
        (FIXED_REGISTERS): Adjust accordingly.
        (CALL_USED_REGISTERS): Likewise.
        (REG_CLASS_CONTENTS): Likewise.
        (REGISTER_NAMES): Likewise.
        * config/bpf/bpf.cc (bpf_compute_frame_layout): Do not reserve
        space for callee-saved registers.
        (bpf_expand_prologue): Do not save callee-saved registers in xbpf.
        (bpf_expand_epilogue): Do not restore callee-saved registers in
        xbpf.

gcc/testsuite/ChangeLog

        * lib/target-supports.exp (check_effective_target_alloca): BPF
        target does not support alloca.
        * gcc.target/bpf/diag-alloca-1.c: New test.
        * gcc.target/bpf/diag-alloca-2.c: Likewise.
        * gcc.target/bpf/xbpf-callee-saved-regs-1.c: Remove test.
        * gcc.target/bpf/xbpf-callee-saved-regs-2.c: Likewise.
        * gcc.target/bpf/regs-availability-1.c: Likewise.
---
 gcc/config/bpf/bpf.cc                         | 128 ++----------------
 gcc/config/bpf/bpf.h                          |  23 ++--
 gcc/config/bpf/bpf.md                         |  13 ++
 gcc/testsuite/gcc.target/bpf/diag-alloca-1.c  |   9 ++
 gcc/testsuite/gcc.target/bpf/diag-alloca-2.c  |   9 ++
 .../gcc.target/bpf/regs-availability-1.c      |  21 +++
 .../gcc.target/bpf/xbpf-callee-saved-regs-1.c |  17 ---
 .../gcc.target/bpf/xbpf-callee-saved-regs-2.c |  17 ---
 gcc/testsuite/lib/target-supports.exp         |   3 +
 9 files changed, 82 insertions(+), 158 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/bpf/diag-alloca-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/diag-alloca-2.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/regs-availability-1.c
 delete mode 100644 gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c
 delete mode 100644 gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c

diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index d27a971d0af..3516b79bce4 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -76,10 +76,6 @@ struct GTY(()) machine_function
 {
   /* Number of bytes saved on the stack for local variables.  */
   int local_vars_size;
-
-  /* Number of bytes saved on the stack for callee-saved
-     registers.  */
-  int callee_saved_reg_size;
 };
 
 /* Handle an attribute requiring a FUNCTION_DECL;
@@ -346,7 +342,7 @@ static void
 bpf_compute_frame_layout (void)
 {
   int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
-  int padding_locals, regno;
+  int padding_locals;
 
   /* Set the space used in the stack by local variables.  This is
      rounded up to respect the minimum stack alignment.  */
@@ -358,23 +354,9 @@ bpf_compute_frame_layout (void)
 
   cfun->machine->local_vars_size += padding_locals;
 
-  if (TARGET_XBPF)
-    {
-      /* Set the space used in the stack by callee-saved used
-        registers in the current function.  There is no need to round
-        up, since the registers are all 8 bytes wide.  */
-      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-       if ((df_regs_ever_live_p (regno)
-            && !call_used_or_fixed_reg_p (regno))
-           || (cfun->calls_alloca
-               && regno == STACK_POINTER_REGNUM))
-         cfun->machine->callee_saved_reg_size += 8;
-    }
-
   /* Check that the total size of the frame doesn't exceed the limit
      imposed by eBPF.  */
-  if ((cfun->machine->local_vars_size
-       + cfun->machine->callee_saved_reg_size) > bpf_frame_limit)
+  if (cfun->machine->local_vars_size > bpf_frame_limit)
     {
       static int stack_limit_exceeded = 0;
 
@@ -393,69 +375,19 @@ bpf_compute_frame_layout (void)
 void
 bpf_expand_prologue (void)
 {
-  HOST_WIDE_INT size;
-
-  size = (cfun->machine->local_vars_size
-         + cfun->machine->callee_saved_reg_size);
-
   /* The BPF "hardware" provides a fresh new set of registers for each
      called function, some of which are initialized to the values of
      the arguments passed in the first five registers.  In doing so,
-     it saves the values of the registers of the caller, and restored
+     it saves the values of the registers of the caller, and restores
      them upon returning.  Therefore, there is no need to save the
-     callee-saved registers here.  What is worse, the kernel
-     implementation refuses to run programs in which registers are
-     referred before being initialized.  */
-  if (TARGET_XBPF)
-    {
-      int regno;
-      int fp_offset = -cfun->machine->local_vars_size;
-
-      /* Save callee-saved hard registes.  The register-save-area
-        starts right after the local variables.  */
-      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-       {
-         if ((df_regs_ever_live_p (regno)
-              && !call_used_or_fixed_reg_p (regno))
-             || (cfun->calls_alloca
-                 && regno == STACK_POINTER_REGNUM))
-           {
-             rtx mem;
-
-             if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
-               /* This has been already reported as an error in
-                  bpf_compute_frame_layout. */
-               break;
-             else
-               {
-                 mem = gen_frame_mem (DImode,
-                                      plus_constant (DImode,
-                                                     hard_frame_pointer_rtx,
-                                                     fp_offset - 8));
-                 emit_move_insn (mem, gen_rtx_REG (DImode, regno));
-                 fp_offset -= 8;
-               }
-           }
-       }
-    }
-
-  /* Set the stack pointer, if the function allocates space
-     dynamically.  Note that the value of %sp should be directly
-     derived from %fp, for the kernel verifier to track it as a stack
-     accessor.  */
-  if (cfun->calls_alloca)
-    {
-      emit_move_insn (stack_pointer_rtx,
-                      hard_frame_pointer_rtx);
-
-      if (size > 0)
-       {
-         emit_insn (gen_rtx_SET (stack_pointer_rtx,
-                                  gen_rtx_PLUS (Pmode,
-                                                stack_pointer_rtx,
-                                                GEN_INT (-size))));
-       }
-    }
+     callee-saved registers here.  In fact, the kernel implementation
+     refuses to run programs in which registers are referred before
+     being initialized.  */
+
+  /* BPF does not support functions that allocate stack space
+     dynamically.  This should have been checked already and an error
+     emitted.  */
+  gcc_assert (!cfun->calls_alloca);
 }
 
 /* Expand to the instructions in a function epilogue.  This function
@@ -466,37 +398,6 @@ bpf_expand_epilogue (void)
 {
   /* See note in bpf_expand_prologue for an explanation on why we are
      not restoring callee-saved registers in BPF.  */
-  if (TARGET_XBPF)
-    {
-      int regno;
-      int fp_offset = -cfun->machine->local_vars_size;
-
-      /* Restore callee-saved hard registes from the stack.  */
-      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-       {
-         if ((df_regs_ever_live_p (regno)
-              && !call_used_or_fixed_reg_p (regno))
-             || (cfun->calls_alloca
-                 && regno == STACK_POINTER_REGNUM))
-           {
-             rtx mem;
-
-             if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
-               /* This has been already reported as an error in
-                  bpf_compute_frame_layout. */
-               break;
-             else
-               {
-                 mem = gen_frame_mem (DImode,
-                                      plus_constant (DImode,
-                                                     hard_frame_pointer_rtx,
-                                                     fp_offset - 8));
-                 emit_move_insn (gen_rtx_REG (DImode, regno), mem);
-                 fp_offset -= 8;
-               }
-           }
-       }
-    }
 
   emit_jump_insn (gen_exit ());
 }
@@ -543,11 +444,10 @@ bpf_initial_elimination_offset (int from, int to)
 {
   HOST_WIDE_INT ret;
 
-  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
-    ret = (cfun->machine->local_vars_size
-          + cfun->machine->callee_saved_reg_size);
-  else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+  if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
     ret = 0;
+  else if (from == STACK_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+    ret = -(cfun->machine->local_vars_size);
   else
     gcc_unreachable ();
 
diff --git a/gcc/config/bpf/bpf.h b/gcc/config/bpf/bpf.h
index ccba7f8b333..82702aa7b6b 100644
--- a/gcc/config/bpf/bpf.h
+++ b/gcc/config/bpf/bpf.h
@@ -153,24 +153,27 @@
 #define BPF_R7 7
 #define BPF_R8 8
 #define BPF_R9 9
-#define BPF_SP BPF_R9
 #define BPF_R10        10
 #define BPF_FP  BPF_R10
+#define BPF_R11 11
+#define BPF_R12 12
+#define BPF_SP  BPF_R12
+
 /* 11 is not a real eBPF hard register and is eliminated or not used
    in the final assembler.  See below.  */
 
-#define FIRST_PSEUDO_REGISTER 12
+#define FIRST_PSEUDO_REGISTER 13
 
 /* The registers %r0..%r8 are available for general allocation.
-   %r9 is the pseudo-stack pointer.
    %r10 is the stack frame, which is read-only.
-   %r11 (__arg__) is a fake register that always gets eliminated.  */
+   %r11 (__arg__) is a fake register that always gets eliminated.
+   %r12 is the pseudo-stack pointer that always gets eliminated.  */
 #define FIXED_REGISTERS                                \
-  {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
+  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
 
 /* %r0..%r5 are clobbered by function calls.  */
 #define CALL_USED_REGISTERS                            \
-  {1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1}
+  {1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1}
 
 /**** Register Classes.  */
 
@@ -206,7 +209,7 @@ enum reg_class
 {                                              \
    0x00000000, /* NO_REGS */                   \
    0x00000001, /* R0 */                         \
-   0x00000fff, /* ALL_REGS */                  \
+   0x00001fff, /* ALL_REGS */                  \
 }
 
 /* A C expression whose value is a register class containing hard
@@ -260,15 +263,15 @@ enum reg_class
 /*** Registers That Address the Stack Frame.  */
 
 #define FRAME_POINTER_REGNUM 10
-#define STACK_POINTER_REGNUM 9
 #define ARG_POINTER_REGNUM 11
+#define STACK_POINTER_REGNUM 12
 #define STATIC_CHAIN_REGNUM 8
 
 /*** Registers elimination.  */
 
 #define ELIMINABLE_REGS                                        \
   {{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM },       \
-   { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }}
+   { STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM }}
 
 /* Define the offset between two registers, one to be eliminated, and
    the other its replacement, at the start of a routine.  */
@@ -444,7 +447,7 @@ enum reg_class
 
 #define REGISTER_NAMES                                         \
   { "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",    \
-    "%r8", "%r9", "%fp", "__arg__" }
+      "%r8", "%r9", "%fp", "__arg__", "__sp__" }
 
 #define ADDITIONAL_REGISTER_NAMES              \
   { { "%a", 0 }, { "%ctx", 6 }, { "%r10" , 10 } }
diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md
index e9c00e445af..e0a42b9f939 100644
--- a/gcc/config/bpf/bpf.md
+++ b/gcc/config/bpf/bpf.md
@@ -115,6 +115,19 @@ (define_insn "nop"
   "{ja\t0|goto 0}"
   [(set_attr "type" "alu")])
 
+;;;; Stack usage
+
+(define_expand "allocate_stack"
+  [(match_operand:DI 0 "general_operand" "")
+   (match_operand:DI 1 "general_operand" "")]
+  ""
+  "
+{
+  error (\"BPF does not support dynamic stack allocation\");
+  emit_insn (gen_nop ());
+  DONE;
+}")
+
 ;;;; Arithmetic/Logical
 
 ;; The arithmetic and logic operations below are defined for SI and DI
diff --git a/gcc/testsuite/gcc.target/bpf/diag-alloca-1.c 
b/gcc/testsuite/gcc.target/bpf/diag-alloca-1.c
new file mode 100644
index 00000000000..0406f2c3595
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/diag-alloca-1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int
+foo (int x)
+{
+  int *p = __builtin_alloca (x); /* { dg-error "support" } */
+
+  return p[2];
+}
diff --git a/gcc/testsuite/gcc.target/bpf/diag-alloca-2.c 
b/gcc/testsuite/gcc.target/bpf/diag-alloca-2.c
new file mode 100644
index 00000000000..ef7170b2c3d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/diag-alloca-2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-std=gnu89" } */
+
+int
+foo (int x)
+{
+  int arr[x]; /* { dg-error "support" } */
+  return arr[3];
+}
diff --git a/gcc/testsuite/gcc.target/bpf/regs-availability-1.c 
b/gcc/testsuite/gcc.target/bpf/regs-availability-1.c
new file mode 100644
index 00000000000..dda3410a128
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/regs-availability-1.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+/* The purpose of this test is to make sure that registers r7, r8 and r9 are
+   available for register allocation.  r9 used to be reserved as a stack
+   pointer but not any longer.  */
+
+int bar ()
+{
+  int a, b, c = 10;
+
+  asm volatile ("#lala %[a] %[c]"
+               :
+               : [a]"r"(&a),
+                 [b]"r"(&b),
+                 [c]"r"(c)
+               : "r0", "r1", "r2", "r3", "r4", "r5", "memory", "r6"
+               );
+
+  return a + b + c;
+}
diff --git a/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c 
b/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c
deleted file mode 100644
index 6d6fe6e8e1b..00000000000
--- a/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-mxbpf" } */
-
-/* GCC should save and restore callee-saved registers when generating
-   code for xBPF.  */
-
-int
-foo ()
-{
-  register int f asm ("r6");
-
-  f = 20;
-  return f + 1;
-}
-
-/* { dg-final { scan-assembler "stxdw\t\\\[%fp\\+-8\\\],%r6" } } */
-/* { dg-final { scan-assembler "ldxdw\t%r6,\\\[%fp\\+-8\\\]" } } */
diff --git a/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c 
b/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c
deleted file mode 100644
index dec71cfe65d..00000000000
--- a/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c
+++ /dev/null
@@ -1,17 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-mno-xbpf" } */
-
-/* GCC should not save and restore callee-saved registers unless
-   generating code for xBPF.  */
-
-int
-foo ()
-{
-  register int f asm ("r6");
-
-  f = 20;
-  return f + 1;
-}
-
-/* { dg-final { scan-assembler-not "stxdw\t\\\[%fp\\+-8\\\],%r6" } } */
-/* { dg-final { scan-assembler-not "ldxdw\t%r6,\\\[%fp\\+-8\\\]" } } */
diff --git a/gcc/testsuite/lib/target-supports.exp 
b/gcc/testsuite/lib/target-supports.exp
index eda53ff3a09..92b6f69730e 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -975,6 +975,9 @@ proc check_effective_target_untyped_assembly {} {
 # Return 1 if alloca is supported, 0 otherwise.
 
 proc check_effective_target_alloca {} {
+    if { [istarget bpf-*-*] } {
+      return 0
+    }
     if { [istarget nvptx-*-*] } {
        return [check_no_compiler_messages alloca assembly {
            void f (void*);
-- 
2.30.2

Reply via email to