https://gcc.gnu.org/g:2c83c3522e563ad0d85d50b39dcfe24336cf419a

commit r16-6661-g2c83c3522e563ad0d85d50b39dcfe24336cf419a
Author: Jakub Jelinek <[email protected]>
Date:   Fri Jan 9 20:46:39 2026 +0100

    c: Optimize TARGET_EXPRs for _Atomic loads [PR123475]
    
    On the following testcase we emit a false positive warning that
    a temporary (TARGET_EXPR slot) is used uninitialized, from the early_uninit
    pass.
    
    This regressed with my change to instrument for
    -ftrivial-auto-var-init={zero,pattern} not just DECL_EXPRs, but also
    TARGET_EXPR initializations if the TARGET_EXPR_INITIALIZER has void type.
    Those cases are where the initializer doesn't necessarily have to initialize
    the whole TARGET_EXPR slot, or might use parts or the whole slot before
    those are initialized; this is how e.g. various C++ temporary objects are
    constructed.
    
    The problem is in pass interaction.  The FE creates a TARGET_EXPR with
    void type initializer because the initializer is originally
    __atomic_load (&expr, &tmp, SEQ_CST); but it is folded instantly into
    (void) (tmp = (type) __atomic_load_N (&expr, SEQ_CST)).  The FE also
    marks the TARGET_EXPR slot as TREE_ADDRESSABLE, because it would be
    if it will use libatomic, but nothing in the IL then takes its address.
    Now, since my r16-4212 change which was for mainly C++26 compliance
    we see the TARGET_EXPR and because it has void type TARGET_EXPR_INITIALIZER,
    we start with tmp = .DEFERRED_INIT (...); just in case the initialization
    would attempt to use the slot before initialization or not initialize fully.
    Because tmp is TREE_ADDRESSABLE and has gimple reg type, it is actually not
    gimplified as tmp = .DEFERRED_INIT (...); but as _1 = .DEFERRED_INIT (...);
    tmp = _1; but because it is not actually address taken in the IL, already
    the ssa pass turns it into SSA_NAME (dead one), so we have
    _1 = .DEFERRED_INIT (...); _2 = _1; and _2 is unused.  Next comes
    early_uninit and warns on the dead SSA_NAME copy that it uses uninitialized
    var.
    
    The following patch attempts to fix that by checking if
    c_build_function_call_vec has optimized the call right away into pure
    assignment to the TARGET_EXPR slot without the slot being used anywhere
    else in the expression and 1) clearing again TREE_ADDRESSABLE on the slot,
    because it isn't really addressable 2) optimizing the TARGET_EXPR, so that
    it doesn't have void type TARGET_EXPR_INITIALIZER by changing it to the rhs
    of the MODIFY_EXPR.  That way gimplifier doesn't bother creating
    .DEFERRED_INIT for it at all.
    
    Or should something like this be done instead in the TARGET_EXPR
    gimplification?  I mean not the TREE_ADDRESSABLE clearing, that can't be
    done without knowing what we know in the FE, but the rest, generally
    TARGET_EXPR with initializer (void) (TARGET_EXPR_SLOT = something)
    where something doesn't refer to TARGET_EXPR_SLOT can be optimized into
    just something TARGET_EXPR_INITIALIZER.
    
    2026-01-09  Jakub Jelinek  <[email protected]>
    
            PR c/123475
            * c-typeck.cc (c_find_var_r): New function.
            (convert_lvalue_to_rvalue): If c_build_function_call_vec
            folded __atomic_load (&expr, &tmp, SEQ_CST); into
            (void) (tmp = __atomic_load_<N> (&expr, SEQ_CST)), drop
            TREE_ADDRESSABLE flag from tmp and set TARGET_EXPR
            initializer just to the rhs of the MODIFY_EXPR.
    
            * gcc.dg/pr123475.c: New test.

Diff:
---
 gcc/c/c-typeck.cc               | 32 ++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr123475.c | 12 ++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 26805e5cfd6b..43d4a5d5d9e8 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2590,6 +2590,19 @@ maybe_get_constexpr_init (tree expr)
   return build_zero_cst (TREE_TYPE (expr));
 }
 
+/* Helper function for convert_lvalue_to_rvalue called via
+   walk_tree_without_duplicates.  Find DATA inside of the expression.  */
+
+static tree
+c_find_var_r (tree *tp, int *walk_subtrees, void *data)
+{
+  if (TYPE_P (*tp))
+    *walk_subtrees = 0;
+  else if (*tp == (tree) data)
+    return *tp;
+  return NULL_TREE;
+}
+
 /* Convert expression EXP (location LOC) from lvalue to rvalue,
    including converting functions and arrays to pointers if CONVERT_P.
    If READ_P, also mark the expression as having been read.  If
@@ -2664,6 +2677,25 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr 
exp,
       /* EXPR is always read.  */
       mark_exp_read (exp.value);
 
+      /* Optimize the common case where c_build_function_call_vec
+        immediately folds __atomic_load (&expr, &tmp, SEQ_CST); into
+        tmp = __atomic_load_<N> (&expr, SEQ_CST);
+        In that case tmp is not addressable and can be initialized
+        fully by the rhs of the MODIFY_EXPR.  */
+      tree tem = func_call;
+      if (CONVERT_EXPR_P (tem) && VOID_TYPE_P (TREE_TYPE (tem)))
+       {
+         tem = TREE_OPERAND (tem, 0);
+         if (TREE_CODE (tem) == MODIFY_EXPR
+             && TREE_OPERAND (tem, 0) == tmp
+             && !walk_tree_without_duplicates (&TREE_OPERAND (tem, 1),
+                                               c_find_var_r, tmp))
+           {
+             TREE_ADDRESSABLE (tmp) = 0;
+             func_call = TREE_OPERAND (tem, 1);
+           }
+       }
+
       /* Return tmp which contains the value loaded.  */
       exp.value = build4 (TARGET_EXPR, nonatomic_type, tmp, func_call,
                          NULL_TREE, NULL_TREE);
diff --git a/gcc/testsuite/gcc.dg/pr123475.c b/gcc/testsuite/gcc.dg/pr123475.c
new file mode 100644
index 000000000000..ad8e5bb8ef33
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr123475.c
@@ -0,0 +1,12 @@
+/* PR c/123475 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wuninitialized -ftrivial-auto-var-init=zero" } */
+
+int
+foo (void)
+{
+  _Atomic int a = 1000;
+  int b;
+  b = a;       /* { dg-bogus "is used uninitialized" } */
+  return b;
+}

Reply via email to