https://gcc.gnu.org/g:a0cbca7aefb94b0241e706fe737e0326eead413d
commit r17-4246-ga0cbca7aefb94b0241e706fe737e0326eead413d Author: Kyrylo Tkachov <[email protected]> Date: Mon Sep 14 10:33:55 2026 +0200 strub: Preserve load MEM_NOTRAP for watermarks [PR127185] expand_builtin_strub_update expands a watermark with EXPAND_MEMORY because it needs a MEM. The same MEM is first loaded for a comparison and can then be the destination of a store. EXPAND_MEMORY has conservative store semantics, so the load loses its valid MEM_NOTRAP flag: (set (reg:DI ...) (mem/f:DI (... watermark_ptr ...))) (set (mem/f:DI (... watermark_ptr ...)) (reg:DI ...)) Make a shallow copy for the load and derive its MEM_NOTRAP flag with load trap semantics. Keep the original MEM for the store: (set (reg:DI ...) (mem/f/c:DI (... watermark_ptr ...))) (set (mem/f:DI (... watermark_ptr ...)) (reg:DI ...)) This keeps the memory-result requirement and does not mark the conditional store as nontrapping. Apply load trap semantics to the load-only watermarks in expand_builtin_strub_leave as well. Tighten the existing update test so that it identifies the load and store positions independently of the target mode and MEM flag order. Add a nested at-calls test that checks the incoming watermark loads from both update and leave. Bootstrapped and tested on aarch64-unknown-linux-gnu. Tested on x86_64-pc-linux-gnu. PR middle-end/127185 gcc/ChangeLog: * builtins.cc (expand_builtin_strub_update): Use a separate MEM with load trap semantics for each watermark load. (expand_builtin_strub_leave): Use load trap semantics for watermark MEMs. gcc/testsuite/ChangeLog: * c-c++-common/strub-mem-notrap.c: Tighten the load and store scans. * c-c++-common/strub-mem-notrap-leave.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/builtins.cc | 11 +++++++++-- .../c-c++-common/strub-mem-notrap-leave.c | 23 ++++++++++++++++++++++ gcc/testsuite/c-c++-common/strub-mem-notrap.c | 7 ++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/gcc/builtins.cc b/gcc/builtins.cc index 02170d74ae9b..f2e21a57a59d 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "memmodel.h" #include "gimple.h" +#include "tree-eh.h" #include "predict.h" #include "tm_p.h" #include "stringpool.h" @@ -5617,7 +5618,9 @@ expand_builtin_strub_update (tree exp) build_int_cst (TREE_TYPE (wmptr), 0)); rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); - rtx wmarkr = force_reg (ptr_mode, wmark); + rtx wmark_load = shallow_copy_rtx (wmark); + MEM_NOTRAP_P (wmark_load) = !tree_could_trap_p (wmtree); + rtx wmarkr = force_reg (ptr_mode, wmark_load); rtx_code_label *lab = gen_label_rtx (); do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED, @@ -5642,7 +5645,9 @@ expand_builtin_strub_update (tree exp) wmtree = fold_build2 (MEM_REF, wmtype, wmptr, build_int_cst (TREE_TYPE (wmptr), 0)); wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); - wmarkr = force_reg (ptr_mode, wmark); + wmark_load = shallow_copy_rtx (wmark); + MEM_NOTRAP_P (wmark_load) = !tree_could_trap_p (wmtree); + wmarkr = force_reg (ptr_mode, wmark_load); do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED, ptr_mode, NULL_RTX, lab, NULL, @@ -5677,6 +5682,7 @@ expand_builtin_strub_leave (tree exp) tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, build_int_cst (TREE_TYPE (wmptr), 0)); rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + MEM_NOTRAP_P (wmark) = !tree_could_trap_p (wmtree); stktop = force_reg (ptr_mode, wmark); } @@ -5688,6 +5694,7 @@ expand_builtin_strub_leave (tree exp) tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, build_int_cst (TREE_TYPE (wmptr), 0)); rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + MEM_NOTRAP_P (wmark) = !tree_could_trap_p (wmtree); rtx wmarkr = force_reg (ptr_mode, wmark); diff --git a/gcc/testsuite/c-c++-common/strub-mem-notrap-leave.c b/gcc/testsuite/c-c++-common/strub-mem-notrap-leave.c new file mode 100644 index 000000000000..246c4c2088f3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-mem-notrap-leave.c @@ -0,0 +1,23 @@ +// Keep watermark loads nontrapping when a strub context calls another. + +// { dg-do compile } +// { dg-options "-O2 -fstrub=strict -fnon-call-exceptions" } +// { dg-additional-options "-fdelete-null-pointer-checks -fdump-rtl-expand" } +// { dg-require-effective-target strub } +// { dg-skip-if "" keeps_null_pointer_checks } + +extern int callee (int) + __attribute__ ((__strub__ ("at-calls"), __nothrow__)); +volatile int sink; + +__attribute__ ((__strub__ ("at-calls"), __noinline__)) +int +caller (int value) +{ + int result = callee (value); + sink = result; + return result + 1; +} + +// The incoming watermark is loaded once by update and once by leave. +// { dg-final { scan-rtl-dump-times {\(set[ \t\n]+\(reg(?:/[a-z])*:[^\n\r]*\)[ \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*[*][.]strub[.]watermark_ptr} 2 "expand" } } diff --git a/gcc/testsuite/c-c++-common/strub-mem-notrap.c b/gcc/testsuite/c-c++-common/strub-mem-notrap.c index ef9bbc39535a..e0f4fe7b34d8 100644 --- a/gcc/testsuite/c-c++-common/strub-mem-notrap.c +++ b/gcc/testsuite/c-c++-common/strub-mem-notrap.c @@ -16,6 +16,7 @@ scrubbed (int value) return sink; } -/* The watermark load is nontrapping. The conditional store can trap. */ -/* { dg-final { scan-rtl-dump {\(mem/f/c:[^\n]*watermark_ptr} "expand" } } */ -/* { dg-final { scan-rtl-dump {\(mem/f:[^\n]*watermark_ptr} "expand" } } */ +// The watermark load is nontrapping. The conditional store can trap. +// { dg-final { scan-rtl-dump {\(set[ \t\n]+\(reg(?:/[a-z])*:[^\n\r]*\)[ \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*[*][.]strub[.]watermark_ptr} "expand" } } +// { dg-final { scan-rtl-dump {\(set[ \t\n]+\(mem(?:/[a-z])*:[^\n\r]*[*][.]strub[.]watermark_ptr} "expand" } } +// { dg-final { scan-rtl-dump-not {\(set[ \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*[*][.]strub[.]watermark_ptr} "expand" } }
