xtensa: Add support for sibling call optimization

This patch introduces support for sibling call optimization, when call0
ABI is in effect.

gcc/ChangeLog:

	* config/xtensa/xtensa-protos.h (xtensa_prepare_expand_call,
	xtensa_emit_sibcall): New prototypes.
	(xtensa_expand_epilogue): Add new argument that specifies whether
	or not sibling call.
	* config/xtensa/xtensa.cc (TARGET_FUNCTION_OK_FOR_SIBCALL):
	New macro definition.
	(xtensa_prepare_expand_call): New function in order to share
	the common code.
	(xtensa_emit_sibcall, xtensa_function_ok_for_sibcall):
	New functions.
	(xtensa_expand_epilogue): Add new argument sibcall_p and use it
	for sibling call handling.
	* config/xtensa/xtensa.md (call, call_value):
	Use xtensa_prepare_expand_call.
	(call_internal, call_value_internal):
	Add the condition in order to be disabled if sibling call.
	(sibcall, sibcall_value, sibcall_epilogue): New expansions.
	(sibcall_internal, sibcall_value_internal): New insn patterns,
	and split ones in order to take care of the indirect sibcalls.

gcc/testsuite/ChangeLog:

	* gcc.target/xtensa/sibcalls.c: New.
This commit is contained in:
Takayuki 'January June' Suwa 2022-06-15 21:21:21 +09:00 committed by Max Filippov
parent 96518f714e
commit 43b0c56fda
4 changed files with 155 additions and 20 deletions

View file

@ -53,7 +53,9 @@ extern void xtensa_expand_atomic (enum rtx_code, rtx, rtx, rtx, bool);
extern void xtensa_emit_loop_end (rtx_insn *, rtx *);
extern char *xtensa_emit_branch (bool, rtx *);
extern char *xtensa_emit_movcc (bool, bool, bool, rtx *);
extern void xtensa_prepare_expand_call (int, rtx *);
extern char *xtensa_emit_call (int, rtx *);
extern char *xtensa_emit_sibcall (int, rtx *);
extern bool xtensa_tls_referenced_p (rtx);
extern enum rtx_code xtensa_shlrd_which_direction (rtx, rtx);
@ -73,7 +75,7 @@ extern int xtensa_dbx_register_number (int);
extern long compute_frame_size (poly_int64);
extern bool xtensa_use_return_instruction_p (void);
extern void xtensa_expand_prologue (void);
extern void xtensa_expand_epilogue (void);
extern void xtensa_expand_epilogue (bool);
extern void order_regs_for_local_alloc (void);
extern enum reg_class xtensa_regno_to_class (int regno);
extern HOST_WIDE_INT xtensa_initial_elimination_offset (int from, int to);

View file

