https://gcc.gnu.org/g:1bcce941ebbb9ad97997a86432da88c8a9117d4b
commit r17-4243-g1bcce941ebbb9ad97997a86432da88c8a9117d4b Author: Kyrylo Tkachov <[email protected]> Date: Tue Sep 8 11:05:32 2026 +0200 middle-end: Use conservative store semantics for MEM_NOTRAP [PR127185] set_mem_attributes derives MEM_NOTRAP from tree_could_trap_p. That predicate describes a load. A store to readonly or otherwise unwritable memory can therefore be marked as nontrapping. RTL exception analysis can then omit an edge for a store that can raise a non-call exception. For example, the asm output in the test was marked nontrapping: (set (mem/u/c:SI (...) [ output+0 ]) ...) Use store trap semantics for it instead: (set (mem/u:SI (...) [ output+0 ]) ...) Add MAY_STORE_P to the memory attribute setters and use lhs_could_trap_p when it is true. Default MAY_STORE_P to true so that an unclassified caller cannot create an unsafe nontrapping claim. EXPAND_NORMAL is the only expand modifier that guarantees that a memory reference describes a load. Treat the other modifiers conservatively. This includes EXPAND_STACK_PARM and the direction-neutral EXPAND_MEMORY. Recompute MEM_NOTRAP on copied declaration RTL so that a normal load keeps the nontrapping form: (mem/u/c:SI (...) [ output+0 ]) This can lose MEM_NOTRAP precision for loads expanded with a direction-neutral modifier. Later patches restore that precision for get_memory_rtx, memory-only inline-assembly inputs, partial loads, and STRUB watermark loads. Bootstrapped and tested on aarch64-unknown-linux-gnu. Tested on x86_64-pc-linux-gnu. PR middle-end/127185 gcc/ChangeLog: * emit-rtl.cc (set_mem_attributes_minus_bitpos): Add MAY_STORE_P. Use lhs_could_trap_p when it is true. (set_mem_attributes): Add MAY_STORE_P and pass it through. * emit-rtl.h (set_mem_attributes): Add MAY_STORE_P with a conservative default. (set_mem_attributes_minus_bitpos): Likewise. * expr.cc (expand_assignment): Mark the destination as a store. (expand_expr_real_1): Treat only EXPAND_NORMAL as a known load. Set MEM_NOTRAP from the access direction. gcc/testsuite/ChangeLog: * g++.dg/eh/noncall-store-2.C: New test. * g++.dg/opt/mem-notrap-store-asm.C: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/emit-rtl.cc | 12 ++++---- gcc/emit-rtl.h | 11 ++++--- gcc/expr.cc | 20 +++++++++---- gcc/testsuite/g++.dg/eh/noncall-store-2.C | 39 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C | 23 +++++++++++++++ 5 files changed, 91 insertions(+), 14 deletions(-) diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc index a190429cf22c..d064b2a1064b 100644 --- a/gcc/emit-rtl.cc +++ b/gcc/emit-rtl.cc @@ -1959,11 +1959,12 @@ get_mem_align_offset (rtx mem, unsigned int align) /* Given REF (a MEM) and T, either the type of X or the expression corresponding to REF, set the memory attributes. OBJECTP is nonzero if we are making a new object of this type. BITPOS is nonzero if - there is an offset outstanding on T that will be applied later. */ + there is an offset outstanding on T that will be applied later. + MAY_STORE_P is true when REF can be the destination of a store. */ void set_mem_attributes_minus_bitpos (rtx ref, tree t, int objectp, - poly_int64 bitpos) + poly_int64 bitpos, bool may_store_p) { poly_int64 apply_bitpos = 0; tree type; @@ -2059,7 +2060,8 @@ set_mem_attributes_minus_bitpos (rtx ref, tree t, int objectp, t = TREE_OPERAND (t, 0); /* Note whether this expression can trap. */ - MEM_NOTRAP_P (ref) = !tree_could_trap_p (t); + MEM_NOTRAP_P (ref) + = !(may_store_p ? lhs_could_trap_p (t) : tree_could_trap_p (t)); base = get_base_address (t); if (base) @@ -2191,9 +2193,9 @@ set_mem_attributes_minus_bitpos (rtx ref, tree t, int objectp, } void -set_mem_attributes (rtx ref, tree t, int objectp) +set_mem_attributes (rtx ref, tree t, int objectp, bool may_store_p) { - set_mem_attributes_minus_bitpos (ref, t, objectp, 0); + set_mem_attributes_minus_bitpos (ref, t, objectp, 0, may_store_p); } /* Set the alias set of MEM to SET. */ diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 0b572580a98e..5fae3264cb5e 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -552,13 +552,16 @@ extern rtx offset_address (rtx, rtx, unsigned HOST_WIDE_INT); /* Given REF, a MEM, and T, either the type of X or the expression corresponding to REF, set the memory attributes. OBJECTP is nonzero - if we are making a new object of this type. */ -extern void set_mem_attributes (rtx, tree, int); + if we are making a new object of this type. MAY_STORE_P is true when REF + can be the destination of a store. */ +extern void set_mem_attributes (rtx, tree, int, bool = true); /* Similar, except that BITPOS has not yet been applied to REF, so if we alter MEM_OFFSET according to T then we should subtract BITPOS - expecting that it'll be added back in later. */ -extern void set_mem_attributes_minus_bitpos (rtx, tree, int, poly_int64); + expecting that it'll be added back in later. MAY_STORE_P is true when REF + can be the destination of a store. */ +extern void set_mem_attributes_minus_bitpos (rtx, tree, int, poly_int64, + bool = true); /* Return OFFSET if XEXP (MEM, 0) - OFFSET is known to be ALIGN bits aligned for 0 <= OFFSET < ALIGN / BITS_PER_UNIT, or diff --git a/gcc/expr.cc b/gcc/expr.cc index a64fa974048d..a9157dc5d415 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "rtx-vector-builder.h" #include "tree-pretty-print.h" +#include "tree-eh.h" #include "flags.h" #include "internal-fn.h" #include "gimple-range.h" @@ -6328,7 +6329,7 @@ expand_assignment (tree to, tree from, bool nontemporal) /* If the field is at offset zero, we could have been given the DECL_RTX of the parent struct. Don't munge it. */ to_rtx = shallow_copy_rtx (to_rtx); - set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos); + set_mem_attributes_minus_bitpos (to_rtx, to, 0, bitpos, true); if (volatilep) MEM_VOLATILE_P (to_rtx) = 1; } @@ -11523,6 +11524,10 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, tree ssa_name = NULL_TREE; gimple *g; + /* EXPAND_NORMAL is the only modifier that guarantees that a memory + reference describes a load. Use store semantics otherwise. */ + const bool may_store_p = modifier != EXPAND_NORMAL; + /* Some ABIs define padding bits in _BitInt uninitialized. Normally, RTL expansion sign/zero extends integral types with less than mode precision when reading from bit-fields and after arithmetic operations (see @@ -11760,6 +11765,11 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, else decl_rtl = copy_rtx (decl_rtl); + if (exp && MEM_P (decl_rtl)) + MEM_NOTRAP_P (decl_rtl) + = !(may_store_p + ? lhs_could_trap_p (exp) : tree_could_trap_p (exp)); + /* Record writes to register variables. */ if (modifier == EXPAND_WRITE && REG_P (decl_rtl) @@ -12078,7 +12088,7 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, op0 = addr_for_mem_ref (exp, as, true); op0 = memory_address_addr_space (mode, op0, as); temp = gen_rtx_MEM (mode, op0); - set_mem_attributes (temp, exp, 0); + set_mem_attributes (temp, exp, 0, may_store_p); set_mem_addr_space (temp, as); align = get_object_alignment (exp); if (modifier != EXPAND_WRITE @@ -12165,7 +12175,7 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, op0 = memory_address_addr_space (mode, op0, as); } temp = gen_rtx_MEM (mode, op0); - set_mem_attributes (temp, exp, 0); + set_mem_attributes (temp, exp, 0, may_store_p); set_mem_addr_space (temp, as); if (TREE_THIS_VOLATILE (exp)) MEM_VOLATILE_P (temp) = 1; @@ -12734,7 +12744,7 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, we should just honor its original memory attributes. */ if (!(TREE_CODE (tem) == SSA_NAME && (MEM_P (orig_op0) || CONSTANT_P (orig_op0)))) - set_mem_attributes (op0, exp, 0); + set_mem_attributes (op0, exp, 0, may_store_p); if (REG_P (XEXP (op0, 0))) mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0)); @@ -12866,7 +12876,7 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, if (op0 == orig_op0) op0 = copy_rtx (op0); - set_mem_attributes (op0, treeop0, 0); + set_mem_attributes (op0, treeop0, 0, may_store_p); if (REG_P (XEXP (op0, 0))) mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0)); diff --git a/gcc/testsuite/g++.dg/eh/noncall-store-2.C b/gcc/testsuite/g++.dg/eh/noncall-store-2.C new file mode 100644 index 000000000000..c095c71ca930 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/noncall-store-2.C @@ -0,0 +1,39 @@ +// Direct stores through reference and this parameters can throw non-call +// exceptions. + +// { dg-do compile } +// { dg-options "-O2 -fnon-call-exceptions -fdump-rtl-expand" } + +void +store_ref (int &value) +{ + try + { + value = 1; + } + catch (...) + { + __builtin_trap (); + } +} + +struct S +{ + int value; + void store (); +}; + +void +S::store () +{ + try + { + value = 1; + } + catch (...) + { + __builtin_trap (); + } +} + +// { dg-final { scan-rtl-dump-times {REG_EH_REGION \(const_int [1-9][0-9]*} 2 "expand" } } diff --git a/gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C b/gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C new file mode 100644 index 000000000000..ef26e2283317 --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C @@ -0,0 +1,23 @@ +// Keep a normal load nontrapping. A memory asm output is a store even when +// expansion must return a MEM. + +// { dg-do compile } +// { dg-options "-O2 -fnon-call-exceptions -fdump-rtl-expand" } + +extern const int output; + +int +load () +{ + return output; +} + +void +store () +{ + asm volatile ("" : "=m" (const_cast<int &> (output))); +} + +// { dg-final { scan-rtl-dump {\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*\[[0-9]+ output\+0} "expand" } } +// { dg-final { scan-rtl-dump {\(set[ \t\n]+\(mem(?:/[a-z])*:[^\n\r]*\[[0-9]+ output\+0} "expand" } } +// { dg-final { scan-rtl-dump-not {\(set[ \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:[^\n\r]*\[[0-9]+ output\+0} "expand" } }
