From: Kyrylo Tkachov <[email protected]>

tree_could_trap_p describes evaluation as an rvalue.  However,
gimple_could_trap_p_1 uses it for an assignment LHS when store checks are
requested.  It also scans the LHS of other statements as an rvalue.  A load
can be safe while a store to the same address can trap.

Use gimple_store_p and lhs_could_trap_p for memory LHSs when store checks are
requested.  This keeps register definitions as non-stores and includes memory
results of GIMPLE_CALL.  Omit every statement LHS from the rvalue operand
scan.

A nothrow call statement can still trap when the caller stores its result.
The return-slot optimization flag permits direct construction in the
destination, but expansion can use a temporary and a caller-side copy.  Check
a memory call LHS when non-call exceptions are enabled.

Add self-tests for register definitions, assignment stores, and call result
stores.  Add a non-call exception test for nothrow call results.

Bootstrapped and tested on aarch64-unknown-linux-gnu.

        PR middle-end/127184

gcc/ChangeLog:

        * gimple.cc (gimple_could_trap_p_1): Check a memory LHS with
        lhs_could_trap_p.  Do not inspect an LHS as an rvalue.
        (test_could_trap): New self-test.
        (gimple_cc_tests): Run it.
        * tree-eh.cc (stmt_could_throw_p): Check a nothrow call's memory LHS.

gcc/testsuite/ChangeLog:

        * g++.dg/eh/noncall-store-call.C: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/gimple.cc                                | 74 +++++++++++++++++++-
 gcc/testsuite/g++.dg/eh/noncall-store-call.C | 49 +++++++++++++
 gcc/tree-eh.cc                               | 12 +++-
 3 files changed, 131 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/eh/noncall-store-call.C

diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index 3756a60a8e6..04ad17f5955 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -2382,8 +2382,8 @@ gimple_has_side_effects (const gimple *s)
 
 /* Helper for gimple_could_trap_p and gimple_assign_rhs_could_trap_p.
    Return true if S can trap.  When INCLUDE_MEM is true, check whether
-   the memory operations could trap.  When INCLUDE_STORES is true and
-   S is a GIMPLE_ASSIGN, the LHS of the assignment is also checked.  */
+   the memory operations could trap.  When INCLUDE_STORES is true, also
+   check a memory LHS with store semantics.  */
 
 bool
 gimple_could_trap_p_1 (const gimple *s, bool include_mem, bool include_stores)
@@ -2393,7 +2393,17 @@ gimple_could_trap_p_1 (const gimple *s, bool 
include_mem, bool include_stores)
 
   if (include_mem)
     {
-      unsigned i, start = (is_gimple_assign (s) && !include_stores) ? 1 : 0;
+      unsigned i, start = 0;
+
+      /* Use store semantics for the LHS.  A call marked for return-slot
+        optimization can still use a temporary and a caller-side copy.  */
+      if (include_stores
+         && gimple_store_p (s)
+         && lhs_could_trap_p (gimple_get_lhs (s)))
+       return true;
+
+      if (gimple_has_lhs (s))
+       start = 1;
 
       for (i = start; i < gimple_num_ops (s); i++)
        if (tree_could_trap_p (gimple_op (s, i)))
@@ -3670,6 +3680,63 @@ test_assign_binop ()
   ASSERT_EQ (MULT_EXPR, gimple_assign_rhs_code (stmt));
 }
 
