Some of the rs6000 call patterns, on some ABIs, issue multiple opcodes
out of a single call insn, but the call (bl) or jump (b) is not always
the last opcode in the sequence.

This does not seem to be a problem for exception handling tables, but
the return_pc attribute in the call graph output in dwarf2+ debug
information, that takes the address of a label output right after the
call, does not match the value of the link register even for non-tail
calls.  E.g., with ABI_AIX or ABI_ELFv2, such code as:

  foo ();

outputs:

  bl foo
  nop
 LVL#:
[...]
  .8byte .LVL#  # DW_AT_call_return_pc

but debug info consumers may rely on the return_pc address, and draw
incorrect conclusions from its off-by-4 value.

This patch introduces infrastructure for targets to add an offset to
the label issued after the call_insn to set the call_return_pc
attribute, and uses that on rs6000 to account for nop and l opcodes
issued after actual call opcode as part of call insns output patterns.


for  gcc/ChangeLog

        * target.def (call_offset_return_label): New hook.
        * gcc/doc/tm.texi.in (TARGET_CALL_OFFSET_RETURN_LABEL): Add
        placeholder.
        * gcc/doc/tm.texi: Rebuild.
        * dwarf2out.cc (struct call_arg_loc_node): Record call_insn
        instad of call_arg_loc_note.
        (add_AT_lbl_id): Add optional offset argument.
        (gen_call_site_die): Compute and pass on a return pc offset.
        (gen_subprogram_die): Move call_arg_loc_note computation...
        (dwarf2out_var_location): ... from here.  Set call_insn.
        * config/rs6000/rs6000.cc (TARGET_CALL_OFFSET_RETURN_LABEL):
        Override.
        (rs6000_call_offset_return_label): New.
        * config/rs6000/rs6000.md (call_needs_return_offset): New
        attribute.  Set it on call patterns that may require
        offsetting.
---
 gcc/config/rs6000/rs6000.cc |   37 +++++++++++++++++++++++++++++++++++++
 gcc/config/rs6000/rs6000.md |   24 ++++++++++++++++++++++++
 gcc/doc/tm.texi             |    7 +++++++
 gcc/doc/tm.texi.in          |    2 ++
 gcc/dwarf2out.cc            |   26 +++++++++++++++++---------
 gcc/target.def              |    9 +++++++++
 6 files changed, 96 insertions(+), 9 deletions(-)

diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index 8e0b0d022db2f..edbc7a011886c 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1760,6 +1760,9 @@ static const struct attribute_spec 
rs6000_attribute_table[] =
 
 #undef TARGET_UPDATE_IPA_FN_TARGET_INFO
 #define TARGET_UPDATE_IPA_FN_TARGET_INFO rs6000_update_ipa_fn_target_info
+
+#undef TARGET_CALL_OFFSET_RETURN_LABEL
+#define TARGET_CALL_OFFSET_RETURN_LABEL rs6000_call_offset_return_label
 
 
 /* Processor table.  */
@@ -14593,6 +14596,40 @@ rs6000_assemble_integer (rtx x, unsigned int size, int 
aligned_p)
   return default_assemble_integer (x, size, aligned_p);
 }
 
+/* Return the offset to be added to the label output after CALL_INSN
+   to compute the address to be placed in DW_AT_call_return_pc.  Some
+   call insns output nop or l after bl, so the return address would be
+   wrong without this offset.  */
+
+static int
+rs6000_call_offset_return_label (rtx_insn *call_insn)
+{
+  /* We don't expect SEQUENCEs in this port.  */
+  gcc_checking_assert (GET_CODE (call_insn) == CALL_INSN);
+
+  enum attr_call_needs_return_offset cnro
+    = get_attr_call_needs_return_offset (call_insn);
+
+  if (cnro == CALL_NEEDS_RETURN_OFFSET_NONE)
+    return 0;
+
+  if (rs6000_pcrel_p ())
+    return 0;
+  else if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
+    /* rs6000_call_template_1 outputs a nop after non-sibcall insns;
+       we mark sibcall insns with NONE rather than DIRECT, so we
+       should have returned zero above.
+       rs6000_indirect_call_template_1 outputs an l insn after
+       indirect calls in these ABIs.  */
+    return -4;
+  else if (DEFAULT_ABI == ABI_V4)
+    return 0;
+  else if (DEFAULT_ABI == ABI_DARWIN)
+    return 0;
+  else
+    return 0;
+}
+
 /* Return a template string for assembly to emit when making an
    external call.  FUNOP is the call mem argument operand number.  */
 
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 81bffb04ceb0c..7dc73b21af731 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -342,6 +342,12 @@ (define_attr "max_prefixed_insns" "" (const_int 1))
 ;; num_insns and recurse).
 (define_attr "length" "" (const_int 4))
 
