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.

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.  A separate patch restores that precision where
get_memory_rtx knows the access direction.

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]>
---
 gcc/emit-rtl.cc                               | 12 +++---
 gcc/emit-rtl.h                                | 11 ++++--
 gcc/expr.cc                                   | 20 +++++++---
 gcc/testsuite/g++.dg/eh/noncall-store-2.C     | 39 +++++++++++++++++++
 .../g++.dg/opt/mem-notrap-store-asm.C         | 23 +++++++++++
 5 files changed, 91 insertions(+), 14 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

diff --git a/gcc/emit-rtl.cc b/gcc/emit-rtl.cc
index 4a23eaefe02..87e13685867 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 0b572580a98..5fae3264cb5 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 e09dab95953..ff4aea8922a 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;
            }
@@ -11522,6 +11523,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
@@ -11759,6 +11764,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)
@@ -12077,7 +12087,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
@@ -12164,7 +12174,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;
@@ -12733,7 +12743,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));
@@ -12865,7 +12875,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 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..ef26e228331
--- /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" } }
-- 
2.50.1 (Apple Git-155)

Reply via email to