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

commit r17-4245-gdbda2911da5be5e11b7c776893fbc96938acf3ce
Author: Kyrylo Tkachov <[email protected]>
Date:   Mon Sep 14 11:56:09 2026 +0200

    middle-end: Restore MEM_NOTRAP for memory-only loads [PR127185]
    
    EXPAND_MEMORY requires a MEM result but does not state whether the access
    is a load or a store.  Its conservative store semantics avoid unsafe
    MEM_NOTRAP claims, but two known load callers lose valid nontrapping
    information.
    
    On this patch's parent, a memory-only inline asm input is:
    
      (mem/u:SI (...) [ input+0 ])
    
    Restore its load semantics:
    
      (mem/u/c:SI (...) [ input+0 ])
    
    The partial-load expander has the same problem.  It uses EXPAND_WRITE even
    though its MEM is the source of a masked or length-limited load.  Its source
    MEM changes from:
    
      (mem/u:V4SI (...))
    
    to:
    
      (mem/u/c:V4SI (...))
    
    Use EXPAND_MEMORY for partial loads because they require a MEM, then set
    MEM_NOTRAP from tree_could_trap_p at both caller sites.
    
    Bootstrapped and tested on aarch64-unknown-linux-gnu.  Tested on
    x86_64-pc-linux-gnu.
    
            PR middle-end/127185
    
    gcc/ChangeLog:
    
            * cfgexpand.cc (expand_asm_stmt): Restore load MEM_NOTRAP for memory
            inputs.
            * internal-fn.cc: Include tree-eh.h.
            (expand_partial_load_optab_fn): Expand the source as memory and
            restore load MEM_NOTRAP.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/opt/mem-notrap-load-asm.C: New test.
            * gcc.target/i386/mem-notrap-mask-load.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/cfgexpand.cc                                   |  5 ++++-
 gcc/internal-fn.cc                                 |  4 +++-
 gcc/testsuite/g++.dg/opt/mem-notrap-load-asm.C     | 14 +++++++++++++
 .../gcc.target/i386/mem-notrap-mask-load.c         | 23 ++++++++++++++++++++++
 4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index 245ba357b5ef..4bd311b11a34 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -3781,7 +3781,10 @@ expand_asm_stmt (gasm *stmt)
       if (GET_CODE (op) == CONCAT)
        op = force_reg (GET_MODE (op), op);
       else if (MEM_P (op))
-       op = validize_mem (op);
+       {
+         op = validize_mem (op);
+         MEM_NOTRAP_P (op) = !tree_could_trap_p (val);
+       }
 
       if (asm_operand_ok (op, constraint, NULL) <= 0)
        {
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index eae0ca9bf51b..3fdf89e0e6e1 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "rtl.h"
 #include "tree.h"
+#include "tree-eh.h"
 #include "gimple.h"
 #include "predict.h"
 #include "stringpool.h"
@@ -3144,8 +3145,9 @@ expand_partial_load_optab_fn (internal_fn ifn, gcall 
*stmt, convert_optab optab)
     icode = convert_optab_handler (optab, TYPE_MODE (type),
                                   TYPE_MODE (TREE_TYPE (maskt)));
 
-  mem = expand_expr (rhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  mem = expand_expr (rhs, NULL_RTX, VOIDmode, EXPAND_MEMORY);
   gcc_assert (MEM_P (mem));
+  MEM_NOTRAP_P (mem) = !tree_could_trap_p (rhs);
   /* The built MEM_REF does not accurately reflect that the load
      is only partial.  Clear it.  */
   set_mem_expr (mem, NULL_TREE);
diff --git a/gcc/testsuite/g++.dg/opt/mem-notrap-load-asm.C 
b/gcc/testsuite/g++.dg/opt/mem-notrap-load-asm.C
new file mode 100644
index 000000000000..797d6793c0d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/mem-notrap-load-asm.C
@@ -0,0 +1,14 @@
+// A memory-only asm input is a load even though expansion must return a MEM.
+
+// { dg-do compile }
+// { dg-options "-O2 -fnon-call-exceptions -fdump-rtl-expand" }
+
+extern const int input;
+
+void
+load ()
+{
+  asm volatile ("" : : "m" (input));
+}
+
+// { dg-final { scan-rtl-dump {\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*\[[0-9]+ 
input\+0} "expand" } }
diff --git a/gcc/testsuite/gcc.target/i386/mem-notrap-mask-load.c 
b/gcc/testsuite/gcc.target/i386/mem-notrap-mask-load.c
new file mode 100644
index 000000000000..bc510e6ad4f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/mem-notrap-mask-load.c
@@ -0,0 +1,23 @@
+/* A masked load is a read even though its expander requires a MEM.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O -fgimple -mavx2 -mno-avx512f -fnon-call-exceptions 
-fdump-tree-optimized -fdump-rtl-expand" } */
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+extern const v4si values;
+
+v4si __GIMPLE (ssa)
+load (v4si mask)
+{
+  v4si result;
+
+  __BB(2):
+  result_2 = .MASK_LOAD (&values, _Literal (v4si *) 128,
+                       mask_1(D),
+                       _Literal (v4si) { 0, 0, 0, 0 });
+  return result_2;
+}
+
+/* { dg-final { scan-tree-dump {\.MASK_LOAD} "optimized" } } */
+/* { dg-final { scan-rtl-dump-times {\(mem/u/c:V4SI} 1 "expand" } } */

Reply via email to