> On 5 Sep 2026, at 07:07, Andrea Pinski <[email protected]> wrote: > > 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.
Thanks, I’ve split the comment changes in a separate patch and resent the ones that still need approval. Kyrill > >> >> 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) >>
