> On Sep 17, 2025, at 12:08, Jakub Jelinek <[email protected]> wrote:
>
> Hi!
>
> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
> everything is initialized; trying to emit .DEFERRED_INIT in such a case
> breaks a lot of stuff because e.g. const vars turned into static but
> not static when .DEFERRED_INIT is done will try to write into .rodata).
A little confused with the above. Do you mean that not all temporaries are
initialized to zero, only
the temporaries that have VOID_TYPE_P TARGET_EXPR_INITIAL are initialized to
zero?
Should we initialize all temporaries?
If not, should we explain this in the documentation?
>
> -std=c++26 implies -ftrivial-auto-var-init= mode that doesn't have an
> explicit user option and is like -ftrivial-auto-var-init=zero except
> __builtin_clear_padding calls aren't added (C++26 doesn't make reads from
> padding erroneous). User can still override it in both directions,
> so -ftrivial-auto-var-init={zero,pattern} will behave like before (except
> for the gotos/switches working for C++ right and temporaries being also
> initialized), and -ftrivial-auto-var-init=uninitialized allows to get
> C++23-ish behavior with other C++26 features enabled (if performance
> is more important than some sanity of erroneous code).
In the above, “temporaries being also initialized”, so I assume that all
temporaries should be initialized?
>
> The -flifetime-dse=2 {CLOBBER(bob)} clobbers aren't emitted anymore
> at the start of (some) constructors but instead when gimplifying DECL_EXPRs
> and TARGET_EXPRs. Haven't done it in gimplify.cc so that it roughly matches
> previous behavior (C++ only, not all languages, and adding bob CLOBBERs
> only on types which used them inside ctors for now (doing more breaks a lot
> of stuff in the testsuite).
>
> I understood you have a patch for {CLOBBER(bob)} for new expressions, there
> are some regressions without that, see below.
>
> There are some regressions caused by the removal of {CLOBBER(bob)}
> clobbers from the start of certain constructors, e.g. one testcase has
> struct A
> {
> int f,g;
> A() { f = g; // { dg-warning "g. is used uninitialized" } }
> } a;
> and this doesn't warn anymore for any C++ mode. Wonder what we could
> do for the warnings, for code generation we have to assume the code
> can read members in *this (for C++26 read there .DEFERRED_INIT, for
> older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> Perhaps we should add some artificial attribute to the functions we'd
> add the -flifetime-dse=2 clobber to and treat *this reaching the start
> of the function for those specially in the uninit pass?
>
> Also there is one ICEs and various FAILs which reproduce even without
> this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
> Some tests got that already in the patch (e.g. ubsan/vla-1.c is testing UB
> and will crash if one tries to write something into the -1 sized VLAs,
> which -ftrivial-auto-var-init=zero or -std=c++26 do).
>
> So far only tested with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-c++
> and make check in libstdc++-v3.
>
> The regressions (categorized) are below.
> Your thoughts on this?
>
> I hope the following would be fixed by your clobber new patch:
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++11 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++14 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++17 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++20 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++23 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++26 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++98 execution test
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 note (test for warnings,
> line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 brace (test for warnings,
> line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 LP64 note (test for
> warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 note (test for warnings,
> line 48)
>
> Dunno what to do with these, see above.
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++11 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++14 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++17 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++20 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++23 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++26 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++98 (test for warnings, line
> 10)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++11 (test for
> warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++14 (test for
> warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++17 (test for
> warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++20 (test for
> warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++23 (test for
> warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++26 (test for
> warnings, line 14)
>
> PR121975 - perhaps I can just make those effective target c++23_down
> temporarily with a comment
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 at line 30 (test for
> warnings, line 31)
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 at line 76 (test
> for warnings, line 75)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 (test for excess
> errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at
> line 23 (test for warnings, line 24)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at
> line 28 (test for warnings, line 27)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 (test
> for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at
> line 32 (test for warnings, line 33)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at
> line 37 (test for warnings, line 36)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 (test
> for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 w/
> debug at line 29 (test for bogus messages, line 30)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 at
> line 26 (test for warnings, line 25)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 (test
> for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 at
> line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 w/
> debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 at
> line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 w/
> debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104132-1.c -std=c++26 (test for
> excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104133-1.c -std=c++26 (test for
> excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104774-1.c -std=c++26 (test for
> excess errors)
> FAIL: c-c++-common/goacc/mdc-1.c -std=c++26 scan-tree-dump-times omplower
> "pragma omp target oacc_data map.tofrom:.z .len: 40.. map.struct:s .len: 1..
> map.alloc:s.a .len: 8.. map.tofrom:._1 .len: 40.. map.attach:s.a .bias: 0.." 1
>
> PR121977 - I'd like to fix this, but ideally separately from this patch, so
> perhaps also c++23_down
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (internal compiler error:
> Segmentation fault)
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (test for excess errors)
>
> Dunno about these analyzer issues, will probably need David for that
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for
> warnings, line 15)
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for
> warnings, line 18)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings,
> line 34)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings,
> line 35)
>
> 2025-09-17 Jakub Jelinek <[email protected]>
>
> PR c++/114457
> gcc/
> * flag-types.h (enum auto_init_type): Add AUTO_INIT_CXX26.
> * tree.h (VACUOUS_INIT_LABEL_P): Define.
> * gimplify.cc (is_var_need_auto_init): Renamed to ...
> (var_needs_auto_init_p): ... this. Don't return true for
> vars with "indeterminate" attribute. Formatting fixes.
> (gimplify_decl_expr): Use var_needs_auto_init_p instead of
> is_var_need_auto_init.
> (emit_warn_switch_unreachable): Remove the flag_auto_var_init
> special cases.
> (warn_switch_unreachable_and_auto_init_r): Handle them here
> by doing just returning NULL.
> (last_stmt_in_scope): Don't skip just debug stmts to find
> the last stmt in seq, skip for
> flag_auto_var_init > AUTO_INIT_UNINITIALIZED also IFN_DEFERRED_INIT
> calls.
> (collect_fallthrough_labels): For
> flag_auto_var_init > AUTO_INIT_UNINITIALIZED ignore
> IFN_DEFERRED_INIT calls and GIMPLE_GOTOs to
> VACUOUS_INIT_LABEL_P.
> (should_warn_for_implicit_fallthrough): For
> flag_auto_var_init > AUTO_INIT_UNINITIALIZED also skip over
> IFN_DEFERRED_INIT calls.
> (expand_FALLTHROUGH_r): Likewise, and handle GIMPLE_GOTOs
> to VACUOUS_INIT_LABEL_P.
> (gimplify_init_constructor): Use var_needs_auto_init_p instead
> of is_var_need_auto_init and for flag_auto_var_init
> AUTO_INIT_CXX26 don't call gimple_add_padding_init_for_auto_var.
> (gimplify_target_expr): If var_needs_auto_init_p and init has
> void type, call gimple_add_init_for_auto_var and for
> AUTO_INIT_PATTERN also gimple_add_padding_init_for_auto_var.
> * doc/invoke.texi (ftrivial-auto-var-init=): Adjust documentation.
> gcc/c-family/
> * c-opts.cc (c_common_post_options): For C++26 set
> flag_auto_var_init to AUTO_INIT_CXX26 if not specified explicitly.
> For C++ disable warn_trivial_auto_var_init.
> gcc/cp/
> * cp-tree.h: Implement C++26 P2795R5 - Erroneous behavior for
> uninitialized reads.
> (IF_STMT_VACUOUS_INIT_P): Define.
> (check_goto): Change argument type from tree to tree *.
> * call.cc (build_over_call): Add indeterminate attribute to
> TARGET_EXPR slots for indeterminate parameters.
> * constexpr.cc (cxx_eval_internal_function): Handle IFN_DEFERRED_INIT.
> (cxx_eval_store_expression): Temporarily work around PR121965 bug.
> * cp-gimplify.cc (genericize_if_stmt): Handle IF_STMT_VACUOUS_INIT_P.
> (maybe_emit_clobber_object_begin): New function.
> (cp_gimplify_expr): Call it for DECL_EXPRs and TARGET_EXPRs with
> void type non-NULL TARGET_EXPR_INITIAL.
> * decl.cc (struct named_label_fwd_direct_goto,
> struct named_label_bck_direct_goto): New types.
> (struct named_label_use_entry): Add direct_goto member. Formatting
> fix.
> (struct named_label_entry): Add direct_goto member. Turn bool members
> into bool : 1. Add has_bad_decls bitfield.
> (adjust_backward_gotos): New function.
> (pop_labels): For flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> call adjust_backward_gotos if needed.
> (poplevel_named_label_1): For decl_jump_unsafe also set
> ent->has_bad_decls, and for decl_instrument_init_bypass_p decls
> push them into ent->bad_decls vector too.
> (duplicate_decls): Complain if indeterminate attribute on function
> parameter isn't present on the first function declaration.
> (decl_instrument_init_bypass_p): New function.
> (build_deferred_init_call): Likewise.
> (maybe_add_deferred_init_calls): Likewise.
> (adjust_backward_goto): Likewise.
> (check_previous_goto_1): Add direct_goto and case_label arguments.
> For decl_instrument_init_bypass_p decls seen if
> direct_goto || case_label move case label if needed, call
> maybe_add_deferred_init_calls and adjust GOTO_EXPR operands remembered
> in direct_goto. Change return type from bool to int, return 0 on
> error, 1 for success with no need to adjust vacuous inits and 2 for
> success with need to adjust those.
> (check_previous_goto): Adjust check_previous_goto_1 call, vec_free
> direct_goto vector.
> (check_switch_goto): Add case_label argument, adjust
> check_previous_goto_1 call. Change return type from bool to int.
> (check_goto_1): Remove computed argument, add declp argument. Don't
> reuse previous ent->uses if
> ent->binding_level != current_binding_level. Push declp into
> direct_goto vectors if needed.
> (check_goto): Remove decl argument, add declp argument. Adjust
> check_goto_1 calls.
> (finish_case_label): Call check_switch_goto up to twice, first time
> to detect errors and find out if second call will be needed, and
> after c_add_case_label second time if needed. In the first case
> pass NULL_TREE as new argument to it, in the second case r.
> (start_preparsed_function): Don't emit CLOBBER_OBJECT_BEGIN here
> for -flifetime-dse=2.
> * parser.cc (cp_parser_asm_label_list): Call check_goto only
> after the TREE_LIST is created and pass address of its TREE_VALUE to
> it instead of the label.
> * semantics.cc (finish_goto_stmt): Call check_goto only after
> build_stmt has been called and pass it address of its first operand
> rather than destination.
> * tree.cc (handle_indeterminate_attribute): New function.
> (cxx_gnu_attributes): Add entry for indeterminate attribute.
> gcc/testsuite/
> * g++.dg/cpp1y/vla-initlist1.C: Remove dg-skip-if for powerpc.
> Initialize i to 43 for ctor from initializer_list and expect value
> 43 instead of 42.
> * g++.dg/cpp26/attr-indeterminate1.C: New test.
> * g++.dg/cpp26/attr-indeterminate2.C: New test.
> * g++.dg/cpp26/attr-indeterminate3.C: New test.
> * g++.dg/cpp26/attr-indeterminate4.C: New test.
> * g++.dg/cpp26/erroneous1.C: New test.
> * g++.dg/cpp26/erroneous2.C: New test.
> * g++.dg/cpp26/erroneous3.C: New test.
> * g++.dg/cpp26/erroneous4.C: New test.
> * g++.dg/opt/store-merging-1.C: Add
> -ftrivial-auto-var-init=uninitialized to dg-options.
> * g++.dg/uninit-pred-loop-1_b.C: Expect a warning for C++26.
> * g++.dg/warn/Wuninitialized-13.C: Expect warning on a different
> line.
> * c-c++-common/ubsan/vla-1.c: Add
> -ftrivial-auto-var-init=uninitialized to dg-options.
> * c-c++-common/uninit-17.c: For c++26 expect warning on a different
> line.
>
> --- gcc/flag-types.h.jj 2025-08-23 18:15:36.338457849 +0200
> +++ gcc/flag-types.h 2025-09-17 10:20:47.847835866 +0200
> @@ -288,7 +288,8 @@ enum vect_cost_model {
> enum auto_init_type {
> AUTO_INIT_UNINITIALIZED = 0,
> AUTO_INIT_PATTERN = 1,
> - AUTO_INIT_ZERO = 2
> + AUTO_INIT_ZERO = 2,
> + AUTO_INIT_CXX26 = 3
> };
>
> /* Initialization of padding bits with zeros. */
> --- gcc/tree.h.jj 2025-08-23 18:15:36.386457215 +0200
> +++ gcc/tree.h 2025-09-17 10:20:47.849835839 +0200
> @@ -900,6 +900,19 @@ extern void omp_clause_range_check_faile
> #define UNUSED_LABEL_P(NODE) \
> (LABEL_DECL_CHECK (NODE)->base.default_def_flag)
>
> +/* Label used to goto around artificial .DEFERRED_INIT code for
> + C++ -ftrivial-auto-var-init= purposes with a goto around it.
> + VACUOUS_INIT_LABEL_P flag is used on the lab LABEL_DECL in:
> + goto lab;
> + lab1:
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab2:
> + v3 = .DEFERRED_INIT (...);
> + lab: */
> +#define VACUOUS_INIT_LABEL_P(NODE) \
> + (LABEL_DECL_CHECK (NODE)->base.nothrow_flag)
> +
> /* Nonzero means this expression is volatile in the C sense:
> its address should be of type `volatile WHATEVER *'.
> In other words, the declared item is volatile qualified.
> --- gcc/gimplify.cc.jj 2025-09-15 19:51:56.193398974 +0200
> +++ gcc/gimplify.cc 2025-09-17 16:25:30.279823667 +0200
> @@ -2102,13 +2102,13 @@ gimple_add_padding_init_for_auto_var (tr
> /* Return true if the DECL need to be automaticly initialized by the
> compiler. */
> static bool
> -is_var_need_auto_init (tree decl)
> +var_needs_auto_init_p (tree decl)
> {
> if (auto_var_p (decl)
> - && (TREE_CODE (decl) != VAR_DECL
> - || !DECL_HARD_REGISTER (decl))
> - && (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && (!lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl)))
> + && (TREE_CODE (decl) != VAR_DECL || !DECL_HARD_REGISTER (decl))
> + && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
> + && !lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))
> && !OPAQUE_TYPE_P (TREE_TYPE (decl))
> && !is_empty_type (TREE_TYPE (decl)))
> return true;
> @@ -2221,7 +2221,7 @@ gimplify_decl_expr (tree *stmt_p, gimple
> /* When there is no explicit initializer, if the user requested,
> We should insert an artifical initializer for this automatic
> variable. */
> - else if (is_var_need_auto_init (decl)
> + else if (var_needs_auto_init_p (decl)
> && !decl_had_value_expr_p)
> {
> gimple_add_init_for_auto_var (decl,
> @@ -2315,27 +2315,6 @@ emit_warn_switch_unreachable (gimple *st
> /* Don't warn for compiler-generated gotos. These occur
> in Duff's devices, for example. */
> return NULL;
> - else if ((flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && ((gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> - || (gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> - && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> - || (is_gimple_assign (stmt)
> - && gimple_assign_single_p (stmt)
> - && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> - && gimple_call_internal_p (
> - SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)),
> - IFN_DEFERRED_INIT))))
> - /* Don't warn for compiler-generated initializations for
> - -ftrivial-auto-var-init.
> - There are 3 cases:
> - case 1: a call to .DEFERRED_INIT;
> - case 2: a call to __builtin_clear_padding with the 2nd argument is
> - present and non-zero;
> - case 3: a gimple assign store right after the call to .DEFERRED_INIT
> - that has the LHS of .DEFERRED_INIT as the RHS as following:
> - _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> - i1 = _1. */
> - return NULL;
> else
> warning_at (gimple_location (stmt), OPT_Wswitch_unreachable,
> "statement will never be executed");
> @@ -2383,6 +2362,18 @@ warn_switch_unreachable_and_auto_init_r
> there will be non-debug stmts too, and we'll catch those. */
> break;
>
> + case GIMPLE_ASSIGN:
> + /* See comment below in the GIMPLE_CALL case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_assign_single_p (stmt)
> + && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> + {
> + gimple *g = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt));
> + if (gimple_call_internal_p (g, IFN_DEFERRED_INIT))
> + break;
> + }
> + goto do_default;
> +
> case GIMPLE_LABEL:
> /* Stop till the first Label. */
> return integer_zero_node;
> @@ -2392,24 +2383,41 @@ warn_switch_unreachable_and_auto_init_r
> *handled_ops_p = false;
> break;
> }
> - if (warn_trivial_auto_var_init
> - && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + /* Don't warn for compiler-generated initializations for
> + -ftrivial-auto-var-init for -Wswitch-unreachable. Though
> + do warn for -Wtrivial-auto-var-init.
> + There are 3 cases:
> + case 1: a call to .DEFERRED_INIT;
> + case 2: a call to __builtin_clear_padding with the 2nd argument is
> + present and non-zero;
> + case 3: a gimple assign store right after the call to .DEFERRED_INIT
> + that has the LHS of .DEFERRED_INIT as the RHS as following:
> + _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> + i1 = _1.
> + case 3 is handled above in the GIMPLE_ASSIGN case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> {
> - /* Get the variable name from the 3rd argument of call. */
> - tree var_name = gimple_call_arg (stmt, 2);
> - var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> - const char *var_name_str = TREE_STRING_POINTER (var_name);
> -
> - warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> - "%qs cannot be initialized with "
> - "%<-ftrivial-auto-var_init%>",
> - var_name_str);
> + if (warn_trivial_auto_var_init)
> + {
> + /* Get the variable name from the 3rd argument of call. */
> + tree var_name = gimple_call_arg (stmt, 2);
> + var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> + const char *var_name_str = TREE_STRING_POINTER (var_name);
> +
> + warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> + "%qs cannot be initialized with "
> + "%<-ftrivial-auto-var_init%>", var_name_str);
> + }
> break;
> }
> -
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> + && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> + break;
> /* Fall through. */
> default:
> + do_default:
> /* check the first "real" statement (not a decl/lexical scope/...),
> issue
> warning if needed. */
> if (warn_switch_unreachable && !unreachable_issued)
> @@ -2489,26 +2497,38 @@ last_stmt_in_scope (gimple *stmt)
> if (!stmt)
> return NULL;
>
> + auto last_stmt_in_seq = [] (gimple_seq s) {
> + gimple_seq_node n;
> + for (n = gimple_seq_last (s);
> + n && (is_gimple_debug (n)
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (n, IFN_DEFERRED_INIT)));
> + n = n->prev)
> + if (n == s)
> + return (gimple *) NULL;
> + return (gimple *) n;
> + };
> +
> switch (gimple_code (stmt))
> {
> case GIMPLE_BIND:
> {
> gbind *bind = as_a <gbind *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_bind_body (bind));
> + stmt = last_stmt_in_seq (gimple_bind_body (bind));
> return last_stmt_in_scope (stmt);
> }
>
> case GIMPLE_TRY:
> {
> gtry *try_stmt = as_a <gtry *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_eval (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_eval (try_stmt));
> gimple *last_eval = last_stmt_in_scope (stmt);
> if (gimple_stmt_may_fallthru (last_eval)
> && (last_eval == NULL
> || !gimple_call_internal_p (last_eval, IFN_FALLTHROUGH))
> && gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
> {
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_cleanup (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_cleanup (try_stmt));
> return last_stmt_in_scope (stmt);
> }
> else
> @@ -2661,8 +2681,16 @@ collect_fallthrough_labels (gimple_stmt_
> }
> else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (*gsi_p),
> + IFN_DEFERRED_INIT))
> + ;
> else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_PREDICT)
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (gsi_stmt (*gsi_p))))
> + ;
> else if (!is_gimple_debug (gsi_stmt (*gsi_p)))
> prev = gsi_stmt (*gsi_p);
> gsi_next (gsi_p);
> @@ -2699,9 +2727,13 @@ should_warn_for_implicit_fallthrough (gi
> {
> tree l;
> while (!gsi_end_p (gsi)
> - && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - && (l = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> - && !case_label_p (&gimplify_ctxp->case_labels, l))
> + && ((gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> + && (l
> + = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> + && !case_label_p (&gimplify_ctxp->case_labels, l))
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
> if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
> return false;
> @@ -2714,7 +2746,10 @@ should_warn_for_implicit_fallthrough (gi
> /* Skip all immediately following labels. */
> while (!gsi_end_p (gsi)
> && (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT))
> + || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
>
> /* { ... something; default:; } */
> @@ -2891,7 +2926,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
>
> gimple_stmt_iterator gsi2 = *gsi_p;
> stmt = gsi_stmt (gsi2);
> - if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (stmt) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (stmt)))
> + {
> + /* Handle for C++ artificial -ftrivial-auto-var-init=
> + sequences. Those look like:
> + goto lab1;
> + lab2:;
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab3:;
> + v3 = .DEFERRED_INIT (...);
> + lab1:;
> + In this case, a case/default label can be either in between
> + the GIMPLE_GOTO and the corresponding GIMPLE_LABEL, if jumps
> + from the switch condition to the case/default label cross
> + vacuous initialization of some variables, or after the
> + corresponding GIMPLE_LABEL, if those jumps don't cross
> + any such initialization but there is an adjacent named label
> + which crosses such initialization. So, for the purpose of
> + this function, just ignore the goto but until reaching the
> + corresponding GIMPLE_LABEL allow also .DEFERRED_INIT
> + calls. */
> + gsi_next (&gsi2);
> + }
> + else if (gimple_code (stmt) == GIMPLE_GOTO
> + && !gimple_has_location (stmt))
> {
> /* Go on until the artificial label. */
> tree goto_dest = gimple_goto_dest (stmt);
> @@ -2926,6 +2987,9 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
> }
> else if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> + ;
> else if (!is_gimple_debug (stmt))
> /* Anything else is not expected. */
> break;
> @@ -6753,7 +6817,8 @@ gimplify_init_constructor (tree *expr_p,
> && clear_padding_type_may_have_padding_p (type)
> && ((AGGREGATE_TYPE_P (type) && !cleared && !is_empty_ctor)
> || !AGGREGATE_TYPE_P (type))
> - && is_var_need_auto_init (object))
> + && var_needs_auto_init_p (object)
> + && flag_auto_var_init != AUTO_INIT_CXX26)
> gimple_add_padding_init_for_auto_var (object, false, pre_p);
>
> return ret;
> @@ -8460,6 +8525,7 @@ gimplify_target_expr (tree *expr_p, gimp
> if (init)
> {
> gimple_seq init_pre_p = NULL;
> + bool is_vla = false;
>
> /* TARGET_EXPR temps aren't part of the enclosing block, so add it
> to the temps list. Handle also variable length TARGET_EXPRs. */
> @@ -8470,6 +8536,7 @@ gimplify_target_expr (tree *expr_p, gimp
> /* FIXME: this is correct only when the size of the type does
> not depend on expressions evaluated in init. */
> gimplify_vla_decl (temp, &init_pre_p);
> + is_vla = true;
> }
> else
> {
> @@ -8481,6 +8548,15 @@ gimplify_target_expr (tree *expr_p, gimp
> gimple_add_tmp_var (temp);
> }
>
> + if (var_needs_auto_init_p (temp) && VOID_TYPE_P (TREE_TYPE (init)))
> + {
> + gimple_add_init_for_auto_var (temp, flag_auto_var_init, &init_pre_p);
> + if (flag_auto_var_init == AUTO_INIT_PATTERN
> + && !is_gimple_reg (temp)
> + && clear_padding_type_may_have_padding_p (TREE_TYPE (temp)))
> + gimple_add_padding_init_for_auto_var (temp, is_vla, &init_pre_p);
> + }
> +
> /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
> expression is supposed to initialize the slot. */
> if (VOID_TYPE_P (TREE_TYPE (init)))
> --- gcc/doc/invoke.texi.jj 2025-09-15 19:52:51.461668311 +0200
> +++ gcc/doc/invoke.texi 2025-09-17 10:20:47.858835715 +0200
> @@ -14696,27 +14696,25 @@ and @option{-fauto-profile}.
>
> @opindex ftrivial-auto-var-init
> @item -ftrivial-auto-var-init=@var{choice}
> -Initialize automatic variables with either a pattern or with zeroes to
> increase
> -the security and predictability of a program by preventing uninitialized
> memory
> -disclosure and use.
> +Initialize automatic variables or temporary objects with either a pattern or
> with
> +zeroes to increase the security and predictability of a program by preventing
> +uninitialized memory disclosure and use.
> GCC still considers an automatic variable that doesn't have an explicit
> initializer as uninitialized, @option{-Wuninitialized} and
> @option{-Wanalyzer-use-of-uninitialized-value} will still report
> -warning messages on such automatic variables and the compiler will
> -perform optimization as if the variable were uninitialized.
> +warning messages on such automatic variables or temporary objects and the
> +compiler will perform optimization as if the variable were uninitialized.
> With this option, GCC will also initialize any padding of automatic variables
> -that have structure or union types to zeroes.
> -However, the current implementation cannot initialize automatic variables
> that
> -are declared between the controlling expression and the first case of a
> -@code{switch} statement. Using @option{-Wtrivial-auto-var-init} to report
> all
> -such cases.
> +or temporary objects that have structure or union types to zeroes.
> +However, the current implementation cannot initialize automatic variables
> +whose initialization is bypassed through @code{switch} or @code{goto}
> +statement. Using @option{-Wtrivial-auto-var-init} to report all such cases.
>
> The three values of @var{choice} are:
>
> @itemize @bullet
> @item
> @samp{uninitialized} doesn't initialize any automatic variables.
> -This is C and C++'s default.
>
> @item
> @samp{pattern} Initialize automatic variables with values which will likely
> @@ -14730,7 +14728,10 @@ The values used for pattern initializati
> @samp{zero} Initialize automatic variables with zeroes.
> @end itemize
>
> -The default is @samp{uninitialized}.
> +The default is @samp{uninitialized} except for C++26, in which case
> +if @option{-ftrivial-auto-var-init=} is not specified at all automatic
> +variables or temporary objects are zero initialized, but zero initialization
> +of padding bits does not happen.
>
> Note that the initializer values, whether @samp{zero} or @samp{pattern},
> refer to data representation (in memory or machine registers), rather
> @@ -14743,7 +14744,7 @@ with the bit patterns @code{0x00} or @co
> @var{choice}, whether or not these representations stand for values in
> that range, and even if they do, the interpretation of the value held by
> the variable will depend on the bias. A @samp{hardbool} variable that
> -uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
> +uses say @code{0x5A} and @code{0xA5} for @code{false} and @code{true},
> respectively, will trap with either @samp{choice} of trivial
> initializer, i.e., @samp{zero} initialization will not convert to the
> representation for @code{false}, even if it would for a @code{static}
> @@ -14754,7 +14755,8 @@ are initialized with @code{false} (zero)
> requested.
>
> You can control this behavior for a specific variable by using the variable
> -attribute @code{uninitialized} (@pxref{Variable Attributes}).
> +attribute @code{uninitialized} standard attribute (@pxref{Variable
> Attributes})
> +or the C++26 @code{[[indeterminate]]}.
>
> @opindex fvect-cost-model
> @item -fvect-cost-model=@var{model}
> --- gcc/c-family/c-opts.cc.jj 2025-08-10 19:43:47.082657317 +0200
> +++ gcc/c-family/c-opts.cc 2025-09-17 10:20:47.849835839 +0200
> @@ -913,6 +913,16 @@ c_common_post_options (const char **pfil
> else
> flag_permitted_flt_eval_methods = PERMITTED_FLT_EVAL_METHODS_C11;
>
> + if (cxx_dialect >= cxx26)
> + SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> + flag_auto_var_init, AUTO_INIT_CXX26);
> +
> + /* The -Wtrivial-auto-var-init warning is useless for C++, where we always
> + add .DEFERRED_INIT calls when some (vacuous) initializers are bypassed
> + through jumps from switch condition to case/default label. */
> + if (c_dialect_cxx ())
> + warn_trivial_auto_var_init = 0;
If this is the case, should we mentioned this in the documentation of
-Wtrivial-auto-var-init?
Or for cxx, we don’t have the issue?
thanks.
Qing