https://gcc.gnu.org/g:ca5cffec066fb8a7edd13dd12792505739381b42

commit r17-4751-gca5cffec066fb8a7edd13dd12792505739381b42
Author: Michiel Derhaeg <[email protected]>
Date:   Wed Jun 24 21:45:30 2026 +0000

    arc: Fix invalid register index addressing for uncached stores [PR126157]
    
    `stdi<mode>` incorrectly uses a too liberal predicate for the
    destination operand, producing something like this: stb.di r2,[r13,r1]
    These [reg,reg] operands are not valid.
    
    This bug appeared because the introduction of
    `-ffuse-ops-with-volatile-access` in r16-5947-ga6c50ec2c6ebcb.  Before
    its existence, recog/combine would've disallowed `(plus (reg) (reg))` as
    the destination operand.
    
    POST_INC (and friends) is now produced as well. Didn't try implementing
    that for *stdidi_split and simply disallowed it instead.
    
            PR target/126157
    
    gcc/ChangeLog:
    
            * config/arc/arc.cc (prepare_move_operands): Force the address of
            an uncached store into a register when it is not a valid store
            destination.
            * config/arc/arc.md (stdi<mode>): Use move_dest_operand for the
            destination operand.
            (*stdidi_split): Use non_mod_move_dest_operand for the destination
            operand.
            * config/arc/predicates.md (non_mod_move_dest_operand): New
            predicate that disallows PRE/POST_INC/DEC.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/arc/uncached-10.c: New test.
            * gcc.target/arc/uncached-11.c: New test.
    
    Signed-off-by: Alexandru Turjan <[email protected]>
    Signed-off-by: Michiel Derhaeg <[email protected]>

Diff:
---
 gcc/config/arc/arc.cc                      |  9 ++++++
 gcc/config/arc/arc.md                      |  4 +--
 gcc/config/arc/predicates.md               | 17 ++++++++++
 gcc/testsuite/gcc.target/arc/uncached-10.c | 50 ++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/arc/uncached-11.c | 50 ++++++++++++++++++++++++++++++
 5 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/gcc/config/arc/arc.cc b/gcc/config/arc/arc.cc
