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