diff mbox series

[COMMITTED,2/4] bpf: do not save/restore callee-saved registers in function prolog/epilog

Message ID 20200812150652.24541-3-jose.marchesi@oracle.com
State New
Headers show
Series bpf: backports to releases/gcc-10 | expand

Commit Message

Jose E. Marchesi Aug. 12, 2020, 3:06 p.m. UTC
BPF considers that every call to a function allocates a fresh set of
registers that are available to the callee, of which the first five
may have bee initialized with the function arguments.  This is
implemented by both interpreter and JIT in the Linux kernel.

This is enforced by the kernel BPF verifier, which will reject any
code in which non-initialized registers are accessed before being
written.  Consequently, the spill instructions generated in function
prologue were causing the verifier to reject our compiled programs.

This patch makes GCC to not save/restore callee-saved registers in
function prologue/epilogue, unless xBPF mode is enabled.

(cherry pick of commit 98456a64b0b5c20eeb8f964c7718072ba9b0e568)

2020-05-19  Jose E. Marchesi  <jose.marchesi@oracle.com>

gcc/
	* config/bpf/bpf.c (bpf_compute_frame_layout): Include space for
	callee saved registers only in xBPF.
	(bpf_expand_prologue): Save callee saved registers only in xBPF.
	(bpf_expand_epilogue): Likewise for restoring.
	* doc/invoke.texi (eBPF Options): Document this is activated by
	-mxbpf.

gcc/testsuite/
	* gcc.target/bpf/xbpf-callee-saved-regs-1.c: New test.
	* gcc.target/bpf/xbpf-callee-saved-regs-2.c: Likewise.
---
 gcc/config/bpf/bpf.c                          | 133 ++++++++++--------
 gcc/doc/invoke.texi                           |   6 +-
 .../gcc.target/bpf/xbpf-callee-saved-regs-1.c |  17 +++
 .../gcc.target/bpf/xbpf-callee-saved-regs-2.c |  17 +++
 4 files changed, 115 insertions(+), 58 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c
diff mbox series

Patch

diff --git a/gcc/config/bpf/bpf.c b/gcc/config/bpf/bpf.c
index 368b99c199e..36e08338630 100644
--- a/gcc/config/bpf/bpf.c
+++ b/gcc/config/bpf/bpf.c
@@ -267,15 +267,18 @@  bpf_compute_frame_layout (void)
 
   cfun->machine->local_vars_size += padding_locals;
 
-  /* 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;
+  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.  */
@@ -299,38 +302,50 @@  bpf_compute_frame_layout (void)
 void
 bpf_expand_prologue (void)
 {
-  int regno, fp_offset;
   rtx insn;
   HOST_WIDE_INT size;
 
   size = (cfun->machine->local_vars_size
 	  + cfun->machine->callee_saved_reg_size);
-  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++)
+  /* 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
+     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)
     {
-      if ((df_regs_ever_live_p (regno)
-	   && !call_used_or_fixed_reg_p (regno))
-	  || (cfun->calls_alloca
-	      && regno == STACK_POINTER_REGNUM))
-	{
-	  rtx mem;
+      int regno;
+      int fp_offset = -cfun->machine->local_vars_size;
 
-	  if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
-	    /* This has been already reported as an error in
-	       bpf_compute_frame_layout. */
-	    break;
-	  else
+      /* 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))
 	    {
-	      mem = gen_frame_mem (DImode,
-				   plus_constant (DImode,
-						  hard_frame_pointer_rtx,
-						  fp_offset - 8));
-	      insn = emit_move_insn (mem, gen_rtx_REG (DImode, regno));
-	      RTX_FRAME_RELATED_P (insn) = 1;
-	      fp_offset -= 8;
+	      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));
+		  insn = emit_move_insn (mem, gen_rtx_REG (DImode, regno));
+		  RTX_FRAME_RELATED_P (insn) = 1;
+		  fp_offset -= 8;
+		}
 	    }
 	}
     }
@@ -362,34 +377,38 @@  bpf_expand_prologue (void)
 void
 bpf_expand_epilogue (void)
 {
-  int regno, fp_offset;
-  rtx insn;
-
-  fp_offset = -cfun->machine->local_vars_size;
-
-  /* Restore callee-saved hard registes from the stack.  */
-  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+  /* See note in bpf_expand_prologue for an explanation on why we are
+     not restoring callee-saved registers in BPF.  */
+  if (TARGET_XBPF)
     {
-      if ((df_regs_ever_live_p (regno)
-	   && !call_used_or_fixed_reg_p (regno))
-	  || (cfun->calls_alloca
-	      && regno == STACK_POINTER_REGNUM))
-	{
-	  rtx mem;
+      rtx insn;
+      int regno;
+      int fp_offset = -cfun->machine->local_vars_size;
 
-	  if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
-	    /* This has been already reported as an error in
-	       bpf_compute_frame_layout. */
-	    break;
-	  else
+      /* 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))
 	    {
-	      mem = gen_frame_mem (DImode,
-				   plus_constant (DImode,
-						  hard_frame_pointer_rtx,
-						  fp_offset - 8));
-	      insn = emit_move_insn (gen_rtx_REG (DImode, regno), mem);
-	      RTX_FRAME_RELATED_P (insn) = 1;
-	      fp_offset -= 8;
+	      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));
+		  insn = emit_move_insn (gen_rtx_REG (DImode, regno), mem);
+		  RTX_FRAME_RELATED_P (insn) = 1;
+		  fp_offset -= 8;
+		}
 	    }
 	}
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3cbf7640bbf..977f9f5125f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -21048,7 +21048,11 @@  Generate code for a little-endian target.  This is the default.
 
 @item -mxbpf
 Generate code for an expanded version of BPF, which relaxes some of
-the restrictions imposed by the BPF architecture.
+the restrictions imposed by the BPF architecture:
+@itemize @minus
+@item Save and restore callee-saved registers at function entry and
+exit, respectively.
+@end itemize
 @end table
 
 @node FR30 Options
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
new file mode 100644
index 00000000000..6d6fe6e8e1b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-1.c
@@ -0,0 +1,17 @@ 
+/* { 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
new file mode 100644
index 00000000000..dec71cfe65d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/xbpf-callee-saved-regs-2.c
@@ -0,0 +1,17 @@ 
+/* { 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\\\]" } } */