Commit e18a6d14 by Andrew Burgess Committed by Andrew Burgess

gcc/riscv: Add a mechanism to remove some calls to _riscv_save_0

When using the -msave-restore flag we end up with calls to
_riscv_save_0 and _riscv_restore_0.  These functions adjust the stack
and save or restore the return address.  Due to grouping multiple
save/restore stub functions together the save/restore 0 calls actually
save s0, s1, s2, and the return address, but only the return address
actually matters.  Leaf functions don't call the save/restore stubs,
so whenever we do see a call to the save/restore stubs, the store of
the return address is required.

If we look in gcc/config/riscv/riscv.c at the function
riscv_expand_prologue and riscv_expand_epilogue we can see that it
would be reasonably easy to adjust these functions to avoid the calls
to the save/restore stubs for those cases where we are about to call
_riscv_save_0 and _riscv_restore_0, however, the actual code size
saving this would give is debatable, with linker relaxation, the calls
to save/restore are often just 4-bytes, and can sometimes even be
2-bytes, while leaving the stack adjust and return address save inline
is always going to be 4-bytes.

The interesting case is when we call _riscv_save_0 and
_riscv_restore_0, and also have a frame that would (without
save/restore) have resulted in a tail call.  In this case if we could
remove the save/restore calls, and restore the tail call then we would
get a real size saving.

The problem is that the choice of generating a tail call or not is
done during the gimple expand pass, at which point we don't know how
many registers we need to save (or restore).

The solution presented in this patch offers a partial solution to this
problem.  By using the TARGET_MACHINE_DEPENDENT_REORG pass to
implement a very limited pattern matching we identify functions that
call _riscv_save_0 and _riscv_restore_0, and which could be converted
to make use of a tail call.  These functions are then converted to the
non save/restore tail call form.

This should result in a code size reduction when compiling with -Os
and with the -msave-restore flag.

gcc/ChangeLog:

        * config.gcc: Add riscv-sr.o to extra_objs for riscv.
        * config/riscv/riscv-sr.c: New file.
        * config/riscv/riscv.c (riscv_reorg): New function.
        (TARGET_MACHINE_DEPENDENT_REORG): Define.
        * config/riscv/riscv.h (SIBCALL_REG_P): Define.
        (riscv_remove_unneeded_save_restore_calls): Declare.
        * config/riscv/t-riscv (riscv-sr.o): New build rule.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/save-restore-2.c: New file.
        * gcc.target/riscv/save-restore-3.c: New file.
        * gcc.target/riscv/save-restore-4.c: New file.
        * gcc.target/riscv/save-restore-5.c: New file.
        * gcc.target/riscv/save-restore-6.c: New file.
        * gcc.target/riscv/save-restore-7.c: New file.
        * gcc.target/riscv/save-restore-8.c: New file.

