This picks up Alexandre Oliva's series from 2023-2024.  The generic half
landed as 3472b5749df5, whose commit message says "This will be used on
rs6000, that sometimes issues another instruction after the call proper as
part of a call insn."  Nothing in gcc/config implements
TARGET_CALL_OFFSET_RETURN_LABEL today, so the hook has had no user for two
years.  The rs6000 half stalled in review; the objections are answered below.

The defect.  On AIX and ELFv2 a call that is not pc-relative is two machine
instructions inside one insn: the branch, and a slot the linker may rewrite to
restore the TOC pointer -- a nop after a direct call, an explicit load after an
indirect one.  Control returns to that slot.  Power ISA 3.1B, Book I, section
2.4, page 41: "if LK then LR <-iea CIA + 4", and in prose, "If LK=1 then the
effective address of the instruction following the Branch instruction is placed
into the Link Register."  dwarf2out emits the call-site label after the whole
insn, so DW_AT_call_return_pc names branch+8 where the link register holds
branch+4.

This is observable on the machine rather than only in the specification.
Built with the patched compiler on AIX 7.3, linked, and stopped in the callee:

  main+16   0x100004b0   bl      0x10000500 <callee>
  main+20   0x100004b4   nop

  (gdb) p/x $lr
  $1 = 0x100004b4

The link register holds the address of the nop.  The label GCC emits sits at
0x100004b8; the corrected value is 0x100004b4, which is exactly what the
hardware put in LR.

IBM states both halves of this for AIX.  AIX 5L Version 5.3 Assembler Language
Reference, SC23-4923-02, page 20, "Branch Instructions":

  If a branch instruction has the Link bit set to 1, then the Link Register is
  altered to store the return address for use by an invoked subroutine.  The
  return address is the address of the instruction immediately following the
  branch instruction.

and page 77, "Calling Routine's Responsibilities":

  Except for when loading parameters into the proper registers, calls to
  functions are expanded by compilers to include an NOP instruction after each
  branch and link instruction.  This extra instruction is modified by the
  linkage editor to restore the contents of the TOC register (register 2) on
  return from an out-of-module call.

So on AIX the return address is the slot, and the slot runs on return.

DWARF 5 section 3.4.1, page 89, anchors the attribute to "the return address
computed by call frame information in the called subprogram", which is
branch+4.  The sentence that follows, about architectures with delay slots, is
typeset in italics and so is explanatory rather than normative (section 1.2).
It also describes the opposite situation: on a delay-slot architecture the
hardware return address really is past the slot, whereas here the hardware
return address is the slot.

Under -gdwarf-4 and earlier the same wrong value is emitted as DW_AT_low_pc on
DW_TAG_GNU_call_site, via dwarf_AT in dwarf2out.cc; there is no separate GNU
attribute for it.  One fix covers both encodings.

Why -4 rather than 4 - get_attr_length (call_insn).

The v3 formula was rejected with "This isn't true.  There is a crlogical insn
before the bcl for sysv for example", and the reply at the time disputed it.
The objection was correct, and the list is longer than that reply suggested:
*call_local<mode>, *call_value_local<mode>, *sibcall_local<mode>,
*sibcall_value_local<mode> and the ten sysv patterns all emit crxor 6,6,6 or
creqv 6,6,6 before the branch.  A formula counting forward from the start of
the insn is wrong for every one of them.

Counting back from the end is not affected by any of that, and the reason is
structural: everything emitted before the branch is a prefix, and on AIX and
ELFv2 everything emitted after it is exactly one four-byte linker-fixup opcode.
The patterns that reach the hook, from rs6000.md:

  pattern                              ABI        length           last opcode
  *call{,_value}_nonlocal_aix<mode>    AIX/ELFv2  8, 4 if pcrel    nop
  *call{,_value}_indirect_aix<mode>    AIX        12 / 16          l{d,wz} 
2,%N(1)
  *call{,_value}_indirect_elfv2<mode>  ELFv2      8 / 12           ld 2,%N(1)
  *call{,_value}_indirect_pcrel<mode>  pcrel      4 / 8            the branch
  *{,sib}call{,_value}_local<mode>     any        4 / 8            the branch
  *sibcall{,_value}_aix<mode>          AIX/ELFv2  4, attr default  the branch

