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)
>

Reply via email to