From 98456a64b0b5c20eeb8f964c7718072ba9b0e568 Mon Sep 17 00:00:00 2001 From: "Jose E. Marchesi" Date: Tue, 19 May 2020 11:46:40 +0200 Subject: [PATCH] bpf: do not save/restore callee-saved registers in function prolog/epilog 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. 2020-05-19 Jose E. Marchesi 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/ChangeLog | 9 ++ gcc/config/bpf/bpf.c | 133 ++++++++++-------- gcc/doc/invoke.texi | 6 +- gcc/testsuite/ChangeLog | 5 + .../gcc.target/bpf/xbpf-callee-saved-regs-1.c | 17 +++ .../gcc.target/bpf/xbpf-callee-saved-regs-2.c | 17 +++ 6 files changed, 129 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 --git a/gcc/ChangeLog b/gcc/ChangeLog index ef3bcee3911..8447bd56d98 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2020-05-19 Jose E. Marchesi + + * 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. + 2020-05-19 Jose E. Marchesi * config/bpf/bpf.opt (mxbpf): New option. 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 0c33deb830b..7217e27151d 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -21016,7 +21016,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/ChangeLog b/gcc/testsuite/ChangeLog index cbafdfc7169..1fa18f2982a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-05-19 Jose E. Marchesi + + * gcc.target/bpf/xbpf-callee-saved-regs-1.c: New test. + * gcc.target/bpf/xbpf-callee-saved-regs-2.c: Likewise. + 2020-05-19 Uroš Bizjak PR target/92658 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\\\]" } } */ -- 2.30.2