| /* Loop versioning pass. |
| Copyright (C) 2018-2022 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by the |
| Free Software Foundation; either version 3, or (at your option) any |
| later version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "gimple-iterator.h" |
| #include "tree-pass.h" |
| #include "gimplify-me.h" |
| #include "cfgloop.h" |
| #include "tree-ssa-loop.h" |
| #include "ssa.h" |
| #include "tree-scalar-evolution.h" |
| #include "tree-ssa-loop-ivopts.h" |
| #include "fold-const.h" |
| #include "tree-ssa-propagate.h" |
| #include "tree-inline.h" |
| #include "domwalk.h" |
| #include "tree-vectorizer.h" |
| #include "omp-general.h" |
| #include "predict.h" |
| #include "tree-into-ssa.h" |
| #include "gimple-range.h" |
| #include "tree-cfg.h" |
| |
| namespace { |
| |
| /* This pass looks for loops that could be simplified if certain loop |
| invariant conditions were true. It is effectively a form of loop |
| splitting in which the pass produces the split conditions itself, |
| instead of using ones that are already present in the IL. |
| |
| Versioning for when strides are 1 |
| --------------------------------- |
| |
| At the moment the only thing the pass looks for are memory references |
| like: |
| |
| for (auto i : ...) |
| ...x[i * stride]... |
| |
| It considers changing such loops to: |
| |
| if (stride == 1) |
| for (auto i : ...) [A] |
| ...x[i]... |
| else |
| for (auto i : ...) [B] |
| ...x[i * stride]... |
| |
| This can have several benefits: |
| |
| (1) [A] is often easier or cheaper to vectorize than [B]. |
| |
| (2) The scalar code in [A] is simpler than the scalar code in [B] |
| (if the loops cannot be vectorized or need an epilogue loop). |
| |
| (3) We might recognize [A] as a pattern, such as a memcpy or memset. |
| |
| (4) [A] has simpler address evolutions, which can help other passes |
| like loop interchange. |
| |
| The optimization is particularly useful for assumed-shape arrays in |
| Fortran, where the stride of the innermost dimension depends on the |
| array descriptor but is often equal to 1 in practice. For example: |
| |
| subroutine f1(x) |
| real :: x(:) |
| x(:) = 100 |
| end subroutine f1 |
| |
| generates the equivalent of: |
| |
| raw_stride = *x.dim[0].stride; |
| stride = raw_stride != 0 ? raw_stride : 1; |
| x_base = *x.data; |
| ... |
| tmp1 = stride * S; |
| tmp2 = tmp1 - stride; |
| *x_base[tmp2] = 1.0e+2; |
| |
| but in the common case that stride == 1, the last three statements |
| simplify to: |
| |
| tmp3 = S + -1; |
| *x_base[tmp3] = 1.0e+2; |
| |
| The optimization is in principle very simple. The difficult parts are: |
| |
| (a) deciding which parts of a general address calculation correspond |
| to the inner dimension of an array, since this usually isn't explicit |
| in the IL, and for C often isn't even explicit in the source code |
| |
| (b) estimating when the transformation is worthwhile |
| |
| Structure |
| --------- |
| |
| The pass has four phases: |
| |
| (1) Walk through the statements looking for and recording potential |
| versioning opportunities. Stop if there are none. |
| |
| (2) Use context-sensitive range information to see whether any versioning |
| conditions are impossible in practice. Remove them if so, and stop |
| if no opportunities remain. |
| |
| (We do this only after (1) to keep compile time down when no |
| versioning opportunities exist.) |
| |
| (3) Apply the cost model. Decide which versioning opportunities are |
| worthwhile and at which nesting level they should be applied. |
| |
| (4) Attempt to version all the loops selected by (3), so that: |
| |
| for (...) |
| ... |
| |
| becomes: |
| |
| if (!cond) |
| for (...) // Original loop |
| ... |
| else |
| for (...) // New loop |
| ... |
| |
| Use the version condition COND to simplify the new loop. */ |
| |
| /* Enumerates the likelihood that a particular value indexes the inner |
| dimension of an array. */ |
| enum inner_likelihood { |
| INNER_UNLIKELY, |
| INNER_DONT_KNOW, |
| INNER_LIKELY |
| }; |
| |
| /* Information about one term of an address_info. */ |
| struct address_term_info |
| { |
| /* The value of the term is EXPR * MULTIPLIER. */ |
| tree expr; |
| unsigned HOST_WIDE_INT multiplier; |
| |
| /* The stride applied by EXPR in each iteration of some unrecorded loop, |
| or null if no stride has been identified. */ |
| tree stride; |
| |
| /* Enumerates the likelihood that EXPR indexes the inner dimension |
| of an array. */ |
| enum inner_likelihood inner_likelihood; |
| |
| /* True if STRIDE == 1 is a versioning opportunity when considered |
| in isolation. */ |
| bool versioning_opportunity_p; |
| }; |
| |
| /* Information about an address calculation, and the range of constant |
| offsets applied to it. */ |
| class address_info |
| { |
| public: |
| static const unsigned int MAX_TERMS = 8; |
| |
| /* One statement that calculates the address. If multiple statements |
| share the same address, we only record the first. */ |
| gimple *stmt; |
| |
| /* The loop containing STMT (cached for convenience). If multiple |
| statements share the same address, they all belong to this loop. */ |
| class loop *loop; |
| |
| /* A decomposition of the calculation into a sum of terms plus an |
| optional base. When BASE is provided, it is never an SSA name. |
| Once initialization is complete, all members of TERMs are SSA names. */ |
| tree base; |
| auto_vec<address_term_info, MAX_TERMS> terms; |
| |
| /* All bytes accessed from the address fall in the offset range |
| [MIN_OFFSET, MAX_OFFSET). */ |
| HOST_WIDE_INT min_offset, max_offset; |
| }; |
| |
| /* Stores addresses based on their base and terms (ignoring the offsets). */ |
| struct address_info_hasher : nofree_ptr_hash <address_info> |
| { |
| static hashval_t hash (const address_info *); |
| static bool equal (const address_info *, const address_info *); |
| }; |
| |
| /* Information about the versioning we'd like to apply to a loop. */ |
| class loop_info |
| { |
| public: |
| bool worth_versioning_p () const; |
| |
| /* True if we've decided not to version this loop. The remaining |
| fields are meaningless if so. */ |
| bool rejected_p; |
| |
| /* True if at least one subloop of this loop benefits from versioning. */ |
| bool subloops_benefit_p; |
| |
| /* An estimate of the total number of instructions in the loop, |
| excluding those in subloops that benefit from versioning. */ |
| unsigned int num_insns; |
| |
| /* The outermost loop that can handle all the version checks |
| described below. */ |
| class loop *outermost; |
| |
| /* The first entry in the list of blocks that belong to this loop |
| (and not to subloops). m_next_block_in_loop provides the chain |
| pointers for the list. */ |
| basic_block block_list; |
| |
| /* We'd like to version the loop for the case in which these SSA names |
| (keyed off their SSA_NAME_VERSION) are all equal to 1 at runtime. */ |
| bitmap_head unity_names; |
| |
| /* If versioning succeeds, this points the version of the loop that |
| assumes the version conditions holds. */ |
| class loop *optimized_loop; |
| }; |
| |
| /* The main pass structure. */ |
| class loop_versioning |
| { |
| public: |
| loop_versioning (function *); |
| ~loop_versioning (); |
| unsigned int run (); |
| |
| private: |
| /* Used to walk the dominator tree to find loop versioning conditions |
| that are always false. */ |
| class lv_dom_walker : public dom_walker |
| { |
| public: |
| lv_dom_walker (loop_versioning &); |
| |
| edge before_dom_children (basic_block) FINAL OVERRIDE; |
| |
| private: |
| /* The parent pass. */ |
| loop_versioning &m_lv; |
| }; |
| |
| /* Used to simplify statements based on conditions that are established |
| by the version checks. */ |
| class name_prop : public substitute_and_fold_engine |
| { |
| public: |
| name_prop (loop_info &li) : m_li (li) {} |
| tree value_of_expr (tree name, gimple *) FINAL OVERRIDE; |
| |
| private: |
| /* Information about the versioning we've performed on the loop. */ |
| loop_info &m_li; |
| }; |
| |
| loop_info &get_loop_info (class loop *loop) { return m_loops[loop->num]; } |
| |
| unsigned int max_insns_for_loop (class loop *); |
| bool expensive_stmt_p (gimple *); |
| |
| void version_for_unity (gimple *, tree); |
| bool acceptable_multiplier_p (tree, unsigned HOST_WIDE_INT, |
| unsigned HOST_WIDE_INT * = 0); |
| bool acceptable_type_p (tree, unsigned HOST_WIDE_INT *); |
| bool multiply_term_by (address_term_info &, tree); |
| inner_likelihood get_inner_likelihood (tree, unsigned HOST_WIDE_INT); |
| void dump_inner_likelihood (address_info &, address_term_info &); |
| void analyze_stride (address_info &, address_term_info &, |
| tree, class loop *); |
| bool find_per_loop_multiplication (address_info &, address_term_info &); |
| bool analyze_term_using_scevs (address_info &, address_term_info &); |
| void analyze_arbitrary_term (address_info &, address_term_info &); |
| void analyze_address_fragment (address_info &); |
| void record_address_fragment (gimple *, unsigned HOST_WIDE_INT, |
| tree, unsigned HOST_WIDE_INT, HOST_WIDE_INT); |
| void analyze_expr (gimple *, tree); |
| bool analyze_block (basic_block); |
| bool analyze_blocks (); |
| |
| void prune_loop_conditions (class loop *); |
| bool prune_conditions (); |
| |
| void merge_loop_info (class loop *, class loop *); |
| void add_loop_to_queue (class loop *); |
| bool decide_whether_loop_is_versionable (class loop *); |
| bool make_versioning_decisions (); |
| |
| bool version_loop (class loop *); |
| void implement_versioning_decisions (); |
| |
| /* The function we're optimizing. */ |
| function *m_fn; |
| |
| /* The obstack to use for all pass-specific bitmaps. */ |
| bitmap_obstack m_bitmap_obstack; |
| |
| /* An obstack to use for general allocation. */ |
| obstack m_obstack; |
| |
| /* The number of loops in the function. */ |
| unsigned int m_nloops; |
| |
| /* The total number of loop version conditions we've found. */ |
| unsigned int m_num_conditions; |
| |
| /* Assume that an address fragment of the form i * stride * scale |
| (for variable stride and constant scale) will not benefit from |
| versioning for stride == 1 when scale is greater than this value. */ |
| unsigned HOST_WIDE_INT m_maximum_scale; |
| |
| /* Information about each loop. */ |
| auto_vec<loop_info> m_loops; |
| |
| /* Used to form a linked list of blocks that belong to a loop, |
| started by loop_info::block_list. */ |
| auto_vec<basic_block> m_next_block_in_loop; |
| |
| /* The list of loops that we've decided to version. */ |
| auto_vec<class loop *> m_loops_to_version; |
| |
| /* A table of addresses in the current loop, keyed off their values |
| but not their offsets. */ |
| hash_table <address_info_hasher> m_address_table; |
| |
| /* A list of all addresses in M_ADDRESS_TABLE, in a predictable order. */ |
| auto_vec <address_info *, 32> m_address_list; |
| }; |
| |
| /* If EXPR is an SSA name and not a default definition, return the |
| defining statement, otherwise return null. */ |
| |
| static gimple * |
| maybe_get_stmt (tree expr) |
| { |
| if (TREE_CODE (expr) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (expr)) |
| return SSA_NAME_DEF_STMT (expr); |
| return NULL; |
| } |
| |
| /* Like maybe_get_stmt, but also return null if the defining |
| statement isn't an assignment. */ |
| |
| static gassign * |
| maybe_get_assign (tree expr) |
| { |
| return safe_dyn_cast <gassign *> (maybe_get_stmt (expr)); |
| } |
| |
| /* Return true if this pass should look through a cast of expression FROM |
| to type TYPE when analyzing pieces of an address. */ |
| |
| static bool |
| look_through_cast_p (tree type, tree from) |
| { |
| return (INTEGRAL_TYPE_P (TREE_TYPE (from)) == INTEGRAL_TYPE_P (type) |
| && POINTER_TYPE_P (TREE_TYPE (from)) == POINTER_TYPE_P (type)); |
| } |
| |
| /* Strip all conversions of integers or pointers from EXPR, regardless |
| of whether the conversions are nops. This is useful in the context |
| of this pass because we're not trying to fold or simulate the |
| expression; we just want to see how it's structured. */ |
| |
| static tree |
| strip_casts (tree expr) |
| { |
| const unsigned int MAX_NITERS = 4; |
| |
| tree type = TREE_TYPE (expr); |
| while (CONVERT_EXPR_P (expr) |
| && look_through_cast_p (type, TREE_OPERAND (expr, 0))) |
| expr = TREE_OPERAND (expr, 0); |
| |
| for (unsigned int niters = 0; niters < MAX_NITERS; ++niters) |
| { |
| gassign *assign = maybe_get_assign (expr); |
| if (assign |
| && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (assign)) |
| && look_through_cast_p (type, gimple_assign_rhs1 (assign))) |
| expr = gimple_assign_rhs1 (assign); |
| else |
| break; |
| } |
| return expr; |
| } |
| |
| /* Compare two address_term_infos in the same address_info. */ |
| |
| static int |
| compare_address_terms (const void *a_uncast, const void *b_uncast) |
| { |
| const address_term_info *a = (const address_term_info *) a_uncast; |
| const address_term_info *b = (const address_term_info *) b_uncast; |
| |
| if (a->expr != b->expr) |
| return SSA_NAME_VERSION (a->expr) < SSA_NAME_VERSION (b->expr) ? -1 : 1; |
| |
| if (a->multiplier != b->multiplier) |
| return a->multiplier < b->multiplier ? -1 : 1; |
| |
| return 0; |
| } |
| |
| /* Dump ADDRESS using flags FLAGS. */ |
| |
| static void |
| dump_address_info (dump_flags_t flags, address_info &address) |
| { |
| if (address.base) |
| dump_printf (flags, "%T + ", address.base); |
| for (unsigned int i = 0; i < address.terms.length (); ++i) |
| { |
| if (i != 0) |
| dump_printf (flags, " + "); |
| dump_printf (flags, "%T", address.terms[i].expr); |
| if (address.terms[i].multiplier != 1) |
| dump_printf (flags, " * %wd", address.terms[i].multiplier); |
| } |
| dump_printf (flags, " + [%wd, %wd]", |
| address.min_offset, address.max_offset - 1); |
| } |
| |
| /* Hash an address_info based on its base and terms. */ |
| |
| hashval_t |
| address_info_hasher::hash (const address_info *info) |
| { |
| inchash::hash hash; |
| hash.add_int (info->base ? TREE_CODE (info->base) : 0); |
| hash.add_int (info->terms.length ()); |
| for (unsigned int i = 0; i < info->terms.length (); ++i) |
| { |
| hash.add_int (SSA_NAME_VERSION (info->terms[i].expr)); |
| hash.add_hwi (info->terms[i].multiplier); |
| } |
| return hash.end (); |
| } |
| |
| /* Return true if two address_infos have equal bases and terms. Other |
| properties might be different (such as the statement or constant |
| offset range). */ |
| |
| bool |
| address_info_hasher::equal (const address_info *a, const address_info *b) |
| { |
| if (a->base != b->base |
| && (!a->base || !b->base || !operand_equal_p (a->base, b->base, 0))) |
| return false; |
| |
| if (a->terms.length () != b->terms.length ()) |
| return false; |
| |
| for (unsigned int i = 0; i < a->terms.length (); ++i) |
| if (a->terms[i].expr != b->terms[i].expr |
| || a->terms[i].multiplier != b->terms[i].multiplier) |
| return false; |
| |
| return true; |
| } |
| |
| /* Return true if we want to version the loop, i.e. if we have a |
| specific reason for doing so and no specific reason not to. */ |
| |
| bool |
| loop_info::worth_versioning_p () const |
| { |
| return (!rejected_p |
| && (!bitmap_empty_p (&unity_names) || subloops_benefit_p)); |
| } |
| |
| loop_versioning::lv_dom_walker::lv_dom_walker (loop_versioning &lv) |
| : dom_walker (CDI_DOMINATORS), m_lv (lv) |
| { |
| } |
| |
| /* Process BB before processing the blocks it dominates. */ |
| |
| edge |
| loop_versioning::lv_dom_walker::before_dom_children (basic_block bb) |
| { |
| if (bb == bb->loop_father->header) |
| m_lv.prune_loop_conditions (bb->loop_father); |
| |
| return NULL; |
| } |
| |
| /* Decide whether to replace VAL with a new value in a versioned loop. |
| Return the new value if so, otherwise return null. */ |
| |
| tree |
| loop_versioning::name_prop::value_of_expr (tree val, gimple *) |
| { |
| if (TREE_CODE (val) == SSA_NAME |
| && bitmap_bit_p (&m_li.unity_names, SSA_NAME_VERSION (val))) |
| return build_one_cst (TREE_TYPE (val)); |
| return NULL_TREE; |
| } |
| |
| /* Initialize the structure to optimize FN. */ |
| |
| loop_versioning::loop_versioning (function *fn) |
| : m_fn (fn), |
| m_nloops (number_of_loops (fn)), |
| m_num_conditions (0), |
| m_address_table (31) |
| { |
| bitmap_obstack_initialize (&m_bitmap_obstack); |
| gcc_obstack_init (&m_obstack); |
| |
| /* Initialize the loop information. */ |
| m_loops.safe_grow_cleared (m_nloops, true); |
| for (unsigned int i = 0; i < m_nloops; ++i) |
| { |
| m_loops[i].outermost = get_loop (m_fn, 0); |
| bitmap_initialize (&m_loops[i].unity_names, &m_bitmap_obstack); |
| } |
| |
| /* Initialize the list of blocks that belong to each loop. */ |
| unsigned int nbbs = last_basic_block_for_fn (fn); |
| m_next_block_in_loop.safe_grow (nbbs, true); |
| basic_block bb; |
| FOR_EACH_BB_FN (bb, fn) |
| { |
| loop_info &li = get_loop_info (bb->loop_father); |
| m_next_block_in_loop[bb->index] = li.block_list; |
| li.block_list = bb; |
| } |
| |
| /* MAX_FIXED_MODE_SIZE should be a reasonable maximum scale for |
| unvectorizable code, since it is the largest size that can be |
| handled efficiently by scalar code. omp_max_vf calculates the |
| maximum number of bytes in a vector, when such a value is relevant |
| to loop optimization. */ |
| m_maximum_scale = estimated_poly_value (omp_max_vf ()); |
| m_maximum_scale = MAX (m_maximum_scale, MAX_FIXED_MODE_SIZE); |
| } |
| |
| loop_versioning::~loop_versioning () |
| { |
| bitmap_obstack_release (&m_bitmap_obstack); |
| obstack_free (&m_obstack, NULL); |
| } |
| |
| /* Return the maximum number of instructions allowed in LOOP before |
| it becomes too big for versioning. |
| |
| There are separate limits for inner and outer loops. The limit for |
| inner loops applies only to loops that benefit directly from versioning. |
| The limit for outer loops applies to all code in the outer loop and |
| its subloops that *doesn't* benefit directly from versioning; such code |
| would be "taken along for the ride". The idea is that if the cost of |
| the latter is small, it is better to version outer loops rather than |
| inner loops, both to reduce the number of repeated checks and to enable |
| more of the loop nest to be optimized as a natural nest (e.g. by loop |
| interchange or outer-loop vectorization). */ |
| |
| unsigned int |
| loop_versioning::max_insns_for_loop (class loop *loop) |
| { |
| return (loop->inner |
| ? param_loop_versioning_max_outer_insns |
| : param_loop_versioning_max_inner_insns); |
| } |
| |
| /* Return true if for cost reasons we should avoid versioning any loop |
| that contains STMT. |
| |
| Note that we don't need to check whether versioning is invalid for |
| correctness reasons, since the versioning process does that for us. |
| The conditions involved are too rare to be worth duplicating here. */ |
| |
| bool |
| loop_versioning::expensive_stmt_p (gimple *stmt) |
| { |
| if (gcall *call = dyn_cast <gcall *> (stmt)) |
| /* Assume for now that the time spent in an "expensive" call would |
| overwhelm any saving from versioning. */ |
| return !gimple_inexpensive_call_p (call); |
| return false; |
| } |
| |
| /* Record that we want to version the loop that contains STMT for the |
| case in which SSA name NAME is equal to 1. We already know that NAME |
| is invariant in the loop. */ |
| |
| void |
| loop_versioning::version_for_unity (gimple *stmt, tree name) |
| { |
| class loop *loop = loop_containing_stmt (stmt); |
| loop_info &li = get_loop_info (loop); |
| |
| if (bitmap_set_bit (&li.unity_names, SSA_NAME_VERSION (name))) |
| { |
| /* This is the first time we've wanted to version LOOP for NAME. |
| Keep track of the outermost loop that can handle all versioning |
| checks in LI. */ |
| class loop *outermost |
| = outermost_invariant_loop_for_expr (loop, name); |
| if (loop_depth (li.outermost) < loop_depth (outermost)) |
| li.outermost = outermost; |
| |
| if (dump_enabled_p ()) |
| { |
| dump_printf_loc (MSG_NOTE, stmt, "want to version containing loop" |
| " for when %T == 1", name); |
| if (outermost == loop) |
| dump_printf (MSG_NOTE, "; cannot hoist check further"); |
| else |
| { |
| dump_printf (MSG_NOTE, "; could implement the check at loop" |
| " depth %d", loop_depth (outermost)); |
| if (loop_depth (li.outermost) > loop_depth (outermost)) |
| dump_printf (MSG_NOTE, ", but other checks only allow" |
| " a depth of %d", loop_depth (li.outermost)); |
| } |
| dump_printf (MSG_NOTE, "\n"); |
| } |
| |
| m_num_conditions += 1; |
| } |
| else |
| { |
| /* This is a duplicate request. */ |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, stmt, "already asked to version containing" |
| " loop for when %T == 1\n", name); |
| } |
| } |
| |
| /* Return true if OP1_TREE is constant and if in principle it is worth |
| versioning an address fragment of the form: |
| |
| i * OP1_TREE * OP2 * stride |
| |
| for the case in which stride == 1. This in practice means testing |
| whether: |
| |
| OP1_TREE * OP2 <= M_MAXIMUM_SCALE. |
| |
| If RESULT is nonnull, store OP1_TREE * OP2 there when returning true. */ |
| |
| bool |
| loop_versioning::acceptable_multiplier_p (tree op1_tree, |
| unsigned HOST_WIDE_INT op2, |
| unsigned HOST_WIDE_INT *result) |
| { |
| if (tree_fits_uhwi_p (op1_tree)) |
| { |
| unsigned HOST_WIDE_INT op1 = tree_to_uhwi (op1_tree); |
| /* The first part checks for overflow. */ |
| if (op1 * op2 >= op2 && op1 * op2 <= m_maximum_scale) |
| { |
| if (result) |
| *result = op1 * op2; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Return true if it is worth using loop versioning on a memory access |
| of type TYPE. Store the size of the access in *SIZE if so. */ |
| |
| bool |
| loop_versioning::acceptable_type_p (tree type, unsigned HOST_WIDE_INT *size) |
| { |
| return (TYPE_SIZE_UNIT (type) |
| && acceptable_multiplier_p (TYPE_SIZE_UNIT (type), 1, size)); |
| } |
| |
| /* See whether OP is constant and whether we can multiply TERM by that |
| constant without exceeding M_MAXIMUM_SCALE. Return true and update |
| TERM if so. */ |
| |
| bool |
| loop_versioning::multiply_term_by (address_term_info &term, tree op) |
| { |
| return acceptable_multiplier_p (op, term.multiplier, &term.multiplier); |
| } |
| |
| /* Decide whether an address fragment of the form STRIDE * MULTIPLIER |
| is likely to be indexing an innermost dimension, returning the result |
| as an INNER_* probability. */ |
| |
| inner_likelihood |
| loop_versioning::get_inner_likelihood (tree stride, |
| unsigned HOST_WIDE_INT multiplier) |
| { |
| const unsigned int MAX_NITERS = 8; |
| |
| /* Iterate over possible values of STRIDE. Return INNER_LIKELY if at |
| least one of those values is likely to be for the innermost dimension. |
| Record in UNLIKELY_P if at least one of those values is unlikely to be |
| for the innermost dimension. |
| |
| E.g. for: |
| |
| stride = cond ? a * b : 1 |
| |
| we should treat STRIDE as being a likely inner dimension, since |
| we know that it is 1 under at least some circumstances. (See the |
| Fortran example below.) However: |
| |
| stride = a * b |
| |
| on its own is unlikely to be for the innermost dimension, since |
| that would require both a and b to be 1 at runtime. */ |
| bool unlikely_p = false; |
| tree worklist[MAX_NITERS]; |
| unsigned int length = 0; |
| worklist[length++] = stride; |
| for (unsigned int i = 0; i < length; ++i) |
| { |
| tree expr = worklist[i]; |
| |
| if (CONSTANT_CLASS_P (expr)) |
| { |
| /* See if EXPR * MULTIPLIER would be consistent with an individual |
| access or a small grouped access. */ |
| if (acceptable_multiplier_p (expr, multiplier)) |
| return INNER_LIKELY; |
| else |
| unlikely_p = true; |
| } |
| else if (gimple *stmt = maybe_get_stmt (expr)) |
| { |
| /* If EXPR is set by a PHI node, queue its arguments in case |
| we find one that is consistent with an inner dimension. |
| |
| An important instance of this is the Fortran handling of array |
| descriptors, which calculates the stride of the inner dimension |
| using a PHI equivalent of: |
| |
| raw_stride = a.dim[0].stride; |
| stride = raw_stride != 0 ? raw_stride : 1; |
| |
| (Strides for outer dimensions do not treat 0 specially.) */ |
| if (gphi *phi = dyn_cast <gphi *> (stmt)) |
| { |
| unsigned int nargs = gimple_phi_num_args (phi); |
| for (unsigned int j = 0; j < nargs && length < MAX_NITERS; ++j) |
| worklist[length++] = strip_casts (gimple_phi_arg_def (phi, j)); |
| } |
| /* If the value is set by an assignment, expect it to be read |
| from memory (such as an array descriptor) rather than be |
| calculated. */ |
| else if (gassign *assign = dyn_cast <gassign *> (stmt)) |
| { |
| if (!gimple_assign_load_p (assign)) |
| unlikely_p = true; |
| } |
| /* Things like calls don't really tell us anything. */ |
| } |
| } |
| |
| /* We didn't find any possible values of STRIDE that were likely to be |
| for the innermost dimension. If we found one that was actively |
| unlikely to be for the innermost dimension, assume that that applies |
| to STRIDE too. */ |
| return unlikely_p ? INNER_UNLIKELY : INNER_DONT_KNOW; |
| } |
| |
| /* Dump the likelihood that TERM's stride is for the innermost dimension. |
| ADDRESS is the address that contains TERM. */ |
| |
| void |
| loop_versioning::dump_inner_likelihood (address_info &address, |
| address_term_info &term) |
| { |
| if (term.inner_likelihood == INNER_LIKELY) |
| dump_printf_loc (MSG_NOTE, address.stmt, "%T is likely to be the" |
| " innermost dimension\n", term.stride); |
| else if (term.inner_likelihood == INNER_UNLIKELY) |
| dump_printf_loc (MSG_NOTE, address.stmt, "%T is probably not the" |
| " innermost dimension\n", term.stride); |
| else |
| dump_printf_loc (MSG_NOTE, address.stmt, "cannot tell whether %T" |
| " is the innermost dimension\n", term.stride); |
| } |
| |
| /* The caller has identified that STRIDE is the stride of interest |
| in TERM, and that the stride is applied in OP_LOOP. Record this |
| information in TERM, deciding whether STRIDE is likely to be for |
| the innermost dimension of an array and whether it represents a |
| versioning opportunity. ADDRESS is the address that contains TERM. */ |
| |
| void |
| loop_versioning::analyze_stride (address_info &address, |
| address_term_info &term, |
| tree stride, class loop *op_loop) |
| { |
| term.stride = stride; |
| |
| term.inner_likelihood = get_inner_likelihood (stride, term.multiplier); |
| if (dump_enabled_p ()) |
| dump_inner_likelihood (address, term); |
| |
| /* To be a versioning opportunity we require: |
| |
| - The multiplier applied by TERM is equal to the access size, |
| so that when STRIDE is 1, the accesses in successive loop |
| iterations are consecutive. |
| |
| This is deliberately conservative. We could relax it to handle |
| other cases (such as those with gaps between iterations) if we |
| find any real testcases for which it's useful. |
| |
| - the stride is applied in the same loop as STMT rather than |
| in an outer loop. Although versioning for strides applied in |
| outer loops could help in some cases -- such as enabling |
| more loop interchange -- the savings are much lower than for |
| inner loops. |
| |
| - the stride is an SSA name that is invariant in STMT's loop, |
| since otherwise versioning isn't possible. */ |
| unsigned HOST_WIDE_INT access_size = address.max_offset - address.min_offset; |
| if (term.multiplier == access_size |
| && address.loop == op_loop |
| && TREE_CODE (stride) == SSA_NAME |
| && expr_invariant_in_loop_p (address.loop, stride)) |
| { |
| term.versioning_opportunity_p = true; |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, address.stmt, "%T == 1 is a versioning" |
| " opportunity\n", stride); |
| } |
| } |
| |
| /* See whether address term TERM (which belongs to ADDRESS) is the result |
| of multiplying a varying SSA name by a loop-invariant SSA name. |
| Return true and update TERM if so. |
| |
| This handles both cases that SCEV might handle, such as: |
| |
| for (int i = 0; i < n; ++i) |
| res += a[i * stride]; |
| |
| and ones in which the term varies arbitrarily between iterations, such as: |
| |
| for (int i = 0; i < n; ++i) |
| res += a[index[i] * stride]; */ |
| |
| bool |
| loop_versioning::find_per_loop_multiplication (address_info &address, |
| address_term_info &term) |
| { |
| gassign *mult = maybe_get_assign (term.expr); |
| if (!mult || gimple_assign_rhs_code (mult) != MULT_EXPR) |
| return false; |
| |
| class loop *mult_loop = loop_containing_stmt (mult); |
| if (!loop_outer (mult_loop)) |
| return false; |
| |
| tree op1 = strip_casts (gimple_assign_rhs1 (mult)); |
| tree op2 = strip_casts (gimple_assign_rhs2 (mult)); |
| if (TREE_CODE (op1) != SSA_NAME || TREE_CODE (op2) != SSA_NAME) |
| return false; |
| |
| bool invariant1_p = expr_invariant_in_loop_p (mult_loop, op1); |
| bool invariant2_p = expr_invariant_in_loop_p (mult_loop, op2); |
| if (invariant1_p == invariant2_p) |
| return false; |
| |
| /* Make sure that the loop invariant is OP2 rather than OP1. */ |
| if (invariant1_p) |
| std::swap (op1, op2); |
| |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, address.stmt, "address term %T = varying %T" |
| " * loop-invariant %T\n", term.expr, op1, op2); |
| analyze_stride (address, term, op2, mult_loop); |
| return true; |
| } |
| |
| /* Try to use scalar evolutions to find an address stride for TERM, |
| which belongs to ADDRESS. Return true and update TERM if so. |
| |
| Here we are interested in any evolution information we can find, |
| not just evolutions wrt ADDRESS->LOOP. For example, if we find that |
| an outer loop obviously iterates over the inner dimension of an array, |
| that information can help us eliminate worthless versioning opportunities |
| in inner loops. */ |
| |
| bool |
| loop_versioning::analyze_term_using_scevs (address_info &address, |
| address_term_info &term) |
| { |
| gimple *setter = maybe_get_stmt (term.expr); |
| if (!setter) |
| return false; |
| |
| class loop *wrt_loop = loop_containing_stmt (setter); |
| if (!loop_outer (wrt_loop)) |
| return false; |
| |
| tree chrec = strip_casts (analyze_scalar_evolution (wrt_loop, term.expr)); |
| if (TREE_CODE (chrec) == POLYNOMIAL_CHREC) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, address.stmt, |
| "address term %T = %T\n", term.expr, chrec); |
| |
| /* Peel casts and accumulate constant multiplications, up to the |
| limit allowed by M_MAXIMUM_SCALE. */ |
| tree stride = strip_casts (CHREC_RIGHT (chrec)); |
| while (TREE_CODE (stride) == MULT_EXPR |
| && multiply_term_by (term, TREE_OPERAND (stride, 1))) |
| stride = strip_casts (TREE_OPERAND (stride, 0)); |
| |
| gassign *assign; |
| while ((assign = maybe_get_assign (stride)) |
| && gimple_assign_rhs_code (assign) == MULT_EXPR |
| && multiply_term_by (term, gimple_assign_rhs2 (assign))) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, address.stmt, |
| "looking through %G", assign); |
| stride = strip_casts (gimple_assign_rhs1 (assign)); |
| } |
| |
| analyze_stride (address, term, stride, get_chrec_loop (chrec)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Address term TERM is an arbitrary term that provides no versioning |
| opportunities. Analyze it to see whether it contains any likely |
| inner strides, so that we don't mistakenly version for other |
| (less likely) candidates. |
| |
| This copes with invariant innermost indices such as: |
| |
| x(i, :) = 100 |
| |
| where the "i" component of the address is invariant in the loop |
| but provides the real inner stride. |
| |
| ADDRESS is the address that contains TERM. */ |
| |
| void |
| loop_versioning::analyze_arbitrary_term (address_info &address, |
| address_term_info &term) |
| |
| { |
| /* A multiplication offers two potential strides. Pick the one that |
| is most likely to be an innermost stride. */ |
| tree expr = term.expr, alt = NULL_TREE; |
| gassign *mult = maybe_get_assign (expr); |
| if (mult && gimple_assign_rhs_code (mult) == MULT_EXPR) |
| { |
| expr = strip_casts (gimple_assign_rhs1 (mult)); |
| alt = strip_casts (gimple_assign_rhs2 (mult)); |
| } |
| term.stride = expr; |
| term.inner_likelihood = get_inner_likelihood (expr, term.multiplier); |
| if (alt) |
| { |
| inner_likelihood alt_l = get_inner_likelihood (alt, term.multiplier); |
| if (alt_l > term.inner_likelihood) |
| { |
| term.stride = alt; |
| term.inner_likelihood = alt_l; |
| } |
| } |
| if (dump_enabled_p ()) |
| dump_inner_likelihood (address, term); |
| } |
| |
| /* Try to identify loop strides in ADDRESS and try to choose realistic |
| versioning opportunities based on these strides. |
| |
| The main difficulty here isn't finding strides that could be used |
| in a version check (that's pretty easy). The problem instead is to |
| avoid versioning for some stride S that is unlikely ever to be 1 at |
| runtime. Versioning for S == 1 on its own would lead to unnecessary |
| code bloat, while adding S == 1 to more realistic version conditions |
| would lose the optimisation opportunity offered by those other conditions. |
| |
| For example, versioning for a stride of 1 in the Fortran code: |
| |
| integer :: a(:,:) |
| a(1,:) = 1 |
| |
| is not usually a good idea, since the assignment is iterating over |
| an outer dimension and is relatively unlikely to have a stride of 1. |
| (It isn't impossible, since the inner dimension might be 1, or the |
| array might be transposed.) Similarly, in: |
| |
| integer :: a(:,:), b(:,:) |
| b(:,1) = a(1,:) |
| |
| b(:,1) is relatively likely to have a stride of 1 while a(1,:) isn't. |
| Versioning for when both strides are 1 would lose most of the benefit |
| of versioning for b's access. |
| |
| The approach we take is as follows: |
| |
| - Analyze each term to see whether it has an identifiable stride, |
| regardless of which loop applies the stride. |
| |
| - Evaluate the likelihood that each such stride is for the innermost |
| dimension of an array, on the scale "likely", "don't know" or "unlikely". |
| |
| - If there is a single "likely" innermost stride, and that stride is |
| applied in the loop that contains STMT, version the loop for when the |
| stride is 1. This deals with the cases in which we're fairly |
| confident of doing the right thing, such as the b(:,1) reference above. |
| |
| - If there are no "likely" innermost strides, and the loop that contains |
| STMT uses a stride that we rated as "don't know", version for when |
| that stride is 1. This is principally used for C code such as: |
| |
| for (int i = 0; i < n; ++i) |
| a[i * x] = ...; |
| |
| and: |
| |
| for (int j = 0; j < n; ++j) |
| for (int i = 0; i < n; ++i) |
| a[i * x + j * y] = ...; |
| |
| where nothing in the way "x" and "y" are set gives a hint as to |
| whether "i" iterates over the innermost dimension of the array. |
| In these situations it seems reasonable to assume the |
| programmer has nested the loops appropriately (although of course |
| there are examples like GEMM in which this assumption doesn't hold |
| for all accesses in the loop). |
| |
| This case is also useful for the Fortran equivalent of the |
| above C code. */ |
| |
| void |
| loop_versioning::analyze_address_fragment (address_info &address) |
| { |
| if (dump_enabled_p ()) |
| { |
| dump_printf_loc (MSG_NOTE, address.stmt, "analyzing address fragment "); |
| dump_address_info (MSG_NOTE, address); |
| dump_printf (MSG_NOTE, "\n"); |
| } |
| |
| /* Analyze each component of the sum to see whether it involves an |
| apparent stride. |
| |
| There is an overlap between the addresses that |
| find_per_loop_multiplication and analyze_term_using_scevs can handle, |
| but the former is much cheaper than SCEV analysis, so try it first. */ |
| for (unsigned int i = 0; i < address.terms.length (); ++i) |
| if (!find_per_loop_multiplication (address, address.terms[i]) |
| && !analyze_term_using_scevs (address, address.terms[i]) |
| && !POINTER_TYPE_P (TREE_TYPE (address.terms[i].expr))) |
| analyze_arbitrary_term (address, address.terms[i]); |
| |
| /* Check for strides that are likely to be for the innermost dimension. |
| |
| 1. If there is a single likely inner stride, if it is an SSA name, |
| and if it is worth versioning the loop for when the SSA name |
| equals 1, record that we want to do so. |
| |
| 2. Otherwise, if there any likely inner strides, bail out. This means |
| one of: |
| |
| (a) There are multiple likely inner strides. This suggests we're |
| confused and be can't be confident of doing the right thing. |
| |
| (b) There is a single likely inner stride and it is a constant |
| rather than an SSA name. This can mean either that the access |
| is a natural one without any variable strides, such as: |
| |
| for (int i = 0; i < n; ++i) |
| a[i] += 1; |
| |
| or that a variable stride is applied to an outer dimension, |
| such as: |
| |
| for (int i = 0; i < n; ++i) |
| for (int j = 0; j < n; ++j) |
| a[j * stride][i] += 1; |
| |
| (c) There is a single likely inner stride, and it is an SSA name, |
| but it isn't a worthwhile versioning opportunity. This usually |
| means that the variable stride is applied by an outer loop, |
| such as: |
| |
| for (int i = 0; i < n; ++i) |
| for (int j = 0; j < n; ++j) |
| a[j][i * stride] += 1; |
| |
| or (using an example with a more natural loop nesting): |
| |
| for (int i = 0; i < n; ++i) |
| for (int j = 0; j < n; ++j) |
| a[i][j] += b[i * stride]; |
| |
| in cases where b[i * stride] cannot (yet) be hoisted for |
| aliasing reasons. |
| |
| 3. If there are no likely inner strides, fall through to the next |
| set of checks. |
| |
| Pointer equality is enough to check for uniqueness in (1), since we |
| only care about SSA names. */ |
| tree chosen_stride = NULL_TREE; |
| tree version_stride = NULL_TREE; |
| for (unsigned int i = 0; i < address.terms.length (); ++i) |
| if (chosen_stride != address.terms[i].stride |
| && address.terms[i].inner_likelihood == INNER_LIKELY) |
| { |
| if (chosen_stride) |
| return; |
| chosen_stride = address.terms[i].stride; |
| if (address.terms[i].versioning_opportunity_p) |
| version_stride = chosen_stride; |
| } |
| |
| /* If there are no likely inner strides, see if there is a single |
| versioning opportunity for a stride that was rated as INNER_DONT_KNOW. |
| See the comment above the function for the cases that this code |
| handles. */ |
| if (!chosen_stride) |
| for (unsigned int i = 0; i < address.terms.length (); ++i) |
| if (version_stride != address.terms[i].stride |
| && address.terms[i].inner_likelihood == INNER_DONT_KNOW |
| && address.terms[i].versioning_opportunity_p) |
| { |
| if (version_stride) |
| return; |
| version_stride = address.terms[i].stride; |
| } |
| |
| if (version_stride) |
| version_for_unity (address.stmt, version_stride); |
| } |
| |
| /* Treat EXPR * MULTIPLIER + OFFSET as a fragment of an address that addresses |
| TYPE_SIZE bytes and record this address fragment for later processing. |
| STMT is the statement that contains the address. */ |
| |
| void |
| loop_versioning::record_address_fragment (gimple *stmt, |
| unsigned HOST_WIDE_INT type_size, |
| tree expr, |
| unsigned HOST_WIDE_INT multiplier, |
| HOST_WIDE_INT offset) |
| { |
| /* We're only interested in computed values. */ |
| if (TREE_CODE (expr) != SSA_NAME) |
| return; |
| |
| /* Quick exit if no part of the address is calculated in STMT's loop, |
| since such addresses have no versioning opportunities. */ |
| class loop *loop = loop_containing_stmt (stmt); |
| if (expr_invariant_in_loop_p (loop, expr)) |
| return; |
| |
| /* Set up an address_info for EXPR * MULTIPLIER. */ |
| address_info *address = XOBNEW (&m_obstack, address_info); |
| new (address) address_info; |
| address->stmt = stmt; |
| address->loop = loop; |
| address->base = NULL_TREE; |
| address->terms.quick_grow (1); |
| address->terms[0].expr = expr; |
| address->terms[0].multiplier = multiplier; |
| address->terms[0].stride = NULL_TREE; |
| address->terms[0].inner_likelihood = INNER_UNLIKELY; |
| address->terms[0].versioning_opportunity_p = false; |
| address->min_offset = offset; |
| |
| /* Peel apart the expression into a sum of address_terms, where each |
| term is multiplied by a constant. Treat a + b and a - b the same, |
| since it doesn't matter for our purposes whether an address is |
| increasing or decreasing. Distribute (a + b) * constant into |
| a * constant + b * constant. |
| |
| We don't care which loop each term belongs to, since we want to |
| examine as many candidate strides as possible when determining |
| which is likely to be for the innermost dimension. We therefore |
| don't limit the search to statements in STMT's loop. */ |
| for (unsigned int i = 0; i < address->terms.length (); ) |
| { |
| if (gassign *assign = maybe_get_assign (address->terms[i].expr)) |
| { |
| tree_code code = gimple_assign_rhs_code (assign); |
| if (code == PLUS_EXPR |
| || code == POINTER_PLUS_EXPR |
| || code == MINUS_EXPR) |
| { |
| tree op1 = gimple_assign_rhs1 (assign); |
| tree op2 = gimple_assign_rhs2 (assign); |
| if (TREE_CODE (op2) == INTEGER_CST) |
| { |
| address->terms[i].expr = strip_casts (op1); |
| /* This is heuristic only, so don't worry about truncation |
| or overflow. */ |
| address->min_offset += (TREE_INT_CST_LOW (op2) |
| * address->terms[i].multiplier); |
| continue; |
| } |
| else if (address->terms.length () < address_info::MAX_TERMS) |
| { |
| unsigned int j = address->terms.length (); |
| address->terms.quick_push (address->terms[i]); |
| address->terms[i].expr = strip_casts (op1); |
| address->terms[j].expr = strip_casts (op2); |
| continue; |
| } |
| } |
| if (code == MULT_EXPR) |
| { |
| tree op1 = gimple_assign_rhs1 (assign); |
| tree op2 = gimple_assign_rhs2 (assign); |
| if (multiply_term_by (address->terms[i], op2)) |
| { |
| address->terms[i].expr = strip_casts (op1); |
| continue; |
| } |
| } |
| if (CONVERT_EXPR_CODE_P (code)) |
| { |
| tree op1 = gimple_assign_rhs1 (assign); |
| address->terms[i].expr = strip_casts (op1); |
| continue; |
| } |
| } |
| i += 1; |
| } |
| |
| /* Peel off any symbolic pointer. */ |
| if (TREE_CODE (address->terms[0].expr) != SSA_NAME |
| && address->terms[0].multiplier == 1) |
| { |
| if (address->terms.length () == 1) |
| { |
| obstack_free (&m_obstack, address); |
| return; |
| } |
| address->base = address->terms[0].expr; |
| address->terms.ordered_remove (0); |
| } |
| |
| /* Require all remaining terms to be SSA names. (This could be false |
| for unfolded statements, but they aren't worth dealing with.) */ |
| for (unsigned int i = 0; i < address->terms.length (); ++i) |
| if (TREE_CODE (address->terms[i].expr) != SSA_NAME) |
| { |
| obstack_free (&m_obstack, address); |
| return; |
| } |
| |
| /* The loop above set MIN_OFFSET based on the first byte of the |
| referenced data. Calculate the end + 1. */ |
| address->max_offset = address->min_offset + type_size; |
| |
| /* Put the terms into a canonical order for the hash table lookup below. */ |
| address->terms.qsort (compare_address_terms); |
| |
| if (dump_enabled_p ()) |
| { |
| dump_printf_loc (MSG_NOTE, stmt, "recording address fragment %T", expr); |
| if (multiplier != 1) |
| dump_printf (MSG_NOTE, " * %wd", multiplier); |
| dump_printf (MSG_NOTE, " = "); |
| dump_address_info (MSG_NOTE, *address); |
| dump_printf (MSG_NOTE, "\n"); |
| } |
| |
| /* Pool address information with the same terms (but potentially |
| different offsets). */ |
| address_info **slot = m_address_table.find_slot (address, INSERT); |
| if (address_info *old_address = *slot) |
| { |
| /* We've already seen an address with the same terms. Extend the |
| offset range to account for this access. Doing this can paper |
| over gaps, such as in: |
| |
| a[i * stride * 4] + a[i * stride * 4 + 3]; |
| |
| where nothing references "+ 1" or "+ 2". However, the vectorizer |
| handles such gapped accesses without problems, so it's not worth |
| trying to exclude them. */ |
| if (old_address->min_offset > address->min_offset) |
| old_address->min_offset = address->min_offset; |
| if (old_address->max_offset < address->max_offset) |
| old_address->max_offset = address->max_offset; |
| obstack_free (&m_obstack, address); |
| } |
| else |
| { |
| /* This is the first time we've seen an address with these terms. */ |
| *slot = address; |
| m_address_list.safe_push (address); |
| } |
| } |
| |
| /* Analyze expression EXPR, which occurs in STMT. */ |
| |
| void |
| loop_versioning::analyze_expr (gimple *stmt, tree expr) |
| { |
| unsigned HOST_WIDE_INT type_size; |
| |
| while (handled_component_p (expr)) |
| { |
| /* See whether we can use versioning to avoid a multiplication |
| in an array index. */ |
| if (TREE_CODE (expr) == ARRAY_REF |
| && acceptable_type_p (TREE_TYPE (expr), &type_size)) |
| record_address_fragment (stmt, type_size, |
| TREE_OPERAND (expr, 1), type_size, 0); |
| expr = TREE_OPERAND (expr, 0); |
| } |
| |
| /* See whether we can use versioning to avoid a multiplication |
| in the pointer calculation of a MEM_REF. */ |
| if (TREE_CODE (expr) == MEM_REF |
| && acceptable_type_p (TREE_TYPE (expr), &type_size)) |
| record_address_fragment (stmt, type_size, TREE_OPERAND (expr, 0), 1, |
| /* This is heuristic only, so don't worry |
| about truncation or overflow. */ |
| TREE_INT_CST_LOW (TREE_OPERAND (expr, 1))); |
| |
| /* These would be easy to handle if they existed at this stage. */ |
| gcc_checking_assert (TREE_CODE (expr) != TARGET_MEM_REF); |
| } |
| |
| /* Analyze all the statements in BB looking for useful version checks. |
| Return true on success, false if something prevents the block from |
| being versioned. */ |
| |
| bool |
| loop_versioning::analyze_block (basic_block bb) |
| { |
| class loop *loop = bb->loop_father; |
| loop_info &li = get_loop_info (loop); |
| for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| { |
| gimple *stmt = gsi_stmt (gsi); |
| if (is_gimple_debug (stmt)) |
| continue; |
| |
| if (expensive_stmt_p (stmt)) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, stmt, "expensive statement" |
| " prevents versioning: %G", stmt); |
| return false; |
| } |
| |
| /* Only look for direct versioning opportunities in inner loops |
| since the benefit tends to be much smaller for outer loops. */ |
| if (!loop->inner) |
| { |
| unsigned int nops = gimple_num_ops (stmt); |
| for (unsigned int i = 0; i < nops; ++i) |
| if (tree op = gimple_op (stmt, i)) |
| analyze_expr (stmt, op); |
| } |
| |
| /* The point of the instruction limit is to prevent excessive |
| code growth, so this is a size-based estimate even though |
| the optimization is aimed at speed. */ |
| li.num_insns += estimate_num_insns (stmt, &eni_size_weights); |
| } |
| |
| return true; |
| } |
| |
| /* Analyze all the blocks in the function, looking for useful version checks. |
| Return true if we found one. */ |
| |
| bool |
| loop_versioning::analyze_blocks () |
| { |
| AUTO_DUMP_SCOPE ("analyze_blocks", |
| dump_user_location_t::from_function_decl (m_fn->decl)); |
| |
| /* For now we don't try to version the whole function, although |
| versioning at that level could be useful in some cases. */ |
| get_loop_info (get_loop (m_fn, 0)).rejected_p = true; |
| |
| for (auto loop : loops_list (cfun, LI_FROM_INNERMOST)) |
| { |
| loop_info &linfo = get_loop_info (loop); |
| |
| /* Ignore cold loops. */ |
| if (!optimize_loop_for_speed_p (loop)) |
| linfo.rejected_p = true; |
| |
| /* See whether an inner loop prevents versioning of this loop. */ |
| if (!linfo.rejected_p) |
| for (class loop *inner = loop->inner; inner; inner = inner->next) |
| if (get_loop_info (inner).rejected_p) |
| { |
| linfo.rejected_p = true; |
| break; |
| } |
| |
| /* If versioning the loop is still a possibility, examine the |
| statements in the loop to look for versioning opportunities. */ |
| if (!linfo.rejected_p) |
| { |
| void *start_point = obstack_alloc (&m_obstack, 0); |
| |
| for (basic_block bb = linfo.block_list; bb; |
| bb = m_next_block_in_loop[bb->index]) |
| if (!analyze_block (bb)) |
| { |
| linfo.rejected_p = true; |
| break; |
| } |
| |
| if (!linfo.rejected_p) |
| { |
| /* Process any queued address fragments, now that we have |
| complete grouping information. */ |
| address_info *address; |
| unsigned int i; |
| FOR_EACH_VEC_ELT (m_address_list, i, address) |
| analyze_address_fragment (*address); |
| } |
| |
| m_address_table.empty (); |
| m_address_list.truncate (0); |
| obstack_free (&m_obstack, start_point); |
| } |
| } |
| |
| return m_num_conditions != 0; |
| } |
| |
| /* Use the ranges in VRS to remove impossible versioning conditions from |
| LOOP. */ |
| |
| void |
| loop_versioning::prune_loop_conditions (class loop *loop) |
| { |
| loop_info &li = get_loop_info (loop); |
| |
| int to_remove = -1; |
| bitmap_iterator bi; |
| unsigned int i; |
| int_range_max r; |
| EXECUTE_IF_SET_IN_BITMAP (&li.unity_names, 0, i, bi) |
| { |
| tree name = ssa_name (i); |
| gimple *stmt = first_stmt (loop->header); |
| |
| if (get_range_query (cfun)->range_of_expr (r, name, stmt) |
| && !r.contains_p (build_one_cst (TREE_TYPE (name)))) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, find_loop_location (loop), |
| "%T can never be 1 in this loop\n", name); |
| |
| if (to_remove >= 0) |
| bitmap_clear_bit (&li.unity_names, to_remove); |
| to_remove = i; |
| m_num_conditions -= 1; |
| } |
| } |
| if (to_remove >= 0) |
| bitmap_clear_bit (&li.unity_names, to_remove); |
| } |
| |
| /* Remove any scheduled loop version conditions that will never be true. |
| Return true if any remain. */ |
| |
| bool |
| loop_versioning::prune_conditions () |
| { |
| AUTO_DUMP_SCOPE ("prune_loop_conditions", |
| dump_user_location_t::from_function_decl (m_fn->decl)); |
| |
| calculate_dominance_info (CDI_DOMINATORS); |
| lv_dom_walker dom_walker (*this); |
| dom_walker.walk (ENTRY_BLOCK_PTR_FOR_FN (m_fn)); |
| return m_num_conditions != 0; |
| } |
| |
| /* Merge the version checks for INNER into immediately-enclosing loop |
| OUTER. */ |
| |
| void |
| loop_versioning::merge_loop_info (class loop *outer, class loop *inner) |
| { |
| loop_info &inner_li = get_loop_info (inner); |
| loop_info &outer_li = get_loop_info (outer); |
| |
| if (dump_enabled_p ()) |
| { |
| bitmap_iterator bi; |
| unsigned int i; |
| EXECUTE_IF_SET_IN_BITMAP (&inner_li.unity_names, 0, i, bi) |
| if (!bitmap_bit_p (&outer_li.unity_names, i)) |
| dump_printf_loc (MSG_NOTE, find_loop_location (inner), |
| "hoisting check that %T == 1 to outer loop\n", |
| ssa_name (i)); |
| } |
| |
| bitmap_ior_into (&outer_li.unity_names, &inner_li.unity_names); |
| if (loop_depth (outer_li.outermost) < loop_depth (inner_li.outermost)) |
| outer_li.outermost = inner_li.outermost; |
| } |
| |
| /* Add LOOP to the queue of loops to version. */ |
| |
| void |
| loop_versioning::add_loop_to_queue (class loop *loop) |
| { |
| loop_info &li = get_loop_info (loop); |
| |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, find_loop_location (loop), |
| "queuing this loop for versioning\n"); |
| m_loops_to_version.safe_push (loop); |
| |
| /* Don't try to version superloops. */ |
| li.rejected_p = true; |
| } |
| |
| /* Decide whether the cost model would allow us to version LOOP, |
| either directly or as part of a parent loop, and return true if so. |
| This does not imply that the loop is actually worth versioning in its |
| own right, just that it would be valid to version it if something |
| benefited. |
| |
| We have already made this decision for all inner loops of LOOP. */ |
| |
| bool |
| loop_versioning::decide_whether_loop_is_versionable (class loop *loop) |
| { |
| loop_info &li = get_loop_info (loop); |
| |
| if (li.rejected_p) |
| return false; |
| |
| /* Examine the decisions made for inner loops. */ |
| for (class loop *inner = loop->inner; inner; inner = inner->next) |
| { |
| loop_info &inner_li = get_loop_info (inner); |
| if (inner_li.rejected_p) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, find_loop_location (loop), |
| "not versioning this loop because one of its" |
| " inner loops should not be versioned\n"); |
| return false; |
| } |
| |
| if (inner_li.worth_versioning_p ()) |
| li.subloops_benefit_p = true; |
| |
| /* Accumulate the number of instructions from subloops that are not |
| the innermost, or that don't benefit from versioning. Only the |
| instructions from innermost loops that benefit from versioning |
| should be weighed against loop-versioning-max-inner-insns; |
| everything else should be weighed against |
| loop-versioning-max-outer-insns. */ |
| if (!inner_li.worth_versioning_p () || inner->inner) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, find_loop_location (loop), |
| "counting %d instructions from this loop" |
| " against its parent loop\n", inner_li.num_insns); |
| li.num_insns += inner_li.num_insns; |
| } |
| } |
| |
| /* Enforce the size limits. */ |
| if (li.worth_versioning_p ()) |
| { |
| unsigned int max_num_insns = max_insns_for_loop (loop); |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_NOTE, find_loop_location (loop), |
| "this loop has %d instructions, against" |
| " a versioning limit of %d\n", |
| li.num_insns, max_num_insns); |
| if (li.num_insns > max_num_insns) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_MISSED_OPTIMIZATION |
| | MSG_PRIORITY_USER_FACING, |
| find_loop_location (loop), |
| "this loop is too big to version"); |
| return false; |
| } |
| } |
| |
| /* Hoist all version checks from subloops to this loop. */ |
| for (class loop *subloop = loop->inner; subloop; subloop = subloop->next) |
| merge_loop_info (loop, subloop); |
| |
| return true; |
| } |
| |
| /* Decide which loops to version and add them to the versioning queue. |
| Return true if there are any loops to version. */ |
| |
| bool |
| loop_versioning::make_versioning_decisions () |
| { |
| AUTO_DUMP_SCOPE ("make_versioning_decisions", |
| dump_user_location_t::from_function_decl (m_fn->decl)); |
| |
| for (auto loop : loops_list (cfun, LI_FROM_INNERMOST)) |
| { |
| loop_info &linfo = get_loop_info (loop); |
| if (decide_whether_loop_is_versionable (loop)) |
| { |
| /* Commit to versioning LOOP directly if we can't hoist the |
| version checks any further. */ |
| if (linfo.worth_versioning_p () |
| && (loop_depth (loop) == 1 || linfo.outermost == loop)) |
| add_loop_to_queue (loop); |
| } |
| else |
| { |
| /* We can't version this loop, so individually version any |
| subloops that would benefit and haven't been versioned yet. */ |
| linfo.rejected_p = true; |
| for (class loop *subloop = loop->inner; subloop; |
| subloop = subloop->next) |
| if (get_loop_info (subloop).worth_versioning_p ()) |
| add_loop_to_queue (subloop); |
| } |
| } |
| |
| return !m_loops_to_version.is_empty (); |
| } |
| |
| /* Attempt to implement loop versioning for LOOP, using the information |
| cached in the associated loop_info. Return true on success. */ |
| |
| bool |
| loop_versioning::version_loop (class loop *loop) |
| { |
| loop_info &li = get_loop_info (loop); |
| |
| /* Build up a condition that selects the original loop instead of |
| the simplified loop. */ |
| tree cond = boolean_false_node; |
| bitmap_iterator bi; |
| unsigned int i; |
| EXECUTE_IF_SET_IN_BITMAP (&li.unity_names, 0, i, bi) |
| { |
| tree name = ssa_name (i); |
| tree ne_one = fold_build2 (NE_EXPR, boolean_type_node, name, |
| build_one_cst (TREE_TYPE (name))); |
| cond = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, cond, ne_one); |
| } |
| |
| /* Convert the condition into a suitable gcond. */ |
| gimple_seq stmts = NULL; |
| cond = force_gimple_operand_1 (cond, &stmts, is_gimple_condexpr, NULL_TREE); |
| |
| /* Version the loop. */ |
| initialize_original_copy_tables (); |
| basic_block cond_bb; |
| li.optimized_loop = loop_version (loop, cond, &cond_bb, |
| profile_probability::unlikely (), |
| profile_probability::likely (), |
| profile_probability::unlikely (), |
| profile_probability::likely (), true); |
| free_original_copy_tables (); |
| if (!li.optimized_loop) |
| { |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_MISSED_OPTIMIZATION, find_loop_location (loop), |
| "tried but failed to version this loop for when" |
| " certain strides are 1\n"); |
| return false; |
| } |
| |
| if (dump_enabled_p ()) |
| dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, find_loop_location (loop), |
| "versioned this loop for when certain strides are 1\n"); |
| |
| /* Insert the statements that feed COND. */ |
| if (stmts) |
| { |
| gimple_stmt_iterator gsi = gsi_last_bb (cond_bb); |
| gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT); |
| } |
| |
| return true; |
| } |
| |
| /* Attempt to version all loops in the versioning queue. */ |
| |
| void |
| loop_versioning::implement_versioning_decisions () |
| { |
| /* No AUTO_DUMP_SCOPE here since all messages are top-level and |
| user-facing at this point. */ |
| |
| bool any_succeeded_p = false; |
| class loop *loop; |
| unsigned int i; |
| FOR_EACH_VEC_ELT (m_loops_to_version, i, loop) |
| if (version_loop (loop)) |
| any_succeeded_p = true; |
| if (!any_succeeded_p) |
| return; |
| |
| update_ssa (TODO_update_ssa); |
| |
| /* Simplify the new loop, which is used when COND is false. */ |
| FOR_EACH_VEC_ELT (m_loops_to_version, i, loop) |
| { |
| loop_info &linfo = get_loop_info (loop); |
| if (linfo.optimized_loop) |
| name_prop (linfo).substitute_and_fold (linfo.optimized_loop->header); |
| } |
| } |
| |
| /* Run the pass and return a set of TODO_* flags. */ |
| |
| unsigned int |
| loop_versioning::run () |
| { |
| gcc_assert (scev_initialized_p ()); |
| |
| if (analyze_blocks () |
| && prune_conditions () |
| && make_versioning_decisions ()) |
| implement_versioning_decisions (); |
| |
| return 0; |
| } |
| |
| /* Loop versioning pass. */ |
| |
| const pass_data pass_data_loop_versioning = |
| { |
| GIMPLE_PASS, /* type */ |
| "lversion", /* name */ |
| OPTGROUP_LOOP, /* optinfo_flags */ |
| TV_LOOP_VERSIONING, /* tv_id */ |
| PROP_cfg, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| 0, /* todo_flags_finish */ |
| }; |
| |
| class pass_loop_versioning : public gimple_opt_pass |
| { |
| public: |
| pass_loop_versioning (gcc::context *ctxt) |
| : gimple_opt_pass (pass_data_loop_versioning, ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| virtual bool gate (function *) { return flag_version_loops_for_strides; } |
| virtual unsigned int execute (function *); |
| }; |
| |
| unsigned int |
| pass_loop_versioning::execute (function *fn) |
| { |
| if (number_of_loops (fn) <= 1) |
| return 0; |
| |
| enable_ranger (fn); |
| unsigned int ret = loop_versioning (fn).run (); |
| disable_ranger (fn); |
| return ret; |
| } |
| |
| } // anon namespace |
| |
| gimple_opt_pass * |
| make_pass_loop_versioning (gcc::context *ctxt) |
| { |
| return new pass_loop_versioning (ctxt); |
| } |