https://gcc.gnu.org/g:2791799fe11f886736869e3aff093e0c14fb10b8

commit r17-3183-g2791799fe11f886736869e3aff093e0c14fb10b8
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Aug 6 22:31:27 2026 +0200

    pair-fusion: Verify that a fused pair really is adjacent
    
    fuse_pair addresses the second arm of a pair as the first plus the access 
size.
    For a pair found through a MEM_EXPR base that distance comes from the 
MEM_EXPR
    offsets rather than from the addresses, so two objects sharing one MEM_EXPR
    read as adjacent when they are not, and the second access is redirected into
    the first object.  That is how PR121957, PR123625 and PR126405 each became
    wrong code, in each case silently.
    
    When both accesses already use the same base register, which is what two 
stack
    slots give, their offsets relative to that base are computed here anyway, so
    compare them.  A break in the invariant out-of-SSA maintains is then an ICE 
in
    the pass that acts on it, whichever producer let it through.
    
    Bootstrapped and tested on aarch64-none-linux-gnu.
    Ok for trunk?
    Thanks,
    Kyrill
    
    gcc/ChangeLog:
    
            * pair-fusion.cc (pair_fusion_bb_info::fuse_pair): Check that the
            two arms of the pair are one access size apart.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/pair-fusion.cc | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/gcc/pair-fusion.cc b/gcc/pair-fusion.cc
index 915497c97d32..03a64c226ef9 100644
--- a/gcc/pair-fusion.cc
+++ b/gcc/pair-fusion.cc
@@ -1786,6 +1786,13 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
       gcc_checking_assert (base_regno == REGNO (base));
     }
 
+  // The pair insn addresses its second arm as the first plus ACCESS_SIZE, so
+  // the two accesses have to be that far apart for real.  A pair found through
+  // a MEM_EXPR base is only as good as that base: two objects sharing one
+  // MEM_EXPR look adjacent when they are not, and the second access then
+  // lands in the first object.  Catch that here rather than in the output.
+  gcc_checking_assert (known_eq (offsets[1], offsets[0] + access_size));
+
   // If either of the original insns had writeback, but the resulting pair insn
   // does not (can happen e.g. in the load pair edge case above, or if the
   // writeback effects cancel out), then drop the def (s) of the base register

Reply via email to