The ten *{,sib}call{,_value}{,_indirect}_nonlocal_sysv{,_secure}<mode> patterns
are conditioned on ABI_V4 or ABI_DARWIN and never reach the hook.

rs6000_call_template_1 appends "\n\tnop" for a non-sibcall, non-pcrel AIX or
ELFv2 call -- the pcrel branch is tested first and emits b%s ...@notoc with
nothing after it.  rs6000_indirect_call_template_1 appends the TOC reload in
its ABI_AIX and ABI_ELFv2 arms only; the final V4/Darwin arm emits b%T0l or
beq%T0l- with nothing after it.  So -4 is not a magic constant: it is one
linker-fixup opcode, and the length test selects the calls that have one.

Why not split the call insn instead, so the slot becomes an ordinary
instruction and the label lands in the right place by itself?  Three targets do
exactly that -- MIPS has define_insn_and_split "call_internal" under
TARGET_SPLIT_CALLS, split by mips_split_call; ia64 gives call_gp the template
"#" and splits it in ia64_split_call; x86 emits endbr from a separate pass.

rs6000 cannot follow them, and GCC has already written down the reason, in
mips.md above those very patterns:

  ;;   - We shouldn't restore $gp after calls that never return.
  ;;     It isn't valid to insert instructions between a noreturn
  ;;     call and the following barrier.

MIPS can honour that because the $gp reload is a caller convenience it is free
to omit.  The TOC slot is not: the linker requires a word at branch+4 to rewrite
for any external call and reports "call to `X' lacks nop, can't restore toc"
when it is missing, so rs6000_call_template_1 emits it after noreturn calls as
well -- it has no noreturn test, and cannot have one.  The same applies to a
call that can throw, where can_throw_internal ends the basic block right after
it.  So rs6000 has two classes of call where the slot may not be a separate
insn and the ABI nevertheless demands one at exactly branch+4.  That is why the
offset has to be applied at the label rather than removed from the pattern.

And for a direct call there is nothing to split.  The nop has no RTL
representation at all -- rs6000_call_template_1 produces it with sprintf into
the output template.  Splitting would mean inventing an insn for a slot whose
whole purpose is to be a placeholder the linker overwrites, while the ABI
requires it at exactly branch+4: "Object files must contain a nop slot after a
bl instruction to an external symbol" (ELFv2 ABI Rev 1.4, section 3.6.1), and
ld enforces it by reading the word at r_offset+4 in ppc64_elf_relocate_section,
reporting "call to `X' lacks nop, can't restore toc" when it is missing.  For
indirect calls the restore does exist in RTL, but as an element of the same
call PARALLEL, never as a separate insn.

I could find no patch, RFC or PR that ever proposed splitting it.

On get_attr_length in an output-time context, since that was part of the
earlier objection: rs6000 already does this.  output_cbranch chooses between
the short and long branch forms with get_attr_length (insn) == 8, and
rs6000_pltseq_template computes byte offsets relative to the current position
in the same spirit.

Nor is this an rs6000 wart that a target-specific bodge would paper over.  The
same class of defect is visible elsewhere: SPARC records call+4 whenever the
delay slot gets filled, because the call then becomes a SEQUENCE and the label
is emitted inside it, while %o7+8 is the return address; and AArch64 sibcalls
under -mharden-sls=retbr carry an SLS barrier inside the pattern, making the
insn twelve bytes.  Neither is fixed here, but they are why I think the generic
hook is the right layer and rs6000 is simply its first user.

The pcrel test is load-bearing, not defensive.  *call_indirect_pcrel<mode> and
*call_value_indirect_pcrel<mode> have length 8 under
-mno-speculate-indirect-jumps, emitting crset 2 followed by beq%T0l- -- the
branch is last, so the offset must stay 0.  A length-only rule would put the
return PC on the branch itself.  pr107909-7.c pins this.

