On Fri, Sep 4, 2026 at 4:57 AM <[email protected]> wrote: > > 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. Can you split the comment changes out?
> (stmt_could_throw_p): Check a nothrow call's memory LHS. This is PR 127184. > > 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) >
