diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 1c1caa90edeb..09545278dcf8 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -24833,14 +24833,27 @@ rs6000_sibcall_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie) { rtx call[2]; rtx insn; + rtx r12 = NULL_RTX; + rtx func_addr = func_desc; gcc_assert (INTVAL (cookie) == 0); if (global_tlsarg) tlsarg = global_tlsarg; + /* For ELFv2, r12 and CTR need to hold the function address + for an indirect call. */ + if (GET_CODE (func_desc) != SYMBOL_REF && DEFAULT_ABI == ABI_ELFv2) + { + r12 = gen_rtx_REG (Pmode, 12); + if (!rtx_equal_p (r12, func_desc)) + emit_move_insn (r12, func_desc); + func_addr = gen_rtx_REG (Pmode, CTR_REGNO); + emit_move_insn (func_addr, r12); + } + /* Create the call. */ - call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_desc), tlsarg); + call[0] = gen_rtx_CALL (VOIDmode, gen_rtx_MEM (SImode, func_addr), tlsarg); if (value != NULL_RTX) call[0] = gen_rtx_SET (value, call[0]); @@ -24853,6 +24866,10 @@ rs6000_sibcall_aix (rtx value, rtx func_desc, rtx tlsarg, rtx cookie) if (!rs6000_pcrel_p (cfun)) use_reg (&CALL_INSN_FUNCTION_USAGE (insn), gen_rtx_REG (Pmode, TOC_REGNUM)); + + /* Note use of r12. */ + if (r12) + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), r12); } /* Expand code to perform a call under the SYSV4 ABI. */ diff --git a/gcc/testsuite/gcc.target/powerpc/pr96787-1.c b/gcc/testsuite/gcc.target/powerpc/pr96787-1.c new file mode 100644 index 000000000000..3c58e63797f3 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr96787-1.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target powerpc_elfv2 } */ +/* { dg-require-effective-target power10_ok } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10" } */ + +/* Verify that we generate an indirect sibcall for ELFv2 on P10 and + later, with r12 and CTR containing the function address. PR96787. */ + +extern int f (int); + +int main () +{ + if (f (3) != 6) + return 1; + return 0; +} + + +int g (int a) +{ + return a * 2; +} + + +int h (int a) +{ + return a + 2; +} + +int __attribute__((__noinline__)) f (int a) +{ + int (*x) (int) = a % 2 ? &g : &h; + (*x) (a); +} + +/* { dg-final { scan-assembler {\mmtctr 12\M} } } */ +/* { dg-final { scan-assembler {\mbctr\M} } } */ +/* { dg-final { scan-assembler-not {\mbctrl\M} } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/pr96787-2.c b/gcc/testsuite/gcc.target/powerpc/pr96787-2.c new file mode 100644 index 000000000000..b10ab7a8ce82 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr96787-2.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ +/* { dg-require-effective-target power10_hw } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10" } */ + +/* Verify that we generate an indirect sibcall for ELFv2 on P10 and + later, with r12 and CTR containing the function address. PR96787. */ + +extern void abort (void); +extern int f (int); + +int main () +{ + if (f (3) != 6) + abort (); + return 0; +} + + +int g (int a) +{ + return a * 2; +} + + +int h (int a) +{ + return a + 2; +} + +int __attribute__((__noinline__)) f (int a) +{ + int (*x) (int) = a % 2 ? &g : &h; + (*x) (a); +} +