One structural hazard is worth spelling out.  *call_local<mode>,
*call_value_local<mode>, *sibcall_local<mode> and *sibcall_value_local<mode>
carry no ABI condition and have an eight-byte alternative that ends in the
branch, behind a crlogical prefix.  If that alternative were reachable on AIX
or ELFv2, the hook would return -4 and land on the crxor.  It is not.  All four
select it only with a nonzero call cookie; rs6000-call.cc sets
CALL_V4_SET_FP_ARGS and CALL_V4_CLEAR_FP_ARGS only under ABI_V4 and
CALL_LIBCALL only under DEFAULT_ABI == ABI_V4, masking the latter out on the
other return path; and CALL_LONG both fails the patterns' own condition and is
rerouted through rs6000_longcall_ref by rs6000_call_aix.  Nothing in the tree
enforces that invariant, so I would not object to a gcc_checking_assert here if
a reviewer prefers one.

Two questions a reader will have about the hook body.

rs6000_pcrel_p () cannot disagree with what was used at emit time, even under a
per-function target attribute.  It reads rs6000_isa_flags, which has an
explicit TargetSave in rs6000.opt and is therefore restored per function by
rs6000_set_current_function; and gen_call_site_die runs inside pass_final,
where cfun and the active target node are still the emitting function's.

get_attr_length is valid there.  shorten_branches is a separate, earlier pass
and is not re-run; more decisively, the length of every pattern above depends
only on rs6000_pcrel_p (), which_alternative and INTVAL (operands[n]) -- none
on branch distance -- so there is no stale insn_current_address exposure.

Bootstrapped and regtested on powerpc64le-unknown-linux-gnu: a three-stage
bootstrap with --enable-languages=c,c++ completed and "Comparison successful."
-- stage 2 and stage 3 identical.

Testing.  Three ABIs, in each case two cc1 binaries built from one tree with
nothing else differing:

  powerpc64le-linux, ELFv2     16 corrected, 0 broken
  powerpc-ibm-aix7.3.1.0       12 corrected, 0 broken (32-bit, XCOFF)
  powerpc64-linux, ELFv1       12 corrected, 0 broken

Direct and indirect calls are corrected; local calls, sibcalls, pc-relative
calls and the whole V4 path are unchanged.  Generated code is unaffected: on
ELFv2 the .text section is byte-identical in every configuration tested, and on
AIX the entire assembly file differs in one line, inside .dwsect 0x10000:

  -     .vbyte  4,LVL..1
  +     .vbyte  4,LVL..1-4

Full make check-gcc on two ABIs, in each case both arms from the same tree with
only cc1 swapped.

  powerpc64le-linux, ELFv2   186812 result lines each, 28 differing
  powerpc64-linux, ELFv1     195214 result lines each, 18 differing

Every differing line is accounted for and none is a regression.  On ELFv2: one
timestamp, sixteen pre-existing musttail ERROR lines whose text embeds a
PID-derived temporary filename, six from the new tests going FAIL to PASS, and
four tally lines.  On ELFv1: one timestamp and the same sixteen musttail lines,
with every tally identical between the arms -- 187387 expected passes, 609
unexpected failures, 37 unexpected successes, 1670 expected failures, 28
unresolved and 5257 unsupported on both sides.

All seven run.  Filtered through the testsuite on powerpc64le-linux with only
cc1 swapped: 13 passes and 3 failures unpatched, 16 passes and 0 failures
patched.  Three discriminate -- pr107909-1.c, -3.c and -6.c go from FAIL to
PASS.  The other four are scan-assembler-not guards against over-correction:
they pass before and after by construction, and exist so that the local-call,
sibcall and pcrel cases cannot be broken by a later simplification of the
condition.

The three discriminating tests need an ABI that has the slot, so this adds
check_effective_target_powerpc_toc_restore.  It was checked in both directions
on powerpc64-linux: natively the three run and pass, and under
--target_board=unix/-m32 they are skipped, which is the point of it.  It tests 
_AIX and _CALL_ELF, which
was checked on all four configurations: ELFv2 defines _CALL_ELF 2, ELFv1
defines _CALL_ELF 1, AIX defines _AIX, and 32-bit V4 defines _CALL_SYSV and
neither of the other two.

They are scan-assembler tests rather than guality tests, deliberately.  A
guality test cannot show this: gdb has carried a PowerPC workaround since 14.1
(ppc64_update_call_site_pc in gdb/rs6000-tdep.c), so on ppc64le an end-to-end
test passes either way.  Each test states at the top what it asserts and why.

