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.
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 (tree_could_trap_1, tree_could_trap_p,
lhs_could_trap_p): Clarify the function comments.
(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 | 50 +++++++++++++
gcc/tree-eh.cc | 27 ++++---
3 files changed, 138 insertions(+), 13 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..9027bb09cc1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/noncall-store-call.C
@@ -0,0 +1,50 @@
+// 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" } }
+// { dg-final { scan-tree-dump-times "return slot optimization" 1 "eh" } }
diff --git a/gcc/tree-eh.cc b/gcc/tree-eh.cc
index d2c2f3b3157..e48d9502fb0 100644
--- a/gcc/tree-eh.cc
+++ b/gcc/tree-eh.cc
@@ -2753,9 +2753,9 @@ ref_outside_object_p (tree size, poly_offset_int off,
tree refsz)
}
/* Return true if EXPR can trap, as in dereferencing an invalid pointer
- location or floating point arithmetic. C.f. the rtl version, may_trap_p.
- This routine expects only GIMPLE lhs or rhs input.
- LHS is true when LHS is a lhs. */
+ location or evaluating floating-point arithmetic. See may_trap_p for the
+ RTL counterpart. This routine expects only GIMPLE lhs or rhs input. LHS
+ is true when EXPR is the lhs of a store. */
static bool
tree_could_trap_1 (tree expr, bool lhs)
@@ -2935,11 +2935,8 @@ tree_could_trap_1 (tree expr, bool lhs)
}
}
-
-
-/* Return true if EXPR can trap, as in dereferencing an invalid pointer
- location or floating point arithmetic. C.f. the rtl version, may_trap_p.
- This routine expects only GIMPLE lhs or rhs input. */
+/* Return true if EXPR can trap when evaluated as an rvalue. See may_trap_p
+ for the RTL counterpart. */
bool
tree_could_trap_p (tree expr)
@@ -2947,7 +2944,7 @@ tree_could_trap_p (tree expr)
return tree_could_trap_1 (expr, false);
}
-/* Returns true if LHS is known not to trap as a store. */
+/* Return true if LHS can trap as a store. */
bool
lhs_could_trap_p (tree lhs)
{
@@ -3094,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)