+;; Calls that output insns after bl need DW_AT_call_return_pc to be
+;; adjusted.  rs6000_call_offset_return_label uses this attribute to
+;; conservatively recognize the relevant patterns.
+(define_attr "call_needs_return_offset" "none,direct,indirect"
+  (const_string "none"))
+
 ;; Processor type -- this attribute must exactly match the processor_type
 ;; enumeration in rs6000-opts.h.
 (define_attr "cpu"
@@ -11355,6 +11361,7 @@ (define_insn "*call_indirect_nonlocal_sysv<mode>"
   return rs6000_indirect_call_template (operands, 0);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
                         (match_test "which_alternative != 1"))
@@ -11384,6 +11391,7 @@ (define_insn "*call_nonlocal_sysv<mode>"
   return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch,branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set_attr "length" "4,8")])
 
 (define_insn "*call_nonlocal_sysv_secure<mode>"
@@ -11405,6 +11413,7 @@ (define_insn "*call_nonlocal_sysv_secure<mode>"
   return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch,branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set_attr "length" "4,8")])
 
 (define_insn "*call_value_indirect_nonlocal_sysv<mode>"
@@ -11425,6 +11434,7 @@ (define_insn "*call_value_indirect_nonlocal_sysv<mode>"
   return rs6000_indirect_call_template (operands, 1);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (plus
          (if_then_else (match_test "IS_V4_FP_ARGS (operands[3])")
@@ -11454,6 +11464,7 @@ (define_insn "*call_value_nonlocal_sysv<mode>"
   return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set (attr "length")
        (if_then_else (match_test "IS_V4_FP_ARGS (operands[3])")
          (const_int 8)
@@ -11479,6 +11490,7 @@ (define_insn "*call_value_nonlocal_sysv_secure<mode>"
   return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set (attr "length")
        (if_then_else (match_test "IS_V4_FP_ARGS (operands[3])")
          (const_int 8)
@@ -11498,6 +11510,7 @@ (define_insn "*call_nonlocal_aix<mode>"
   return rs6000_call_template (operands, 0);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set (attr "length")
        (if_then_else (match_test "rs6000_pcrel_p ()")
          (const_int 4)
@@ -11515,6 +11528,7 @@ (define_insn "*call_value_nonlocal_aix<mode>"
   return rs6000_call_template (operands, 1);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "direct")
    (set (attr "length")
        (if_then_else (match_test "rs6000_pcrel_p ()")
            (const_int 4)
@@ -11537,6 +11551,7 @@ (define_insn "*call_indirect_aix<mode>"
   return rs6000_indirect_call_template (operands, 0);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11558,6 +11573,7 @@ (define_insn "*call_value_indirect_aix<mode>"
   return rs6000_indirect_call_template (operands, 1);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11579,6 +11595,7 @@ (define_insn "*call_indirect_elfv2<mode>"
   return rs6000_indirect_call_template (operands, 0);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11595,6 +11612,7 @@ (define_insn "*call_indirect_pcrel<mode>"
   return rs6000_indirect_call_template (operands, 0);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11615,6 +11633,7 @@ (define_insn "*call_value_indirect_elfv2<mode>"
   return rs6000_indirect_call_template (operands, 1);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11632,6 +11651,7 @@ (define_insn "*call_value_indirect_pcrel<mode>"
   return rs6000_indirect_call_template (operands, 1);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (if_then_else (and (match_test "!rs6000_speculate_indirect_jumps")
                           (match_test "which_alternative != 1"))
@@ -11783,6 +11803,7 @@ (define_insn "*sibcall_indirect_nonlocal_sysv<mode>"
   return rs6000_indirect_sibcall_template (operands, 0);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
                         (match_test "which_alternative != 1"))
@@ -11812,6 +11833,7 @@ (define_insn "*sibcall_nonlocal_sysv<mode>"
   return rs6000_sibcall_template (operands, 0);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "none")
    (set_attr "length" "4,8")])
 
 (define_insn "*sibcall_value_indirect_nonlocal_sysv<mode>"
@@ -11832,6 +11854,7 @@ (define_insn 
"*sibcall_value_indirect_nonlocal_sysv<mode>"
   return rs6000_indirect_sibcall_template (operands, 1);
 }
   [(set_attr "type" "jmpreg")
+   (set_attr "call_needs_return_offset" "indirect")
    (set (attr "length")
        (cond [(and (and (match_test "!rs6000_speculate_indirect_jumps")
                         (match_test "which_alternative != 1"))
@@ -11862,6 +11885,7 @@ (define_insn "*sibcall_value_nonlocal_sysv<mode>"
   return rs6000_sibcall_template (operands, 1);
 }
   [(set_attr "type" "branch")
+   (set_attr "call_needs_return_offset" "none")
    (set_attr "length" "4,8")])
 
 ;; AIX ABI sibling call patterns.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index ec90c46ea2ffd..16bb089a9a657 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -5426,6 +5426,13 @@ except the last are treated as named.
 You need not define this hook if it always returns @code{false}.
 @end deftypefn
 
+@deftypefn {Target Hook} int TARGET_CALL_OFFSET_RETURN_LABEL (rtx_insn 
*@var{call_insn})
+While generating call-site debug info for a CALL insn, or a SEQUENCE
+insn starting with a CALL, this target hook is invoked to compute the
+offset to be added to the debug label emitted after the call to obtain
+the return address that should be recorded as the return PC.
+@end deftypefn
+
 @deftypefn {Target Hook} void TARGET_CALL_ARGS (rtx, @var{tree})
 While generating RTL for a function call, this target hook is invoked once
 for each argument passed to the function, either a register returned by
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 930b109863f23..1706965e9a0cd 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -3785,6 +3785,8 @@ These machine description macros help implement varargs:
 
 @hook TARGET_STRICT_ARGUMENT_NAMING
 
+@hook TARGET_CALL_OFFSET_RETURN_LABEL
+
 @hook TARGET_CALL_ARGS
 
 @hook TARGET_END_CALL_ARGS
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 1f39df3b1e250..b706c36b87af6 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -3584,7 +3584,7 @@ typedef struct var_loc_list_def var_loc_list;
 
 /* Call argument location list.  */
 struct GTY ((chain_next ("%h.next"))) call_arg_loc_node {
-  rtx GTY (()) call_arg_loc_note;
+  rtx_insn * GTY (()) call_insn;
   const char * GTY (()) label;
   tree GTY (()) block;
   bool tail_call_p;
@@ -3768,7 +3768,8 @@ static void remove_addr_table_entry (addr_table_entry *);
 static void add_AT_addr (dw_die_ref, enum dwarf_attribute, rtx, bool);
 static inline rtx AT_addr (dw_attr_node *);
 static void add_AT_symview (dw_die_ref, enum dwarf_attribute, const char *);
-static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *);
+static void add_AT_lbl_id (dw_die_ref, enum dwarf_attribute, const char *,
+                          int = 0);
 static void add_AT_lineptr (dw_die_ref, enum dwarf_attribute, const char *);
 static void add_AT_macptr (dw_die_ref, enum dwarf_attribute, const char *);
 static void add_AT_range_list (dw_die_ref, enum dwarf_attribute,
@@ -5327,14 +5328,17 @@ add_AT_symview (dw_die_ref die, enum dwarf_attribute 
attr_kind,
 
 static inline void
 add_AT_lbl_id (dw_die_ref die, enum dwarf_attribute attr_kind,
-               const char *lbl_id)
+              const char *lbl_id, int offset)
 {
   dw_attr_node attr;
 
   attr.dw_attr = attr_kind;
   attr.dw_attr_val.val_class = dw_val_class_lbl_id;
   attr.dw_attr_val.val_entry = NULL;
-  attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id);
+  if (!offset)
+    attr.dw_attr_val.v.val_lbl_id = xstrdup (lbl_id);
+  else
+    attr.dw_attr_val.v.val_lbl_id = xasprintf ("%s%+i", lbl_id, offset);
   if (dwarf_split_debug_info)
     attr.dw_attr_val.val_entry
         = add_addr_table_entry (attr.dw_attr_val.v.val_lbl_id,
@@ -23405,7 +23409,9 @@ gen_call_site_die (tree decl, dw_die_ref subr_die,
   if (stmt_die == NULL)
     stmt_die = subr_die;
   die = new_die (dwarf_TAG (DW_TAG_call_site), stmt_die, NULL_TREE);
-  add_AT_lbl_id (die, dwarf_AT (DW_AT_call_return_pc), ca_loc->label);
+  add_AT_lbl_id (die, dwarf_AT (DW_AT_call_return_pc),
+                ca_loc->label,
+                targetm.calls.call_offset_return_label (ca_loc->call_insn));
   if (ca_loc->tail_call_p)
     add_AT_flag (die, dwarf_AT (DW_AT_call_tail_call), 1);
   if (ca_loc->symbol_ref)
@@ -24092,11 +24098,14 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
            {
              dw_die_ref die = NULL;
              rtx tloc = NULL_RTX, tlocc = NULL_RTX;
+             rtx call_arg_loc_note
+               = find_reg_note (ca_loc->call_insn,
+                                REG_CALL_ARG_LOCATION, NULL_RTX);
              rtx arg, next_arg;
              tree arg_decl = NULL_TREE;
 
-             for (arg = (ca_loc->call_arg_loc_note != NULL_RTX
-                         ? XEXP (ca_loc->call_arg_loc_note, 0)
+             for (arg = (call_arg_loc_note != NULL_RTX
+                         ? XEXP (call_arg_loc_note, 0)
                          : NULL_RTX);
                   arg; arg = next_arg)
                {
@@ -28122,8 +28131,7 @@ create_label:
        = ggc_cleared_alloc<call_arg_loc_node> ();
       rtx_insn *prev = call_insn;
 
-      ca_loc->call_arg_loc_note
-       = find_reg_note (call_insn, REG_CALL_ARG_LOCATION, NULL_RTX);
+      ca_loc->call_insn = call_insn;
       ca_loc->next = NULL;
       ca_loc->label = last_label;
       gcc_assert (prev
diff --git a/gcc/target.def b/gcc/target.def
index 4b2c53aba14e6..a5c2046e98250 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4738,6 +4738,15 @@ not generate any instructions in this case.",
        int *pretend_args_size, int second_time),
  default_setup_incoming_varargs)
 
+DEFHOOK
+(call_offset_return_label,
+ "While generating call-site debug info for a CALL insn, or a SEQUENCE\n\
+insn starting with a CALL, this target hook is invoked to compute the\n\
+offset to be added to the debug label emitted after the call to obtain\n\
+the return address that should be recorded as the return PC.",
+ int, (rtx_insn *call_insn),
+ hook_int_rtx_insn_0)
+
 DEFHOOK
 (call_args,
  "While generating RTL for a function call, this target hook is invoked once\n\


-- 
Alexandre Oliva, happy hacker                https://FSFLA.org/blogs/lxo/
   Free Software Activist                       GNU Toolchain Engineer
Disinformation flourishes because many people care deeply about injustice
but very few check the facts.  Ask me about <https://stallmansupport.org>

Reply via email to