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
