Since gcc-15 combine can produce moves from an auto-inc/dec source to
a destination that uses the same register, causing wrong code on m68k.
Both glibc and Python are known to be miscompiled due to this.
This adjusts the m68k backend to reject such moves.

For glibc the difference is

-       move.l -(%a0),(%a0,%d0.l)
+       lea (%a0,%d0.l),%a1
+       move.l -(%a0),(%a1)

Python has a similar problem, but with a post-inc source operand.

Bootstrapped and regression tested on m68k-linux-gnu, no regressions.

Ok for trunk? And maybe gcc-16/15 after a week?

gcc/

        2026-07-07  Mikael Pettersson  <[email protected]>

        PR rtl-optimization/123853
        PR target/123076
        * config/m68k/m68k-protos.h (check_move_simode): Declare.
        * config/m68k/m68k.cc (check_move_simode): New, reject move from
        auto-inc/dec source mem whose reg occurs in the destination address.
        * config/m68k/m68k.md (*movsi_m68k): Add check_move_simode to
        condition.
        (*movsi_m68k2): Likewise.
---
Changes since v1:
 - also reject POST_INC sources, required for Python build

 gcc/config/m68k/m68k-protos.h |  1 +
 gcc/config/m68k/m68k.cc       | 25 +++++++++++++++++++++++++
 gcc/config/m68k/m68k.md       |  4 ++--
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/gcc/config/m68k/m68k-protos.h b/gcc/config/m68k/m68k-protos.h
index abb8e9b061d..248414eb167 100644
--- a/gcc/config/m68k/m68k-protos.h
+++ b/gcc/config/m68k/m68k-protos.h
@@ -26,6 +26,7 @@ extern HOST_WIDE_INT m68k_initial_elimination_offset (int 
from, int to);
 extern void split_di (rtx[], int, rtx[], rtx[]);
 
 extern bool valid_mov3q_const (HOST_WIDE_INT);
+extern bool check_move_simode (const rtx *);
 extern const char *output_move_simode (rtx *);
 extern const char *output_move_himode (rtx *);
 extern const char *output_move_qimode (rtx *);
diff --git a/gcc/config/m68k/m68k.cc b/gcc/config/m68k/m68k.cc
index b6134892883..dffc603d257 100644
--- a/gcc/config/m68k/m68k.cc
+++ b/gcc/config/m68k/m68k.cc
@@ -3227,6 +3227,31 @@ valid_mov3q_const (HOST_WIDE_INT i)
   return TARGET_ISAB && (i == -1 || IN_RANGE (i, 1, 7));
 }
 
+/* Return true if OPERANDS[] are valid for output_move_simode.
+   In particular, if OPERANDS[1] is a MEM with PRE_DEC or POST_INC
+   adressing and its REG is mentioned in OPERANDS[0], it's invalid.  */
+
+bool
+check_move_simode (const rtx *operands)
+{
+  rtx src = operands[1];
+  if (MEM_P (src))
+    {
+      rtx src1 = XEXP (src, 0);
+      if (GET_CODE (src1) == PRE_DEC || GET_CODE (src1) == POST_INC)
+       {
+         rtx src2 = XEXP (src1, 0);
+         if (REG_P (src2))
+           {
+             rtx dst = operands[0];
+             if (reg_overlap_mentioned_p (src2, dst))
+               return false;
+           }
+       }
+    }
+  return true;
+}
+
 /* Return an instruction to move CONST_INT OPERANDS[1] into OPERANDS[0].
    I is the value of OPERANDS[1].  */
 
diff --git a/gcc/config/m68k/m68k.md b/gcc/config/m68k/m68k.md
index e77ac13cf1b..f8b6a2f79dd 100644
--- a/gcc/config/m68k/m68k.md
+++ b/gcc/config/m68k/m68k.md
@@ -993,7 +993,7 @@
   ;; We don't allow f-regs since fixed point cannot go in them.
   [(set (match_operand:SI 0 "nonimmediate_operand" "=g,d,a<")
         (match_operand:SI 1 "general_src_operand" "damSnT,n,i"))]
-  "!TARGET_COLDFIRE && reload_completed"
+  "!TARGET_COLDFIRE && reload_completed && check_move_simode (operands)"
 {
   return output_move_simode (operands);
 }
@@ -1006,7 +1006,7 @@
   [(set (match_operand:SI 0 "nonimmediate_operand" "=g,d,a<")
         (match_operand:SI 1 "general_src_operand" "damSKT,n,i"))]
 
-  "!TARGET_COLDFIRE"
+  "!TARGET_COLDFIRE && check_move_simode (operands)"
 {
   return output_move_simode (operands);
 }
-- 
2.54.0

Reply via email to