On Fri, Sep 4, 2026 at 4:57 AM <[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. > > 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.
part of this is PR 127185. > > 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" } } */ > -- > 2.50.1 (Apple Git-155) >
