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" } } */
