| /* SSA range support functions. |
| Copyright (C) 2017-2018 Free Software Foundation, Inc. |
| Contributed by Andrew MacLeod <amacleod@redhat.com>. |
| |
| 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 "insn-codes.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "cfghooks.h" |
| #include "tree-pass.h" |
| #include "ssa.h" |
| #include "optabs-tree.h" |
| #include "gimple-pretty-print.h" |
| #include "diagnostic-core.h" |
| #include "flags.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "calls.h" |
| #include "cfganal.h" |
| #include "gimple-fold.h" |
| #include "tree-eh.h" |
| #include "gimple-iterator.h" |
| #include "gimple-walk.h" |
| #include "tree-cfg.h" |
| #include "wide-int.h" |
| #include "ssa-range.h" |
| #include "ssa-range-gori.h" |
| #include "fold-const.h" |
| #include "cfgloop.h" |
| #include "tree-scalar-evolution.h" |
| #include "tree-ssa-loop.h" |
| #include "alloc-pool.h" |
| #include "vr-values.h" |
| #include "dbgcnt.h" |
| |
| irange |
| stmt_ranger::range_of_ssa_name (tree name, gimple *s ATTRIBUTE_UNUSED) |
| { |
| return ssa_name_range (name); |
| } |
| |
| // This function returns a range for a tree node. If optional statement S |
| // is present, then the range would be if it were to appear as a use on S. |
| // Return false if ranges are not supported. |
| |
| bool |
| stmt_ranger::range_of_expr (irange &r, tree expr, gimple *s) |
| { |
| tree type; |
| if (TYPE_P (expr)) |
| type = expr; |
| else |
| type = TREE_TYPE (expr); |
| |
| // Return false if the type isn't suported. |
| if (!irange::supports_type_p (type)) |
| return false; |
| |
| switch (TREE_CODE (expr)) |
| { |
| case INTEGER_CST: |
| // If we encounter an overflow, simply punt and drop to varying |
| // since we have no idea how it will be used. |
| if (!TREE_OVERFLOW_P (expr)) |
| { |
| r = irange (expr, expr); |
| return true; |
| } |
| break; |
| |
| case SSA_NAME: |
| r = range_of_ssa_name (expr, s); |
| return true; |
| |
| case ADDR_EXPR: |
| { |
| // handle &var which can show up in phi arguments |
| bool ov; |
| if (tree_single_nonzero_warnv_p (expr, &ov)) |
| { |
| r = range_nonzero (type); |
| return true; |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| r = irange (type); |
| return true; |
| } |
| |
| // Calculate a range for statement S and return it in R. If NAME is provided it |
| // represents the SSA_NAME on the LHS of the statement. It is only required |
| // if there is more than one lhs/output. If a range cannot |
| // be calculated, return false. |
| |
| bool |
| stmt_ranger::range_of_stmt (irange &r, gimple *s, tree name) |
| { |
| bool res = false; |
| // If name is specified, make sure it a LHS of S. |
| gcc_checking_assert (name ? SSA_NAME_DEF_STMT (name) == s : true); |
| |
| if (is_a<grange *> (s)) |
| res = range_of_grange (r, as_a<grange *> (s)); |
| else if (is_a<gphi *>(s)) |
| res = range_of_phi (r, as_a<gphi *> (s)); |
| else if (is_a<gcall *>(s)) |
| res = range_of_call (r, as_a<gcall *> (s)); |
| else if (is_a<gassign *> (s) && gimple_assign_rhs_code (s) == COND_EXPR) |
| res = range_of_cond_expr (r, as_a<gassign *> (s)); |
| else |
| { |
| // If no name is specified, try the expression kind. |
| if (!name) |
| { |
| tree t = gimple_expr_type (s); |
| if (!irange::supports_type_p (t)) |
| return false; |
| r.set_varying (t); |
| return true; |
| } |
| // We don't understand the stmt, so return the global range. |
| r = ssa_name_range (name); |
| return true; |
| } |
| if (res) |
| { |
| if (r.undefined_p ()) |
| return true; |
| if (name && TREE_TYPE (name) != r.type ()) |
| range_cast (r, TREE_TYPE (name)); |
| return true; |
| } |
| return false; |
| } |
| |
| // Calculate a range for statement S and return it in R. If NAME is found |
| // in any of the operands, replace it with NAME_RANGE rather than looking it |
| // up. If a range cannot be calculated on this statement, return false. |
| |
| bool |
| stmt_ranger::range_of_stmt_with_range (irange &r, gimple *s, tree name, |
| const irange &name_range) |
| { |
| if (is_a<grange *> (s)) |
| return range_of_grange (r, as_a<grange *> (s), name, &name_range); |
| if (is_a<gphi *>(s)) |
| return range_of_phi (r, as_a<gphi *> (s), name, &name_range); |
| if (is_a<gcall *>(s)) |
| return range_of_call (r, as_a<gcall *> (s), name, &name_range); |
| if (is_a<gassign *> (s) && gimple_assign_rhs_code (s) == COND_EXPR) |
| return range_of_cond_expr (r, as_a<gassign *> (s), name, &name_range); |
| |
| return false; |
| } |
| |
| |
| // Calculate a range for range_op statement S and return it in R. If any |
| // operand matches NAME, replace it with NAME_RANGE. If a range |
| // cannot be calculated, return false. |
| |
| bool |
| stmt_ranger::range_of_grange (irange &r, grange *s, tree name, |
| const irange *name_range) |
| { |
| irange range1, range2; |
| bool res = true; |
| tree type = gimple_expr_type (s); |
| gcc_checking_assert (irange::supports_type_p (type)); |
| |
| tree op1 = gimple_range_operand1 (s); |
| tree op2 = gimple_range_operand2 (s); |
| |
| // Calculate a range for operand 1. |
| if (op1 == name) |
| range1 = *name_range; |
| else |
| res = range_of_expr (range1, op1, s); |
| |
| if (res) |
| { |
| if (!op2) |
| return gimple_range_fold (s, r, range1); |
| |
| if (op2 == name) |
| range2 = *name_range; |
| else |
| res = range_of_expr (range2, op2, s); |
| if (res) |
| return gimple_range_fold (s, r, range1, range2); |
| } |
| r.set_varying (type); |
| return true; |
| } |
| |
| |
| // Calculate a range for phi statement S and return it in R. |
| // If NAME is non-null, replace any occurences of NAME in arguments with |
| // NAME_RANGE. |
| // If EVAL_FOM is non-null, evaluate the PHI as if it occured right before |
| // EVAL_FROM. |
| // if ON_EDGE is non-null, only evaluate the argument on edge ON_EDGE. |
| // If a range cannot be calculated, return false. |
| |
| bool |
| stmt_ranger::range_of_phi (irange &r, gphi *phi, tree name, |
| const irange *name_range) |
| { |
| tree phi_def = gimple_phi_result (phi); |
| tree type = TREE_TYPE (phi_def); |
| irange phi_range; |
| unsigned x; |
| |
| if (!irange::supports_type_p (type)) |
| return false; |
| |
| // And start with an empty range, unioning in each argument's range. |
| r.set_undefined (); |
| for (x = 0; x < gimple_phi_num_args (phi); x++) |
| { |
| irange arg_range; |
| tree arg = gimple_phi_arg_def (phi, x); |
| if (name == arg) |
| arg_range = *name_range; |
| else |
| gcc_assert (range_of_expr (arg_range, arg, phi)); |
| |
| r.union_ (arg_range); |
| // Once the value reaches varying, stop looking. |
| if (r.varying_p ()) |
| break; |
| } |
| |
| return true; |
| } |
| |
| // Calculate a range for call statement S and return it in R. |
| // If NAME is non-null, replace any occurences of NAME in arguments with |
| // NAME_RANGE. |
| // If EVAL_FOM is non-null, evaluate the PHI as if it occured right before |
| // EVAL_FROM. |
| // if ON_EDGE is non-null, only evaluate the argument on edge ON_EDGE. |
| // If a range cannot be calculated, return false. |
| |
| bool |
| stmt_ranger::range_of_call (irange &r, gcall *call, tree name ATTRIBUTE_UNUSED, |
| const irange *name_range ATTRIBUTE_UNUSED) |
| { |
| tree type = gimple_call_return_type (call); |
| if (!irange::supports_type_p (type)) |
| return false; |
| |
| if (gimple_call_nonnull_result_p (call)) |
| { |
| r = range_nonzero (type); |
| return true; |
| } |
| r.set_varying (type); |
| return true; |
| } |
| |
| |
| |
| // Calculate a range for COND_EXPR statement S and return it in R. |
| // If NAME is non-null, replace any occurences of NAME in arguments with |
| // NAME_RANGE. |
| // If EVAL_FOM is non-null, evaluate the COND_EXPR as if it occured right |
| // before EVAL_FROM. |
| // If a range cannot be calculated, return false. |
| |
| bool |
| stmt_ranger::range_of_cond_expr (irange &r, gassign *s, tree name, |
| const irange *name_range) |
| { |
| irange cond_range, range1, range2; |
| tree cond = gimple_assign_rhs1 (s); |
| tree op1 = gimple_assign_rhs2 (s); |
| tree op2 = gimple_assign_rhs3 (s); |
| |
| gcc_checking_assert (gimple_assign_rhs_code (s) == COND_EXPR); |
| gcc_checking_assert (useless_type_conversion_p (TREE_TYPE (op1), |
| TREE_TYPE (op2))); |
| if (!irange::supports_type_p (TREE_TYPE (op1))) |
| return false; |
| |
| if (name == cond) |
| cond_range = *name_range; |
| else |
| gcc_assert (range_of_expr (cond_range, cond, s)); |
| |
| if (name == op1) |
| range1 = *name_range; |
| else |
| gcc_assert (range_of_expr (range1, op1, s)); |
| |
| if (name == op2) |
| range2 = *name_range; |
| else |
| gcc_assert (range_of_expr (range2, op2, s)); |
| |
| if (cond_range.singleton_p ()) |
| { |
| // False, pick second operand |
| if (cond_range.zero_p ()) |
| r = range2; |
| else |
| r = range1; |
| } |
| else |
| r = range_union (range1, range2); |
| return true; |
| } |
| |
| |
| // Calculate a range on edge E and return it in R. Try to evaluate a range |
| // for NAME on this edge. Return FALSE if this is either not a control edge |
| // or NAME is not defined by this edge. |
| |
| bool |
| ssa_ranger::outgoing_edge_range_p (irange &r, edge e, tree name, |
| irange *name_range) |
| { |
| irange lhs; |
| |
| gcc_checking_assert (valid_range_ssa_p (name)); |
| // Determine if there is an outgoing edge. |
| gimple *s = gimple_outgoing_edge_range_p (lhs, e); |
| |
| // If there is no outgoing stmt, there is no range produced. |
| if (!s) |
| return false; |
| |
| // Otherwise use the outgoing edge as a LHS and try to calculate a range. |
| return compute_operand_range_on_stmt (r, s, lhs, name, name_range); |
| } |
| |
| // Calculate a range for NAME on edge E and return it in R. |
| // if NAME is a PHI node at the dest of E, get the argument result. |
| // Return false if no range can be determined. |
| |
| void |
| ssa_ranger::range_on_edge (irange &r, edge e, tree name) |
| { |
| irange edge_range; |
| gcc_checking_assert (irange::supports_p (name)); |
| |
| if (!valid_range_ssa_p (name)) |
| { |
| gcc_assert (range_of_expr (r, name)); |
| return; |
| } |
| range_on_exit (r, e->src, name); |
| gcc_checking_assert (r.undefined_p () |
| || types_compatible_p (r.type(), TREE_TYPE (name))); |
| |
| // Check to see if NAME is defined on edge e. |
| if (outgoing_edge_range_p (edge_range, e, name, &r)) |
| r = edge_range; |
| } |
| |
| |
| // Return the range for NAME on entry to basic block BB in R. |
| // Return false if no range can be calculated. |
| // Calculation is performed by unioning all the ranges on incoming edges. |
| |
| void |
| ssa_ranger::range_on_entry (irange &r, basic_block bb, tree name) |
| { |
| edge_iterator ei; |
| edge e; |
| tree type = TREE_TYPE (name); |
| irange pred_range; |
| |
| gcc_checking_assert (irange::supports_type_p (type)); |
| |
| // Start with an empty range. |
| r.set_undefined (); |
| |
| gcc_checking_assert (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun)); |
| |
| // Visit each predecessor to resolve them. |
| FOR_EACH_EDGE (e, ei, bb->preds) |
| { |
| range_on_edge (pred_range, e, name); |
| r.union_ (pred_range); |
| // If varying is reach, stop processing. |
| if (r.varying_p ()) |
| break; |
| } |
| } |
| |
| |
| // Calculate the range for NAME at the end of block BB and return it in R. |
| // Return false if no range can be calculated. |
| |
| void |
| ssa_ranger::range_on_exit (irange &r, basic_block bb, tree name) |
| { |
| // on-exit from the exit block? |
| gcc_checking_assert (bb != EXIT_BLOCK_PTR_FOR_FN (cfun)); |
| |
| gimple *s = last_stmt (bb); |
| // If there is no statement in the block and this isnt the entry block, |
| // go get the range_on_entry for this block. |
| // For the entry block, a NULL stmt will return the global value for NAME. |
| if (!s && bb != ENTRY_BLOCK_PTR_FOR_FN (cfun)) |
| range_on_entry (r, bb, name); |
| else |
| gcc_assert (range_of_expr (r, name, s)); |
| gcc_checking_assert (r.undefined_p () |
| || types_compatible_p (r.type(), TREE_TYPE (name))); |
| } |
| |
| bool |
| ssa_ranger::range_of_phi (irange &r, gphi *phi, tree name, |
| const irange *name_range) |
| { |
| tree phi_def = gimple_phi_result (phi); |
| tree type = TREE_TYPE (phi_def); |
| irange phi_range; |
| unsigned x; |
| |
| if (!irange::supports_type_p (type)) |
| return false; |
| |
| // And start with an empty range, unioning in each argument's range. |
| r.set_undefined (); |
| for (x = 0; x < gimple_phi_num_args (phi); x++) |
| { |
| irange arg_range; |
| tree arg = gimple_phi_arg_def (phi, x); |
| edge e = gimple_phi_arg_edge (phi, x); |
| if (name == arg) |
| arg_range = *name_range; |
| else |
| range_on_edge (arg_range, e, arg); |
| |
| r.union_ (arg_range); |
| // Once the value reaches varying, stop looking. |
| if (r.varying_p ()) |
| break; |
| } |
| |
| return true; |
| } |
| |
| // ------------------------------------------------------------------------- |
| |
| // Initialize a global_ranger. |
| |
| global_ranger::global_ranger () |
| { |
| } |
| |
| // Deallocate global_ranger members. |
| |
| global_ranger::~global_ranger () |
| { |
| } |
| |
| // Return the range of NAME on entry to block BB in R. |
| |
| void |
| global_ranger::range_on_entry (irange &r, basic_block bb, tree name) |
| { |
| irange entry_range; |
| gcc_checking_assert (valid_range_ssa_p (name)); |
| |
| // Start with any known range |
| gcc_assert (range_of_stmt (r, SSA_NAME_DEF_STMT (name), name)); |
| |
| // Now see if there is any on_entry value which may refine it . |
| if (m_gori.block_range (entry_range, bb, name)) |
| r.intersect (entry_range); |
| } |
| |
| // Calculate a range for statement S and return it in R. If NAME is provided |
| // it represents the SSA_NAME on the LHS of the statement. It is only required |
| // if there is more than one lhs/output. |
| // Check the global cache for NAME first to see if the evaluation can be |
| // avoided. If a range cannot be calculated, return false. |
| |
| bool |
| global_ranger::range_of_stmt (irange &r, gimple *s, tree name) |
| { |
| // If no name, simply call the base routine. |
| if (!name) |
| { |
| // first check to see if the stmt has a name. |
| name = gimple_get_lhs (s); |
| if (!name) |
| return ssa_ranger::range_of_stmt (r, s, name); |
| } |
| |
| gcc_checking_assert (irange::supports_ssa_p (name)); |
| |
| // If this STMT has already been processed, return that value. |
| if (m_gori.m_globals.get_global_range (r, name)) |
| return true; |
| |
| // Avoid infinite recursion by initializing global cache |
| irange tmp = ssa_name_range (name); |
| m_gori.m_globals.set_global_range (name, tmp); |
| |
| gcc_assert (ssa_ranger::range_of_stmt (r, s, name)); |
| |
| if (is_a<gphi *> (s)) |
| r.intersect (tmp); |
| m_gori.m_globals.set_global_range (name, r); |
| return true; |
| } |
| |
| // Determine a range for OP on stmt S, returning the result in R. |
| // If OP is not defined in BB, find the range on entry to this block. |
| |
| irange |
| global_ranger::range_of_ssa_name (tree name, gimple *s) |
| { |
| // If there is no statement, just get the global value. |
| if (!s) |
| return ssa_name_range (name); |
| |
| irange r; |
| basic_block bb = gimple_bb (s); |
| gimple *def_stmt = SSA_NAME_DEF_STMT (name); |
| |
| // if name is defined in this block, try to get an range from S. |
| if (def_stmt && gimple_bb (def_stmt) == bb) |
| gcc_assert (range_of_stmt (r, def_stmt, name)); |
| else |
| // Otherwise OP comes from outside this block, use range on entry. |
| range_on_entry (r, bb, name); |
| |
| // No range yet, see if there is a dereference in the block. |
| // We don't care if it's between the def and a use within a block |
| // because the entire block must be executed anyway. |
| // FIXME:?? For non-call exceptions we could have a statement throw |
| // which causes an early block exit. |
| // in which case we may need to walk from S back to the def/top of block |
| // to make sure the deref happens between S and there before claiming |
| // there is a deref. Punt for now. |
| if (!cfun->can_throw_non_call_exceptions && r.varying_p () && |
| m_gori.non_null_deref_p (name, bb)) |
| r = range_nonzero (TREE_TYPE (name)); |
| |
| return r; |
| } |
| |
| // Calculate a range on edge E and return it in R. Try to evaluate a range |
| // for NAME on this edge. Return FALSE if this is either not a control edge |
| // or NAME is not defined by this edge. |
| |
| bool |
| global_ranger::outgoing_edge_range_p (irange &r, edge e, tree name, |
| irange *name_range) |
| { |
| return m_gori.outgoing_edge_range_p (r, e, name, name_range); |
| } |
| |
| // If NAME's derived chain has a termnial name, return it otherwise NULL_TREE. |
| |
| tree |
| global_ranger::terminal_name (tree name) |
| { |
| return m_gori.terminal_name (name); |
| } |
| |
| // This routine will export whatever global ranges are known to GCC |
| // SSA_RANGE_NAME_INFO fields. |
| |
| void |
| global_ranger::export_global_ranges () |
| { |
| unsigned x; |
| irange r; |
| if (dump_file) |
| { |
| fprintf (dump_file, "Exported global range table\n"); |
| fprintf (dump_file, "===========================\n"); |
| } |
| |
| for ( x = 1; x < num_ssa_names; x++) |
| { |
| tree name = ssa_name (x); |
| if (name && !SSA_NAME_IN_FREE_LIST (name) && |
| valid_range_ssa_p (name) && m_gori.m_globals.get_global_range (r, name) && |
| !r.varying_p()) |
| { |
| // Make sure the new range is a subset of the old range. |
| irange old_range; |
| old_range = ssa_name_range (name); |
| old_range.intersect (r); |
| /* Disable this while we fix tree-ssa/pr61743-2.c. */ |
| //gcc_checking_assert (old_range == r); |
| |
| // WTF? Can't write non-null pointer ranges?? stupid set_range_info! |
| if (!POINTER_TYPE_P (TREE_TYPE (name)) && !r.undefined_p ()) |
| { |
| if (!dbg_cnt (ranger_export_count)) |
| return; |
| |
| value_range_base vr = irange_to_value_range (r); |
| set_range_info (name, vr); |
| if (dump_file) |
| { |
| print_generic_expr (dump_file, name , TDF_SLIM); |
| fprintf (dump_file, " --> "); |
| vr.dump (dump_file); |
| fprintf (dump_file, "\n"); |
| fprintf (dump_file, " irange : "); |
| r.dump (dump_file); |
| fprintf (dump_file, "\n"); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| // Print the known table values to file F. |
| |
| void |
| global_ranger::dump (FILE *f) |
| { |
| basic_block bb; |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| unsigned x; |
| edge_iterator ei; |
| edge e; |
| irange range; |
| fprintf (f, "\n=========== BB %d ============\n", bb->index); |
| m_gori.dump_block (f, bb); |
| |
| dump_bb (f, bb, 4, TDF_NONE); |
| |
| // Now find any globals defined in this block |
| for (x = 1; x < num_ssa_names; x++) |
| { |
| tree name = ssa_name (x); |
| if (valid_range_ssa_p (name) && SSA_NAME_DEF_STMT (name) && |
| gimple_bb (SSA_NAME_DEF_STMT (name)) == bb && |
| m_gori.m_globals.get_global_range (range, name)) |
| { |
| if (!range.varying_p ()) |
| { |
| print_generic_expr (f, name, TDF_SLIM); |
| fprintf (f, " : "); |
| range.dump (f); |
| fprintf (f, "\n"); |
| } |
| |
| } |
| } |
| |
| // And now outgoing edges, if they define anything. |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| { |
| for (x = 1; x < num_ssa_names; x++) |
| { |
| tree name = valid_range_ssa_p (ssa_name (x)); |
| if (name && outgoing_edge_range_p (range, e, name)) |
| { |
| gimple *s = SSA_NAME_DEF_STMT (name); |
| // Only print the range if this is the def block, |
| // or the on entry cache for either end of the edge is set. |
| if ((s && bb == gimple_bb (s)) || |
| m_gori.block_range (range, bb, name, false) || |
| m_gori.block_range (range, e->dest, name, false)) |
| { |
| range_on_edge (range, e, name); |
| if (!range.varying_p ()) |
| { |
| fprintf (f, "%d->%d ", e->src->index, |
| e->dest->index); |
| char c = (m_gori.is_export_p (name, bb) ? ' ' : '*'); |
| if (e->flags & EDGE_TRUE_VALUE) |
| fprintf (f, " (T)%c", c); |
| else if (e->flags & EDGE_FALSE_VALUE) |
| fprintf (f, " (F)%c", c); |
| else |
| fprintf (f, " "); |
| print_generic_expr (f, name, TDF_SLIM); |
| fprintf(f, " : \t"); |
| range.dump(f); |
| fprintf (f, "\n"); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| m_gori.m_globals.dump (dump_file); |
| fprintf (f, "\n"); |
| |
| if (dump_flags & TDF_DETAILS) |
| { |
| fprintf (f, "\nDUMPING GORI MAP\n"); |
| m_gori.dump (f); |
| fprintf (f, "\n"); |
| } |
| } |
| |
| // Calculate all ranges by visiting every block and asking for the range of |
| // each ssa_name on each statement, and then dump those ranges to OUTPUT. |
| |
| void |
| global_ranger::calculate_and_dump (FILE *output) |
| { |
| basic_block bb; |
| irange r; |
| |
| // Walk every statement asking for a range. |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); |
| gsi_next (&gpi)) |
| { |
| gphi *phi = gpi.phi (); |
| tree phi_def = gimple_phi_result (phi); |
| if (valid_range_ssa_p (phi_def)) |
| gcc_assert (range_of_stmt (r, phi)); |
| } |
| |
| for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); |
| gsi_next (&gsi)) |
| { |
| gimple *stmt = gsi_stmt (gsi); |
| ssa_op_iter iter; |
| use_operand_p use_p; |
| |
| // Calculate a range for the LHS if there is one. |
| if (valid_range_ssa_p (gimple_get_lhs (stmt))) |
| range_of_stmt (r, stmt); |
| // and make sure to query every operand. |
| FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE) |
| { |
| tree use = valid_range_ssa_p (USE_FROM_PTR (use_p)); |
| if (use) |
| range_of_expr (r, use, stmt); |
| } |
| } |
| } |
| // The dump it. |
| dump (output); |
| fprintf (output, "\n"); |
| } |
| |
| loop_ranger::loop_ranger () |
| { |
| m_vr_values = new vr_values; |
| } |
| |
| loop_ranger::~loop_ranger () |
| { |
| delete m_vr_values; |
| } |
| |
| // Adjust a PHI result with loop information. |
| |
| void |
| loop_ranger::adjust_phi_with_loop_info (irange &r, gphi *phi) |
| { |
| struct loop *l = loop_containing_stmt (phi); |
| if (l && l->header == gimple_bb (phi)) |
| { |
| tree phi_result = PHI_RESULT (phi); |
| value_range_base vr = irange_to_value_range (r); |
| m_vr_values->adjust_range_with_scev (&vr, l, phi, phi_result); |
| if (vr.constant_p ()) |
| { |
| irange old = r; |
| r = vr; |
| if (old != r |
| && dump_file && (dump_flags & TDF_DETAILS)) |
| { |
| fprintf (dump_file, "LOOP_RANGER refined this PHI result: "); |
| print_gimple_stmt (dump_file, phi, 0, TDF_SLIM); |
| fprintf (dump_file, " from this range: "); |
| old.dump (dump_file); |
| fprintf (dump_file, "\n"); |
| fprintf (dump_file, " into this range: "); |
| r.dump (dump_file); |
| fprintf (dump_file, "\n"); |
| } |
| } |
| } |
| } |
| |
| // Virtual override of global_ranger::range_of_phi() that adjusts the |
| // result with loop information if available. |
| |
| bool |
| loop_ranger::range_of_phi (irange &r, gphi *phi, tree name, |
| const irange *name_range) |
| { |
| bool result = global_ranger::range_of_phi (r, phi, name, name_range); |
| if (result && scev_initialized_p ()) |
| adjust_phi_with_loop_info (r, phi); |
| return result; |
| } |
| |
| // Constructor for trace_ranger. |
| |
| trace_ranger::trace_ranger () |
| { |
| indent = 0; |
| trace_count = 0; |
| } |
| |
| // If dumping, return true and print the prefix for the next output line. |
| |
| inline bool |
| trace_ranger::dumping (unsigned counter, bool trailing) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| { |
| // Print counter index as well as INDENT spaces. |
| if (!trailing) |
| fprintf (dump_file, " %-7u ", counter); |
| else |
| fprintf (dump_file, " "); |
| unsigned x; |
| for (x = 0; x< indent; x++) |
| fputc (' ', dump_file); |
| return true; |
| } |
| return false; |
| } |
| |
| // After calling a routine, if dumping, print the CALLER, NAME, and RESULT, |
| // returning RESULT. |
| |
| bool |
| trace_ranger::trailer (unsigned counter, const char *caller, bool result, |
| tree name, const irange &r) |
| { |
| indent -= bump; |
| if (dumping (counter, true)) |
| { |
| fputs(result ? "TRUE : " : "FALSE : ", dump_file); |
| fprintf (dump_file, "(%u) ", counter); |
| fputs (caller, dump_file); |
| fputs (" (",dump_file); |
| if (name) |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fputs (") ",dump_file); |
| if (result) |
| { |
| r.dump (dump_file); |
| fputc('\n', dump_file); |
| } |
| else |
| fputc('\n', dump_file); |
| } |
| // Marks the end of a request. |
| if (indent == 0) |
| fputc('\n', dump_file); |
| return result; |
| } |
| |
| // Tracing version of range_of_expr. Call it with printing wrappers. |
| |
| irange |
| trace_ranger::range_of_ssa_name (tree name, gimple *s) |
| { |
| irange r; |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "range_of_ssa_name ("); |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fprintf (dump_file, ") at stmt "); |
| if (s) |
| print_gimple_stmt (dump_file, s , 0, TDF_SLIM); |
| else |
| fprintf (dump_file, " NULL\n"); |
| indent += bump; |
| } |
| |
| r = super::range_of_ssa_name (name, s); |
| |
| trailer (idx, "range_of_ssa_name", true, name, r); |
| return r; |
| } |
| |
| // Tracing version of range_on_edge. Call it with printing wrappers. |
| |
| void |
| trace_ranger::range_on_edge (irange &r, edge e, tree name) |
| { |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "range_on_edge ("); |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fprintf (dump_file, ") on edge %d->%d\n", e->src->index, e->dest->index); |
| indent += bump; |
| } |
| |
| super::range_on_edge (r, e, name); |
| |
| trailer (idx, "range_on_edge", true, name, r); |
| } |
| |
| // Tracing version of range_on_entry. Call it with printing wrappers. |
| |
| void |
| trace_ranger::range_on_entry (irange &r, basic_block bb, tree name) |
| { |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "range_on_entry ("); |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fprintf (dump_file, ") to BB %d\n", bb->index); |
| indent += bump; |
| } |
| |
| super::range_on_entry (r, bb, name); |
| |
| trailer (idx, "range_on_entry", true, name, r); |
| } |
| |
| // Tracing version of range_on_exit. Call it with printing wrappers. |
| |
| void |
| trace_ranger::range_on_exit (irange &r, basic_block bb, tree name) |
| { |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "range_on_exit ("); |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fprintf (dump_file, ") from BB %d\n", bb->index); |
| indent += bump; |
| } |
| |
| super::range_on_exit (r, bb, name); |
| |
| trailer (idx, "range_on_exit", true, name, r); |
| } |
| |
| // Tracing version of range_of_stmt. Call it with printing wrappers. |
| |
| bool |
| trace_ranger::range_of_stmt (irange &r, gimple *s, tree name) |
| { |
| bool res; |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "range_of_stmt ("); |
| if (name) |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fputs (") at stmt ", dump_file); |
| print_gimple_stmt (dump_file, s, 0, TDF_SLIM); |
| indent += bump; |
| } |
| |
| res = super::range_of_stmt (r, s, name); |
| |
| return trailer (idx, "range_of_stmt", res, name, r); |
| } |
| |
| // Tracing version of outgoing_edge_range_p. Call it with printing wrappers. |
| |
| bool |
| trace_ranger::outgoing_edge_range_p (irange &r, edge e, tree name, |
| irange *name_range) |
| { |
| bool res; |
| unsigned idx = ++trace_count; |
| if (dumping (idx)) |
| { |
| fprintf (dump_file, "outgoing_edge_range_p ("); |
| print_generic_expr (dump_file, name, TDF_SLIM); |
| fprintf (dump_file, ") on edge %d->%d, with range ", e->src->index, |
| e->dest->index); |
| if (name_range) |
| { |
| name_range->dump (dump_file); |
| fprintf (dump_file, "\n"); |
| } |
| else |
| fputs ("NULL\n", dump_file); |
| indent += bump; |
| } |
| |
| res = super::outgoing_edge_range_p (r, e, name, name_range); |
| |
| return trailer (idx, "outgoing_edge_range_p", res, name, r); |
| } |