On Tue, Sep 23, 2025 at 10:13:07AM +0200, Jakub Jelinek wrote:
> I think the first question needs to be, do we throw those away during
> inlining or not?
> My preference would be that we do throw those away.
> Consider say
> struct A { A () {} int a; };
> struct B : A { B () {} int b; };
> struct C : B { C () {} int c; };
> struct D : C { D () {} A a; B b; C c; };
> void bar (D &);
> void
> foo ()
> {
> D d;
> bar (d);
> }
> right now we get after einline
> d ={v} {CLOBBER(bob)};
> MEM[(struct C *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 12B] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d + 16B] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 16B] ={v} {CLOBBER(bob)};
> MEM[(struct C *)&d + 24B] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d + 24B] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 24B] ={v} {CLOBBER(bob)};
> clobbers at the start, but all but the first one are useless. And
> even if we don't inline everything, the clobber at the start of
> a ctor not being inlined covers everything that needs to be clobbered
> and later clobbers on it are just IL waste. When ctor is inlined
> into the ultimate caller of the ctor, either there will be a normal
> CLOBBER(bob) added by the patch for the whole variable or temporary
> or with Jason's changes for new expression too.
>
> > So a CLOBBER doesn't do because it might cause earlier writes to be
> > DSEd (even if it itself doesn't alter storage)?
>
> If it is dropped during inlining, that wouldn't be a problem.
> But a problem would be say SRA or other optimizations optimizing
> reads from the
> [MEM[this] ={v} {CLOBBER(newkind)};
> destination, we don't want optimize those to just _3(D) or some
> smaller temporary initialized with a clobber. Except for -W*uninitialized
> we need to treat it as what the memory contains is unknown.
> Unless we tweak SRA (does e.g. SCCVN do that too?) for that new kind.
>
> > I wonder if we can re-use .DEFERRED_INIT with a special mode here?
> > Again I'm noticing we do not document internal-functions anywhere,
> > in particular their expected signature or semantics ... :/
> > >From internal-fn.cc it seems we have an INIT_TYPE parameter, can
> > we add AUTO_INIT_KEEP_OLD or is AUTO_INIT_UNINITIALIZED already
> > doing what we want here?
>
> If we drop it during inlining, maybe, but same problem with SRA and maybe
> SCCVN. We don't want for this case to see what PR121894 mentions,
> that
> MEM[(struct C *)this] = .DEFERRED_INIT (16, 6, &""[0]);
> would be SRA optimized into
> this$a_1 = .DEFERRED_INIT (4, 6, &""[0]);
> this$b_2 = .DEFERRED_INIT (4, 6, &""[0]);
> this$c_3 = .DEFERRED_INIT (4, 6, &""[0]);
> this$d_4 = .DEFERRED_INIT (4, 6, &""[0]);
> because for this kind we'd want to expand it into nothing at all, not having
> to track which byte or whatever to read from which offset (which
> .DEFERRED_INIT doesn't express, it is meant to set all bytes to the same
> value).
So, I've played with the attribute on the parameter idea rather than
new ifn or new CLOBBER kind or new .DEFERRED_INIT kind, and I think
it has an existing precedent.
After all, we could implement it purely in the C++ FE by adding
__attribute__((access (write_only, 1))) attribute on all the this PARM_DECLs
of the ctors, just I don't like the wording in that case, talking
about attribute access on the ctor when the user has not added anything
like that and furthermore for write_only access it emits only
-Wmaybe-uninitialized warning.
So, this incremental patch (where most of the C++ FE condition is just
readded earlier condition for build_clobber_this at that spot, just with
DECL_CLONED_FUNCTION_P check removed and lookup_attribute added) restores
the Wuninitialized-10.C Wuninitialized-pr111123-1.C tests to previous
behavior.
--- gcc/cp/decl.cc.jj 2025-09-23 10:55:43.332823523 +0200
+++ gcc/cp/decl.cc 2025-09-23 13:20:34.023736838 +0200
@@ -19730,6 +19730,21 @@ start_preparsed_function (tree decl1, tr
start_function_contracts (decl1);
if (!processing_template_decl
+ && flag_lifetime_dse > 1
+ && DECL_CONSTRUCTOR_P (decl1)
+ /* Clobbering an empty base is harmful if it overlays real data. */
+ && !is_empty_class (current_class_type)
+ /* We can't clobber safely for an implicitly-defined default constructor
+ because part of the initialization might happen before we enter the
+ constructor, via AGGR_INIT_ZERO_FIRST (c++/68006). */
+ && !implicit_default_ctor_p (decl1)
+ && !lookup_attribute ("clobber *this",
+ DECL_ATTRIBUTES (current_class_ptr)))
+ DECL_ATTRIBUTES (current_class_ptr)
+ = tree_cons (get_identifier ("clobber *this"), NULL_TREE,
+ DECL_ATTRIBUTES (current_class_ptr));
+
+ if (!processing_template_decl
&& DECL_CONSTRUCTOR_P (decl1)
&& sanitize_flags_p (SANITIZE_VPTR)
&& !DECL_CLONED_FUNCTION_P (decl1)
--- gcc/tree-ssa-uninit.cc.jj 2025-04-08 14:09:30.101741017 +0200
+++ gcc/tree-ssa-uninit.cc 2025-09-23 12:57:05.901318089 +0200
@@ -641,6 +641,7 @@ maybe_warn_operand (ao_ref &ref, gimple
return NULL_TREE;
bool found_alloc = false;
+ bool found_clobber_deref_this = false;
if (fentry_reached)
{
@@ -662,12 +663,23 @@ maybe_warn_operand (ao_ref &ref, gimple
tree fndecl = gimple_call_fndecl (def_stmt);
const built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
if (fncode == BUILT_IN_ALLOCA
- || fncode == BUILT_IN_ALLOCA_WITH_ALIGN
- || fncode == BUILT_IN_MALLOC)
+ || fncode == BUILT_IN_ALLOCA_WITH_ALIGN
+ || fncode == BUILT_IN_MALLOC)
found_alloc = true;
break;
}
+ if (SSA_NAME_IS_DEFAULT_DEF (base)
+ && POINTER_TYPE_P (TREE_TYPE (base))
+ && SSA_NAME_VAR (base)
+ && TREE_CODE (SSA_NAME_VAR (base)) == PARM_DECL
+ && lookup_attribute ("clobber *this",
+ DECL_ATTRIBUTES (SSA_NAME_VAR (base))))
+ {
+ found_clobber_deref_this = true;
+ break;
+ }
+
if (!is_gimple_assign (def_stmt))
break;
@@ -702,7 +714,7 @@ maybe_warn_operand (ao_ref &ref, gimple
/* Do not warn if it can be initialized outside this function.
If we did not reach function entry then we found killing
clobbers on all paths to entry. */
- if (!found_alloc && fentry_reached)
+ if ((!found_alloc && !found_clobber_deref_this) && fentry_reached)
{
if (TREE_CODE (base) == SSA_NAME)
{
Jakub