@ -189,7 +189,7 @@ static bool xtensa_can_eliminate (const int from ATTRIBUTE_UNUSED,
const int to);
static HOST_WIDE_INT xtensa_starting_frame_offset (void);
static unsigned HOST_WIDE_INT xtensa_asan_shadow_offset (void);
static bool xtensa_function_ok_for_sibcall (tree, tree);
static rtx xtensa_delegitimize_address (rtx);
@ -347,6 +347,9 @@ static rtx xtensa_delegitimize_address (rtx);
#undef TARGET_DELEGITIMIZE_ADDRESS
#define TARGET_DELEGITIMIZE_ADDRESS xtensa_delegitimize_address
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL xtensa_function_ok_for_sibcall
struct gcc_target targetm = TARGET_INITIALIZER;
@ -2127,6 +2130,20 @@ xtensa_emit_movcc (bool inverted, bool isfp, bool isbool, rtx *operands)
}
void
xtensa_prepare_expand_call (int callop, rtx *operands)
{
rtx addr = XEXP (operands[callop], 0);
if (flag_pic && SYMBOL_REF_P (addr)
&& (!SYMBOL_REF_LOCAL_P (addr) || SYMBOL_REF_EXTERNAL_P (addr)))
addr = gen_sym_PLT (addr);
if (!call_insn_operand (addr, VOIDmode))
XEXP (operands[callop], 0) = copy_to_mode_reg (Pmode, addr);
}
char *
xtensa_emit_call (int callop, rtx *operands)
{
@ -2145,6 +2162,24 @@ xtensa_emit_call (int callop, rtx *operands)
}
char *
xtensa_emit_sibcall (int callop, rtx *operands)
{
static char result[64];
rtx tgt = operands[callop];
if (GET_CODE (tgt) == CONST_INT)
sprintf (result, "j.l\t" HOST_WIDE_INT_PRINT_HEX ", a9",
INTVAL (tgt));
else if (register_operand (tgt, VOIDmode))
sprintf (result, "jx\t%%%d", callop);
else
sprintf (result, "j.l\t%%%d, a9", callop);
return result;
}
bool
xtensa_legitimate_address_p (machine_mode mode, rtx addr, bool strict)
{
@ -3270,7 +3305,7 @@ xtensa_expand_prologue (void)
}
void
xtensa_expand_epilogue (void)
xtensa_expand_epilogue (bool sibcall_p)
{
if (!TARGET_WINDOWED_ABI)
{
@ -3304,10 +3339,13 @@ xtensa_expand_epilogue (void)
if (xtensa_call_save_reg(regno))
{
rtx x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
rtx reg;
offset -= UNITS_PER_WORD;
emit_move_insn (gen_rtx_REG (SImode, regno),
emit_move_insn (reg = gen_rtx_REG (SImode, regno),
gen_frame_mem (SImode, x));
if (regno == A0_REG && sibcall_p)
emit_use (reg);
}
}
@ -3342,7 +3380,8 @@ xtensa_expand_epilogue (void)
EH_RETURN_STACKADJ_RTX));
}
cfun->machine->epilogue_done = true;
emit_jump_insn (gen_return ());
if (!sibcall_p)
emit_jump_insn (gen_return ());
}
bool
@ -4869,6 +4908,17 @@ xtensa_asan_shadow_offset (void)
return HOST_WIDE_INT_UC (0x10000000);
}
/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
static bool
xtensa_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, tree exp ATTRIBUTE_UNUSED)
{
/* Do not allow sibcalls when windowed registers ABI is in effect. */
if (TARGET_WINDOWED_ABI)
return false;
return true;
}
static rtx
xtensa_delegitimize_address (rtx op)
{

View file

@ -25,6 +25,7 @@
(A7_REG 7)
(A8_REG 8)
(A9_REG 9)
(A10_REG 10)
(UNSPEC_NOP 2)
(UNSPEC_PLT 3)
@ -2148,18 +2149,13 @@
(match_operand 1 "" ""))]
""
{
rtx addr = XEXP (operands[0], 0);
if (flag_pic && GET_CODE (addr) == SYMBOL_REF
&& (!SYMBOL_REF_LOCAL_P (addr) || SYMBOL_REF_EXTERNAL_P (addr)))
addr = gen_sym_PLT (addr);
if (!call_insn_operand (addr, VOIDmode))
XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr);
xtensa_prepare_expand_call (0, operands);
})
(define_insn "call_internal"
[(call (mem (match_operand:SI 0 "call_insn_operand" "nir"))
(match_operand 1 "" "i"))]
""
"!SIBLING_CALL_P (insn)"
{
return xtensa_emit_call (0, operands);
}
@ -2173,19 +2169,14 @@
(match_operand 2 "" "")))]
""
{
rtx addr = XEXP (operands[1], 0);
if (flag_pic && GET_CODE (addr) == SYMBOL_REF
&& (!SYMBOL_REF_LOCAL_P (addr) || SYMBOL_REF_EXTERNAL_P (addr)))
addr = gen_sym_PLT (addr);
if (!call_insn_operand (addr, VOIDmode))
XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr);
xtensa_prepare_expand_call (1, operands);
})
(define_insn "call_value_internal"
[(set (match_operand 0 "register_operand" "=a")
(call (mem (match_operand:SI 1 "call_insn_operand" "nir"))
(match_operand 2 "" "i")))]
""
"!SIBLING_CALL_P (insn)"
{
return xtensa_emit_call (1, operands);
}
@ -2193,6 +2184,70 @@
(set_attr "mode" "none")
(set_attr "length" "3")])
(define_expand "sibcall"
[(call (match_operand 0 "memory_operand" "")
(match_operand 1 "" ""))]
"!TARGET_WINDOWED_ABI"
{
xtensa_prepare_expand_call (0, operands);
})
(define_insn "sibcall_internal"
[(call (mem:SI (match_operand:SI 0 "call_insn_operand" "nir"))
(match_operand 1 "" "i"))]
"!TARGET_WINDOWED_ABI && SIBLING_CALL_P (insn)"
{
return xtensa_emit_sibcall (0, operands);
}
[(set_attr "type" "call")
(set_attr "mode" "none")
(set_attr "length" "3")])
(define_split
[(call (mem:SI (match_operand:SI 0 "register_operand"))
(match_operand 1 ""))]
"reload_completed
&& !TARGET_WINDOWED_ABI && SIBLING_CALL_P (insn)
&& IN_RANGE (REGNO (operands[0]), 12, 15)"
[(set (reg:SI A10_REG)
(match_dup 0))
(call (mem:SI (reg:SI A10_REG))
(match_dup 1))])
(define_expand "sibcall_value"
[(set (match_operand 0 "register_operand" "")
(call (match_operand 1 "memory_operand" "")
(match_operand 2 "" "")))]
"!TARGET_WINDOWED_ABI"
{
xtensa_prepare_expand_call (1, operands);
})
(define_insn "sibcall_value_internal"
[(set (match_operand 0 "register_operand" "=a")
(call (mem:SI (match_operand:SI 1 "call_insn_operand" "nir"))
(match_operand 2 "" "i")))]
"!TARGET_WINDOWED_ABI && SIBLING_CALL_P (insn)"
{
return xtensa_emit_sibcall (1, operands);
}
[(set_attr "type" "call")
(set_attr "mode" "none")
(set_attr "length" "3")])
(define_split
[(set (match_operand 0 "register_operand")
(call (mem:SI (match_operand:SI 1 "register_operand"))
(match_operand 2 "")))]
"reload_completed
&& !TARGET_WINDOWED_ABI && SIBLING_CALL_P (insn)
&& IN_RANGE (REGNO (operands[1]), 12, 15)"
[(set (reg:SI A10_REG)
(match_dup 1))
(set (match_dup 0)
(call (mem:SI (reg:SI A10_REG))
(match_dup 2)))])
(define_insn "entry"
[(set (reg:SI A1_REG)
(unspec_volatile:SI [(match_operand:SI 0 "const_int_operand" "i")]
@ -2260,7 +2315,15 @@
[(return)]
""
{
xtensa_expand_epilogue ();
xtensa_expand_epilogue (false);
DONE;
})
(define_expand "sibcall_epilogue"
[(return)]
"!TARGET_WINDOWED_ABI"
{
xtensa_expand_epilogue (true);
DONE;
})

View file

@ -0,0 +1,20 @@
/* { dg-do compile } */
/* { dg-options "-O2 -mabi=call0 -foptimize-sibling-calls" } */
extern int foo(int);
extern void bar(int);
int test_0(int a) {
return foo(a);
}
void test_1(int a) {
bar(a);
}
int test_2(int (*a)(void)) {
bar(0);
return a();
}
/* { dg-final { scan-assembler-not "ret" } } */