Attached is another attempt to fix the problem caused by allowing front-end trees representing nontrivial VLA bound expressions to stay in attribute access attached to functions. Since removing these trees seems to be everyone's preference this patch does that by extending the free_lang_data pass to look for and zero out these trees.
Because free_lang_data only frees anything when LTO is enabled and we want these trees cleared regardless to keep them from getting clobbered during gimplification, this change also modifies the pass to do the clearing even when the pass is otherwise inactive. Tested on x86_64-linux. Martin
PR middle-end/97172 - ICE: tree code 'ssa_name' is not supported in LTO streams gcc/ChangeLog: PR middle-end/97172 * attribs.c (attr_access::free_lang_data): Define new function. * attribs.h (attr_access::free_lang_data): Declare new function. * tree.c (free_lang_data_in_type): Call attr_access::free_lang_data. (array_bound_from_maxval): Define new function. * tree.h (array_bound_from_maxval): Declare new function. gcc/c-family/ChangeLog: PR middle-end/97172 * c-pretty-print.c (c_pretty_printer::direct_abstract_declarator): Call array_bound_from_maxval. gcc/c/ChangeLog: PR middle-end/97172 * c-decl.c (get_parm_array_spec): Call array_bound_from_maxval. gcc/testsuite/ChangeLog: PR middle-end/97172 * gcc.dg/pr97172.c: New test. diff --git a/gcc/attribs.c b/gcc/attribs.c index 94991fbbeab..81322d40f1d 100644 --- a/gcc/attribs.c +++ b/gcc/attribs.c @@ -2238,6 +2238,38 @@ attr_access::vla_bounds (unsigned *nunspec) const return list_length (size); } +/* Reset front end-specific attribute access data from ATTRS. + Called from the free_lang_data pass. */ + +/* static */ void +attr_access::free_lang_data (tree attrs) +{ + for (tree acs = attrs; (acs = lookup_attribute ("access", acs)); + acs = TREE_CHAIN (acs)) + { + tree vblist = TREE_VALUE (acs); + vblist = TREE_CHAIN (vblist); + if (!vblist) + continue; + + vblist = TREE_VALUE (vblist); + if (!vblist) + continue; + + for (vblist = TREE_VALUE (vblist); vblist; vblist = TREE_CHAIN (vblist)) + { + tree *pvbnd = &TREE_VALUE (vblist); + if (!*pvbnd || DECL_P (*pvbnd)) + continue; + + /* VLA bounds that are expressions as opposed to DECLs are + only used in the front end. Reset them to keep front end + trees leaking into the middle end (see pr97172) and to + free up memory. */ + *pvbnd = NULL_TREE; + } + } +} /* Defined in attr_access. */ constexpr char attr_access::mode_chars[]; diff --git a/gcc/attribs.h b/gcc/attribs.h index 21d28a47f39..898e73db3e4 100644 --- a/gcc/attribs.h +++ b/gcc/attribs.h @@ -274,6 +274,9 @@ struct attr_access /* Return the access mode corresponding to the character code. */ static access_mode from_mode_char (char); + /* Reset front end-specific attribute access data from attributes. */ + static void free_lang_data (tree); + /* The character codes corresponding to all the access modes. */ static constexpr char mode_chars[5] = { '-', 'r', 'w', 'x', '^' }; diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c index 2095d4badf7..c6e8a45afd5 100644 --- a/gcc/c-family/c-pretty-print.c +++ b/gcc/c-family/c-pretty-print.c @@ -635,22 +635,7 @@ c_pretty_printer::direct_abstract_declarator (tree t) /* Strip the expressions from around a VLA bound added internally to make it fit the domain mold, including any casts. */ - if (TREE_CODE (maxval) == NOP_EXPR) - maxval = TREE_OPERAND (maxval, 0); - if (TREE_CODE (maxval) == PLUS_EXPR - && integer_all_onesp (TREE_OPERAND (maxval, 1))) - { - maxval = TREE_OPERAND (maxval, 0); - if (TREE_CODE (maxval) == NOP_EXPR) - maxval = TREE_OPERAND (maxval, 0); - } - if (TREE_CODE (maxval) == SAVE_EXPR) - { - maxval = TREE_OPERAND (maxval, 0); - if (TREE_CODE (maxval) == NOP_EXPR) - maxval = TREE_OPERAND (maxval, 0); - } - + maxval = array_bound_from_maxval (maxval); expression (maxval); } } diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 4ba9477f5d1..9dcad5e362d 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -5781,7 +5781,8 @@ get_parm_array_spec (const struct c_parm *parm, tree attrs) { /* Each variable VLA bound is represented by the dollar sign. */ - spec += "$"; + spec += '$'; + nelts = array_bound_from_maxval (nelts); tpbnds = tree_cons (NULL_TREE, nelts, tpbnds); } } @@ -5835,7 +5836,8 @@ get_parm_array_spec (const struct c_parm *parm, tree attrs) } /* Each variable VLA bound is represented by a dollar sign. */ - spec += "$"; + spec += '$'; + nelts = array_bound_from_maxval (nelts); vbchain = tree_cons (NULL_TREE, nelts, vbchain); } diff --git a/gcc/testsuite/gcc.dg/pr97172.c b/gcc/testsuite/gcc.dg/pr97172.c new file mode 100644 index 00000000000..ab5b2e9e7e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr97172.c @@ -0,0 +1,50 @@ +/* PR middle-end/97172 - ICE: tree code ‘ssa_name’ is not supported in LTO + streams + { dg-do compile } + { dg-options "-Wall -flto" } + { dg-require-effective-target lto } */ + +int n; + +void fn (int a[n]); +void fnp1 (int a[n + 1]); + +void fx_n (int a[][n]); +void fx_np1 (int a[][n + 1]); + +void f2_n (int a[2][n]); +void f2_np1 (int a[2][n + 1]); + +void fn_3 (int a[n][3]); +void fnp1_3 (int a[n + 1][3]); + +void fn_n (int a[n][n]); +void fn_np1 (int a[n][n + 1]); +void fnp1_np1 (int a[n + 1][n + 1]); + +void fn_n_n (int a[n][n][n]); +void fn_n_np1 (int a[n][n][n + 1]); +void fn_np1_np1 (int a[n][n + 1][n + 1]); +void fnp1_np1_np1 (int a[n + 1][n + 1][n + 1]); + + +void gn (int a[n]) { fn (a); } +void gnp1 (int a[n + 1]) { fnp1 (a); } + +void gx_n (int a[][n]) { fx_n (a); } +void gx_np1 (int a[][n + 1]) { fx_np1 (a); } + +void g2_n (int a[2][n]) { f2_n (a); } +void g2_np1 (int a[2][n + 1]) { f2_np1 (a); } + +void gn_3 (int a[n][3]) { fn_3 (a); } +void gnp1_3 (int a[n + 1][3]) { fnp1_3 (a); } + +void gn_n (int a[n][n]) { fn_n (a); } +void gn_np1 (int a[n][n + 1]) { fn_np1 (a); } +void gnp1_np1 (int a[n + 1][n + 1]) { fnp1_np1 (a); } + +void gn_n_n (int a[n][n][n]) { fn_n_n (a); } +void gn_n_np1 (int a[n][n][n + 1]) { fn_n_np1 (a); } +void gn_np1_np1 (int a[n][n + 1][n + 1]) { fn_np1_np1 (a); } +void gnp1_np1_np1 (int a[n + 1][n + 1][n + 1]) { fnp1_np1_np1 (a); } diff --git a/gcc/tree.c b/gcc/tree.c index f9d57e6d409..2d9003bfcae 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -5622,6 +5622,10 @@ free_lang_data_in_type (tree type, class free_lang_data_d *fld) if (TREE_CODE (type) == FUNCTION_TYPE) { TREE_TYPE (type) = fld_simplified_type (TREE_TYPE (type), fld); + + /* Free front end-specific attribute access data. */ + attr_access::free_lang_data (TYPE_ATTRIBUTES (type)); + /* Remove the const and volatile qualifiers from arguments. The C++ front end removes them, but the C front end does not, leading to false ODR violation errors when merging two @@ -6374,6 +6378,26 @@ free_lang_data_in_cgraph (class free_lang_data_d *fld) free_lang_data_in_type (t, fld); } +/* Free attribute access data that are not needed by the middle end. */ + +static void +free_attr_access_data () +{ + struct cgraph_node *n; + + /* Iterate over all functions declared in the translation unit. */ + FOR_EACH_FUNCTION (n) + { + tree fntype = TREE_TYPE (n->decl); + if (!fntype) + continue; + tree attrs = TYPE_ATTRIBUTES (fntype); + if (!attrs) + continue; + + attr_access::free_lang_data (attrs); + } +} /* Free resources that are used by FE but are not needed once they are done. */ @@ -6387,6 +6411,9 @@ free_lang_data (void) if (in_lto_p || (!flag_generate_lto && !flag_generate_offload)) { + if (!in_lto_p) + free_attr_access_data (); + /* Rebuild type inheritance graph even when not doing LTO to get consistent profile data. */ rebuild_type_inheritance_graph (); @@ -9356,6 +9383,48 @@ variably_modified_type_p (tree type, tree fn) #undef RETURN_TRUE_IF_VAR } +/* Strip any expressions from around the array bound MAXVAL, including + any casts, added internally to VLAs to make them fit the array domain + mold. Return the result. */ + +tree +array_bound_from_maxval (tree maxval) +{ + tree bound = maxval; + + if (TREE_CODE (bound) == NOP_EXPR) + bound = TREE_OPERAND (bound, 0); + + if (TREE_CODE (bound) == CONVERT_EXPR) + { + tree op0 = TREE_OPERAND (bound, 0); + tree bndtyp = TREE_TYPE (bound); + tree op0typ = TREE_TYPE (op0); + if (TYPE_PRECISION (bndtyp) == TYPE_PRECISION (op0typ)) + bound = op0; + } + + if (TREE_CODE (bound) == NON_LVALUE_EXPR) + bound = TREE_OPERAND (bound, 0); + + if (TREE_CODE (bound) == PLUS_EXPR + && integer_all_onesp (TREE_OPERAND (bound, 1))) + { + bound = TREE_OPERAND (bound, 0); + if (TREE_CODE (bound) == NOP_EXPR) + bound = TREE_OPERAND (bound, 0); + } + + if (TREE_CODE (bound) == SAVE_EXPR) + { + bound = TREE_OPERAND (bound, 0); + if (TREE_CODE (bound) == NOP_EXPR) + bound = TREE_OPERAND (bound, 0); + } + + return bound; +} + /* Given a DECL or TYPE, return the scope in which it was declared, or NULL_TREE if there is no containing scope. */ diff --git a/gcc/tree.h b/gcc/tree.h index 17a811c02e8..0c4ec76ad21 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -5234,6 +5234,7 @@ extern bool int_fits_type_p (const_tree, const_tree) extern void get_type_static_bounds (const_tree, mpz_t, mpz_t); #endif extern bool variably_modified_type_p (tree, tree); +extern tree array_bound_from_maxval (tree) ATTRIBUTE_NONNULL (1); extern int tree_log2 (const_tree); extern int tree_floor_log2 (const_tree); extern unsigned int tree_ctz (const_tree);