On Fri, 4 Sep 2026, [email protected] wrote:
> From: Kyrylo Tkachov <[email protected]>
>
> 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.
>
> Pass the access direction through set_mem_attributes and use
> lhs_could_trap_p for stores. Propagate store semantics from EXPAND_WRITE,
> assignment destinations, and built-in string destinations. Make every
> get_memory_rtx caller state its access direction.
>
> Some expanders use EXPAND_MEMORY because they require a MEM result. Apply
> store semantics explicitly to asm outputs and to destination copies of strub
> watermarks. The partial-load expander instead used EXPAND_WRITE for a load,
> so use EXPAND_MEMORY there.
>
> Bootstrapped and tested on aarch64-unknown-linux-gnu. Tested on
> x86_64-pc-linux-gnu.
Is it worth the trouble? Esp. being not conservative with the
defaulted argument?
+++ b/gcc/cfgexpand.cc
@@ -3688,7 +3688,10 @@ expand_asm_stmt (gasm *stmt)
op = expand_expr (val, NULL_RTX, VOIDmode,
!allows_reg ? EXPAND_MEMORY : EXPAND_WRITE);
if (MEM_P (op))
- op = validize_mem (op);
+ {
+ op = validize_mem (op);
+ MEM_NOTRAP_P (op) = !lhs_could_trap_p (val);
+ }
this seems to also do more than we did before.
I'd like to see this split up, go with a conservative default
for the argument.
- set_mem_attributes (temp, exp, 0);
+ set_mem_attributes (temp, exp, 0, modifier == EXPAND_WRITE);
I don't think modifier != EXPAND_WRITE means this isn't part of a store?
For example EXPAND_STACK_PARM, and possibly even EXPAND_MEMORY.
Richard.
>
> gcc/ChangeLog:
>
> * builtins.cc (get_memory_rtx): Add STORE_P. Pass it to
> set_mem_attributes.
> (expand_builtin_memory_copy_args): Mark destination memory as a store
> and source memory as a load.
> (expand_movstr): Likewise.
> (expand_builtin_strncpy): Likewise.
> (expand_builtin_memset_args): Likewise.
> (expand_builtin_memcmp): Mark both memory operands as loads.
> (expand_builtin_strcmp): Likewise.
> (expand_builtin_strncmp): Likewise.
> (expand_builtin_strub_enter): Expand the watermark as a store.
> (expand_builtin_strub_update): Apply store semantics to destination
> copies of the watermark.
> (inline_string_cmp): Mark the memory operand as a load.
> * builtins.h (get_memory_rtx): Add STORE_P.
> * cfgexpand.cc (expand_asm_stmt): Apply store semantics to memory
> outputs.
> * emit-rtl.cc (set_mem_attributes_minus_bitpos): Add STORE_P. Use
> lhs_could_trap_p for stores.
> (set_mem_attributes): Add STORE_P and pass it through.
> * emit-rtl.h (set_mem_attributes): Add STORE_P.
> (set_mem_attributes_minus_bitpos): Likewise.
> * expr.cc (expand_assignment): Mark destination memory as a store.
> (expand_expr_real_1): Use lhs_could_trap_p for store MEMs. Pass the
> access direction to memory attribute generation.
> * internal-fn.cc (expand_partial_load_optab_fn): Expand the source as
> memory, not as a store.
> (expand_RAWMEMCHR): Mark the memory operand as a load.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/eh/noncall-store-2.C: New test.
> * g++.dg/opt/mem-notrap-store-asm.C: New test.
> * g++.dg/opt/mem-notrap-store-builtin.C: New test.
> * gcc.target/i386/mem-notrap-mask-load.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/builtins.cc | 46 +++++++++++--------
> gcc/builtins.h | 2 +-
> gcc/cfgexpand.cc | 5 +-
> gcc/emit-rtl.cc | 12 +++--
> gcc/emit-rtl.h | 11 +++--
> gcc/expr.cc | 15 ++++--
> gcc/internal-fn.cc | 4 +-
> gcc/testsuite/g++.dg/eh/noncall-store-2.C | 39 ++++++++++++++++
> .../g++.dg/opt/mem-notrap-store-asm.C | 15 ++++++
> .../g++.dg/opt/mem-notrap-store-builtin.C | 22 +++++++++
> .../gcc.target/i386/mem-notrap-mask-load.c | 23 ++++++++++
> 11 files changed, 156 insertions(+), 38 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/eh/noncall-store-2.C
> create mode 100644 gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C
> create mode 100644 gcc/testsuite/g++.dg/opt/mem-notrap-store-builtin.C
> create mode 100644 gcc/testsuite/gcc.target/i386/mem-notrap-mask-load.c
>
> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
> index 056fd20c4c6..d9471340e30 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"
> @@ -1364,10 +1365,10 @@ expand_builtin_prefetch (tree exp)
> /* Get a MEM rtx for expression EXP which is the address of an operand
> to be used in a string instruction (cmpstrsi, cpymemsi, ..). LEN is
> the maximum length of the block of memory that might be accessed or
> - NULL if unknown. */
> + NULL if unknown. STORE_P is true when EXP is the destination. */
>
> rtx
> -get_memory_rtx (tree exp, tree len)
> +get_memory_rtx (tree exp, tree len, bool store_p)
> {
> tree orig_exp = exp, base;
> rtx addr, mem;
> @@ -1401,7 +1402,7 @@ get_memory_rtx (tree exp, tree len)
> from the original address we got, and build an all-aliasing
> unknown-sized access to that one. */
> if (is_gimple_mem_ref_addr (TREE_OPERAND (exp, 0)))
> - set_mem_attributes (mem, exp, 0);
> + set_mem_attributes (mem, exp, 0, store_p);
> else if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
> && (base = get_base_address (TREE_OPERAND (TREE_OPERAND (exp, 0),
> 0))))
> @@ -1414,7 +1415,7 @@ get_memory_rtx (tree exp, tree len)
> size_zero_node,
> NULL)),
> exp, build_int_cst (ptr_type_node, 0));
> - set_mem_attributes (mem, exp, 0);
> + set_mem_attributes (mem, exp, 0, store_p);
> /* Since we stripped parts make sure the offset is unknown and the
> alignment is computed from the original address. */
> clear_mem_offset (mem);
> @@ -3770,7 +3771,7 @@ expand_builtin_memory_copy_args (tree dest, tree src,
> tree len,
>
> if (expected_align < dest_align)
> expected_align = dest_align;
> - dest_mem = get_memory_rtx (dest, len);
> + dest_mem = get_memory_rtx (dest, len, true);
> set_mem_align (dest_mem, dest_align);
> len_rtx = expand_normal (len);
> determine_block_size (len, len_rtx, &min_size, &max_size,
> @@ -3804,7 +3805,7 @@ expand_builtin_memory_copy_args (tree dest, tree src,
> tree len,
> return dest_mem;
> }
>
> - src_mem = get_memory_rtx (src, len);
> + src_mem = get_memory_rtx (src, len, false);
> set_mem_align (src_mem, src_align);
>
> /* Copy word part most expediently. */
> @@ -3873,8 +3874,8 @@ expand_movstr (tree dest, tree src, rtx target,
> memop_ret retmode)
> if (!targetm.have_movstr ())
> return NULL_RTX;
>
> - dest_mem = get_memory_rtx (dest, NULL);
> - src_mem = get_memory_rtx (src, NULL);
> + dest_mem = get_memory_rtx (dest, NULL, true);
> + src_mem = get_memory_rtx (src, NULL, false);
> if (retmode == RETURN_BEGIN)
> {
> target = force_reg (Pmode, XEXP (dest_mem, 0));
> @@ -4150,7 +4151,7 @@ expand_builtin_strncpy (tree exp, rtx target)
> dest_align, false))
> return NULL_RTX;
>
> - dest_mem = get_memory_rtx (dest, len);
> + dest_mem = get_memory_rtx (dest, len, true);
> store_by_pieces (dest_mem, tree_to_uhwi (len),
> builtin_strncpy_read_str,
> const_cast<char *> (p), dest_align, false,
> @@ -4709,7 +4710,7 @@ expand_builtin_memset_args (tree dest, tree val, tree
> len,
> len_rtx = expand_normal (len);
> determine_block_size (len, len_rtx, &min_size, &max_size,
> &probable_max_size);
> - dest_mem = get_memory_rtx (dest, len);
> + dest_mem = get_memory_rtx (dest, len, true);
> val_mode = TYPE_MODE (unsigned_char_type_node);
>
> if (TREE_CODE (val) != INTEGER_CST
> @@ -4893,8 +4894,8 @@ expand_builtin_memcmp (tree exp, rtx target, bool
> result_eq)
> if (arg1_align == 0 || arg2_align == 0)
> return NULL_RTX;
>
> - rtx arg1_rtx = get_memory_rtx (arg1, len);
> - rtx arg2_rtx = get_memory_rtx (arg2, len);
> + rtx arg1_rtx = get_memory_rtx (arg1, len, false);
> + rtx arg2_rtx = get_memory_rtx (arg2, len, false);
> rtx len_rtx = expand_normal (fold_convert_loc (loc, sizetype, len));
>
> /* Set MEM_SIZE as appropriate. */
> @@ -4987,8 +4988,8 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx
> target)
> arg1 = builtin_save_expr (arg1);
> arg2 = builtin_save_expr (arg2);
>
> - rtx arg1_rtx = get_memory_rtx (arg1, NULL);
> - rtx arg2_rtx = get_memory_rtx (arg2, NULL);
> + rtx arg1_rtx = get_memory_rtx (arg1, NULL, false);
> + rtx arg2_rtx = get_memory_rtx (arg2, NULL, false);
>
> /* Try to call cmpstrsi. */
> if (cmpstr_icode != CODE_FOR_nothing)
> @@ -5144,8 +5145,8 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx
> target,
> len = fold_convert_loc (loc, sizetype, len);
> len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
> }
> - rtx arg1_rtx = get_memory_rtx (arg1, len);
> - rtx arg2_rtx = get_memory_rtx (arg2, len);
> + rtx arg1_rtx = get_memory_rtx (arg1, len, false);
> + rtx arg2_rtx = get_memory_rtx (arg2, len, false);
> rtx arg3_rtx = expand_normal (len);
> result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
> arg2_rtx, TREE_TYPE (len), arg3_rtx,
> @@ -5545,7 +5546,7 @@ expand_builtin_strub_enter (tree exp)
> tree wmtype = TREE_TYPE (TREE_TYPE (wmptr));
> 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);
> + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_WRITE);
>
> emit_move_insn (wmark, stktop);
>
> @@ -5623,7 +5624,9 @@ expand_builtin_strub_update (tree exp)
> do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED,
> ptr_mode, NULL_RTX, lab, NULL,
> profile_probability::very_likely ());
> - emit_move_insn (wmark, stktop);
> + rtx wmark_store = shallow_copy_rtx (wmark);
> + MEM_NOTRAP_P (wmark_store) = !lhs_could_trap_p (wmtree);
> + emit_move_insn (wmark_store, stktop);
>
> /* If this is an inlined strub function, also bump the watermark for the
> enclosing function. This avoids a problem with the following scenario:
> A
> @@ -5647,7 +5650,9 @@ expand_builtin_strub_update (tree exp)
> do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED,
> ptr_mode, NULL_RTX, lab, NULL,
> profile_probability::very_likely ());
> - emit_move_insn (wmark, stktop);
> + wmark_store = shallow_copy_rtx (wmark);
> + MEM_NOTRAP_P (wmark_store) = !lhs_could_trap_p (wmtree);
> + emit_move_insn (wmark_store, stktop);
> }
>
> emit_label (lab);
> @@ -7602,7 +7607,8 @@ inline_string_cmp (rtx target, tree var_str, const char
> *const_str,
> {
> HOST_WIDE_INT offset = 0;
> rtx var_rtx_array
> - = get_memory_rtx (var_str, build_int_cst (unsigned_type_node,length));
> + = get_memory_rtx (var_str, build_int_cst (unsigned_type_node, length),
> + false);
> rtx var_rtx = NULL_RTX;
> rtx const_rtx = NULL_RTX;
> rtx result = target ? target : gen_reg_rtx (mode);
> diff --git a/gcc/builtins.h b/gcc/builtins.h
> index 9cca88c647c..97b79908d0d 100644
> --- a/gcc/builtins.h
> +++ b/gcc/builtins.h
> @@ -162,7 +162,7 @@ extern char target_percent_s[3];
> extern char target_percent_c[3];
> extern char target_percent_s_newline[4];
> extern bool target_char_cst_p (tree t, char *p);
> -extern rtx get_memory_rtx (tree exp, tree len);
> +extern rtx get_memory_rtx (tree exp, tree len, bool store_p);
>
> extern internal_fn associated_internal_fn (combined_fn, tree);
> extern internal_fn associated_internal_fn (tree);
> diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
> index 245ba357b5e..7256c5235aa 100644
> --- a/gcc/cfgexpand.cc
> +++ b/gcc/cfgexpand.cc
> @@ -3688,7 +3688,10 @@ expand_asm_stmt (gasm *stmt)
> op = expand_expr (val, NULL_RTX, VOIDmode,
> !allows_reg ? EXPAND_MEMORY : EXPAND_WRITE);
> if (MEM_P (op))
> - op = validize_mem (op);
> + {
> + op = validize_mem (op);
> + MEM_NOTRAP_P (op) = !lhs_could_trap_p (val);
> + }
>
> if (! allows_reg && !MEM_P (op))
> {
> diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc
> index 4a23eaefe02..8eb42c4facc 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. STORE_P
> + is true when REF is the destination of a store. */
>
> void
> set_mem_attributes_minus_bitpos (rtx ref, tree t, int objectp,
> - poly_int64 bitpos)
> + poly_int64 bitpos, bool 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)
> + = !(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 store_p)
> {
> - set_mem_attributes_minus_bitpos (ref, t, objectp, 0);
> + set_mem_attributes_minus_bitpos (ref, t, objectp, 0, store_p);
> }
>
> /* Set the alias set of MEM to SET. */
> diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
> index 0b572580a98..f50db283657 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. STORE_P is true when REF is
> + the destination of a store. */
> +extern void set_mem_attributes (rtx, tree, int, bool = false);
>
> /* 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. STORE_P is true when REF is
> + the destination of a store. */
> +extern void set_mem_attributes_minus_bitpos (rtx, tree, int, poly_int64,
> + bool = false);
>
> /* 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 feaa8e90470..fd735ac8f93 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;
> }
> @@ -11759,6 +11760,9 @@ expand_expr_real_1 (tree exp, rtx target,
> machine_mode tmode,
> else
> decl_rtl = copy_rtx (decl_rtl);
>
> + if (exp && MEM_P (decl_rtl) && modifier == EXPAND_WRITE)
> + MEM_NOTRAP_P (decl_rtl) = !lhs_could_trap_p (exp);
> +
> /* Record writes to register variables. */
> if (modifier == EXPAND_WRITE
> && REG_P (decl_rtl)
> @@ -12077,7 +12081,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, modifier == EXPAND_WRITE);
> set_mem_addr_space (temp, as);
> align = get_object_alignment (exp);
> if (modifier != EXPAND_WRITE
> @@ -12164,7 +12168,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, modifier == EXPAND_WRITE);
> set_mem_addr_space (temp, as);
> if (TREE_THIS_VOLATILE (exp))
> MEM_VOLATILE_P (temp) = 1;
> @@ -12733,7 +12737,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, modifier == EXPAND_WRITE);
>
> if (REG_P (XEXP (op0, 0)))
> mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
> @@ -12865,7 +12869,8 @@ 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,
> + modifier == EXPAND_WRITE);
> if (REG_P (XEXP (op0, 0)))
> mark_reg_pointer (XEXP (op0, 0), MEM_ALIGN (op0));
>
> diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
> index 0138c6f7ef0..ceddc3787c0 100644
> --- a/gcc/internal-fn.cc
> +++ b/gcc/internal-fn.cc
> @@ -3144,7 +3144,7 @@ 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));
> /* The built MEM_REF does not accurately reflect that the load
> is only partial. Clear it. */
> @@ -3404,7 +3404,7 @@ expand_RAWMEMCHR (internal_fn, gcall *stmt)
> create_call_lhs_operand (&ops[0], lhs_rtx, lhs_mode);
>
> tree mem = gimple_call_arg (stmt, 0);
> - rtx mem_rtx = get_memory_rtx (mem, NULL);
> + rtx mem_rtx = get_memory_rtx (mem, NULL, false);
> create_fixed_operand (&ops[1], mem_rtx);
>
> tree pattern = gimple_call_arg (stmt, 1);
> 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 00000000000..c095c71ca93
> --- /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 00000000000..9fcd13e9f61
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/opt/mem-notrap-store-asm.C
> @@ -0,0 +1,15 @@
> +// 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;
> +
> +void
> +store ()
> +{
> + asm volatile ("" : "=m" (const_cast<int &> (output)));
> +}
> +
> +// { dg-final { scan-rtl-dump {\(set[ \t\n]+\(mem(?:/[a-z])*:} "expand" } }
> +// { dg-final { scan-rtl-dump-not {\(set[
> \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:} "expand" } }
> diff --git a/gcc/testsuite/g++.dg/opt/mem-notrap-store-builtin.C
> b/gcc/testsuite/g++.dg/opt/mem-notrap-store-builtin.C
> new file mode 100644
> index 00000000000..a048648784a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/opt/mem-notrap-store-builtin.C
> @@ -0,0 +1,22 @@
> +// Keep an inline memcpy source nontrapping, but not its destination.
> +
> +// { dg-do compile }
> +// { dg-options "-O2 -fnon-call-exceptions -finline-stringops=memcpy
> -fdump-rtl-expand" }
> +// { dg-require-effective-target ptr32plus }
> +
> +typedef unsigned char block[16384];
> +
> +extern const block destination;
> +extern block source;
> +
> +void
> +copy ()
> +{
> + __builtin_memcpy (const_cast<unsigned char *> (destination), source,
> + sizeof (block));
> +}
> +
> +// { dg-final { scan-rtl-dump {\(mem(?:/[a-z])*/c(?:/[a-z])*:} "expand" } }
> +// { dg-final { scan-rtl-dump {\(set[ \t\n]+\(mem(?:/[a-z])*:} "expand" } }
> +// { dg-final { scan-rtl-dump-not {\(set[
> \t\n]+\(mem(?:/[a-z])*/c(?:/[a-z])*:} "expand" } }
> +// { dg-final { scan-assembler-not {\mmemcpy\M} } }
> 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 00000000000..bc510e6ad4f
> --- /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" } } */
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)