Abstract-origin references in a function body can use decl-state slots.
SCC unification or prevailing-declaration replacement can make those slots
resolve to another input's trees, so subsequent tree-to-DIE lookups return
origins from that input.
Retain sparse slot-to-DIE references with each decl state. Use them when
reading a body to register its instances' input origins for late DWARF.
Copy these records alongside unread bodies in WPA.
Only LGEN output permits deriving the list from input tree slots. WPA
and incremental-link outputs encode explicit lists, including empty lists.
Re-emitted trees carry their saved origins in their per-tree records.
Bootstrapped and regression tested on x86_64-pc-linux-gnu. Also tested
with bootstrap-lto and the C/C++ LTO suites (gcc.dg/lto: 2012,
g++.dg/lto: 1866 expected passes), with no new regressions.
gcc/ChangeLog:
PR debug/126348
* lto-streamer.h (lto_decl_header): Add debug_ref_size.
(lto_decl_origin_ref): New.
(lto_in_decl_state, lto_out_decl_state): Add origin_refs.
(lto_decl_origin_ref_at): Declare.
* lto-streamer-in.cc (data_in::register_origin_refs): Resolve
decl-state slot references.
* lto-streamer-out.cc (copy_function_or_variable): Copy slot origins.
(lto_output_decl_origin_refs): New function.
(produce_asm_for_decls): Write the saved-reference stream.
* lto-section-in.cc (lto_decl_origin_ref_at): New function.
(lto_delete_in_decl_state, lto_free_function_in_decl_state):
Free origin records.
* lto-section-out.cc (lto_delete_out_decl_state): Likewise.
gcc/lto/ChangeLog:
PR debug/126348
* lto-common.cc (lto_read_in_decl_state): Derive slot references
from original inputs or read an explicit list.
(lto_read_decls): Read the saved-reference stream.
gcc/testsuite/ChangeLog:
PR debug/126348
* g++.dg/lto/pr126348_0.C: Also check the inline instance's origin.
* g++.dg/lto/pr126348-2_0.C: New test.
* g++.dg/lto/pr126348-2_1.C: Likewise.
* g++.dg/lto/pr126348-2_2.C: Likewise.
Signed-off-by: Longjun Luo <[email protected]>
---
gcc/lto-section-in.cc | 22 +++++++++++
gcc/lto-section-out.cc | 2 +
gcc/lto-streamer-in.cc | 8 ++++
gcc/lto-streamer-out.cc | 32 ++++++++++++++++
gcc/lto-streamer.h | 18 +++++++++
gcc/lto/lto-common.cc | 49 +++++++++++++++++++++----
gcc/testsuite/g++.dg/lto/pr126348-2_0.C | 17 +++++++++
gcc/testsuite/g++.dg/lto/pr126348-2_1.C | 21 +++++++++++
gcc/testsuite/g++.dg/lto/pr126348-2_2.C | 11 ++++++
gcc/testsuite/g++.dg/lto/pr126348_0.C | 1 +
10 files changed, 174 insertions(+), 7 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_0.C
create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_1.C
create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_2.C
diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc
index 04fafae0273..8da58e07506 100644
--- a/gcc/lto-section-in.cc
+++ b/gcc/lto-section-in.cc
@@ -391,6 +391,26 @@ lto_new_in_decl_state (void)
return ggc_cleared_alloc<lto_in_decl_state> ();
}
+/* Return the source DIE for slot INDEX in STATE, if one was saved. */
+
+const lto_die_ref *
+lto_decl_origin_ref_at (const lto_in_decl_state *state, unsigned index)
+{
+ unsigned first = 0, last = vec_safe_length (state->origin_refs);
+ while (first < last)
+ {
+ unsigned mid = first + (last - first) / 2;
+ const lto_decl_origin_ref &entry = (*state->origin_refs)[mid];
+ if (entry.index < index)
+ first = mid + 1;
+ else if (entry.index > index)
+ last = mid;
+ else
+ return &entry.ref;
+ }
+ return nullptr;
+}
+
/* Delete STATE and its components. */
void
@@ -400,6 +420,7 @@ lto_delete_in_decl_state (struct lto_in_decl_state *state)
for (i = 0; i < LTO_N_DECL_STREAMS; i++)
vec_free (state->streams[i]);
+ vec_free (state->origin_refs);
ggc_free (state);
}
@@ -426,6 +447,7 @@ lto_free_function_in_decl_state (struct lto_in_decl_state
*state)
int i;
for (i = 0; i < LTO_N_DECL_STREAMS; i++)
vec_free (state->streams[i]);
+ vec_free (state->origin_refs);
ggc_free (state);
}
diff --git a/gcc/lto-section-out.cc b/gcc/lto-section-out.cc
index a3cd3364418..134af4900c2 100644
--- a/gcc/lto-section-out.cc
+++ b/gcc/lto-section-out.cc
@@ -222,6 +222,8 @@ lto_delete_out_decl_state (struct lto_out_decl_state *state)
for (i = 0; i < LTO_N_DECL_STREAMS; i++)
lto_destroy_tree_ref_encoder (&state->streams[i]);
+ state->origin_refs.release ();
+
free (state);
}
diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc
index 50c1e1f84ae..981795c3655 100644
--- a/gcc/lto-streamer-in.cc
+++ b/gcc/lto-streamer-in.cc
@@ -1789,6 +1789,14 @@ data_in::register_origin_refs ()
const lto_die_ref *ref = &entry.ref;
if (entry.origin > 0 && die_refs)
ref = die_refs->get (entry.origin - 1);
+ else if (entry.origin < 0)
+ {
+ /* Decode the decl-state slot as in stream_read_tree_ref. */
+ unsigned ix = -entry.origin - 1;
+ gcc_checking_assert (!(ix & 1));
+ ref = lto_decl_origin_ref_at
+ (file_data->current_decl_state, ix / 2);
+ }
if (ref && ref->sym)
{
tree owner = streamer_tree_cache_get_tree (reader_cache, entry.index);
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index dd4d8666f82..a79774277df 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -2856,6 +2856,10 @@ copy_function_or_variable (struct symtab_node *node, int
output_order)
out_state->compressed = in_state->compressed;
gcc_assert (in_state);
+ unsigned nrefs = vec_safe_length (in_state->origin_refs);
+ out_state->origin_refs.reserve_exact (nrefs);
+ for (unsigned k = 0; k < nrefs; ++k)
+ out_state->origin_refs.quick_push ((*in_state->origin_refs)[k]);
for (i = 0; i < LTO_N_DECL_STREAMS; i++)
{
size_t n = vec_safe_length (in_state->streams[i]);
@@ -3320,6 +3324,26 @@ lto_output_decl_state_refs (struct output_block *ob,
write_global_references (ob, &state->streams[i]);
}
+/* Only LGEN output may derive slot origins. All subsequent outputs carry
+ explicit lists, including empty ones, since their trees may be shared. */
+
+static void
+lto_output_decl_origin_refs (output_block *ob, lto_output_stream *stream,
+ lto_out_decl_state *state)
+{
+ bool explicit_p = (flag_wpa
+ || flag_incremental_link == INCREMENTAL_LINK_LTO);
+ streamer_write_uhwi_stream (stream, explicit_p
+ ? state->origin_refs.length () + 1 : 0);
+ if (explicit_p)
+ for (const auto &entry : state->origin_refs)
+ {
+ streamer_write_uhwi_stream (stream, entry.index);
+ streamer_write_string (ob, stream, entry.ref.sym, true);
+ streamer_write_uhwi_stream (stream, entry.ref.off);
+ }
+}
+
/* Return the written size of STATE. */
@@ -3726,6 +3750,13 @@ produce_asm_for_decls (void)
the globals vector, so that it need not be resized as it is extended. */
header.num_nodes = -1;
+ lto_output_stream debug_refs = {};
+ lto_output_decl_origin_refs (ob, &debug_refs, out_state);
+ for (idx = 0; idx < num_fns; idx++)
+ lto_output_decl_origin_refs (ob, &debug_refs,
+ lto_function_decl_states[idx]);
+ header.debug_ref_size = debug_refs.total_size;
+
/* Compute the total size of all decl out states. */
decl_state_size = sizeof (int32_t);
decl_state_size += lto_out_decl_state_written_size (out_state);
@@ -3754,6 +3785,7 @@ produce_asm_for_decls (void)
}
lto_write_stream (ob->main_stream);
+ lto_write_stream (&debug_refs);
lto_write_stream (ob->string_stream);
lto_end_section ();
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index d5ba6edf89a..55b796d63b2 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -427,6 +427,10 @@ struct lto_decl_header : lto_simple_header_with_strings
/* Size of region for decl state. */
int32_t decl_state_size;
+ /* Slot provenance follows the tree stream, so raw function bodies can
+ retain different DIE references for slots naming the same tree. */
+ int32_t debug_ref_size;
+
/* Number of nodes in globals stream. */
int32_t num_nodes;
};
@@ -512,6 +516,13 @@ struct GTY (()) lto_die_ref
unsigned HOST_WIDE_INT off;
};
+/* Source DIE for a decl-state slot. */
+struct GTY (()) lto_decl_origin_ref
+{
+ unsigned index;
+ lto_die_ref ref;
+};
+
/* Origin encoding appended to FUNCTION_DECL and BLOCK records after their
ordinary DIE reference. LGEN permits TREE. WPA and incremental LTO
write DIE or NONE for newly streamed trees; copied bodies retain their
@@ -543,6 +554,9 @@ struct GTY((for_user)) lto_in_decl_state
/* Array of lto_in_decl_buffers to store type and decls streams. */
vec<tree, va_gc> *streams[LTO_N_DECL_STREAMS];
+ /* Source DIEs ordered by slot index, retained for unread bodies. */
+ vec<lto_decl_origin_ref, va_gc> *origin_refs;
+
/* If this in-decl state is associated with a function. FN_DECL
point to the FUNCTION_DECL. */
tree fn_decl;
@@ -580,6 +594,8 @@ struct lto_out_decl_state
seen so far and the indexes assigned to them. */
struct lto_tree_ref_encoder streams[LTO_N_DECL_STREAMS];
+ vec<lto_decl_origin_ref> origin_refs;
+
/* Encoder for cgraph nodes. */
lto_symtab_encoder_t symtab_node_encoder;
@@ -896,6 +912,8 @@ extern void lto_record_renamed_decl (struct
lto_file_decl_data *,
extern const char *lto_get_decl_name_mapping (struct lto_file_decl_data *,
const char *);
extern struct lto_in_decl_state *lto_new_in_decl_state (void);
+extern const lto_die_ref *lto_decl_origin_ref_at (const lto_in_decl_state *,
+ unsigned);
extern void lto_delete_in_decl_state (struct lto_in_decl_state *);
extern struct lto_in_decl_state *lto_get_function_in_decl_state (
struct lto_file_decl_data *, tree);
diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc
index e343f775655..faea3023b65 100644
--- a/gcc/lto/lto-common.cc
+++ b/gcc/lto/lto-common.cc
@@ -180,7 +180,8 @@ lto_splay_tree_new (void)
static const uint32_t *
lto_read_in_decl_state (class data_in *data_in, const uint32_t *data,
- struct lto_in_decl_state *state)
+ struct lto_in_decl_state *state,
+ lto_input_block *ib_refs)
{
uint32_t ix;
tree decl;
@@ -199,6 +200,10 @@ lto_read_in_decl_state (class data_in *data_in, const
uint32_t *data,
state->linemap_id = *data++;
state->fn_decl = decl;
+ /* Zero means derive the list from input slots; otherwise COUNT is the
+ number of saved references plus one, including an empty saved list. */
+ unsigned count = streamer_read_uhwi (ib_refs);
+ auto_vec<lto_decl_origin_ref> refs;
for (i = 0; i < LTO_N_DECL_STREAMS; i++)
{
@@ -207,14 +212,40 @@ lto_read_in_decl_state (class data_in *data_in, const
uint32_t *data,
vec_alloc (decls, size);
for (j = 0; j < size; j++)
- vec_safe_push (decls,
- streamer_tree_cache_get_tree (data_in->reader_cache,
- data[j]));
+ {
+ tree t = streamer_tree_cache_get_tree (data_in->reader_cache,
+ data[j]);
+ vec_safe_push (decls, t);
+ if (!count && state->fn_decl && data_in->die_refs)
+ if (const lto_die_ref *ref = data_in->die_refs->get (data[j]))
+ refs.safe_push ({ j, *ref });
+ }
state->streams[i] = decls;
data += size;
}
+ /* A copied WPA body still uses its input slot numbers, even when the
+ outgoing decl stream contains just one prevailing tree for them. */
+ for (unsigned n = 1; n < count; ++n)
+ {
+ lto_decl_origin_ref entry;
+ entry.index = streamer_read_uhwi (ib_refs);
+ entry.ref.sym = streamer_read_string (data_in, ib_refs);
+ entry.ref.off = streamer_read_uhwi (ib_refs);
+ if (debug_info_level > DINFO_LEVEL_NONE)
+ {
+ entry.ref.sym = IDENTIFIER_POINTER (get_identifier (entry.ref.sym));
+ refs.safe_push (entry);
+ }
+ }
+
+ /* These lists survive until the bodies are consumed. Avoid retaining
+ spare capacity in every function's decl state. */
+ vec_safe_reserve_exact (state->origin_refs, refs.length ());
+ for (const auto &ref : refs)
+ state->origin_refs->quick_push (ref);
+
return data;
}
@@ -1891,7 +1922,8 @@ lto_read_decls (struct lto_file_decl_data *decl_data,
const void *data,
const struct lto_decl_header *header = (const struct lto_decl_header *) data;
const int decl_offset = sizeof (struct lto_decl_header);
const int main_offset = decl_offset + header->decl_state_size;
- const int string_offset = main_offset + header->main_size;
+ const int debug_ref_offset = main_offset + header->main_size;
+ const int string_offset = debug_ref_offset + header->debug_ref_size;
class data_in *data_in;
unsigned int i;
const uint32_t *data_ptr, *data_end;
@@ -1899,6 +1931,8 @@ lto_read_decls (struct lto_file_decl_data *decl_data,
const void *data,
lto_input_block ib_main ((const char *) data + main_offset,
header->main_size, decl_data);
+ lto_input_block ib_refs ((const char *) data + debug_ref_offset,
+ header->debug_ref_size, decl_data);
data_in = lto_data_in_create (decl_data, (const char *) data + string_offset,
header->string_size, resolutions);
@@ -2026,7 +2060,7 @@ lto_read_decls (struct lto_file_decl_data *decl_data,
const void *data,
gcc_assert (num_decl_states > 0);
decl_data->global_decl_state = lto_new_in_decl_state ();
data_ptr = lto_read_in_decl_state (data_in, data_ptr,
- decl_data->global_decl_state);
+ decl_data->global_decl_state, &ib_refs);
/* Read in per-function decl states and enter them in hash table. */
decl_data->function_decl_states
@@ -2036,7 +2070,7 @@ lto_read_decls (struct lto_file_decl_data *decl_data,
const void *data,
{
struct lto_in_decl_state *state = lto_new_in_decl_state ();
- data_ptr = lto_read_in_decl_state (data_in, data_ptr, state);
+ data_ptr = lto_read_in_decl_state (data_in, data_ptr, state, &ib_refs);
lto_in_decl_state **slot
= decl_data->function_decl_states->find_slot (state, INSERT);
gcc_assert (*slot == NULL);
@@ -2045,6 +2079,7 @@ lto_read_decls (struct lto_file_decl_data *decl_data,
const void *data,
if (data_ptr != data_end)
internal_error ("bytecode stream: garbage at the end of symbols section");
+ gcc_checking_assert (ib_refs.p == ib_refs.len);
/* Set the current decl state to be the global state. */
decl_data->current_decl_state = decl_data->global_decl_state;
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_0.C
b/gcc/testsuite/g++.dg/lto/pr126348-2_0.C
new file mode 100644
index 00000000000..7dd5a3057b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_0.C
@@ -0,0 +1,17 @@
+/* PR debug/126348 */
+/* Verify that nested inlined subroutines and their direct children retain
+ the same input TU provenance. */
+/* { dg-lto-do link } */
+/* { dg-skip-if "No DWARF debug support" { hppa*-*-hpux* } } */
+/* { dg-skip-if "AIX DWARF5" { powerpc-ibm-aix* } } */
+/* { dg-lto-options { { -O2 -g -gdwarf-5 -dA -save-temps -flto
-flto-partition=one -fno-ipa-icf --param=max-inline-insns-auto=100 } } } */
+/* { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" } */
+/* { dg-final { scan-lto-assembler
"DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,3}\[^\n\]*DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin"
} } */
+
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int
+one (const Buffer &buffer)
+{
+ return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_1.C
b/gcc/testsuite/g++.dg/lto/pr126348-2_1.C
new file mode 100644
index 00000000000..5e871bdc800
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_1.C
@@ -0,0 +1,21 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+
+void
+fail (int, const char *, const Location &)
+{
+ __builtin_trap ();
+}
+
+void
+note (const char *, int)
+{
+ asm volatile ("" ::: "memory");
+}
+
+__attribute__ ((noinline)) int
+two (const Buffer &buffer)
+{
+ return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_2.C
b/gcc/testsuite/g++.dg/lto/pr126348-2_2.C
new file mode 100644
index 00000000000..f540eef4802
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_2.C
@@ -0,0 +1,11 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+__attribute__ ((noinline)) int two (const Buffer &);
+
+int
+main (int argc, char **)
+{
+ Buffer buffer = { argc };
+ return one (buffer) + two (buffer);
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348_0.C
b/gcc/testsuite/g++.dg/lto/pr126348_0.C
index ffe3f4486c5..99387845ae2 100644
--- a/gcc/testsuite/g++.dg/lto/pr126348_0.C
+++ b/gcc/testsuite/g++.dg/lto/pr126348_0.C
@@ -10,6 +10,7 @@
/* { dg-final { scan-lto-assembler
"DW_TAG_subprogram\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_AT_(?:low_pc|ranges)(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin"
} } */
/* { dg-final { scan-lto-assembler
"DW_TAG_subprogram\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_AT_(?:low_pc|ranges)(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin"
} } */
+/* { dg-final { scan-lto-assembler
"DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin"
} } */
#include "pr126348.h"
--
2.53.0