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;
+}

Reply via email to