From-SVN: r277527
parent 4b0ab0d9
2019-10-28 Andrew Burgess <andrew.burgess@embecosm.com>
* config.gcc: Add riscv-sr.o to extra_objs for riscv.
* config/riscv/riscv-sr.c: New file.
* config/riscv/riscv.c (riscv_reorg): New function.
(TARGET_MACHINE_DEPENDENT_REORG): Define.
* config/riscv/riscv.h (SIBCALL_REG_P): Define.
(riscv_remove_unneeded_save_restore_calls): Declare.
* config/riscv/t-riscv (riscv-sr.o): New build rule.
2019-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org> 2019-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
PR tree-optimization/92163 PR tree-optimization/92163
...@@ -523,7 +523,7 @@ pru-*-*) ...@@ -523,7 +523,7 @@ pru-*-*)
;; ;;
riscv*) riscv*)
cpu_type=riscv cpu_type=riscv
extra_objs="riscv-builtins.o riscv-c.o" extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o"
d_target_objs="riscv-d.o" d_target_objs="riscv-d.o"
;; ;;
rs6000*-*-*) rs6000*-*-*)
......
...@@ -5007,6 +5007,16 @@ riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, ...@@ -5007,6 +5007,16 @@ riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
return mode; return mode;
} }
/* Implement TARGET_MACHINE_DEPENDENT_REORG. */
static void
riscv_reorg (void)
{
/* Do nothing unless we have -msave-restore */
if (TARGET_SAVE_RESTORE)
riscv_remove_unneeded_save_restore_calls ();
}
/* Initialize the GCC target structure. */ /* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP #undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t" #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
...@@ -5181,6 +5191,9 @@ riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED, ...@@ -5181,6 +5191,9 @@ riscv_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS #undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS
#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 #define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG riscv_reorg
struct gcc_target targetm = TARGET_INITIALIZER; struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-riscv.h" #include "gt-riscv.h"
...@@ -289,6 +289,10 @@ along with GCC; see the file COPYING3. If not see ...@@ -289,6 +289,10 @@ along with GCC; see the file COPYING3. If not see
#define FP_REG_P(REGNO) \ #define FP_REG_P(REGNO) \
((unsigned int) ((int) (REGNO) - FP_REG_FIRST) < FP_REG_NUM) ((unsigned int) ((int) (REGNO) - FP_REG_FIRST) < FP_REG_NUM)
/* True when REGNO is in SIBCALL_REGS set. */
#define SIBCALL_REG_P(REGNO) \
TEST_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], REGNO)
#define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X))) #define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
/* Use s0 as the frame pointer if it is so requested. */ /* Use s0 as the frame pointer if it is so requested. */
...@@ -918,4 +922,8 @@ extern unsigned riscv_stack_boundary; ...@@ -918,4 +922,8 @@ extern unsigned riscv_stack_boundary;
#define SWSP_REACH (4LL << C_SxSP_BITS) #define SWSP_REACH (4LL << C_SxSP_BITS)
#define SDSP_REACH (8LL << C_SxSP_BITS) #define SDSP_REACH (8LL << C_SxSP_BITS)
/* Called from RISCV_REORG, this is defined in riscv-sr.c. */
extern void riscv_remove_unneeded_save_restore_calls (void);
#endif /* ! GCC_RISCV_H */ #endif /* ! GCC_RISCV_H */
...@@ -5,6 +5,11 @@ riscv-builtins.o: $(srcdir)/config/riscv/riscv-builtins.c $(CONFIG_H) \ ...@@ -5,6 +5,11 @@ riscv-builtins.o: $(srcdir)/config/riscv/riscv-builtins.c $(CONFIG_H) \
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
$(srcdir)/config/riscv/riscv-builtins.c $(srcdir)/config/riscv/riscv-builtins.c
riscv-sr.o: $(srcdir)/config/riscv/riscv-sr.c $(CONFIG_H) \
$(SYSTEM_H)
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
$(srcdir)/config/riscv/riscv-sr.c
riscv-c.o: $(srcdir)/config/riscv/riscv-c.c $(CONFIG_H) $(SYSTEM_H) \ riscv-c.o: $(srcdir)/config/riscv/riscv-c.c $(CONFIG_H) $(SYSTEM_H) \
coretypes.h $(TM_H) $(TREE_H) output.h $(C_COMMON_H) $(TARGET_H) coretypes.h $(TM_H) $(TREE_H) output.h $(C_COMMON_H) $(TARGET_H)
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
......
2019-10-28 Andrew Burgess <andrew.burgess@embecosm.com>
* gcc.target/riscv/save-restore-2.c: New file.
* gcc.target/riscv/save-restore-3.c: New file.
* gcc.target/riscv/save-restore-4.c: New file.
* gcc.target/riscv/save-restore-5.c: New file.
* gcc.target/riscv/save-restore-6.c: New file.
* gcc.target/riscv/save-restore-7.c: New file.
* gcc.target/riscv/save-restore-8.c: New file.
2019-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org> 2019-10-28 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
PR tree-optimization/92163 PR tree-optimization/92163
......
/* { dg-options "-Os -msave-restore" } */
/* With -msave-restore in use it should not be possible to remove the calls
to the save and restore stubs in this case (in current GCC). */
extern void fn2 ();
volatile int a = 0;
int
fn1 ()
{
fn2 ();
while (a)
;
return 0;
}
/* { dg-final { scan-assembler "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler "tail\[ \t\]*__riscv_restore_0" } } */
/* { dg-options "-Os -msave-restore" } */
/* With -msave-restore in use GCC should be able to remove the calls to the
save and restore stubs in this case, replacing them with a tail call to
foo. */
extern int foo ();
int bar ()
{
return foo ();
}
/* { dg-final { scan-assembler-not "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler-not "tail\[ \t\]*__riscv_restore_0" } } */
/* { dg-final { scan-assembler "tail\[ \t\]*foo" } } */
/* { dg-options "-Os -msave-restore" } */
/* This test covers a case where we can't (currently) remove the calls to
the save/restore stubs. The cast of the return value from BAR requires
a zero extension between the call to BAR, and the return from FOO, this
currently prevents the removal of the save/restore calls. */
typedef unsigned long long u_64;
typedef unsigned int u_32;
extern u_32 bar (u_32 arg);
u_64 foo (u_32 arg)
{
return (u_64) bar (arg);
}
/* { dg-final { scan-assembler "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler "tail\[ \t\]*__riscv_restore_0" } } */
typedef int (*FPTR) (void);
FPTR a;
int
func ()
{
int b = a ();
return b;
}
/* { dg-options "-Os -msave-restore" } */
/* With -msave-restore in use GCC should be able to remove the calls to the
save and restore stubs in this case, replacing them with a tail call to
other_func. */
extern void other_func ();
void func ()
{
other_func ();
}
/* { dg-final { scan-assembler-not "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler-not "tail\[ \t\]*__riscv_restore_0" } } */
/* { dg-final { scan-assembler "tail\[ \t\]*other_func" } } */
/* { dg-options "-Os -msave-restore" } */
/* With -msave-restore in use it should not be possible to remove the calls
to the save and restore stubs in this case (in current GCC). */
enum
{
VAL_A,
VAL_B,
VAL_C,
VAL_D
} a;
extern void other_1 ();
extern void other_2 ();
void func ()
{
switch (a)
{
case VAL_B:
case VAL_C:
other_1 ();
case VAL_D:
other_2 ();
}
}
/* { dg-final { scan-assembler "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler "tail\[ \t\]*__riscv_restore_0" } } */
/* { dg-options "-Os -msave-restore" } */
/* As a leaf function this should never have the calls to the save and
restore stubs added, but lets check anyway. */
int func ()
{
return 3;
}
/* { dg-final { scan-assembler-not "call\[ \t\]*t0,__riscv_save_0" } } */
/* { dg-final { scan-assembler-not "tail\[ \t\]*__riscv_restore_0" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment