X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ftree-sra.c;h=9d22ad76b4a54e0fc135b770722c801d41de541c;hb=bc52a6f14f62252e6abf982401002611f25415df;hp=18cef67c626b037046559de332aad84584374614;hpb=6fc0e9e58d0402444613e6adcd56490191b16eb1;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 18cef67c626..9d22ad76b4a 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -89,6 +89,8 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "flags.h" #include "dbgcnt.h" +#include "tree-inline.h" +#include "gimple-pretty-print.h" /* Enumeration of all aggregate reductions we can do. */ enum sra_mode { SRA_MODE_EARLY_IPA, /* early call regularization */ @@ -186,6 +188,10 @@ struct access statement? This flag is propagated down the access tree. */ unsigned grp_assignment_read : 1; + /* Does this group contain a write access that comes from an assignment + statement? This flag is propagated down the access tree. */ + unsigned grp_assignment_write : 1; + /* Other passes of the analysis use this bit to make function analyze_access_subtree create scalar replacements for this group if possible. */ @@ -213,6 +219,9 @@ struct access cannot be called from within FOR_EACH_REFERENCED_VAR. */ unsigned grp_to_be_replaced : 1; + /* Should TREE_NO_WARNING of a replacement be set? */ + unsigned grp_no_warning : 1; + /* Is it possible that the group refers to data which might be (directly or otherwise) modified? */ unsigned grp_maybe_modified : 1; @@ -358,15 +367,17 @@ dump_access (FILE *f, struct access *access, bool grp) if (grp) fprintf (f, ", grp_write = %d, total_scalarization = %d, " "grp_read = %d, grp_hint = %d, grp_assignment_read = %d," - "grp_covered = %d, grp_unscalarizable_region = %d, " - "grp_unscalarized_data = %d, grp_partial_lhs = %d, " - "grp_to_be_replaced = %d, grp_maybe_modified = %d, " + "grp_assignment_write = %d, grp_covered = %d, " + "grp_unscalarizable_region = %d, grp_unscalarized_data = %d, " + "grp_partial_lhs = %d, grp_to_be_replaced = %d, " + "grp_maybe_modified = %d, " "grp_not_necessarilly_dereferenced = %d\n", access->grp_write, access->total_scalarization, access->grp_read, access->grp_hint, access->grp_assignment_read, - access->grp_covered, access->grp_unscalarizable_region, - access->grp_unscalarized_data, access->grp_partial_lhs, - access->grp_to_be_replaced, access->grp_maybe_modified, + access->grp_assignment_write, access->grp_covered, + access->grp_unscalarizable_region, access->grp_unscalarized_data, + access->grp_partial_lhs, access->grp_to_be_replaced, + access->grp_maybe_modified, access->grp_not_necessarilly_dereferenced); else fprintf (f, ", write = %d, total_scalarization = %d, " @@ -633,7 +644,7 @@ type_internals_preclude_sra_p (tree type) case RECORD_TYPE: case UNION_TYPE: case QUAL_UNION_TYPE: - for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) if (TREE_CODE (fld) == FIELD_DECL) { tree ft = TREE_TYPE (fld); @@ -641,7 +652,8 @@ type_internals_preclude_sra_p (tree type) if (TREE_THIS_VOLATILE (fld) || !DECL_FIELD_OFFSET (fld) || !DECL_SIZE (fld) || !host_integerp (DECL_FIELD_OFFSET (fld), 1) - || !host_integerp (DECL_SIZE (fld), 1)) + || !host_integerp (DECL_SIZE (fld), 1) + || (DECL_BIT_FIELD (fld) && AGGREGATE_TYPE_P (ft))) return true; if (AGGREGATE_TYPE_P (ft) @@ -696,7 +708,7 @@ mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt) for (parm = DECL_ARGUMENTS (current_function_decl); parm && parm != base; - parm = TREE_CHAIN (parm)) + parm = DECL_CHAIN (parm)) parm_index++; gcc_assert (parm_index < func_param_count); @@ -750,7 +762,8 @@ create_access (tree expr, gimple stmt, bool write) base = get_ref_base_and_extent (expr, &offset, &size, &max_size); - if (sra_mode == SRA_MODE_EARLY_IPA && INDIRECT_REF_P (base)) + if (sra_mode == SRA_MODE_EARLY_IPA + && TREE_CODE (base) == MEM_REF) { base = get_ssa_base_param (TREE_OPERAND (base, 0)); if (!base) @@ -770,12 +783,13 @@ create_access (tree expr, gimple stmt, bool write) disqualify_candidate (base, "Encountered a variable sized access."); return NULL; } - if ((offset % BITS_PER_UNIT) != 0 || (size % BITS_PER_UNIT) != 0) + if (TREE_CODE (expr) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (expr, 1))) { - disqualify_candidate (base, - "Encountered an acces not aligned to a byte."); + disqualify_candidate (base, "Encountered a bit-field access."); return NULL; } + gcc_checking_assert ((offset % BITS_PER_UNIT) == 0); if (ptr) mark_parm_dereference (base, offset + size, stmt); @@ -807,73 +821,66 @@ create_access (tree expr, gimple stmt, bool write) /* Return true iff TYPE is a RECORD_TYPE with fields that are either of gimple register types or (recursively) records with only these two kinds of fields. - It also returns false if any of these records has a zero-size field as its - last field. */ + It also returns false if any of these records contains a bit-field. */ static bool type_consists_of_records_p (tree type) { tree fld; - bool last_fld_has_zero_size = false; if (TREE_CODE (type) != RECORD_TYPE) return false; - for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) if (TREE_CODE (fld) == FIELD_DECL) { tree ft = TREE_TYPE (fld); + if (DECL_BIT_FIELD (fld)) + return false; + if (!is_gimple_reg_type (ft) && !type_consists_of_records_p (ft)) return false; - - last_fld_has_zero_size = tree_low_cst (DECL_SIZE (fld), 1) == 0; } - if (last_fld_has_zero_size) - return false; - return true; } /* Create total_scalarization accesses for all scalar type fields in DECL that must be of a RECORD_TYPE conforming to type_consists_of_records_p. BASE must be the top-most VAR_DECL representing the variable, OFFSET must be the - offset of DECL within BASE. */ + offset of DECL within BASE. REF must be the memory reference expression for + the given decl. */ static void -completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset) +completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset, + tree ref) { tree fld, decl_type = TREE_TYPE (decl); - for (fld = TYPE_FIELDS (decl_type); fld; fld = TREE_CHAIN (fld)) + for (fld = TYPE_FIELDS (decl_type); fld; fld = DECL_CHAIN (fld)) if (TREE_CODE (fld) == FIELD_DECL) { HOST_WIDE_INT pos = offset + int_bit_position (fld); tree ft = TREE_TYPE (fld); + tree nref = build3 (COMPONENT_REF, TREE_TYPE (fld), ref, fld, + NULL_TREE); if (is_gimple_reg_type (ft)) { struct access *access; HOST_WIDE_INT size; - tree expr; - bool ok; size = tree_low_cst (DECL_SIZE (fld), 1); - expr = base; - ok = build_ref_for_offset (&expr, TREE_TYPE (base), pos, - ft, false); - gcc_assert (ok); - access = create_access_1 (base, pos, size); - access->expr = expr; + access->expr = nref; access->type = ft; access->total_scalarization = 1; /* Accesses for intraprocedural SRA can have their stmt NULL. */ } else - completely_scalarize_record (base, fld, pos); + completely_scalarize_record (base, fld, pos, nref); } } @@ -884,15 +891,10 @@ completely_scalarize_record (tree base, tree decl, HOST_WIDE_INT offset) static void disqualify_base_of_expr (tree t, const char *reason) { - while (handled_component_p (t)) - t = TREE_OPERAND (t, 0); - - if (sra_mode == SRA_MODE_EARLY_IPA) - { - if (INDIRECT_REF_P (t)) - t = TREE_OPERAND (t, 0); - t = get_ssa_base_param (t); - } + t = get_base_address (t); + if (sra_mode == SRA_MODE_EARLY_IPA + && TREE_CODE (t) == MEM_REF) + t = get_ssa_base_param (TREE_OPERAND (t, 0)); if (t && DECL_P (t)) disqualify_candidate (t, reason); @@ -934,8 +936,9 @@ build_access_from_expr_1 (tree expr, gimple stmt, bool write) switch (TREE_CODE (expr)) { - case INDIRECT_REF: - if (sra_mode != SRA_MODE_EARLY_IPA) + case MEM_REF: + if (TREE_CODE (TREE_OPERAND (expr, 0)) != ADDR_EXPR + && sra_mode != SRA_MODE_EARLY_IPA) return NULL; /* fall through */ case VAR_DECL: @@ -1022,6 +1025,9 @@ build_accesses_from_assign (gimple stmt) racc = build_access_from_expr_1 (rhs, stmt, false); lacc = build_access_from_expr_1 (lhs, stmt, true); + if (lacc) + lacc->grp_assignment_write = 1; + if (racc) { racc->grp_assignment_read = 1; @@ -1284,7 +1290,21 @@ make_fancy_name_1 (tree expr) break; sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (index)); obstack_grow (&name_obstack, buffer, strlen (buffer)); + break; + case ADDR_EXPR: + make_fancy_name_1 (TREE_OPERAND (expr, 0)); + break; + + case MEM_REF: + make_fancy_name_1 (TREE_OPERAND (expr, 0)); + if (!integer_zerop (TREE_OPERAND (expr, 1))) + { + obstack_1grow (&name_obstack, '$'); + sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, + TREE_INT_CST_LOW (TREE_OPERAND (expr, 1))); + obstack_grow (&name_obstack, buffer, strlen (buffer)); + } break; case BIT_FIELD_REF: @@ -1307,11 +1327,103 @@ make_fancy_name (tree expr) return XOBFINISH (&name_obstack, char *); } -/* Helper function for build_ref_for_offset. */ +/* Construct a MEM_REF that would reference a part of aggregate BASE of type + EXP_TYPE at the given OFFSET. If BASE is something for which + get_addr_base_and_unit_offset returns NULL, gsi must be non-NULL and is used + to insert new statements either before or below the current one as specified + by INSERT_AFTER. This function is not capable of handling bitfields. */ + +tree +build_ref_for_offset (location_t loc, tree base, HOST_WIDE_INT offset, + tree exp_type, gimple_stmt_iterator *gsi, + bool insert_after) +{ + tree prev_base = base; + tree off; + HOST_WIDE_INT base_offset; + + gcc_checking_assert (offset % BITS_PER_UNIT == 0); + + base = get_addr_base_and_unit_offset (base, &base_offset); + + /* get_addr_base_and_unit_offset returns NULL for references with a variable + offset such as array[var_index]. */ + if (!base) + { + gimple stmt; + tree tmp, addr; + + gcc_checking_assert (gsi); + tmp = create_tmp_reg (build_pointer_type (TREE_TYPE (prev_base)), NULL); + add_referenced_var (tmp); + tmp = make_ssa_name (tmp, NULL); + addr = build_fold_addr_expr (unshare_expr (prev_base)); + stmt = gimple_build_assign (tmp, addr); + gimple_set_location (stmt, loc); + SSA_NAME_DEF_STMT (tmp) = stmt; + if (insert_after) + gsi_insert_after (gsi, stmt, GSI_NEW_STMT); + else + gsi_insert_before (gsi, stmt, GSI_SAME_STMT); + update_stmt (stmt); + + off = build_int_cst (reference_alias_ptr_type (prev_base), + offset / BITS_PER_UNIT); + base = tmp; + } + else if (TREE_CODE (base) == MEM_REF) + { + off = build_int_cst (TREE_TYPE (TREE_OPERAND (base, 1)), + base_offset + offset / BITS_PER_UNIT); + off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off, 0); + base = unshare_expr (TREE_OPERAND (base, 0)); + } + else + { + off = build_int_cst (reference_alias_ptr_type (base), + base_offset + offset / BITS_PER_UNIT); + base = build_fold_addr_expr (unshare_expr (base)); + } + + return fold_build2_loc (loc, MEM_REF, exp_type, base, off); +} + +/* Construct a memory reference to a part of an aggregate BASE at the given + OFFSET and of the same type as MODEL. In case this is a reference to a + component, the function will replicate the last COMPONENT_REF of model's + expr to access it. GSI and INSERT_AFTER have the same meaning as in + build_ref_for_offset. */ + +static tree +build_ref_for_model (location_t loc, tree base, HOST_WIDE_INT offset, + struct access *model, gimple_stmt_iterator *gsi, + bool insert_after) +{ + if (TREE_CODE (model->expr) == COMPONENT_REF) + { + tree t, exp_type; + offset -= int_bit_position (TREE_OPERAND (model->expr, 1)); + exp_type = TREE_TYPE (TREE_OPERAND (model->expr, 0)); + t = build_ref_for_offset (loc, base, offset, exp_type, gsi, insert_after); + return fold_build3_loc (loc, COMPONENT_REF, model->type, t, + TREE_OPERAND (model->expr, 1), NULL_TREE); + } + else + return build_ref_for_offset (loc, base, offset, model->type, + gsi, insert_after); +} + +/* Construct a memory reference consisting of component_refs and array_refs to + a part of an aggregate *RES (which is of type TYPE). The requested part + should have type EXP_TYPE at be the given OFFSET. This function might not + succeed, it returns true when it does and only then *RES points to something + meaningful. This function should be used only to build expressions that we + might need to present to user (e.g. in warnings). In all other situations, + build_ref_for_model or build_ref_for_offset should be used instead. */ static bool -build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset, - tree exp_type) +build_user_friendly_ref_for_offset (tree *res, tree type, HOST_WIDE_INT offset, + tree exp_type) { while (1) { @@ -1328,7 +1440,7 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset, case UNION_TYPE: case QUAL_UNION_TYPE: case RECORD_TYPE: - for (fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) { HOST_WIDE_INT pos, size; tree expr, *expr_ptr; @@ -1350,19 +1462,13 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset, else if (pos > offset || (pos + size) <= offset) continue; - if (res) - { - expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld, - NULL_TREE); - expr_ptr = &expr; - } - else - expr_ptr = NULL; - if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld), - offset - pos, exp_type)) + expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld, + NULL_TREE); + expr_ptr = &expr; + if (build_user_friendly_ref_for_offset (expr_ptr, TREE_TYPE (fld), + offset - pos, exp_type)) { - if (res) - *res = expr; + *res = expr; return true; } } @@ -1377,14 +1483,11 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset, minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type)); if (TREE_CODE (minidx) != INTEGER_CST || el_size == 0) return false; - if (res) - { - index = build_int_cst (TYPE_DOMAIN (type), offset / el_size); - if (!integer_zerop (minidx)) - index = int_const_binop (PLUS_EXPR, index, minidx, 0); - *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index, - NULL_TREE, NULL_TREE); - } + index = build_int_cst (TYPE_DOMAIN (type), offset / el_size); + if (!integer_zerop (minidx)) + index = int_const_binop (PLUS_EXPR, index, minidx, 0); + *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index, + NULL_TREE, NULL_TREE); offset = offset % el_size; type = TREE_TYPE (type); break; @@ -1401,36 +1504,6 @@ build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset, } } -/* Construct an expression that would reference a part of aggregate *EXPR of - type TYPE at the given OFFSET of the type EXP_TYPE. If EXPR is NULL, the - function only determines whether it can build such a reference without - actually doing it, otherwise, the tree it points to is unshared first and - then used as a base for furhter sub-references. - - FIXME: Eventually this should be replaced with - maybe_fold_offset_to_reference() from tree-ssa-ccp.c but that requires a - minor rewrite of fold_stmt. - */ - -bool -build_ref_for_offset (tree *expr, tree type, HOST_WIDE_INT offset, - tree exp_type, bool allow_ptr) -{ - location_t loc = expr ? EXPR_LOCATION (*expr) : UNKNOWN_LOCATION; - - if (expr) - *expr = unshare_expr (*expr); - - if (allow_ptr && POINTER_TYPE_P (type)) - { - type = TREE_TYPE (type); - if (expr) - *expr = fold_build1_loc (loc, INDIRECT_REF, type, *expr); - } - - return build_ref_for_offset_1 (expr, type, offset, exp_type); -} - /* Return true iff TYPE is stdarg va_list type. */ static inline bool @@ -1504,8 +1577,7 @@ sort_and_splice_var_accesses (tree var) access_count = VEC_length (access_p, access_vec); /* Sort by . */ - qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p), - compare_access_positions); + VEC_qsort (access_p, access_vec, compare_access_positions); i = 0; while (i < access_count) @@ -1514,6 +1586,7 @@ sort_and_splice_var_accesses (tree var) bool grp_write = access->write; bool grp_read = !access->write; bool grp_assignment_read = access->grp_assignment_read; + bool grp_assignment_write = access->grp_assignment_write; bool multiple_reads = false; bool total_scalarization = access->total_scalarization; bool grp_partial_lhs = access->grp_partial_lhs; @@ -1548,6 +1621,7 @@ sort_and_splice_var_accesses (tree var) grp_read = true; } grp_assignment_read |= ac2->grp_assignment_read; + grp_assignment_write |= ac2->grp_assignment_write; grp_partial_lhs |= ac2->grp_partial_lhs; unscalarizable_region |= ac2->grp_unscalarizable_region; total_scalarization |= ac2->total_scalarization; @@ -1567,6 +1641,7 @@ sort_and_splice_var_accesses (tree var) access->grp_write = grp_write; access->grp_read = grp_read; access->grp_assignment_read = grp_assignment_read; + access->grp_assignment_write = grp_assignment_write; access->grp_hint = multiple_reads || total_scalarization; access->grp_partial_lhs = grp_partial_lhs; access->grp_unscalarizable_region = unscalarizable_region; @@ -1643,7 +1718,10 @@ create_access_replacement (struct access *access, bool rename) } SET_DECL_DEBUG_EXPR (repl, debug_expr); DECL_DEBUG_EXPR_IS_FROM (repl) = 1; - TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base); + if (access->grp_no_warning) + TREE_NO_WARNING (repl) = 1; + else + TREE_NO_WARNING (repl) = TREE_NO_WARNING (access->base); } else TREE_NO_WARNING (repl) = 1; @@ -1752,17 +1830,50 @@ expr_with_var_bounded_array_refs_p (tree expr) return false; } -enum mark_read_status { SRA_MR_NOT_READ, SRA_MR_READ, SRA_MR_ASSIGN_READ}; +enum mark_rw_status { SRA_MRRW_NOTHING, SRA_MRRW_DIRECT, SRA_MRRW_ASSIGN}; /* Analyze the subtree of accesses rooted in ROOT, scheduling replacements when both seeming beneficial and when ALLOW_REPLACEMENTS allows it. Also set all sorts of access flags appropriately along the way, notably always set grp_read and grp_assign_read according to MARK_READ and grp_write when - MARK_WRITE is true. */ + MARK_WRITE is true. + + Creating a replacement for a scalar access is considered beneficial if its + grp_hint is set (this means we are either attempting total scalarization or + there is more than one direct read access) or according to the following + table: + + Access written to individually (once or more times) + | + | Parent written to in an assignment statement + | | + | | Access read individually _once_ + | | | + | | | Parent read in an assignment statement + | | | | + | | | | Scalarize Comment +----------------------------------------------------------------------------- + 0 0 0 0 No access for the scalar + 0 0 0 1 No access for the scalar + 0 0 1 0 No Single read - won't help + 0 0 1 1 No The same case + 0 1 0 0 No access for the scalar + 0 1 0 1 No access for the scalar + 0 1 1 0 Yes s = *g; return s.i; + 0 1 1 1 Yes The same case as above + 1 0 0 0 No Won't help + 1 0 0 1 Yes s.i = 1; *g = s; + 1 0 1 0 Yes s.i = 5; g = s.i; + 1 0 1 1 Yes The same case as above + 1 1 0 0 No Won't help. + 1 1 0 1 Yes s.i = 1; *g = s; + 1 1 1 0 Yes s = *g; return s.i; + 1 1 1 1 Yes Any of the above yeses */ static bool analyze_access_subtree (struct access *root, bool allow_replacements, - enum mark_read_status mark_read, bool mark_write) + enum mark_rw_status mark_read, + enum mark_rw_status mark_write) { struct access *child; HOST_WIDE_INT limit = root->offset + root->size; @@ -1770,23 +1881,31 @@ analyze_access_subtree (struct access *root, bool allow_replacements, bool scalar = is_gimple_reg_type (root->type); bool hole = false, sth_created = false; bool direct_read = root->grp_read; + bool direct_write = root->grp_write; - if (mark_read == SRA_MR_ASSIGN_READ) + if (root->grp_assignment_read) + mark_read = SRA_MRRW_ASSIGN; + else if (mark_read == SRA_MRRW_ASSIGN) { root->grp_read = 1; root->grp_assignment_read = 1; } - if (mark_read == SRA_MR_READ) + else if (mark_read == SRA_MRRW_DIRECT) root->grp_read = 1; - else if (root->grp_assignment_read) - mark_read = SRA_MR_ASSIGN_READ; else if (root->grp_read) - mark_read = SRA_MR_READ; + mark_read = SRA_MRRW_DIRECT; - if (mark_write) - root->grp_write = true; + if (root->grp_assignment_write) + mark_write = SRA_MRRW_ASSIGN; + else if (mark_write == SRA_MRRW_ASSIGN) + { + root->grp_write = 1; + root->grp_assignment_write = 1; + } + else if (mark_write == SRA_MRRW_DIRECT) + root->grp_write = 1; else if (root->grp_write) - mark_write = true; + mark_write = SRA_MRRW_DIRECT; if (root->grp_unscalarizable_region) allow_replacements = false; @@ -1811,13 +1930,8 @@ analyze_access_subtree (struct access *root, bool allow_replacements, if (allow_replacements && scalar && !root->first_child && (root->grp_hint - || (root->grp_write && (direct_read || root->grp_assignment_read))) - /* We must not ICE later on when trying to build an access to the - original data within the aggregate even when it is impossible to do in - a defined way like in the PR 42703 testcase. Therefore we check - pre-emptively here that we will be able to do that. */ - && build_ref_for_offset (NULL, TREE_TYPE (root->base), root->offset, - root->type, false)) + || ((direct_write || root->grp_assignment_write) + && (direct_read || root->grp_assignment_read)))) { if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -1856,7 +1970,8 @@ analyze_access_trees (struct access *access) while (access) { - if (analyze_access_subtree (access, true, SRA_MR_NOT_READ, false)) + if (analyze_access_subtree (access, true, + SRA_MRRW_NOTHING, SRA_MRRW_NOTHING)) ret = true; access = access->next_grp; } @@ -1902,16 +2017,20 @@ create_artificial_child_access (struct access *parent, struct access *model, { struct access *access; struct access **child; - tree expr = parent->base;; + tree expr = parent->base; gcc_assert (!model->grp_unscalarizable_region); - if (!build_ref_for_offset (&expr, TREE_TYPE (expr), new_offset, - model->type, false)) - return NULL; - access = (struct access *) pool_alloc (access_pool); memset (access, 0, sizeof (struct access)); + if (!build_user_friendly_ref_for_offset (&expr, TREE_TYPE (expr), new_offset, + model->type)) + { + access->grp_no_warning = true; + expr = build_ref_for_model (EXPR_LOCATION (parent->base), parent->base, + new_offset, model, NULL, false); + } + access->base = parent->base; access->expr = expr; access->offset = new_offset; @@ -1952,11 +2071,16 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc) { tree t = lacc->base; - if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type, - false)) + lacc->type = racc->type; + if (build_user_friendly_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, + racc->type)) + lacc->expr = t; + else { - lacc->expr = t; - lacc->type = racc->type; + lacc->expr = build_ref_for_model (EXPR_LOCATION (lacc->base), + lacc->base, lacc->offset, + racc, NULL, false); + lacc->grp_no_warning = true; } return false; } @@ -1982,13 +2106,6 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc) continue; } - /* If a (part of) a union field is on the RHS of an assignment, it can - have sub-accesses which do not make sense on the LHS (PR 40351). - Check that this is not the case. */ - if (!build_ref_for_offset (NULL, TREE_TYPE (lacc->base), norm_offset, - rchild->type, false)) - continue; - rchild->grp_hint = 1; new_acc = create_artificial_child_access (lacc, rchild, norm_offset); if (new_acc) @@ -2055,7 +2172,7 @@ analyze_all_variable_accesses (void) <= max_total_scalarization_size) && type_consists_of_records_p (TREE_TYPE (var))) { - completely_scalarize_record (var, var, 0); + completely_scalarize_record (var, var, 0, var); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Will attempt to totally scalarize "); @@ -2112,60 +2229,29 @@ analyze_all_variable_accesses (void) return false; } -/* Return true iff a reference statement into aggregate AGG can be built for - every single to-be-replaced accesses that is a child of ACCESS, its sibling - or a child of its sibling. TOP_OFFSET is the offset from the processed - access subtree that has to be subtracted from offset of each access. */ - -static bool -ref_expr_for_all_replacements_p (struct access *access, tree agg, - HOST_WIDE_INT top_offset) -{ - do - { - if (access->grp_to_be_replaced - && !build_ref_for_offset (NULL, TREE_TYPE (agg), - access->offset - top_offset, - access->type, false)) - return false; - - if (access->first_child - && !ref_expr_for_all_replacements_p (access->first_child, agg, - top_offset)) - return false; - - access = access->next_sibling; - } - while (access); - - return true; -} - /* Generate statements copying scalar replacements of accesses within a subtree - into or out of AGG. ACCESS is the first child of the root of the subtree to - be processed. AGG is an aggregate type expression (can be a declaration but - does not have to be, it can for example also be an indirect_ref). - TOP_OFFSET is the offset of the processed subtree which has to be subtracted - from offsets of individual accesses to get corresponding offsets for AGG. - If CHUNK_SIZE is non-null, copy only replacements in the interval - , otherwise copy all. GSI is a - statement iterator used to place the new statements. WRITE should be true - when the statements should write from AGG to the replacement and false if - vice versa. if INSERT_AFTER is true, new statements will be added after the - current statement in GSI, they will be added before the statement - otherwise. */ + into or out of AGG. ACCESS, all its children, siblings and their children + are to be processed. AGG is an aggregate type expression (can be a + declaration but does not have to be, it can for example also be a mem_ref or + a series of handled components). TOP_OFFSET is the offset of the processed + subtree which has to be subtracted from offsets of individual accesses to + get corresponding offsets for AGG. If CHUNK_SIZE is non-null, copy only + replacements in the interval , + otherwise copy all. GSI is a statement iterator used to place the new + statements. WRITE should be true when the statements should write from AGG + to the replacement and false if vice versa. if INSERT_AFTER is true, new + statements will be added after the current statement in GSI, they will be + added before the statement otherwise. */ static void generate_subtree_copies (struct access *access, tree agg, HOST_WIDE_INT top_offset, HOST_WIDE_INT start_offset, HOST_WIDE_INT chunk_size, gimple_stmt_iterator *gsi, bool write, - bool insert_after) + bool insert_after, location_t loc) { do { - tree expr = agg; - if (chunk_size && access->offset >= start_offset + chunk_size) return; @@ -2173,14 +2259,11 @@ generate_subtree_copies (struct access *access, tree agg, && (chunk_size == 0 || access->offset + access->size > start_offset)) { - tree repl = get_access_replacement (access); - bool ref_found; + tree expr, repl = get_access_replacement (access); gimple stmt; - ref_found = build_ref_for_offset (&expr, TREE_TYPE (agg), - access->offset - top_offset, - access->type, false); - gcc_assert (ref_found); + expr = build_ref_for_model (loc, agg, access->offset - top_offset, + access, gsi, insert_after); if (write) { @@ -2201,6 +2284,7 @@ generate_subtree_copies (struct access *access, tree agg, : GSI_SAME_STMT); stmt = gimple_build_assign (expr, repl); } + gimple_set_location (stmt, loc); if (insert_after) gsi_insert_after (gsi, stmt, GSI_NEW_STMT); @@ -2213,7 +2297,7 @@ generate_subtree_copies (struct access *access, tree agg, if (access->first_child) generate_subtree_copies (access->first_child, agg, top_offset, start_offset, chunk_size, gsi, - write, insert_after); + write, insert_after, loc); access = access->next_sibling; } @@ -2227,7 +2311,7 @@ generate_subtree_copies (struct access *access, tree agg, static void init_subtree_with_zero (struct access *access, gimple_stmt_iterator *gsi, - bool insert_after) + bool insert_after, location_t loc) { struct access *child; @@ -2237,17 +2321,17 @@ init_subtree_with_zero (struct access *access, gimple_stmt_iterator *gsi, gimple stmt; stmt = gimple_build_assign (get_access_replacement (access), - fold_convert (access->type, - integer_zero_node)); + build_zero_cst (access->type)); if (insert_after) gsi_insert_after (gsi, stmt, GSI_NEW_STMT); else gsi_insert_before (gsi, stmt, GSI_SAME_STMT); update_stmt (stmt); + gimple_set_location (stmt, loc); } for (child = access->first_child; child; child = child->next_sibling) - init_subtree_with_zero (child, gsi, insert_after); + init_subtree_with_zero (child, gsi, insert_after, loc); } /* Search for an access representative for the given expression EXPR and @@ -2284,6 +2368,7 @@ get_access_for_expr (tree expr) static bool sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) { + location_t loc; struct access *access; tree type, bfr; @@ -2302,6 +2387,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) return false; type = TREE_TYPE (*expr); + loc = gimple_location (gsi_stmt (*gsi)); if (access->grp_to_be_replaced) { tree repl = get_access_replacement (access); @@ -2317,12 +2403,10 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) in assembler statements (see PR42398). */ if (!useless_type_conversion_p (type, access->type)) { - tree ref = access->base; - bool ok; + tree ref; - ok = build_ref_for_offset (&ref, TREE_TYPE (ref), - access->offset, access->type, false); - gcc_assert (ok); + ref = build_ref_for_model (loc, access->base, access->offset, access, + NULL, false); if (write) { @@ -2332,6 +2416,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) ref = force_gimple_operand_gsi (gsi, ref, true, NULL_TREE, false, GSI_NEW_STMT); stmt = gimple_build_assign (repl, ref); + gimple_set_location (stmt, loc); gsi_insert_after (gsi, stmt, GSI_NEW_STMT); } else @@ -2342,6 +2427,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) repl = force_gimple_operand_gsi (gsi, repl, true, NULL_TREE, true, GSI_SAME_STMT); stmt = gimple_build_assign (ref, repl); + gimple_set_location (stmt, loc); gsi_insert_before (gsi, stmt, GSI_SAME_STMT); } } @@ -2365,7 +2451,8 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write) start_offset = chunk_size = 0; generate_subtree_copies (access->first_child, access->base, 0, - start_offset, chunk_size, gsi, write, write); + start_offset, chunk_size, gsi, write, write, + loc); } return true; } @@ -2378,53 +2465,55 @@ enum unscalarized_data_handling { SRA_UDH_NONE, /* Nothing done so far. */ SRA_UDH_LEFT }; /* Data flushed to the LHS. */ /* Store all replacements in the access tree rooted in TOP_RACC either to their - base aggregate if there are unscalarized data or directly to LHS - otherwise. */ + base aggregate if there are unscalarized data or directly to LHS of the + statement that is pointed to by GSI otherwise. */ static enum unscalarized_data_handling -handle_unscalarized_data_in_subtree (struct access *top_racc, tree lhs, +handle_unscalarized_data_in_subtree (struct access *top_racc, gimple_stmt_iterator *gsi) { if (top_racc->grp_unscalarized_data) { generate_subtree_copies (top_racc->first_child, top_racc->base, 0, 0, 0, - gsi, false, false); + gsi, false, false, + gimple_location (gsi_stmt (*gsi))); return SRA_UDH_RIGHT; } else { + tree lhs = gimple_assign_lhs (gsi_stmt (*gsi)); generate_subtree_copies (top_racc->first_child, lhs, top_racc->offset, - 0, 0, gsi, false, false); + 0, 0, gsi, false, false, + gimple_location (gsi_stmt (*gsi))); return SRA_UDH_LEFT; } } -/* Try to generate statements to load all sub-replacements in an access - (sub)tree (LACC is the first child) from scalar replacements in the TOP_RACC - (sub)tree. If that is not possible, refresh the TOP_RACC base aggregate and - load the accesses from it. LEFT_OFFSET is the offset of the left whole - subtree being copied, RIGHT_OFFSET is the same thing for the right subtree. - GSI is stmt iterator used for statement insertions. *REFRESHED is true iff - the rhs top aggregate has already been refreshed by contents of its scalar - reductions and is set to true if this function has to do it. */ +/* Try to generate statements to load all sub-replacements in an access subtree + formed by children of LACC from scalar replacements in the TOP_RACC subtree. + If that is not possible, refresh the TOP_RACC base aggregate and load the + accesses from it. LEFT_OFFSET is the offset of the left whole subtree being + copied. NEW_GSI is stmt iterator used for statement insertions after the + original assignment, OLD_GSI is used to insert statements before the + assignment. *REFRESHED keeps the information whether we have needed to + refresh replacements of the LHS and from which side of the assignments this + takes place. */ static void load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc, HOST_WIDE_INT left_offset, - HOST_WIDE_INT right_offset, gimple_stmt_iterator *old_gsi, gimple_stmt_iterator *new_gsi, - enum unscalarized_data_handling *refreshed, - tree lhs) + enum unscalarized_data_handling *refreshed) { - location_t loc = EXPR_LOCATION (lacc->expr); - do + location_t loc = gimple_location (gsi_stmt (*old_gsi)); + for (lacc = lacc->first_child; lacc; lacc = lacc->next_sibling) { if (lacc->grp_to_be_replaced) { struct access *racc; - HOST_WIDE_INT offset = lacc->offset - left_offset + right_offset; + HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset; gimple stmt; tree rhs; @@ -2441,47 +2530,31 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc, the aggregate. See if we have to update it first... */ if (*refreshed == SRA_UDH_NONE) *refreshed = handle_unscalarized_data_in_subtree (top_racc, - lhs, old_gsi); + old_gsi); if (*refreshed == SRA_UDH_LEFT) - { - bool repl_found; - - rhs = lacc->base; - repl_found = build_ref_for_offset (&rhs, TREE_TYPE (rhs), - lacc->offset, lacc->type, - false); - gcc_assert (repl_found); - } + rhs = build_ref_for_model (loc, lacc->base, lacc->offset, lacc, + new_gsi, true); else - { - bool repl_found; - - rhs = top_racc->base; - repl_found = build_ref_for_offset (&rhs, - TREE_TYPE (top_racc->base), - offset, lacc->type, false); - gcc_assert (repl_found); - } + rhs = build_ref_for_model (loc, top_racc->base, offset, lacc, + new_gsi, true); } stmt = gimple_build_assign (get_access_replacement (lacc), rhs); gsi_insert_after (new_gsi, stmt, GSI_NEW_STMT); + gimple_set_location (stmt, loc); update_stmt (stmt); sra_stats.subreplacements++; } else if (*refreshed == SRA_UDH_NONE && lacc->grp_read && !lacc->grp_covered) - *refreshed = handle_unscalarized_data_in_subtree (top_racc, lhs, + *refreshed = handle_unscalarized_data_in_subtree (top_racc, old_gsi); if (lacc->first_child) - load_assign_lhs_subreplacements (lacc->first_child, top_racc, - left_offset, right_offset, - old_gsi, new_gsi, refreshed, lhs); - lacc = lacc->next_sibling; + load_assign_lhs_subreplacements (lacc, top_racc, left_offset, + old_gsi, new_gsi, refreshed); } - while (lacc); } /* Result code for SRA assignment modification. */ @@ -2499,11 +2572,13 @@ sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi) { tree lhs = gimple_assign_lhs (*stmt); struct access *acc; + location_t loc; acc = get_access_for_expr (lhs); if (!acc) return SRA_AM_NONE; + loc = gimple_location (*stmt); if (VEC_length (constructor_elt, CONSTRUCTOR_ELTS (gimple_assign_rhs1 (*stmt))) > 0) { @@ -2511,30 +2586,30 @@ sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi) following should handle it gracefully. */ if (access_has_children_p (acc)) generate_subtree_copies (acc->first_child, acc->base, 0, 0, 0, gsi, - true, true); + true, true, loc); return SRA_AM_MODIFIED; } if (acc->grp_covered) { - init_subtree_with_zero (acc, gsi, false); + init_subtree_with_zero (acc, gsi, false, loc); unlink_stmt_vdef (*stmt); gsi_remove (gsi, true); return SRA_AM_REMOVED; } else { - init_subtree_with_zero (acc, gsi, true); + init_subtree_with_zero (acc, gsi, true, loc); return SRA_AM_MODIFIED; } } -/* Create a new suitable default definition SSA_NAME and replace all uses of - SSA with it, RACC is access describing the uninitialized part of an - aggregate that is being loaded. */ +/* Create and return a new suitable default definition SSA_NAME for RACC which + is an access describing an uninitialized part of an aggregate that is being + loaded. */ -static void -replace_uses_with_default_def_ssa_name (tree ssa, struct access *racc) +static tree +get_repl_default_def_ssa_name (struct access *racc) { tree repl, decl; @@ -2547,7 +2622,42 @@ replace_uses_with_default_def_ssa_name (tree ssa, struct access *racc) set_default_def (decl, repl); } - replace_uses_by (ssa, repl); + return repl; +} + +/* Return true if REF has a COMPONENT_REF with a bit-field field declaration + somewhere in it. */ + +static inline bool +contains_bitfld_comp_ref_p (const_tree ref) +{ + while (handled_component_p (ref)) + { + if (TREE_CODE (ref) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (ref, 1))) + return true; + ref = TREE_OPERAND (ref, 0); + } + + return false; +} + +/* Return true if REF has an VIEW_CONVERT_EXPR or a COMPONENT_REF with a + bit-field field declaration somewhere in it. */ + +static inline bool +contains_vce_or_bfcref_p (const_tree ref) +{ + while (handled_component_p (ref)) + { + if (TREE_CODE (ref) == VIEW_CONVERT_EXPR + || (TREE_CODE (ref) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (ref, 1)))) + return true; + ref = TREE_OPERAND (ref, 0); + } + + return false; } /* Examine both sides of the assignment statement pointed to by STMT, replace @@ -2563,7 +2673,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) tree lhs, rhs; bool modify_this_stmt = false; bool force_gimple_rhs = false; - location_t loc = gimple_location (*stmt); + location_t loc; gimple_stmt_iterator orig_gsi = *gsi; if (!gimple_assign_single_p (*stmt)) @@ -2590,6 +2700,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) if (!lacc && !racc) return SRA_AM_NONE; + loc = gimple_location (*stmt); if (lacc && lacc->grp_to_be_replaced) { lhs = get_access_replacement (lacc); @@ -2617,27 +2728,23 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) ??? This should move to fold_stmt which we simply should call after building a VIEW_CONVERT_EXPR here. */ if (AGGREGATE_TYPE_P (TREE_TYPE (lhs)) + && !contains_bitfld_comp_ref_p (lhs) && !access_has_children_p (lacc)) { - tree expr = lhs; - if (build_ref_for_offset (&expr, TREE_TYPE (lhs), 0, - TREE_TYPE (rhs), false)) - { - lhs = expr; - gimple_assign_set_lhs (*stmt, expr); - } + lhs = build_ref_for_offset (loc, lhs, 0, TREE_TYPE (rhs), + gsi, false); + gimple_assign_set_lhs (*stmt, lhs); } else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs)) + && !contains_vce_or_bfcref_p (rhs) && !access_has_children_p (racc)) - { - tree expr = rhs; - if (build_ref_for_offset (&expr, TREE_TYPE (rhs), 0, - TREE_TYPE (lhs), false)) - rhs = expr; - } + rhs = build_ref_for_offset (loc, rhs, 0, TREE_TYPE (lhs), + gsi, false); + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) { - rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (lhs), rhs); + rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (lhs), + rhs); if (is_gimple_reg_type (TREE_TYPE (lhs)) && TREE_CODE (lhs) != SSA_NAME) force_gimple_rhs = true; @@ -2679,19 +2786,15 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) This is what the first branch does. */ if (gimple_has_volatile_ops (*stmt) - || contains_view_convert_expr_p (rhs) - || contains_view_convert_expr_p (lhs) - || (access_has_children_p (racc) - && !ref_expr_for_all_replacements_p (racc, lhs, racc->offset)) - || (access_has_children_p (lacc) - && !ref_expr_for_all_replacements_p (lacc, rhs, lacc->offset))) + || contains_vce_or_bfcref_p (rhs) + || contains_vce_or_bfcref_p (lhs)) { if (access_has_children_p (racc)) generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0, - gsi, false, false); + gsi, false, false, loc); if (access_has_children_p (lacc)) generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0, - gsi, true, true); + gsi, true, true, loc); sra_stats.separate_lhs_rhs_handling++; } else @@ -2702,18 +2805,15 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) enum unscalarized_data_handling refreshed; if (lacc->grp_read && !lacc->grp_covered) - refreshed = handle_unscalarized_data_in_subtree (racc, lhs, gsi); + refreshed = handle_unscalarized_data_in_subtree (racc, gsi); else refreshed = SRA_UDH_NONE; - load_assign_lhs_subreplacements (lacc->first_child, racc, - lacc->offset, racc->offset, - &orig_gsi, gsi, &refreshed, lhs); + load_assign_lhs_subreplacements (lacc, racc, lacc->offset, + &orig_gsi, gsi, &refreshed); if (refreshed != SRA_UDH_RIGHT) { - if (*stmt == gsi_stmt (*gsi)) - gsi_next (gsi); - + gsi_next (gsi); unlink_stmt_vdef (*stmt); gsi_remove (&orig_gsi, true); sra_stats.deleted++; @@ -2726,26 +2826,41 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) { if (!racc->grp_to_be_replaced && !racc->grp_unscalarized_data) { - if (racc->first_child) - generate_subtree_copies (racc->first_child, lhs, - racc->offset, 0, 0, gsi, - false, false); - gcc_assert (*stmt == gsi_stmt (*gsi)); - if (TREE_CODE (lhs) == SSA_NAME) - replace_uses_with_default_def_ssa_name (lhs, racc); + if (dump_file) + { + fprintf (dump_file, "Removing load: "); + print_gimple_stmt (dump_file, *stmt, 0, 0); + } - unlink_stmt_vdef (*stmt); - gsi_remove (gsi, true); - sra_stats.deleted++; - return SRA_AM_REMOVED; + if (TREE_CODE (lhs) == SSA_NAME) + { + rhs = get_repl_default_def_ssa_name (racc); + if (!useless_type_conversion_p (TREE_TYPE (lhs), + TREE_TYPE (rhs))) + rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, + TREE_TYPE (lhs), rhs); + } + else + { + if (racc->first_child) + generate_subtree_copies (racc->first_child, lhs, + racc->offset, 0, 0, gsi, + false, false, loc); + + gcc_assert (*stmt == gsi_stmt (*gsi)); + unlink_stmt_vdef (*stmt); + gsi_remove (gsi, true); + sra_stats.deleted++; + return SRA_AM_REMOVED; + } } else if (racc->first_child) - generate_subtree_copies (racc->first_child, lhs, - racc->offset, 0, 0, gsi, false, true); + generate_subtree_copies (racc->first_child, lhs, racc->offset, + 0, 0, gsi, false, true, loc); } if (access_has_children_p (lacc)) generate_subtree_copies (lacc->first_child, rhs, lacc->offset, - 0, 0, gsi, true, true); + 0, 0, gsi, true, true, loc); } } @@ -2756,6 +2871,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) true, GSI_SAME_STMT); if (gimple_assign_rhs1 (*stmt) != rhs) { + modify_this_stmt = true; gimple_assign_set_rhs_from_tree (&orig_gsi, rhs); gcc_assert (*stmt == gsi_stmt (orig_gsi)); } @@ -2764,11 +2880,13 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi) } /* Traverse the function body and all modifications as decided in - analyze_all_variable_accesses. */ + analyze_all_variable_accesses. Return true iff the CFG has been + changed. */ -static void +static bool sra_modify_function_body (void) { + bool cfg_changed = false; basic_block bb; FOR_EACH_BB (bb) @@ -2831,12 +2949,16 @@ sra_modify_function_body (void) if (modified) { update_stmt (stmt); - maybe_clean_eh_stmt (stmt); + if (maybe_clean_eh_stmt (stmt) + && gimple_purge_dead_eh_edges (gimple_bb (stmt))) + cfg_changed = true; } if (!deleted) gsi_next (&gsi); } } + + return cfg_changed; } /* Generate statements initializing scalar replacements of parts of function @@ -2851,7 +2973,7 @@ initialize_parameter_reductions (void) for (parm = DECL_ARGUMENTS (current_function_decl); parm; - parm = TREE_CHAIN (parm)) + parm = DECL_CHAIN (parm)) { VEC (access_p, heap) *access_vec; struct access *access; @@ -2871,7 +2993,8 @@ initialize_parameter_reductions (void) for (access = VEC_index (access_p, access_vec, 0); access; access = access->next_grp) - generate_subtree_copies (access, parm, 0, 0, 0, &gsi, true, true); + generate_subtree_copies (access, parm, 0, 0, 0, &gsi, true, true, + EXPR_LOCATION (parm)); } if (seq) @@ -2896,7 +3019,10 @@ perform_intra_sra (void) if (!analyze_all_variable_accesses ()) goto out; - sra_modify_function_body (); + if (sra_modify_function_body ()) + ret = TODO_update_ssa | TODO_cleanup_cfg; + else + ret = TODO_update_ssa; initialize_parameter_reductions (); statistics_counter_event (cfun, "Scalar replacements created", @@ -2910,8 +3036,6 @@ perform_intra_sra (void) statistics_counter_event (cfun, "Separate LHS and RHS handling", sra_stats.separate_lhs_rhs_handling); - ret = TODO_update_ssa; - out: sra_deinitialize (); return ret; @@ -3025,8 +3149,11 @@ ptr_parm_has_direct_uses (tree parm) tree lhs = gimple_get_lhs (stmt); while (handled_component_p (lhs)) lhs = TREE_OPERAND (lhs, 0); - if (INDIRECT_REF_P (lhs) - && TREE_OPERAND (lhs, 0) == name) + if (TREE_CODE (lhs) == MEM_REF + && TREE_OPERAND (lhs, 0) == name + && integer_zerop (TREE_OPERAND (lhs, 1)) + && types_compatible_p (TREE_TYPE (lhs), + TREE_TYPE (TREE_TYPE (name)))) uses_ok++; } if (gimple_assign_single_p (stmt)) @@ -3034,8 +3161,11 @@ ptr_parm_has_direct_uses (tree parm) tree rhs = gimple_assign_rhs1 (stmt); while (handled_component_p (rhs)) rhs = TREE_OPERAND (rhs, 0); - if (INDIRECT_REF_P (rhs) - && TREE_OPERAND (rhs, 0) == name) + if (TREE_CODE (rhs) == MEM_REF + && TREE_OPERAND (rhs, 0) == name + && integer_zerop (TREE_OPERAND (rhs, 1)) + && types_compatible_p (TREE_TYPE (rhs), + TREE_TYPE (TREE_TYPE (name)))) uses_ok++; } else if (is_gimple_call (stmt)) @@ -3046,8 +3176,11 @@ ptr_parm_has_direct_uses (tree parm) tree arg = gimple_call_arg (stmt, i); while (handled_component_p (arg)) arg = TREE_OPERAND (arg, 0); - if (INDIRECT_REF_P (arg) - && TREE_OPERAND (arg, 0) == name) + if (TREE_CODE (arg) == MEM_REF + && TREE_OPERAND (arg, 0) == name + && integer_zerop (TREE_OPERAND (arg, 1)) + && types_compatible_p (TREE_TYPE (arg), + TREE_TYPE (TREE_TYPE (name)))) uses_ok++; } } @@ -3081,7 +3214,7 @@ find_param_candidates (void) for (parm = DECL_ARGUMENTS (current_function_decl); parm; - parm = TREE_CHAIN (parm)) + parm = DECL_CHAIN (parm)) { tree type = TREE_TYPE (parm); @@ -3104,6 +3237,8 @@ find_param_candidates (void) if (TREE_CODE (type) == FUNCTION_TYPE || TYPE_VOLATILE (type) + || (TREE_CODE (type) == ARRAY_TYPE + && TYPE_NONALIASED_COMPONENT (type)) || !is_gimple_reg (parm) || is_va_list_type (type) || ptr_parm_has_direct_uses (parm)) @@ -3420,8 +3555,7 @@ splice_param_accesses (tree parm, bool *ro_grp) return &no_accesses_representant; access_count = VEC_length (access_p, access_vec); - qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p), - compare_access_positions); + VEC_qsort (access_p, access_vec, compare_access_positions); i = 0; total_size = 0; @@ -3429,10 +3563,12 @@ splice_param_accesses (tree parm, bool *ro_grp) while (i < access_count) { bool modification; + tree a1_alias_type; access = VEC_index (access_p, access_vec, i); modification = access->write; if (access_precludes_ipa_sra_p (access)) return NULL; + a1_alias_type = reference_alias_ptr_type (access->expr); /* Access is about to become group representative unless we find some nasty overlap which would preclude us from breaking this parameter @@ -3453,7 +3589,11 @@ splice_param_accesses (tree parm, bool *ro_grp) else if (ac2->size != access->size) return NULL; - if (access_precludes_ipa_sra_p (ac2)) + if (access_precludes_ipa_sra_p (ac2) + || (ac2->type != access->type + && (TREE_ADDRESSABLE (ac2->type) + || TREE_ADDRESSABLE (access->type))) + || (reference_alias_ptr_type (ac2->expr) != a1_alias_type)) return NULL; modification |= ac2->write; @@ -3573,7 +3713,7 @@ splice_all_param_accesses (VEC (access_p, heap) **representatives) for (parm = DECL_ARGUMENTS (current_function_decl); parm; - parm = TREE_CHAIN (parm)) + parm = DECL_CHAIN (parm)) { if (is_unused_scalar_param (parm)) { @@ -3658,7 +3798,7 @@ turn_representatives_into_adjustments (VEC (access_p, heap) *representatives, parms = ipa_get_vector_of_formal_parms (current_function_decl); adjustments = VEC_alloc (ipa_parm_adjustment_t, heap, adjustments_count); parm = DECL_ARGUMENTS (current_function_decl); - for (i = 0; i < func_param_count; i++, parm = TREE_CHAIN (parm)) + for (i = 0; i < func_param_count; i++, parm = DECL_CHAIN (parm)) { struct access *repr = VEC_index (access_p, representatives, i); @@ -3688,6 +3828,7 @@ turn_representatives_into_adjustments (VEC (access_p, heap) *representatives, adj->base_index = index; adj->base = repr->base; adj->type = repr->type; + adj->alias_ptr_type = reference_alias_ptr_type (repr->expr); adj->offset = repr->offset; adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base)) && (repr->grp_maybe_modified @@ -3916,8 +4057,11 @@ sra_ipa_modify_expr (tree *expr, bool convert, if (!base || size == -1 || max_size == -1) return false; - if (INDIRECT_REF_P (base)) - base = TREE_OPERAND (base, 0); + if (TREE_CODE (base) == MEM_REF) + { + offset += mem_ref_offset (base).low * BITS_PER_UNIT; + base = TREE_OPERAND (base, 0); + } base = get_ssa_base_param (base); if (!base || TREE_CODE (base) != PARM_DECL) @@ -3938,14 +4082,7 @@ sra_ipa_modify_expr (tree *expr, bool convert, return false; if (cand->by_ref) - { - tree folded; - src = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (cand->reduction)), - cand->reduction); - folded = gimple_fold_indirect_ref (src); - if (folded) - src = folded; - } + src = build_simple_mem_ref (cand->reduction); else src = cand->reduction; @@ -4000,7 +4137,7 @@ sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi, { /* V_C_Es of constructors can cause trouble (PR 42714). */ if (is_gimple_reg_type (TREE_TYPE (*lhs_p))) - *rhs_p = fold_convert (TREE_TYPE (*lhs_p), integer_zero_node); + *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p)); else *rhs_p = build_constructor (TREE_TYPE (*lhs_p), 0); } @@ -4032,17 +4169,17 @@ sra_ipa_modify_assign (gimple *stmt_ptr, gimple_stmt_iterator *gsi, } /* Traverse the function body and all modifications as described in - ADJUSTMENTS. */ + ADJUSTMENTS. Return true iff the CFG has been changed. */ -static void +static bool ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments) { + bool cfg_changed = false; basic_block bb; FOR_EACH_BB (bb) { gimple_stmt_iterator gsi; - bool bb_changed = false; for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) replace_removed_params_ssa_names (gsi_stmt (gsi), adjustments); @@ -4104,15 +4241,16 @@ ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments) if (modified) { - bb_changed = true; update_stmt (stmt); - maybe_clean_eh_stmt (stmt); + if (maybe_clean_eh_stmt (stmt) + && gimple_purge_dead_eh_edges (gimple_bb (stmt))) + cfg_changed = true; } gsi_next (&gsi); } - if (bb_changed) - gimple_purge_dead_eh_edges (bb); } + + return cfg_changed; } /* Call gimple_debug_bind_reset_value on all debug statements describing @@ -4166,7 +4304,8 @@ all_callers_have_enough_arguments_p (struct cgraph_node *node) /* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS. */ static void -convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) +convert_callers (struct cgraph_node *node, tree old_decl, + ipa_parm_adjustment_vec adjustments) { tree old_cur_fndecl = current_function_decl; struct cgraph_edge *cs; @@ -4190,11 +4329,8 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) } for (cs = node->callers; cs; cs = cs->next_caller) - if (!bitmap_bit_p (recomputed_callers, cs->caller->uid)) - { - compute_inline_parameters (cs->caller); - bitmap_set_bit (recomputed_callers, cs->caller->uid); - } + if (bitmap_set_bit (recomputed_callers, cs->caller->uid)) + compute_inline_parameters (cs->caller); BITMAP_FREE (recomputed_callers); current_function_decl = old_cur_fndecl; @@ -4213,10 +4349,11 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) if (gimple_code (stmt) != GIMPLE_CALL) continue; call_fndecl = gimple_call_fndecl (stmt); - if (call_fndecl && cgraph_get_node (call_fndecl) == node) + if (call_fndecl == old_decl) { if (dump_file) fprintf (dump_file, "Adjusting recursive call"); + gimple_call_set_fndecl (stmt, node->decl); ipa_modify_call_arguments (NULL, stmt, adjustments); } } @@ -4225,44 +4362,40 @@ convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) return; } -/* Create an abstract origin declaration for OLD_DECL and make it an abstract - origin of the provided decl so that there are preserved parameters for debug - information. */ - -static void -create_abstract_origin (tree old_decl) -{ - if (!DECL_ABSTRACT_ORIGIN (old_decl)) - { - tree new_decl = copy_node (old_decl); - - DECL_ABSTRACT (new_decl) = 1; - SET_DECL_ASSEMBLER_NAME (new_decl, NULL_TREE); - SET_DECL_RTL (new_decl, NULL); - DECL_STRUCT_FUNCTION (new_decl) = NULL; - DECL_ARTIFICIAL (old_decl) = 1; - DECL_ABSTRACT_ORIGIN (old_decl) = new_decl; - } -} - /* Perform all the modification required in IPA-SRA for NODE to have parameters - as given in ADJUSTMENTS. */ + as given in ADJUSTMENTS. Return true iff the CFG has been changed. */ -static void +static bool modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) { - struct cgraph_node *alias; - for (alias = node->same_body; alias; alias = alias->next) - ipa_modify_formal_parameters (alias->decl, adjustments, "ISRA"); - /* current_function_decl must be handled last, after same_body aliases, - as following functions will use what it computed. */ - create_abstract_origin (current_function_decl); + struct cgraph_node *new_node; + struct cgraph_edge *cs; + bool cfg_changed; + VEC (cgraph_edge_p, heap) * redirect_callers; + int node_callers; + + node_callers = 0; + for (cs = node->callers; cs != NULL; cs = cs->next_caller) + node_callers++; + redirect_callers = VEC_alloc (cgraph_edge_p, heap, node_callers); + for (cs = node->callers; cs != NULL; cs = cs->next_caller) + VEC_quick_push (cgraph_edge_p, redirect_callers, cs); + + rebuild_cgraph_edges (); + pop_cfun (); + current_function_decl = NULL_TREE; + + new_node = cgraph_function_versioning (node, redirect_callers, NULL, NULL, + NULL, NULL, "isra"); + current_function_decl = new_node->decl; + push_cfun (DECL_STRUCT_FUNCTION (new_node->decl)); + ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA"); - ipa_sra_modify_function_body (adjustments); + cfg_changed = ipa_sra_modify_function_body (adjustments); sra_ipa_reset_debug_stmts (adjustments); - convert_callers (node, adjustments); - cgraph_make_node_local (node); - return; + convert_callers (new_node, node->decl, adjustments); + cgraph_make_node_local (new_node); + return cfg_changed; } /* Return false the function is apparently unsuitable for IPA-SRA based on it's @@ -4279,6 +4412,13 @@ ipa_sra_preliminary_function_checks (struct cgraph_node *node) return false; } + if (!tree_versionable_function_p (node->decl)) + { + if (dump_file) + fprintf (dump_file, "Function is not versionable.\n"); + return false; + } + if (DECL_VIRTUAL_P (current_function_decl)) { if (dump_file) @@ -4372,9 +4512,11 @@ ipa_early_sra (void) if (dump_file) ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl); - modify_function (node, adjustments); + if (modify_function (node, adjustments)) + ret = TODO_update_ssa | TODO_cleanup_cfg; + else + ret = TODO_update_ssa; VEC_free (ipa_parm_adjustment_t, heap, adjustments); - ret = TODO_update_ssa; statistics_counter_event (cfun, "Unused parameters deleted", sra_stats.deleted_unused_parameters); @@ -4397,7 +4539,7 @@ ipa_early_sra (void) static bool ipa_early_sra_gate (void) { - return flag_ipa_sra; + return flag_ipa_sra && dbg_cnt (eipa_sra); } struct gimple_opt_pass pass_early_ipa_sra =