The user-visible effect is on 32-bit, where that workaround is not registered
-- it is guarded by wordsize == 8.  On AIX 7.3 with gdb 17.2:

  without the patch:
    Breakpoint 1, callee (i=126) at cee.c:6
    DW_OP_entry_value resolving cannot find DW_TAG_call_site 0x100004b4 in main

  with the patch:
    Breakpoint 1, callee (i=126, i@entry=42) at cee.c:6
    $1 = 42

On 64-bit the workaround is a retry after an exact-match miss -- gdb's
find_call_site tries the exact address first and returns on a hit -- so
correcting the compiler makes the first lookup succeed and the retry never
fires.  gdb states the requirement it is working around in gdb/block.c: "PC
must match exactly, it must be the next instruction after call (or after tail
call jump).".  It cannot regress, and gdb 13 and earlier, which have no 
workaround at
all, are fixed too.

For completeness: clang emits branch+8 as well, because PPC::BL8_NOP has the
asm string "bl $LI\n\tnop" and inherits Size = 8 from its I2 base class.  I do
not think that is a considered decision.  PowerPC never calls
setSupportsDebugEntryValues, so TargetOptions::SupportsDebugEntryValues is
false there and nothing consumes the value; and on 64-bit ELFv2 with the medium
code model and pc-relative calls enabled, the BL8_NOTOC form emits no nop and
lands at branch+4 -- LLVM's own return PC already varies with the subtarget.

This patch, its test cases and the analysis in this commit message were
developed with LLM assistance, per <https://gcc.gnu.org/ai-policy.html>.

Assisted-by: Claude Opus 5

        PR target/107909

gcc/ChangeLog:

        * config/rs6000/rs6000.cc (rs6000_call_offset_return_label): New
        function.
        (TARGET_CALL_OFFSET_RETURN_LABEL): Define.

gcc/testsuite/ChangeLog:

        * lib/target-supports.exp
        (check_effective_target_powerpc_toc_restore): New.
        * gcc.target/powerpc/pr107909-1.c: New test.
        * gcc.target/powerpc/pr107909-2.c: New test.
        * gcc.target/powerpc/pr107909-3.c: New test.
        * gcc.target/powerpc/pr107909-4.c: New test.
        * gcc.target/powerpc/pr107909-5.c: New test.
        * gcc.target/powerpc/pr107909-6.c: New test.
        * gcc.target/powerpc/pr107909-7.c: New test.

Co-authored-by: Alexandre Oliva <[email protected]>
Signed-off-by: Bernard Ladenthin <[email protected]>
---
diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index 995559889..f1430a115 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1491,6 +1491,9 @@ static const scoped_attribute_specs *const 
rs6000_attribute_table[] =
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL rs6000_function_ok_for_sibcall
 
+#undef TARGET_CALL_OFFSET_RETURN_LABEL
+#define TARGET_CALL_OFFSET_RETURN_LABEL rs6000_call_offset_return_label
+
 #undef TARGET_REGISTER_MOVE_COST
 #define TARGET_REGISTER_MOVE_COST rs6000_register_move_cost
 #undef TARGET_MEMORY_MOVE_COST
@@ -15307,6 +15310,53 @@ rs6000_indirect_sibcall_template (rtx *operands, 
unsigned int funop)
   return rs6000_indirect_call_template_1 (operands, funop, true);
 }
 