index 28e6f0b97296..e1a4aa6cfc26 100644
--- a/gcc/config/arc/arc.cc
+++ b/gcc/config/arc/arc.cc
@@ -9286,6 +9286,15 @@ prepare_move_operands (rtx *operands, machine_mode mode)
        {
          if (!REG_P (operands[1]))
            operands[1] = force_reg (mode, operands[1]);
+
+         if (!move_dest_operand (operands[0], mode))
+           {
+             rtx tmp0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+             rtx tmp1 = change_address (operands[0], mode, tmp0);
+             MEM_COPY_ATTRIBUTES (tmp1, operands[0]);
+             operands[0] = tmp1;
+           }
+
          emit_insn (gen_rtx_UNSPEC_VOLATILE
                     (VOIDmode, gen_rtvec (2, operands[0], operands[1]),
                      VUNSPEC_ARC_STDI));
diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md
index fb905cfef058..b4b4a6a3e8e2 100644
--- a/gcc/config/arc/arc.md
+++ b/gcc/config/arc/arc.md
@@ -4786,7 +4786,7 @@ archs4x, archs4xd"
   [(set_attr "type" "load")])
 
 (define_insn "stdi<mode>"
-  [(unspec_volatile [(match_operand:ALLI 0 "memory_operand"    "m,m,Usc")
+  [(unspec_volatile [(match_operand:ALLI 0 "move_dest_operand" "m,m,Usc")
                     (match_operand:ALLI 1 "nonmemory_operand" "r,Cm3,i")]
                    VUNSPEC_ARC_STDI)]
   ""
@@ -4795,7 +4795,7 @@ archs4x, archs4xd"
    (set_attr "type" "store")])
 
 (define_insn_and_split "*stdidi_split"
-  [(unspec_volatile [(match_operand:DI 0 "memory_operand"   "m")
+  [(unspec_volatile [(match_operand:DI 0 "non_mod_move_dest_operand" "m")
                     (match_operand:DI 1 "register_operand" "r")]
                    VUNSPEC_ARC_STDI)]
   "!TARGET_LL64"
diff --git a/gcc/config/arc/predicates.md b/gcc/config/arc/predicates.md
index 1b7f53a70ff9..382012dc68df 100644
--- a/gcc/config/arc/predicates.md
+++ b/gcc/config/arc/predicates.md
@@ -775,3 +775,20 @@
   (ior (match_test "register_operand (op, mode)")
        (and (match_code "const_int, symbol_ref")
            (match_test "!optimize_size"))))
+
+(define_predicate "non_mod_move_dest_operand"
+  (match_operand 0 "move_dest_operand")
+{
+  rtx addr = XEXP (op, 0);
+  enum rtx_code code = GET_CODE (addr);
+
+  if (code == POST_INC
+      || code == PRE_INC
+      || code == POST_DEC
+      || code == PRE_DEC
+      || code == POST_MODIFY
+      || code == PRE_MODIFY)
+    return false;
+
+  return true;
+})
diff --git a/gcc/testsuite/gcc.target/arc/uncached-10.c 
b/gcc/testsuite/gcc.target/arc/uncached-10.c
new file mode 100644
index 000000000000..767cd484fb66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-10.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mno-ll64" } */
+
+#ifndef __uncached
+#define __uncached __attribute__((uncached))
+#endif
+
+volatile char *
+pack_byte_ptr (volatile char *p, const void *s, int len)
+{
+    volatile __uncached char *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const char *)s)[i];
+    return p;
+}
+
+volatile short *
+pack_halfword_ptr (volatile short *p, const void *s, int len)
+{
+    volatile __uncached short *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const short *)s)[i];
+    return p;
+}
+
+volatile int *
+pack_word_ptr (volatile int *p, const void *s, int len)
+{
+    volatile __uncached int *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const int *)s)[i];
+    return p;
+}
+
+volatile long long *
+pack_doubleword_ptr (volatile long long *p, const void *s, int len)
+{
+    volatile __uncached long long *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const long long *)s)[i];
+    return p;
+}
+
+/* { dg-final { scan-assembler "stb.*\\.di" } } */
+/* { dg-final { scan-assembler "\\m(sth|stw).*\\.di" } } */
+/* { dg-final { scan-assembler "st.*\\.di" } } */
+
+/* { dg-final { scan-assembler-not 
{stb.*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */
+/* { dg-final { scan-assembler-not 
{\m(sth|stw).*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */
+/* { dg-final { scan-assembler-not 
{st.*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-11.c 
b/gcc/testsuite/gcc.target/arc/uncached-11.c
new file mode 100644
index 000000000000..44efeb2cb054
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-11.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -mno-ll64" } */
+
+#ifndef __uncached
+#define __uncached __attribute__((uncached))
+#endif
+
+volatile char *
+pack_byte_ptr (volatile char *p, const void *s, int len)
+{
+    volatile __uncached char *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const char *)s)[i];
+    return p;
+}
+
+volatile short *
+pack_halfword_ptr (volatile short *p, const void *s, int len)
+{
+    volatile __uncached short *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const short *)s)[i];
+    return p;
+}
+
+volatile int *
+pack_word_ptr (volatile int *p, const void *s, int len)
+{
+    volatile __uncached int *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const int *)s)[i];
+    return p;
+}
+
+volatile long long *
+pack_doubleword_ptr (volatile long long *p, const void *s, int len)
+{
+    volatile __uncached long long *buf = p + 4;
+    for (int i = 0; i < len; i++)
+        buf[i] = ((const long long *)s)[i];
+    return p;
+}
+
+/* { dg-final { scan-assembler "stb.*\\.di" } } */
+/* { dg-final { scan-assembler "\\m(sth|stw).*\\.di" } } */
+/* { dg-final { scan-assembler "st.*\\.di" } } */
+
+/* { dg-final { scan-assembler-not 
{stb.*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */
+/* { dg-final { scan-assembler-not 
{\m(sth|stw).*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */
+/* { dg-final { scan-assembler-not 
{st.*\.di\s+r[0-9]+,\s*\[r[0-9]+,\s*r[0-9]+\]} } } */

Reply via email to