https://gcc.gnu.org/g:1ae9479cc0ebde73867123358920752d98465131
commit r17-3182-g1ae9479cc0ebde73867123358920752d98465131 Author: Kyrylo Tkachov <[email protected]> Date: Thu Aug 6 22:22:48 2026 +0200 cfgexpand: Check partition MEM_EXPRs once the RTL of parameters is final [PR126701] verify_partition_mem_exprs checks that no two out-of-SSA partitions carry one MEM_EXPR at different addresses. It ran before the loop that restores the RTL of PARM_DECL and RESULT_DECL default definitions, and that loop is what gives the partition of such a default definition its decl back: FOR_EACH_SSA_NAME (i, name, cfun) adjust_one_expanded_partition_var (name); /* Merges in a MEM_EXPR. */ ... if (MEM_P (in)) set_mem_attributes (in, var, true); /* Takes it out again. */ Walking the names of a partition attaches the variable of any of them to that partition's location, preferring an ignored one. A partition that holds the default definition of a parameter next to names of a store-motion temporary is therefore given the temporary as its MEM_EXPR for the span between the two loops, and the partition of the temporary itself has it as well: part 20 MEM_EXPR g0_lsm.14 addr (plus (virtual-stack-vars) (const_int -256)) part 26 MEM_EXPR g0_lsm.14 addr (reg/v:DI 142) The parameter takes its MEM_EXPR back before any statement is expanded, so the insn stream is unaffected, but the check in between sees the two and aborts. Run the check once the RTL of every partition is final. With the out-of-SSA fixes for PR126405 reverted it still fires on all three of that PR's tests, the PARM_DECL one included. Bootstrapped and tested on aarch64-none-linux-gnu. Ok for trunk? Thanks, Kyrill gcc/ChangeLog: PR middle-end/126701 * cfgexpand.cc (verify_partition_mem_exprs): Document when it may be called. (pass_expand::execute): Call it after the RTL of PARM_DECL and RESULT_DECL default definitions is restored. * tree-outof-ssa.cc (split_overlapping_partition_decls): Note the variable a partition of a parameter or result default definition ends up with. gcc/testsuite/ChangeLog: PR middle-end/126701 * gcc.dg/pr126701.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/cfgexpand.cc | 13 +++++--- gcc/testsuite/gcc.dg/pr126701.c | 67 +++++++++++++++++++++++++++++++++++++++++ gcc/tree-outof-ssa.cc | 6 +++- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc index 9c56b928376e..128cfc77684b 100644 --- a/gcc/cfgexpand.cc +++ b/gcc/cfgexpand.cc @@ -2467,7 +2467,12 @@ stack_protect_return_slot_p () MEM_EXPR base and an offset from it, so two stack slots carrying one MEM_EXPR read as a single object, which lets an access to one be redirected to the other. out-of-SSA keeps them apart, see the comment above - split_overlapping_partition_decls. */ + split_overlapping_partition_decls. + + Call this once the RTL of every partition is final. The partition of a + PARM_DECL or RESULT_DECL default definition is given the MEM_EXPR of any + other variable in it while its names are walked, and only gets the decl + back when its RTL is restored in pass_expand::execute. */ static void verify_partition_mem_exprs (void) @@ -7197,9 +7202,6 @@ pass_expand::execute (function *fun) adjust_one_expanded_partition_var (name); } - if (flag_checking) - verify_partition_mem_exprs (); - /* Clean up RTL of variables that straddle across multiple partitions, and check that the rtl of any PARM_DECLs that are not cleaned up is that of their default defs. */ @@ -7247,6 +7249,9 @@ pass_expand::execute (function *fun) } } + if (flag_checking) + verify_partition_mem_exprs (); + /* If this function is `main', emit a call to `__main' to run global initializers, etc. */ if (DECL_NAME (current_function_decl) diff --git a/gcc/testsuite/gcc.dg/pr126701.c b/gcc/testsuite/gcc.dg/pr126701.c new file mode 100644 index 000000000000..3f582cce5c36 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr126701.c @@ -0,0 +1,67 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fchecking" } */ + +/* The partition holding the default definition of A2 also holds names of the + store-motion temporaries of G0. Walking the names of that partition gives + its RTL the MEM_EXPR of a temporary, which the partition of that temporary + has too, but the parameter gets its own MEM_EXPR back before any statement + is expanded. */ + +typedef unsigned long long v16u64 __attribute__((vector_size (128))); + +v16u64 g0; +_Bool g9, ob13; +unsigned char g17; +static unsigned char g24 = 3; +void *a3; +__attribute__((cold)) void f8 (_Bool, unsigned char); + +void +f30 (unsigned long long a0, _Bool a1, v16u64 a2) +{ + unsigned long long v8; + _Bool c9; +cont1: + a2 = g0; + g0 = __builtin_shufflevector (g0, g0, 0, 1, 1, 4, 7, 2, 0, 8, 7, 7, 7, 8, 9, + 0, 1, 4); + switch (a0) + { + case 201146615185186167: + goto sw3; + case 1: + goto sw3; + default: + goto cont1; + } +sw3: + c9 = g24; + goto bf66; +bf26: + c9 = v8; + a3 = &c9; + if (c9) + g0 = ~g0; + if (a1) + goto br51; + goto bf66; +br32: + if (ob13) + goto br47; + if (__builtin_expect_with_probability (g9, 0, 0.265)) + goto br32; + goto bf66; +br47: + f8 (a1, g17); +br51: + a2 = g0; + if (c9) + goto bf26; +sw55: + g0 = a2; + return; +bf66: + if (__builtin_expect_with_probability (c9, 0, 0.519)) + goto br32; + goto sw55; +} diff --git a/gcc/tree-outof-ssa.cc b/gcc/tree-outof-ssa.cc index af9d79fedcd7..409e951b2127 100644 --- a/gcc/tree-outof-ssa.cc +++ b/gcc/tree-outof-ssa.cc @@ -1103,7 +1103,11 @@ split_overlapping_partition_decls (var_map map) /* Expansion hands set_rtl the representative before the other names, and expand_leader_merge keeps the variable it is given first unless a later one is DECL_IGNORED_P, so merging the two gives the variable - this partition ends up with. */ + this partition ends up with. A partition holding the default + definition of a parameter or of the result is instead seeded with that + decl, and is given it back once its RTL is restored at the end of + expansion, so the variable it ends up with is one that the rule below + keeps for it alone. */ tree var = SSA_NAME_VAR (repr); if (part_var[i]) var = expand_leader_merge (var, part_var[i]);
