https://gcc.gnu.org/g:d028794ce7aee71c515346528f36ff985afd4f9f
commit r17-4517-gd028794ce7aee71c515346528f36ff985afd4f9f Author: Richard Biener <[email protected]> Date: Mon Sep 21 10:30:43 2026 +0200 ipa/127479 - preserve PHI operand order with LTO streaming LTO streaming does not preserve the order of incoming edges and thus the order of PHI operands, disturbing the IPA ICF IPA reference comparison. The following rectifies this by streaming predecessor edges instead of successor edges (which leaves outgoing edge order not preserved) PR ipa/127479 * lto-streamer-out.cc (output_cfg): Output predecessor edges. * lto-streamer-in.cc (input_cfg): Input predecessor edges. * gcc.dg/lto/pr127479_0.c: New testcase. Diff: --- gcc/lto-streamer-in.cc | 12 +++++----- gcc/lto-streamer-out.cc | 11 +++++---- gcc/testsuite/gcc.dg/lto/pr127479_0.c | 45 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc index 901c32126568..668a5d3065b2 100644 --- a/gcc/lto-streamer-in.cc +++ b/gcc/lto-streamer-in.cc @@ -1113,25 +1113,25 @@ input_cfg (class lto_input_block *ib, class data_in *data_in, bb = make_new_block (fn, index); edge_count = streamer_read_uhwi (ib); + vec_safe_reserve (bb->preds, edge_count); /* Connect up the CFG. */ for (i = 0; i < edge_count; i++) { bitpack_d bp = streamer_read_bitpack (ib); - unsigned int dest_index = bp_unpack_var_len_unsigned (&bp); + unsigned int src_index = bp_unpack_var_len_unsigned (&bp); unsigned int edge_flags = bp_unpack_var_len_unsigned (&bp); - basic_block dest = BASIC_BLOCK_FOR_FN (fn, dest_index); + basic_block src = BASIC_BLOCK_FOR_FN (fn, src_index); - if (dest == NULL) - dest = make_new_block (fn, dest_index); + if (src == NULL) + src = make_new_block (fn, src_index); - edge e = make_edge (bb, dest, edge_flags); + edge e = make_edge (src, bb, edge_flags); data_in->location_cache.input_location_and_block (&e->goto_locus, &bp, ib, data_in); e->probability = profile_probability::stream_in (ib); if (!e->probability.initialized_p ()) full_profile = false; - } index = streamer_read_hwi (ib); diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc index 7afc2673ea2b..6e34d4bea90f 100644 --- a/gcc/lto-streamer-out.cc +++ b/gcc/lto-streamer-out.cc @@ -2334,12 +2334,15 @@ output_cfg (struct output_block *ob, struct function *fn) streamer_write_hwi (ob, bb->index); - /* Output the successors and the edge flags. */ - streamer_write_uhwi (ob, EDGE_COUNT (bb->succs)); - FOR_EACH_EDGE (e, ei, bb->succs) + /* Output the predecessors and the edge flags. We output + predecessors instead of successors so PHI argument order + is preserved when we reconstruct the CFG greedily in + input_cfg. */ + streamer_write_uhwi (ob, EDGE_COUNT (bb->preds)); + FOR_EACH_EDGE (e, ei, bb->preds) { bitpack_d bp = bitpack_create (ob->main_stream); - bp_pack_var_len_unsigned (&bp, e->dest->index); + bp_pack_var_len_unsigned (&bp, e->src->index); bp_pack_var_len_unsigned (&bp, e->flags); stream_output_location_and_block (ob, &bp, e->goto_locus); e->probability.stream_out (ob); diff --git a/gcc/testsuite/gcc.dg/lto/pr127479_0.c b/gcc/testsuite/gcc.dg/lto/pr127479_0.c new file mode 100644 index 000000000000..d96dedabd4d0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/pr127479_0.c @@ -0,0 +1,45 @@ +/* { dg-lto-do run } */ +/* { dg-lto-options { { -O2 -flto } } } */ + +static char false_object; +static char true_object; + +__attribute__((noinline)) +static +char *less_equal(long comparison) { + if (comparison == 0) goto return_true; + if (comparison < 0) goto return_true; + goto return_false; + +return_true: + return &true_object; + +return_false: + return &false_object; +} + +__attribute__((noinline)) +static +char *greater_equal(long comparison) { + if (comparison == 0) goto return_true; + if (comparison < 0) goto return_false; + goto return_true; + +return_true: + return &true_object; + +return_false: + return &false_object; +} + +int main(void) { + volatile long negative = -1; + + int correct = + less_equal(negative) == &true_object && + greater_equal(negative) == &false_object; + + if (!correct) + __builtin_abort (); + return 0; +}
