Since gcc-15 combine can produce moves from a PRE_DEC source to a
destination that uses the same register, causing wrong code on m68k.
In particular the glibc build is broken.  This adjusts the m68k
backend to reject such moves:

        move.l -(%a0),(%a0,%d0.l)

and instead emit:

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

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
        * config/m68k/m68k-protos.h (check_move_simode): Declare.
        * config/m68k/m68k.cc (check_move_simode): New, reject moves from
        a pre-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.
---
 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..e95b83f5e18 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 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)
+       {
+         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