]> git.ipfire.org Git - thirdparty/gcc.git/commit
c: Optimize TARGET_EXPRs for _Atomic loads [PR123475]
authorJakub Jelinek <jakub@redhat.com>
Fri, 9 Jan 2026 19:46:39 +0000 (20:46 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 9 Jan 2026 19:46:39 +0000 (20:46 +0100)
commit2c83c3522e563ad0d85d50b39dcfe24336cf419a
treecdd3e27f5baab73330406e01bef90f33d4981b89
parent4f1bde997f5bbf57b4b22c1d73271bca0b11aeb8
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  <jakub@redhat.com>

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.
gcc/c/c-typeck.cc
gcc/testsuite/gcc.dg/pr123475.c [new file with mode: 0644]