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

Reply via email to