+/* Implement TARGET_CALL_OFFSET_RETURN_LABEL.
+
+   On AIX and ELFv2 a call that is not pc-relative is followed by a slot the
+   linker may rewrite to restore the TOC pointer: a nop for a direct call, an
+   explicit load for an indirect one.  That slot is part of the call insn, so
+   the label dwarf2out emits after the insn is one instruction beyond the
+   address control actually returns to.  Correct it by one instruction.
+
+   Calls that carry no slot must not be adjusted: calls to a symbol in the
+   same file, sibcalls, pc-relative calls, and every other ABI.  Those are all
+   a single four-byte branch, which is what the length test below detects.  */
+
+static int
+rs6000_call_offset_return_label (rtx_insn *call_insn)
+{
+  /* Only AIX and ELFv2 ever emit the slot, and never for a pc-relative call.
+     *call_indirect_pcrel<mode> is what makes this test load-bearing rather
+     than defensive: it is eight bytes under -mno-speculate-indirect-jumps,
+     but there the branch is the last instruction.  */
+  if (rs6000_pcrel_p ()
+      || (DEFAULT_ABI != ABI_AIX && DEFAULT_ABI != ABI_ELFv2))
+    return 0;
+
+  /* The hook is also invoked for a SEQUENCE starting with a call.  rs6000 has
+     no define_delay so that cannot arise here, but recog fails on a SEQUENCE
+     pattern and get_attr_length would be meaningless.  */
+  if (recog_memoized (call_insn) < 0)
+    return 0;
+
+  /* A call to a symbol in this file needs no TOC restore, so the branch is
+     the whole insn and the label after it is already right.  Those calls are
+     four bytes; every call that carries a slot is longer.  */
+  if (get_attr_length (call_insn) <= 4)
+    return 0;
+
+  /* Four, not a word: the slot is one instruction in both word sizes.
+     rs6000_indirect_call_template_1 builds it from a single template, with
+     TARGET_64BIT selecting only the load mnemonic --
+
+       const char *ptrload = TARGET_64BIT ? "d" : "wz";
+
+     so it is lwz 2,20(1) on 32-bit and ld 2,40(1) on 64-bit.  The mnemonic and
+     the TOC save-area offset differ; the instruction count does not.  A direct
+     call gets a nop, which is four bytes either way.  */
+  return -4;
+}
+
 #if HAVE_AS_PLTSEQ
 /* Output indirect call insns.  WHICH identifies the type of sequence.  */
 const char *
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-1.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-1.c
new file mode 100644
index 000000000..d7c282ff1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-1.c
@@ -0,0 +1,26 @@
+/* PR debug/107909 - the call-site return PC must name the instruction after
+   the branch, not after the TOC-restore slot that follows it.
+
+   A call to a symbol outside this file carries the slot, so the label emitted
+   after the call insn is one instruction too far and has to be corrected.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_toc_restore } */
+/* { dg-options "-O1 -g" } */
+
+extern int ext (int);
+
+int
+f (int i)
+{
+  int r = ext (i);
+  return r + 1;
+}
+
+/* Exactly one call, and its return PC is adjusted back by one instruction.  */
+/* { dg-final { scan-assembler-times {LVL[.0-9]+-4} 1 } } */
+
+/* The scan pattern deliberately matches both spellings of the label and the
+   directive: ELF writes `.8byte       .LVL1-4`, AIX writes `.vbyte    
4,LVL..1-4`.
+   Matching on `LVL<digits>-4` covers both and cannot collide with the
+   `-1` idiom used in location lists, nor with AIX's `Ldebug_line..0-4`.  */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-2.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-2.c
new file mode 100644
index 000000000..d57ff7b30
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-2.c
@@ -0,0 +1,26 @@
+/* PR debug/107909 - a call that carries no TOC-restore slot must NOT be
+   adjusted.  This is the regression guard for the obvious over-broad fix:
+   an implementation that keys only on the ABI, without asking whether this
+   particular call is longer than a single branch, turns a correct return PC
+   into one that points before the call.
+
+   A call to a static function in the same file needs no slot.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -g" } */
+
+static int __attribute__((noinline))
+loc (int i)
+{
+  return i + 1;
+}
+
+int
+f (int i)
+{
+  int r = loc (i);
+  return r + 1;
+}
+
+/* No return PC may be adjusted here.  */
+/* { dg-final { scan-assembler-not {LVL[.0-9]+-4} } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-3.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-3.c
new file mode 100644
index 000000000..7c4531a8a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-3.c
@@ -0,0 +1,18 @@
+/* PR debug/107909 - an indirect call ends with an explicit TOC restore, which
+   plays the same role as the nop after a direct call, so its return PC needs
+   the same correction.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_toc_restore } */
+/* { dg-options "-O1 -g" } */
+
+int (*fp) (int);
+
+int
+f (int i)
+{
+  int r = fp (i);
+  return r + 1;
+}
+
+/* { dg-final { scan-assembler-times {LVL[.0-9]+-4} 1 } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-4.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-4.c
new file mode 100644
index 000000000..03f8fe00f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-4.c
@@ -0,0 +1,20 @@
+/* PR debug/107909 - a sibling call is a plain branch with nothing after it,
+   so its call site must be left alone.  Second regression guard against an
+   over-broad fix.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -g" } */
+
+static int __attribute__((noinline))
+loc (int i)
+{
+  return i + 1;
+}
+
+int
+f (int i)
+{
+  return loc (i);
+}
+
+/* { dg-final { scan-assembler-not {LVL[.0-9]+-4} } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-5.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-5.c
new file mode 100644
index 000000000..cb17eb97c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-5.c
@@ -0,0 +1,19 @@
+/* PR debug/107909 - pc-relative calls emit no TOC-restore slot, so nothing
+   needs correcting.  Third regression guard, and the case that originally
+   confirmed the diagnosis: it was already right before the fix.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* { dg-options "-O1 -g -mdejagnu-cpu=power10 -mpcrel" } */
+
+extern int ext (int);
+int (*fp) (int);
+
+int
+f (int i)
+{
+  return ext (i) + fp (i);
+}
+
+/* { dg-final { scan-assembler-not {LVL[.0-9]+-4} } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-6.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-6.c
new file mode 100644
index 000000000..5a796aa48
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-6.c
@@ -0,0 +1,30 @@
+/* PR debug/107909 - all three kinds in one file, so the counts pin down which
+   calls are adjusted and which are not.  Exactly the calls that carry a
+   TOC-restore slot get the correction: the external one and the indirect one,
+   but not the local one.
+
+   This is the test that would have caught an early version of the fix, which
+   adjusted every call on AIX and ELFv2 and so broke local calls and sibcalls
+   while fixing the rest.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target powerpc_toc_restore } */
+/* { dg-options "-O1 -g" } */
+
+extern int ext (int);
+int (*fp) (int);
+
+static int __attribute__((noinline))
+loc (int i)
+{
+  return i + 1;
+}
+
+int
+f (int i)
+{
+  return ext (i) + fp (i) + loc (i);
+}
+
+/* Three calls, two of them adjusted.  */
+/* { dg-final { scan-assembler-times {LVL[.0-9]+-4} 2 } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/pr107909-7.c 
b/gcc/testsuite/gcc.target/powerpc/pr107909-7.c
new file mode 100644
index 000000000..b8a1b654d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr107909-7.c
@@ -0,0 +1,25 @@
+/* PR debug/107909 - a pc-relative indirect call with speculation hardening is
+   TWO instructions long, `crset` followed by `beqctrl-`, but the branch is the
+   LAST of them, so nothing follows the return point and no correction is due.
+
+   This is the case that makes the pc-relative guard load-bearing: keying only
+   on the instruction length would see 8 bytes here and wrongly subtract 4,
+   putting the return PC on the branch itself.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-require-effective-target powerpc_elfv2 } */
+/* { dg-options "-O1 -g -mdejagnu-cpu=power10 -mpcrel 
-mno-speculate-indirect-jumps" } */
+/* { dg-warning "'-mno-speculate-indirect-jumps' is deprecated" "" { target 
*-*-* } 0 } */
+
+int (*fp) (int);
+
+int
+f (int i)
+{
+  int r = fp (i);
+  return r + 1;
+}
+
+/* { dg-final { scan-assembler "beqctrl-" } } */
+/* { dg-final { scan-assembler-not {LVL[.0-9]+-4} } } */
diff --git a/gcc/testsuite/lib/target-supports.exp 
b/gcc/testsuite/lib/target-supports.exp
index ab726f729..5ec2bf6db 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -8515,6 +8515,24 @@ proc check_effective_target_powerpc_elfv2 { } {
     }
 }
 
+# Return 1 if a call on this PowerPC ABI is followed by a slot the linker may
+# rewrite to restore the TOC pointer: AIX, ELFv1 and ELFv2 do, the V4 and
+# Darwin ABIs do not.
+
+proc check_effective_target_powerpc_toc_restore { } {
+    if { [istarget powerpc*-*-*] } {
+       return [check_no_compiler_messages powerpc_toc_restore object {
+           #if !defined (_AIX) && !defined (_CALL_ELF)
+           #error no TOC restore slot after a call
+           #else
+           int dummy;
+           #endif
+       }]
+    } else {
+       return 0
+    }
+}
+
 # Return 1 if this is a PowerPC target supporting -mrop-protect
 
 proc check_effective_target_rop_ok { } {
-- 
2.51.0

Reply via email to