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