+/* Verify that GIMPLE trap queries distinguish a register definition and a
+   trapping store from their nontrapping right-hand sides.  */
+
+static void
+test_could_trap ()
+{
+  tree reg_lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                            get_identifier ("readonly_reg"),
+                            integer_type_node);
+  TREE_READONLY (reg_lhs) = 1;
+  gassign *stmt = gimple_build_assign (reg_lhs, integer_one_node);
+
+  ASSERT_FALSE (gimple_store_p (stmt));
+  ASSERT_FALSE (gimple_could_trap_p (stmt));
+  ASSERT_FALSE (gimple_assign_rhs_could_trap_p (stmt));
+
+  tree mem_lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                            get_identifier ("readonly_mem"),
+                            integer_type_node);
+  TREE_READONLY (mem_lhs) = 1;
+  TREE_STATIC (mem_lhs) = 1;
+  TREE_ADDRESSABLE (mem_lhs) = 1;
+  stmt = gimple_build_assign (mem_lhs, integer_one_node);
+
+  ASSERT_TRUE (gimple_store_p (stmt));
+  ASSERT_TRUE (gimple_could_trap_p (stmt));
+  ASSERT_FALSE (gimple_assign_rhs_could_trap_p (stmt));
+
+  tree record_type = make_node (RECORD_TYPE);
+  tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+                          get_identifier ("value"), integer_type_node);
+  DECL_CONTEXT (field) = record_type;
+  TYPE_FIELDS (record_type) = field;
+  layout_type (record_type);
+
+  tree call_lhs = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                             get_identifier ("readonly_call_mem"),
+                             record_type);
+  TREE_READONLY (call_lhs) = 1;
+  TREE_STATIC (call_lhs) = 1;
+  TREE_ADDRESSABLE (call_lhs) = 1;
+
+  tree fn_type = build_function_type_list (record_type, NULL_TREE);
+  tree fn = build_fn_decl ("nothrow_call", fn_type);
+  gcall *call = gimple_build_call (fn, 0);
+  gimple_call_set_lhs (call, call_lhs);
+  gimple_call_set_nothrow (call, true);
+
+  ASSERT_TRUE (gimple_store_p (call));
+  ASSERT_TRUE (gimple_could_trap_p (call));
+  ASSERT_FALSE (gimple_could_trap_p_1 (call, true, false));
+
+  gimple_call_set_return_slot_opt (call, true);
+  ASSERT_TRUE (gimple_could_trap_p (call));
+  ASSERT_FALSE (gimple_could_trap_p_1 (call, true, false));
+}
+
 /* Build a GIMPLE_NOP and verify various properties of it.  */
 
 static void
@@ -3723,6 +3790,7 @@ gimple_cc_tests ()
 {
   test_assign_single ();
   test_assign_binop ();
+  test_could_trap ();
   test_nop_stmt ();
   test_return_stmt ();
   test_return_without_value ();
diff --git a/gcc/testsuite/g++.dg/eh/noncall-store-call.C 
b/gcc/testsuite/g++.dg/eh/noncall-store-call.C
new file mode 100644
index 00000000000..0be0151450b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/noncall-store-call.C
@@ -0,0 +1,49 @@
+// A nothrow call can still trap when the caller stores its result.  Return
+// slot optimization can fall back to a temporary and a caller-side copy.
+
+// { dg-do compile }
+// { dg-options "-O2 -fnon-call-exceptions -fdump-tree-eh 
-fdump-tree-optimized-eh" }
+
+struct one
+{
+  __UINTPTR_TYPE__ value;
+};
+
+struct large
+{
+  __UINTPTR_TYPE__ value[8];
+  ~large ();
+};
+
+extern one make_one () throw ();
+extern large make_large () throw ();
+
+void
+store_one (one &dest)
+{
+  try
+    {
+      dest = make_one ();
+    }
+  catch (...)
+    {
+      __builtin_trap ();
+    }
+}
+
+void *operator new (__SIZE_TYPE__, void *) throw ();
+
+void
+construct_large (large &dest)
+{
+  try
+    {
+      ::new (&dest) large (make_large ());
+    }
+  catch (...)
+    {
+      __builtin_trap ();
+    }
+}
+
+// { dg-final { scan-tree-dump-times "__cxa_begin_catch" 2 "optimized" } }
diff --git a/gcc/tree-eh.cc b/gcc/tree-eh.cc
index 5939f5563ce..e48d9502fb0 100644
--- a/gcc/tree-eh.cc
+++ b/gcc/tree-eh.cc
@@ -3091,7 +3091,17 @@ stmt_could_throw_p (function *fun, gimple *stmt)
       return true;
 
     case GIMPLE_CALL:
-      return !gimple_call_nothrow_p (as_a <gcall *> (stmt));
+      {
+       gcall *call = as_a <gcall *> (stmt);
+       if (!gimple_call_nothrow_p (call))
+         return true;
+       /* Return slot optimization can fall back to a temporary and a
+          caller-side copy.  */
+       if ((fun && !fun->can_throw_non_call_exceptions)
+           || !gimple_store_p (call))
+         return false;
+       return lhs_could_trap_p (gimple_call_lhs (call));
+      }
 
     case GIMPLE_COND:
       {
-- 
2.50.1 (Apple Git-155)

Reply via email to