This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Simple Value Range Propagation
- From: Josef Zlomek <zlomj9am at artax dot karlin dot mff dot cuni dot cz>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sun, 8 Jun 2003 22:20:33 +0200
- Subject: Simple Value Range Propagation
Hi,
this patch contains a simple Value Range Propagation.
It tracks const -> reg and reg -> reg stores,
and also branches, for example:
if (i > 0)
foo; // i is in range [1,max_val] here
else
bar; // i is in range [min_val,0] here
It also deletes (more accuratelly let cleaup_cfg delete)
code unreachable because of conditions, for example:
if (i < 0)
{
if (i > 5)
delete_this;
}
It takes 2% of compile time and adds 0.5 % overall performance on SPECint2000.
Bootstrapped/regtested x86-64.
OK for mainline?
Josef
2003-06-08 Josef Zlomek <zlomekj@suse.cz>
* vrp.c: New file.
* Makefile.in (vrp.o): Added.
* toplev.c (enum dump_file_index): Added DFI_vrp.
(struct dump_file_info): Added vrp.
(flag_value_range_propagation): New flag.
(const lang_independent_options): New flag.
(rest_of_compilation): Run value range propagation.
(parse_options_and_default_flags): Run VRP at -O1.
* timevar.def (TV_VALUE_RANGE): Added.
* rtl.h (value_range_propagation): New extern function.
* doc/invoke.texi: Added -dV and -fvalue-range-propagation.
* doc/passes.texi: Added value range propagation pass.
Index: vrp.c
===================================================================
RCS file: vrp.c
diff -N vrp.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- vrp.c 8 Jun 2003 11:40:43 -0000
***************
*** 0 ****
--- 1,1632 ----
+ /* Value range propagation and dead branch elimination.
+ Copyright (C) 2003 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 2, 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 COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ /* This file performs simple value range propagation.
+ It also removes branches which can't be reached,
+ all code from the following example may be removed:
+
+ if (i < 0)
+ {
+ if (i > 5)
+ printf ("foo");
+ }
+
+ Currently it only tracks integer registers.
+
+ This pass performs a data-flow on value ranges.
+ As usual it has IN and OUT sets for each basic block. The set is a hash
+ table, for each register there is a range which the value of the register
+ can be in. When the range is full (i.e. from min value for the type
+ to the max value) it is not stored in the hash table to save memory
+ (so when the range for register is not there the range is full).
+ Moreover VRP has a set for each edge too because the value ranges are
+ different when we go through a then edge or an else edge.
+
+ The IN set for a BB is computed as "union bounds" of the sets on edges
+ going to BB. "Union bound" means that when we have 2 intervals
+ [from_1, to_1] and [from_2, to_2] the result is
+ [MIN (from_1, from_2), MAX (to_1, to_2)].
+
+ We are producing the OUT set of BB from IN set by scanning the instructions
+ in BB. When we find an insn assigning a CONST_INT or REG to REG
+ we set the range according to SET_SRC (only simple cases of SET_DST and
+ SET_SRC are handled, in the other cases we set the range to be full).
+
+ Finally we update the sets on edges going from BB. First we copy the OUT set
+ to edges going out from BB. Then we update the ranges on edges according to
+ a cond jump (if cond jump is in the end of BB).
+
+ When we have computed the ranges we redirect "dead" edge for a cond jump
+ to the other edge. "Dead" edge is an edge which has an empty range for some
+ register on it, i.e. we will never go through this edge because of the
+ conditions. */
+
+ #include "config.h"
+ #include "system.h"
+ #include "coretypes.h"
+ #include "tm.h"
+ #include "rtl.h"
+ #include "tree.h"
+ #include "basic-block.h"
+ #include "hard-reg-set.h"
+ #include "expr.h"
+ #include "output.h"
+ #include "alloc-pool.h"
+ #include "sbitmap.h"
+ #include "fibheap.h"
+ #include "hashtab.h"
+
+ /* Signed or unsigned integer. */
+ typedef struct value_def
+ {
+ /* Signed integer value. */
+ signed HOST_WIDE_INT si;
+
+ /* Unsigned integer value. */
+ unsigned HOST_WIDE_INT ui;
+ } value;
+
+ /* Structure describing a range. */
+ typedef struct range_def
+ {
+ /* Register which contains a value from the range. */
+ rtx reg;
+
+ /* Lower bound of the range. */
+ value from;
+
+ /* Upper bound of the range. */
+ value to;
+
+ /* Type of the range, see RANGE_* below. */
+ int type;
+ } range_t;
+
+ /* Range types. */
+ #define RANGE_SIGNED_INT 1 /* Signed integer. */
+ #define RANGE_UNSIGNED_INT 2 /* Unsigned integer. */
+
+ /* Data for each edge. */
+ typedef struct edge_info_def
+ {
+ /* The list of ranges of registers. */
+ htab_t htab;
+ } edge_info;
+
+ /* Data for each basic block. */
+ typedef struct bb_info_def
+ {
+ /* Normalized condition (if any) for the branch in the end of BB. */
+ rtx cond;
+
+ /* The IN list of ranges of registers for BB. */
+ htab_t in;
+
+ /* The OUT list of ranges of registers for BB. */
+ htab_t out;
+ } bb_info;
+
+ /* Get a pointer to data for a basic block BB. */
+ #define BBI(BB) ((bb_info *) (BB)->aux)
+
+ /* Get a pointer to data for an edge E. */
+ #define EI(E) ((edge_info *) (E)->aux)
+
+ /* Alloc pool for range_t. */
+ alloc_pool range_pool;
+
+ /* Local function prototypes. */
+ static hashval_t range_hash (const void *);
+ static int range_eq (const void *, const void *);
+ static void range_del (void *);
+ static bool range_is_empty (const range_t *);
+ static int union_ranges (void **, void *);
+ static void union_all_ranges (htab_t, htab_t);
+ static bool ranges_differ (const range_t *, const range_t *);
+ static int compare_register_tables_1 (void **, void *);
+ static bool compare_register_tables (htab_t, htab_t);
+ static int copy_range (void **, void *);
+ static void copy_register_table (htab_t, htab_t);
+ static void process_ranges_eq (rtx, rtx, edge);
+ static void process_ranges_lt (rtx, rtx, edge, edge);
+ static void process_ranges_ltu (rtx, rtx, edge, edge);
+ static bool update_outgoing_edges (basic_block, bool);
+ static void compute_ranges_for_insn (rtx, rtx, void *);
+ static int delete_call_used_regs (void **, void *);
+ static bool compute_ranges_for_bb (basic_block);
+ static void compute_ranges_for_pending (basic_block, sbitmap, sbitmap);
+ static void compute_ranges (int *);
+ static bool edge_is_dead (edge);
+ static bool redirect_edges (void);
+ static int dump_range (void **, void *);
+ static void dump_all_ranges (FILE *, htab_t);
+ void debug_range (range_t *);
+ void debug_all_ranges (htab_t);
+
+ /* Hash function for register R. */
+ #define HASH_REG(R) (REGNO (R))
+
+ /* Hash function for table of ranges. It is computed from the range XX. */
+
+ static hashval_t
+ range_hash (const void *xx)
+ {
+ const range_t *r = xx;
+
+ return HASH_REG (r->reg);
+ }
+
+ /* Return true when the range XX is the range for register YY. */
+
+ static int
+ range_eq (const void *xx, const void *yy)
+ {
+ const range_t *r = (const range_t *) xx;
+ const rtx y = (const rtx) yy;
+
+ return REGNO (r->reg) == REGNO (y);
+ }
+
+ /* Free the range X. */
+
+ static void
+ range_del (void *x)
+ {
+ pool_free (range_pool, x);
+ }
+
+ /* Return true when the range is empty, i.e. register can't have any value. */
+
+ static bool
+ range_is_empty (const range_t *r)
+ {
+ switch (r->type)
+ {
+ case RANGE_SIGNED_INT:
+ case RANGE_SIGNED_INT | RANGE_UNSIGNED_INT:
+ return (r->from.si > r->to.si);
+
+ case RANGE_UNSIGNED_INT:
+ return (r->from.ui > r->to.ui);
+ }
+
+ return false;
+ }
+
+ /* Data passed from union_all_ranges to union_ranges. */
+
+ typedef struct union_ranges_data_def
+ {
+ /* The list of ranges from one and second edge. */
+ htab_t t1;
+ htab_t t2;
+ } union_ranges_data;
+
+ /* Unify range from *SLOT and the corresponding range from DATA.T2. */
+
+ static int
+ union_ranges (void **slot, void *data)
+ {
+ union_ranges_data *d = data;
+ htab_t t1 = d->t1;
+ htab_t t2 = d->t2;
+ range_t *r1 = *slot;
+ range_t *r2;
+
+ r2 = htab_find_with_hash (t2, r1->reg, HASH_REG (r1->reg));
+ if (!r2)
+ {
+ /* Register from second edge may have any value so the result may have
+ also any value. */
+ htab_clear_slot (t1, slot);
+ return 1;
+ }
+
+ if (range_is_empty (r1))
+ {
+ /* First range is empty so the union is the second range. */
+ *r1 = *r2;
+ return 1;
+ }
+
+ /* Second range is empty so the union is already in the first range. */
+ if (range_is_empty (r2))
+ return 1;
+
+ r1->type &= r2->type;
+ if (r1->type == 0)
+ {
+ /* The ranges are not compatible so make the union to be a full range
+ (i.e. delete it from the table). */
+ htab_clear_slot (t1, slot);
+ return 1;
+ }
+
+ /* For compatible ranges make the bounds to contain both intervals. */
+ if ((r1->type & RANGE_SIGNED_INT) != 0)
+ {
+ if (r1->from.si > r2->from.si)
+ r1->from.si = r2->from.si;
+ if (r1->to.si < r2->to.si)
+ r1->to.si = r2->to.si;
+ }
+
+ if ((r1->type & RANGE_UNSIGNED_INT) != 0)
+ {
+ if (r1->from.ui > r2->from.ui)
+ r1->from.ui = r2->from.ui;
+ if (r1->to.ui < r2->to.ui)
+ r1->to.ui = r2->to.ui;
+ }
+
+ return 1;
+ }
+
+ /* Unify all ranges in table T1 with the corresponding ranges from T2. */
+
+ static void
+ union_all_ranges (htab_t t1, htab_t t2)
+ {
+ union_ranges_data data;
+
+ data.t1 = t1;
+ data.t2 = t2;
+ htab_traverse (t1, union_ranges, &data);
+ }
+
+ /* Return true if ranges R1 and R2 differ. */
+
+ static bool
+ ranges_differ (const range_t *r1, const range_t *r2)
+ {
+ bool empty1;
+ bool empty2;
+
+ /* R1 and R2 do not have a common type. */
+ if ((r1->type & r2->type) == 0)
+ return true;
+
+ if (!rtx_equal_p (r1->reg, r2->reg))
+ return true;
+
+ empty1 = range_is_empty (r1);
+ empty2 = range_is_empty (r2);
+
+ if (empty1 != empty2)
+ return true;
+
+ if (empty1 && empty2)
+ return false;
+
+ if ((r1->type & r2->type & RANGE_SIGNED_INT) != 0)
+ {
+ if (r1->from.si != r2->from.si)
+ return true;
+
+ if (r1->to.si != r2->to.si)
+ return true;
+ }
+
+ if ((r1->type & r2->type & RANGE_UNSIGNED_INT) != 0)
+ {
+ if (r1->from.ui != r2->from.ui)
+ return true;
+
+ if (r1->to.ui != r2->to.ui)
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Variable to hold the temporary result of compare_register_tables. */
+ static bool compare_register_tables_value;
+
+ /* Compare the range *SLOT with the corresponding range in table DATA. */
+
+ static int
+ compare_register_tables_1 (void **slot, void *data)
+ {
+ range_t *r1 = *slot;
+ range_t *r2;
+ htab_t htab = data;
+
+ r2 = htab_find_with_hash (htab, r1->reg, HASH_REG (r1->reg));
+
+ if (r2 == NULL || ranges_differ (r1, r2))
+ {
+ compare_register_tables_value = true;
+
+ /* Stop traversing. */
+ return 0;
+ }
+
+ /* Continue traversing. */
+ return 1;
+ }
+
+ /* Return true when the tables T1 and T2 of ranges differ. */
+
+ static bool
+ compare_register_tables (htab_t t1, htab_t t2)
+ {
+ compare_register_tables_value = false;
+
+ htab_traverse (t1, compare_register_tables_1, t2);
+ if (!compare_register_tables_value)
+ htab_traverse (t2, compare_register_tables_1, t1);
+
+ return compare_register_tables_value;
+ }
+
+ /* Copy range *SRC_SLOT to table DATA. */
+
+ static int
+ copy_range (void **src_slot, void *data)
+ {
+ range_t *src_r = *src_slot;
+ range_t *dst_r;
+ htab_t dst = data;
+ void **dst_slot;
+
+ dst_r = pool_alloc (range_pool);
+ *dst_r = *src_r;
+
+ dst_slot = htab_find_slot_with_hash (dst, src_r->reg, HASH_REG (src_r->reg),
+ INSERT);
+ *dst_slot = dst_r;
+
+ /* Continue traversing the hash table. */
+ return 1;
+ }
+
+ /* Copy the content of table SRC to DST. */
+
+ static void
+ copy_register_table (htab_t dst, htab_t src)
+ {
+ htab_empty (dst);
+ htab_traverse (src, copy_range, dst);
+ }
+
+ /* Update the ranges for OP0 and OP1 on edge E according to
+ result of OP0 EQ OP1. */
+
+ static void
+ process_ranges_eq (rtx op0, rtx op1, edge e)
+ {
+ void **slot0;
+ void **slot1;
+ range_t *r0 = NULL; /* Range for OP0. */
+ range_t *r1 = NULL; /* Range for OP1. */
+ range_t tmp;
+
+ slot0 = htab_find_slot_with_hash (EI (e)->htab, op0, HASH_REG (op0),
+ NO_INSERT);
+ if (slot0)
+ r0 = *slot0;
+
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ unsigned HOST_WIDE_INT mask
+ = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
+
+ /* OP1 is a constant, create a temporary range for it. */
+ r1 = &tmp;
+ tmp.reg = NULL;
+ tmp.from.si = INTVAL (op1);
+ tmp.to.si = INTVAL (op1);
+ tmp.from.ui = (unsigned HOST_WIDE_INT) INTVAL (op1) & mask;
+ tmp.to.ui = (unsigned HOST_WIDE_INT) INTVAL (op1) & mask;
+ if (r0 != NULL)
+ tmp.type = r0->type;
+ else
+ tmp.type = RANGE_SIGNED_INT | RANGE_UNSIGNED_INT;
+ }
+ else if (GET_CODE (op1) == REG
+ && GET_MODE (op0) == GET_MODE (op1))
+ {
+ slot1 = htab_find_slot_with_hash (EI (e)->htab, op1, HASH_REG (op1),
+ NO_INSERT);
+ if (slot1)
+ r1 = *slot1;
+ }
+ else
+ return;
+
+ /* If both ranges are full ranges we have nothing to do. */
+ if (!r0 && !r1)
+ return;
+
+ if (r0)
+ {
+ if (r1)
+ {
+ switch (r0->type & r1->type)
+ {
+ case 0:
+ /* If the ranges are not compatible leave them as they are. */
+ break;
+
+ case RANGE_SIGNED_INT | RANGE_UNSIGNED_INT:
+ /* When both types are (RANGE_SIGNED_INT | RANGE_UNSIGNED_INT)
+ both ranges are constants or a result of union of 2 constants
+ and thus the ranges are not empty. */
+
+ /* If both ranges are constants compare them,
+ otherwise leave ranges as they are because we do not know
+ how to compare them. */
+ if (r0->from.si == r0->to.si && r1->from.si == r1->to.si)
+ {
+ if (r0->from.si != r1->from.si)
+ {
+ /* Make the ranges empty. */
+ r0->from.si = 1;
+ r0->to.si = 0;
+ r0->from.ui = 1;
+ r0->to.ui = 0;
+ r1->from.si = 1;
+ r1->to.si = 0;
+ r1->from.ui = 1;
+ r1->to.ui = 0;
+ }
+ }
+
+ break;
+
+ case RANGE_SIGNED_INT:
+ r0->type = r1->type = r0->type & r1->type;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0))
+ {
+ r1->from.si = 1;
+ r1->to.si = 0;
+ return;
+ }
+
+ if (range_is_empty (r1))
+ {
+ r0->from.si = 1;
+ r0->to.si = 0;
+ return;
+ }
+
+ /* If OP1 EQ OP2 set both ranges to the intersection of them. */
+ if (r0->from.si < r1->from.si)
+ r0->from.si = r1->from.si;
+ else
+ r1->from.si = r0->from.si;
+
+ if (r0->to.si < r1->to.si)
+ r1->to.si = r0->to.si;
+ else
+ r0->to.si = r1->to.si;
+
+ break;
+
+ case RANGE_UNSIGNED_INT:
+ r0->type = r1->type = r0->type & r1->type;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0))
+ {
+ r1->from.ui = 1;
+ r1->to.ui = 0;
+ return;
+ }
+
+ if (range_is_empty (r1))
+ {
+ r0->from.ui = 1;
+ r0->to.ui = 0;
+ return;
+ }
+
+ /* If OP1 EQ OP2 set both ranges to the intersection of them. */
+ if (r0->from.ui < r1->from.ui)
+ r0->from.ui = r1->from.ui;
+ else
+ r1->from.ui = r0->from.ui;
+
+ if (r0->to.ui < r1->to.ui)
+ r1->to.ui = r0->to.ui;
+ else
+ r0->to.ui = r1->to.ui;
+
+ break;
+ }
+ }
+ else
+ {
+ /* R1 is a full range, intersection with R0 is R0. */
+ r1 = pool_alloc (range_pool);
+ slot1 = htab_find_slot_with_hash (EI (e)->htab, op1, HASH_REG (op1),
+ INSERT);
+ *slot1 = r1;
+ *r1 = *r0;
+ r1->reg = op1;
+ }
+ }
+ else if (r1)
+ {
+ /* R0 is a full range, intersection with R1 is R1. */
+ r0 = pool_alloc (range_pool);
+ slot0 = htab_find_slot_with_hash (EI (e)->htab, op0, HASH_REG (op0),
+ INSERT);
+ *slot0 = r0;
+ *r0 = *r1;
+ r0->reg = op0;
+ }
+ }
+
+ /* Update the ranges for OP0 and OP1 on edges THEN_EDGE and ELSE_EDGE according
+ to result of OP0 LT OP1. */
+
+ static void
+ process_ranges_lt (rtx op0, rtx op1, edge then_edge, edge else_edge)
+ {
+ void **slot0t;
+ void **slot0e;
+ void **slot1t;
+ void **slot1e;
+ range_t *r0t = NULL; /* Range for OP0 on THEN_EDGE. */
+ range_t *r0e = NULL; /* Range for OP0 on ELSE_EDGE. */
+ range_t *r1t = NULL; /* Range for OP1 on THEN_EDGE. */
+ range_t *r1e = NULL; /* Range for OP1 on ELSE_EDGE. */
+ range_t tmp0, tmp1;
+ HOST_WIDE_INT max_val = 0; /* Set it to something to avoid a warning. */
+ HOST_WIDE_INT min_val = 0;
+
+ /* If the registers are not compatible leave the ranges as they are. */
+ if (GET_CODE (op0) == REG && GET_CODE (op1) == REG
+ && GET_MODE (op0) != GET_MODE (op1))
+ return;
+
+ if (GET_CODE (op0) == REG)
+ {
+ max_val = (HOST_WIDE_INT) ((unsigned HOST_WIDE_INT)
+ GET_MODE_MASK (GET_MODE (op0)) >> 1);
+ min_val = -max_val - 1;
+
+ /* Find the range for OP0 on THEN_EDGE and ELSE_EDGE. */
+ slot0t = htab_find_slot_with_hash (EI (then_edge)->htab, op0,
+ HASH_REG (op0), NO_INSERT);
+ if (slot0t)
+ r0t = *slot0t;
+ slot0e = htab_find_slot_with_hash (EI (else_edge)->htab, op0,
+ HASH_REG (op0), NO_INSERT);
+ if (slot0e)
+ r0e = *slot0e;
+ }
+ if (GET_CODE (op1) == REG)
+ {
+ max_val = (HOST_WIDE_INT) ((unsigned HOST_WIDE_INT)
+ GET_MODE_MASK (GET_MODE (op1)) >> 1);
+ min_val = -max_val - 1;
+
+ /* Find the range for OP0 on THEN_EDGE and ELSE_EDGE. */
+ slot1t = htab_find_slot_with_hash (EI (then_edge)->htab, op1,
+ HASH_REG (op1), NO_INSERT);
+ if (slot1t)
+ r1t = *slot1t;
+ slot1e = htab_find_slot_with_hash (EI (else_edge)->htab, op1,
+ HASH_REG (op1), NO_INSERT);
+ if (slot1e)
+ r1e = *slot1e;
+ }
+
+ if (GET_CODE (op0) == CONST_INT)
+ {
+ /* OP0 is a constant, create a temporary range for it. */
+ r0t = r0e = &tmp0;
+ tmp0.reg = NULL;
+ tmp0.from.si = INTVAL (op0);
+ tmp0.to.si = INTVAL (op0);
+ tmp0.type = RANGE_SIGNED_INT;
+ }
+ else if (GET_CODE (op0) != REG)
+ return;
+
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ /* OP1 is a constant, create a temporary range for it. */
+ r1t = r1e = &tmp1;
+ tmp1.reg = NULL;
+ tmp1.from.si = INTVAL (op1);
+ tmp1.to.si = INTVAL (op1);
+ tmp1.type = RANGE_SIGNED_INT;
+ }
+ else if (GET_CODE (op1) != REG)
+ return;
+
+ /* If both ranges on TRUE_EDGE are full (and thus on ELSE_EDGE too)
+ we have nothing to do. */
+ if (!r0t && !r1t)
+ return;
+
+ /* If the type of one range is not signed int we do not know how
+ to shorten ranges so we leave them as they are. */
+ if (r0t && (r0t->type & RANGE_SIGNED_INT) == 0)
+ return;
+
+ if (r1t && (r1t->type & RANGE_SIGNED_INT) == 0)
+ return;
+
+ if (r0t)
+ {
+ if (r1t)
+ {
+ r0t->type = r0e->type = RANGE_SIGNED_INT;
+ r1t->type = r1e->type = RANGE_SIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0t))
+ {
+ r1t->from.si = 1;
+ r1t->to.si = 0;
+ r1e->from.si = 1;
+ r1e->to.si = 0;
+ return;
+ }
+
+ if (range_is_empty (r1t))
+ {
+ r0t->from.si = 1;
+ r0t->to.si = 0;
+ r0e->from.si = 1;
+ r0e->to.si = 0;
+ return;
+ }
+
+ if (r0t->from.si == max_val)
+ {
+ /* The condition OP0 LT OP1 may be never true. */
+ r0t->from.si = 1;
+ r0t->to.si = 0;
+ r1t->from.si = 1;
+ r1t->to.si = 0;
+ }
+ else if (r0t->from.si >= r1t->from.si)
+ r1t->from.si = r0t->from.si + 1;
+ else
+ r0e->from.si = r1e->from.si;
+
+ if (r1t->to.si == min_val)
+ {
+ /* The condition OP0 LT OP1 may be never true. */
+ r0t->from.si = 1;
+ r0t->to.si = 0;
+ r1t->from.si = 1;
+ r1t->to.si = 0;
+ }
+ else if (r0t->to.si >= r1t->to.si)
+ r0t->to.si = r1t->to.si - 1;
+ else
+ r1e->to.si = r0e->to.si;
+ }
+ else
+ {
+ /* R1 is full interval, shorten it on THEN_EDGE and ELSE_EDGE. */
+ r1t = pool_alloc (range_pool);
+ slot1t = htab_find_slot_with_hash (EI (then_edge)->htab, op1,
+ HASH_REG (op1), INSERT);
+ *slot1t = r1t;
+ r1e = pool_alloc (range_pool);
+ slot1e = htab_find_slot_with_hash (EI (else_edge)->htab, op1,
+ HASH_REG (op1), INSERT);
+ *slot1e = r1e;
+
+ r1t->reg = r1e->reg = op1;
+ r1t->type = r1e->type = RANGE_SIGNED_INT;
+ r0t->type = r0e->type = RANGE_SIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0t))
+ {
+ r1t->from.si = 1;
+ r1t->to.si = 0;
+ r1e->from.si = 1;
+ r1e->to.si = 0;
+ return;
+ }
+
+ if (r0t->from.si == max_val)
+ {
+ r1t->from.si = 1;
+ r1t->to.si = 0;
+ }
+ else
+ {
+ r1t->from.si = r0t->from.si + 1;
+ r1t->to.si = max_val;
+ }
+
+ r1e->from.si = min_val;
+ r1e->to.si = r0e->to.si;
+ }
+ }
+ else if (r1t)
+ {
+ /* R0 is full interval, shorten it on THEN_EDGE and ELSE_EDGE. */
+ r0t = pool_alloc (range_pool);
+ slot0t = htab_find_slot_with_hash (EI (then_edge)->htab, op0,
+ HASH_REG (op0), INSERT);
+ *slot0t = r0t;
+ r0e = pool_alloc (range_pool);
+ slot0e = htab_find_slot_with_hash (EI (else_edge)->htab, op0,
+ HASH_REG (op0), INSERT);
+ *slot0e = r0e;
+
+ r0t->reg = r0e->reg = op0;
+ r0t->type = r0e->type = RANGE_SIGNED_INT;
+ r1t->type = r1e->type = RANGE_SIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r1t))
+ {
+ r0t->from.si = 1;
+ r0t->to.si = 0;
+ r0e->from.si = 1;
+ r0e->to.si = 0;
+ return;
+ }
+
+ if (r1t->to.si == min_val)
+ {
+ r0t->from.si = 1;
+ r0t->to.si = 0;
+ }
+ else
+ {
+ r0t->from.si = min_val;
+ r0t->to.si = r1t->to.si - 1;
+ }
+
+ r0e->from.si = r1e->from.si;
+ r0e->to.si = max_val;
+ }
+ }
+
+ /* Update the ranges for OP0 and OP1 on edges THEN_EDGE and ELSE_EDGE according
+ to result of OP0 LTU OP1.
+ This function is very similar to process_ranges_lt but it is working
+ with unsigned intervals. */
+
+ static void
+ process_ranges_ltu (rtx op0, rtx op1, edge then_edge, edge else_edge)
+ {
+ void **slot0t;
+ void **slot0e;
+ void **slot1t;
+ void **slot1e;
+ range_t *r0t = NULL; /* Range for OP0 on THEN_EDGE. */
+ range_t *r0e = NULL; /* Range for OP0 on ELSE_EDGE. */
+ range_t *r1t = NULL; /* Range for OP1 on THEN_EDGE. */
+ range_t *r1e = NULL; /* Range for OP1 on ELSE_EDGE. */
+ range_t tmp0, tmp1;
+ unsigned HOST_WIDE_INT max_val = 0;
+ unsigned HOST_WIDE_INT min_val = 0;
+
+ /* If the registers are not compatible leave the ranges as they are. */
+ if (GET_CODE (op0) == REG && GET_CODE (op1) == REG
+ && GET_MODE (op0) != GET_MODE (op1))
+ return;
+
+ if (GET_CODE (op0) == REG)
+ {
+ max_val = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
+
+ /* Find the range for OP0 on THEN_EDGE and ELSE_EDGE. */
+ slot0t = htab_find_slot_with_hash (EI (then_edge)->htab, op0,
+ HASH_REG (op0), NO_INSERT);
+ if (slot0t)
+ r0t = *slot0t;
+ slot0e = htab_find_slot_with_hash (EI (else_edge)->htab, op0,
+ HASH_REG (op0), NO_INSERT);
+ if (slot0e)
+ r0e = *slot0e;
+ }
+ if (GET_CODE (op1) == REG)
+ {
+ max_val = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (op0));
+
+ /* Find the range for OP0 on THEN_EDGE and ELSE_EDGE. */
+ slot1t = htab_find_slot_with_hash (EI (then_edge)->htab, op1,
+ HASH_REG (op1), NO_INSERT);
+ if (slot1t)
+ r1t = *slot1t;
+ slot1e = htab_find_slot_with_hash (EI (else_edge)->htab, op1,
+ HASH_REG (op1), NO_INSERT);
+ if (slot1e)
+ r1e = *slot1e;
+ }
+
+ if (GET_CODE (op0) == CONST_INT)
+ {
+ /* OP0 is a constant, create a temporary range for it. */
+ r0t = r0e = &tmp0;
+ tmp0.reg = NULL;
+ tmp0.from.ui = (unsigned HOST_WIDE_INT) INTVAL (op0) & max_val;
+ tmp0.to.ui = (unsigned HOST_WIDE_INT) INTVAL (op0) & max_val;
+ tmp0.type = RANGE_UNSIGNED_INT;
+ }
+ else if (GET_CODE (op0) != REG)
+ return;
+
+ if (GET_CODE (op1) == CONST_INT)
+ {
+ /* OP1 is a constant, create a temporary range for it. */
+ r1t = r1e = &tmp1;
+ tmp1.reg = NULL;
+ tmp1.from.ui = (unsigned HOST_WIDE_INT) INTVAL (op1) & max_val;
+ tmp1.to.ui = (unsigned HOST_WIDE_INT) INTVAL (op1) & max_val;
+ tmp1.type = RANGE_UNSIGNED_INT;
+ }
+ else if (GET_CODE (op0) != REG)
+ return;
+
+ /* If both ranges on TRUE_EDGE are full (and thus on ELSE_EDGE too)
+ we have nothing to do. */
+ if (!r0t && !r1t)
+ return;
+
+ /* If the type of one range is not unsigned int we do not know how
+ to shorten ranges so we leave them as they are. */
+ if (r0t && (r0t->type & RANGE_UNSIGNED_INT) == 0)
+ return;
+
+ if (r1t && (r1t->type & RANGE_UNSIGNED_INT) == 0)
+ return;
+
+ if (r0t)
+ {
+ if (r1t)
+ {
+ r0t->type = r0e->type = RANGE_UNSIGNED_INT;
+ r1t->type = r1e->type = RANGE_UNSIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0t))
+ {
+ r1t->from.ui = 1;
+ r1t->to.ui = 0;
+ r1e->from.ui = 1;
+ r1e->to.ui = 0;
+ return;
+ }
+
+ if (range_is_empty (r1t))
+ {
+ r0t->from.ui = 1;
+ r0t->to.ui = 0;
+ r0e->from.ui = 1;
+ r0e->to.ui = 0;
+ return;
+ }
+
+ if (r0t->from.ui == max_val)
+ {
+ /* The condition OP0 LT OP1 may be never true. */
+ r0t->from.ui = 1;
+ r0t->to.ui = 0;
+ r1t->from.ui = 1;
+ r1t->to.ui = 0;
+ }
+ else if (r0t->from.ui >= r1t->from.ui)
+ r1t->from.ui = r0t->from.ui + 1;
+ else
+ r0e->from.ui = r1e->from.ui;
+
+ if (r1t->to.ui == min_val)
+ {
+ /* The condition OP0 LT OP1 may be never true. */
+ r0t->from.ui = 1;
+ r0t->to.ui = 0;
+ r1t->from.ui = 1;
+ r1t->to.ui = 0;
+ }
+ else if (r0t->to.ui >= r1t->to.ui)
+ r0t->to.ui = r1t->to.ui - 1;
+ else
+ r1e->to.ui = r0e->to.ui;
+ }
+ else
+ {
+ /* R1 is full interval, shorten it on THEN_EDGE and ELSE_EDGE. */
+ r1t = pool_alloc (range_pool);
+ slot1t = htab_find_slot_with_hash (EI (then_edge)->htab, op1,
+ HASH_REG (op1), INSERT);
+ *slot1t = r1t;
+ r1e = pool_alloc (range_pool);
+ slot1e = htab_find_slot_with_hash (EI (else_edge)->htab, op1,
+ HASH_REG (op1), INSERT);
+ *slot1e = r1e;
+
+ r1t->reg = r1e->reg = op1;
+ r1t->type = r1e->type = RANGE_UNSIGNED_INT;
+ r0t->type = r0e->type = RANGE_UNSIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r0t))
+ {
+ r1t->from.ui = 1;
+ r1t->to.ui = 0;
+ r1e->from.ui = 1;
+ r1e->to.ui = 0;
+ return;
+ }
+
+ if (r0t->from.ui == max_val)
+ {
+ r1t->from.ui = 1;
+ r1t->to.ui = 0;
+ }
+ else
+ {
+ r1t->from.ui = r0t->from.ui + 1;
+ r1t->to.ui = max_val;
+ }
+
+ r1e->from.ui = min_val;
+ r1e->to.ui = r0e->to.ui;
+ }
+ }
+ else if (r1t)
+ {
+ /* R0 is full interval, shorten it on THEN_EDGE and ELSE_EDGE. */
+ r0t = pool_alloc (range_pool);
+ slot0t = htab_find_slot_with_hash (EI (then_edge)->htab, op0,
+ HASH_REG (op0), INSERT);
+ *slot0t = r0t;
+ r0e = pool_alloc (range_pool);
+ slot0e = htab_find_slot_with_hash (EI (else_edge)->htab, op0,
+ HASH_REG (op0), INSERT);
+ *slot0e = r0e;
+
+ r0t->reg = r0e->reg = op0;
+ r0t->type = r0e->type = RANGE_UNSIGNED_INT;
+ r1t->type = r1e->type = RANGE_UNSIGNED_INT;
+
+ /* If one range is empty make the other empty too. */
+ if (range_is_empty (r1t))
+ {
+ r0t->from.ui = 1;
+ r0t->to.ui = 0;
+ r0e->from.ui = 1;
+ r0e->to.ui = 0;
+ return;
+ }
+
+ if (r1t->to.ui == min_val)
+ {
+ r0t->from.ui = 1;
+ r0t->to.ui = 0;
+ }
+ else
+ {
+ r0t->from.ui = min_val;
+ r0t->to.ui = r1t->to.ui - 1;
+ }
+
+ r0e->from.ui = r1e->from.ui;
+ r0e->to.ui = max_val;
+ }
+ }
+
+ /* Update the ranges on the edges going out from BB. */
+
+ static bool
+ update_outgoing_edges (basic_block bb, bool changed)
+ {
+ edge e;
+ htab_t old_then = NULL; /* Set it to something to avoid a warning. */
+ htab_t old_else = NULL;
+
+ if (!changed && BBI (bb)->cond != NULL)
+ {
+ edge then_edge = BRANCH_EDGE (bb);
+ edge else_edge = FALLTHRU_EDGE (bb);
+
+ old_then = htab_create (EI (then_edge)->htab->n_elements, range_hash,
+ range_eq, range_del);
+ copy_register_table (old_then, EI (then_edge)->htab);
+ old_else = htab_create (EI (else_edge)->htab->n_elements, range_hash,
+ range_eq, range_del);
+ copy_register_table (old_else, EI (else_edge)->htab);
+ }
+
+ /* Copy the BB's OUT set to edges. */
+ for (e = bb->succ; e; e = e->succ_next)
+ copy_register_table (EI (e)->htab, BBI (bb)->out);
+
+ /* Update outgoing edges. */
+ if (BBI (bb)->cond != NULL)
+ {
+ edge then_edge = BRANCH_EDGE (bb);
+ edge else_edge = FALLTHRU_EDGE (bb);
+ rtx op0 = XEXP (BBI (bb)->cond, 0);
+ rtx op1 = XEXP (BBI (bb)->cond, 1);
+
+ if (GET_CODE (op0) == REG
+ && (GET_CODE (op1) == REG
+ || GET_CODE (op1) == CONST_INT))
+ {
+ switch (GET_CODE (BBI (bb)->cond))
+ {
+ case EQ:
+ process_ranges_eq (op0, op1, then_edge);
+ break;
+
+ case NE:
+ /* OP0 NE OP1: True -> THEN_EDGE, False -> ELSE_EDGE
+ is equivalent to
+ OP0 EQ OP1: True -> ELSE_EDGE, False -> THEN_EDGE. */
+ process_ranges_eq (op0, op1, else_edge);
+ break;
+
+ case LT:
+ process_ranges_lt (op0, op1, then_edge, else_edge);
+ break;
+
+ case GE:
+ /* OP0 GE OP1: True -> THEN_EDGE, False -> ELSE_EDGE
+ is equivalent to
+ OP0 LT OP1: True -> ELSE_EDGE, False -> THEN_EDGE. */
+ process_ranges_lt (op0, op1, else_edge, then_edge);
+ break;
+
+ case GT:
+ /* OP0 GT OP1: True -> THEN_EDGE, False -> ELSE_EDGE
+ is equivalent to
+ OP1 LT OP0: True -> THEN_EDGE, False -> ELSE_EDGE. */
+ process_ranges_lt (op1, op0, then_edge, else_edge);
+ break;
+
+ case LE:
+ /* OP0 LE OP1: True -> THEN_EDGE, False -> ELSE_EDGE
+ is equivalent to
+ OP0 GT OP1: True -> ELSE_EDGE, False -> THEN_EDGE
+ is equivalent to
+ OP1 LT OP0: True -> ELSE_EDGE, False -> THEN_EDGE. */
+ process_ranges_lt (op1, op0, else_edge, then_edge);
+ break;
+
+ case LTU:
+ process_ranges_ltu (op0, op1, then_edge, else_edge);
+ break;
+
+ case GEU:
+ /* Similarly to GE. */
+ process_ranges_ltu (op0, op1, else_edge, then_edge);
+ break;
+
+ case GTU:
+ /* Similarly to GT. */
+ process_ranges_ltu (op1, op0, then_edge, else_edge);
+ break;
+
+ case LEU:
+ /* Similarly to LE. */
+ process_ranges_ltu (op1, op0, else_edge, then_edge);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!changed)
+ {
+ changed = compare_register_tables (old_then, EI (then_edge)->htab);
+ if (!changed)
+ changed = compare_register_tables (old_else, EI (else_edge)->htab);
+
+ htab_delete (old_then);
+ htab_delete (old_else);
+ }
+ }
+
+ return changed;
+ }
+
+ /* Compute the effect of set to STORE in insn PATTERN and update the ranges
+ in table DATA. */
+
+ static void
+ compute_ranges_for_insn (rtx store, rtx pattern, void *data)
+ {
+ htab_t htab = (htab_t) data;
+ rtx set_src, set_dst;
+ void **slot;
+ range_t *dst_r, *src_r;
+ unsigned HOST_WIDE_INT mask;
+
+ set_dst = SET_DEST (pattern);
+ if (GET_CODE (store) == REG)
+ {
+ if (set_dst != store || GET_CODE (pattern) == CLOBBER)
+ {
+ /* SET_DST is a SUBREG, ZERO_EXTRACT, etc.
+ or we are clobbering the register.
+ We do not know the resulting range so delete the range
+ (i.e. make it to be a full range). */
+
+ slot = htab_find_slot_with_hash (htab, store, HASH_REG (store),
+ NO_INSERT);
+ if (slot)
+ htab_clear_slot (htab, slot);
+ return;
+ }
+
+ mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (GET_MODE (set_dst));
+ set_src = SET_SRC (pattern);
+ switch (GET_CODE (set_src))
+ {
+ case REG:
+ src_r = htab_find_with_hash (htab, set_src, HASH_REG (set_src));
+ if (src_r)
+ {
+ /* Copy the range from SET_SRC to SET_DST. */
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst), NO_INSERT);
+
+ if (slot)
+ dst_r = *slot;
+ else
+ {
+ dst_r = pool_alloc (range_pool);
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst),
+ INSERT);
+ *slot = dst_r;
+ }
+
+ *dst_r = *src_r;
+ dst_r->reg = set_dst;
+ }
+ else
+ {
+ /* SET_SRC has a full range. */
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst),
+ NO_INSERT);
+ if (slot)
+ htab_clear_slot (htab, slot);
+ }
+ return;
+
+ case CONST_INT:
+ /* Set the range of SET_DST to be a single value. */
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst), NO_INSERT);
+ if (slot)
+ dst_r = *slot;
+ else
+ {
+ dst_r = pool_alloc (range_pool);
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst), INSERT);
+ *slot = dst_r;
+ }
+
+ /* We do not know whether the constant is signed or unsigned. */
+ dst_r->type = RANGE_SIGNED_INT | RANGE_UNSIGNED_INT;
+ dst_r->reg = set_dst;
+ dst_r->from.si = INTVAL (set_src);
+ dst_r->to.si = INTVAL (set_src);
+ dst_r->from.ui = (unsigned HOST_WIDE_INT) INTVAL (set_src) & mask;
+ dst_r->to.ui = (unsigned HOST_WIDE_INT) INTVAL (set_src) & mask;
+ return;
+
+ default:
+ /* We do not know how the range changes so make it to be
+ a full range. */
+ slot = htab_find_slot_with_hash (htab, set_dst,
+ HASH_REG (set_dst), NO_INSERT);
+ if (slot)
+ htab_clear_slot (htab, slot);
+ return;
+ }
+ }
+ }
+
+ /* Set the range of register *SLOT to be a full range
+ if the register is in CALL_USED_REG_SET (i.e. delete such a register from
+ hash table DATA. */
+
+ static int
+ delete_call_used_regs (void **slot, void *data)
+ {
+ htab_t htab = data;
+ range_t *r = *slot;
+ int regno;
+
+ regno = REGNO (r->reg);
+ if (regno < FIRST_PSEUDO_REGISTER
+ && TEST_HARD_REG_BIT (call_used_reg_set, regno))
+ {
+ htab_clear_slot (htab, slot);
+ }
+
+ /* Continue traversing. */
+ return 1;
+ }
+
+ /* Compute the effect of insns in block BB. Update the edges going out
+ from BB. Return true if any range changes. */
+
+ static bool
+ compute_ranges_for_bb (basic_block bb)
+ {
+ bool changed;
+ htab_t new_out;
+ rtx insn;
+
+ new_out = htab_create (BBI (bb)->in->n_elements, range_hash, range_eq,
+ range_del);
+ copy_register_table (new_out, BBI (bb)->in);
+
+ for (insn = bb->head; insn != NEXT_INSN (bb->end); insn = NEXT_INSN (insn))
+ {
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ /* Set the range to be a full range for the registers
+ which do not survive during the call. */
+ htab_traverse (new_out, delete_call_used_regs, new_out);
+ }
+ if (INSN_P (insn))
+ {
+ note_stores (PATTERN (insn), compute_ranges_for_insn, new_out);
+ }
+ }
+
+ changed = compare_register_tables (new_out, BBI (bb)->out);
+
+ htab_delete (BBI (bb)->out);
+ BBI (bb)->out = new_out;
+
+ changed |= update_outgoing_edges (bb, changed);
+
+ return changed;
+ }
+
+ /* Compute the effect of blocks in PENDING set, starting in BB. Do not
+ recompute the ranges for blocks in VISITED set.
+ This function is similar to hybrid_search_sbitmap in df.c
+ but modified for different data structures. */
+
+ static void
+ compute_ranges_for_pending (basic_block bb, sbitmap visited, sbitmap pending)
+ {
+ bool changed;
+ edge e;
+
+ SET_BIT (visited, bb->index);
+ if (TEST_BIT (pending, bb->index))
+ {
+ /* Calculate the IN set as union of predecessor OUT sets. */
+ if ((e = bb->pred) != NULL)
+ {
+ copy_register_table (BBI (bb)->in, EI (e)->htab);
+ for (e = e->pred_next; e; e = e->pred_next)
+ union_all_ranges (BBI (bb)->in, EI (e)->htab);
+ }
+
+ changed = compute_ranges_for_bb (bb);
+ RESET_BIT (pending, bb->index);
+
+ if (changed)
+ {
+ for (e = bb->succ; e; e = e->succ_next)
+ {
+ if (e->dest == EXIT_BLOCK_PTR || e->dest->index == bb->index)
+ continue;
+ SET_BIT (pending, e->dest->index);
+ }
+ }
+ }
+
+ for (e = bb->succ; e != 0; e = e->succ_next)
+ {
+ if (e->dest == EXIT_BLOCK_PTR || e->dest->index == bb->index)
+ continue;
+ if (!TEST_BIT (visited, e->dest->index))
+ compute_ranges_for_pending (e->dest, visited, pending);
+ }
+ }
+
+ /* Compute ranges for the whole function. BB_ORDER is the order to iterate in
+ (it maps block numbers -> order). Because this is a forward data-flow problem
+ we use the reverse completion DFS-order (rc_order). */
+
+ static void
+ compute_ranges (int *bb_order)
+ {
+ fibheap_t worklist;
+ basic_block bb;
+ sbitmap visited, pending;
+
+ pending = sbitmap_alloc (last_basic_block);
+ visited = sbitmap_alloc (last_basic_block);
+ sbitmap_zero (pending);
+ sbitmap_zero (visited);
+ worklist = fibheap_new ();
+
+ FOR_EACH_BB (bb)
+ {
+ fibheap_insert (worklist, bb_order[bb->index], bb);
+ SET_BIT (pending, bb->index);
+ }
+
+ while (sbitmap_first_set_bit (pending) != -1)
+ {
+ while (!fibheap_empty (worklist))
+ {
+ bb = fibheap_extract_min (worklist);
+ if (!TEST_BIT (visited, bb->index))
+ compute_ranges_for_pending (bb, visited, pending);
+ }
+ if (sbitmap_first_set_bit (pending) != -1)
+ {
+ FOR_EACH_BB (bb)
+ {
+ fibheap_insert (worklist, bb_order[bb->index], bb);
+ }
+ sbitmap_zero (visited);
+ }
+ else
+ {
+ break;
+ }
+ }
+ sbitmap_free (pending);
+ sbitmap_free (visited);
+ fibheap_delete (worklist);
+ }
+
+ /* Return true if edge E is dead (we will never go though this edge because
+ of the conditions), i.e. (at least) one of the register ranges is empty. */
+
+ static bool
+ edge_is_dead (edge e)
+ {
+ rtx cond = BBI (e->src)->cond;
+ rtx op;
+ int i;
+
+ #ifdef ENABLE_CHECKING
+ if (cond == NULL)
+ abort ();
+ #endif
+
+ for (i = 0; i < 2; i++)
+ {
+ op = XEXP (cond, i);
+ if (GET_CODE (op) == REG)
+ {
+ range_t *r;
+
+ r = htab_find_with_hash (EI (e)->htab, op, HASH_REG (op));
+ if (r && range_is_empty (r))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /* Redirect dead edges to the other edges. */
+
+ static bool
+ redirect_edges ()
+ {
+ bool modified = false;
+ basic_block bb;
+
+ FOR_EACH_BB (bb)
+ {
+ if (any_condjump_p (bb->end)
+ && BBI (bb)->cond != NULL)
+ {
+ edge then_edge = BRANCH_EDGE (bb);
+ edge else_edge = FALLTHRU_EDGE (bb);
+ bool then_dead = edge_is_dead (then_edge);
+ bool else_dead = edge_is_dead (else_edge);
+
+ /* If both edges are dead BB is dead too so it will be removed
+ and thus destinations of both edges too.
+ So perform the redirection only when there is exactly one dead
+ edge. */
+
+ if (then_dead && !else_dead)
+ {
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file, "Redirecting edge %d->%d to %d\n",
+ then_edge->src->index, then_edge->dest->index,
+ else_edge->dest->index);
+ }
+
+ if (!redirect_edge_and_branch (then_edge, else_edge->dest))
+ abort ();
+
+ modified = true;
+ }
+ else if (!then_dead && else_dead)
+ {
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file, "Redirecting edge %d->%d to %d\n",
+ else_edge->src->index, else_edge->dest->index,
+ then_edge->dest->index);
+ }
+
+ if (!redirect_edge_and_branch (else_edge, then_edge->dest))
+ abort ();
+
+ modified = true;
+ }
+ }
+ }
+
+ return modified;
+ }
+
+ /* Print a range *SLOT to file DATA. */
+
+ static int
+ dump_range (void **slot, void *data)
+ {
+ FILE *file = data;
+ range_t *r;
+
+ if (!slot)
+ return 1;
+
+ r = *slot;
+
+ print_inline_rtx (file, r->reg, 2);
+ fprintf (file, "\n");
+
+ if (r->type & RANGE_SIGNED_INT)
+ {
+ fprintf (file, " signed [%ld, %ld]\n", r->from.si, r->to.si);
+ }
+
+ if (r->type & RANGE_UNSIGNED_INT)
+ {
+ fprintf (file, " unsigned [%lu, %lu]\n", r->from.ui, r->to.ui);
+ }
+
+ return 1;
+ }
+
+ /* Print all ranges in table HTAB to file FILE. */
+
+ static void
+ dump_all_ranges (FILE *file, htab_t htab)
+ {
+ htab_traverse (htab, dump_range, file);
+ fprintf (file, "\n");
+ }
+
+ /* Print a range R to STDERR. */
+
+ void
+ debug_range (range_t *r)
+ {
+ void *x = (void *) r; /* Avoid warning. */
+
+ dump_range (&x, stderr);
+ }
+
+ void
+ debug_all_ranges (htab_t htab)
+ {
+ dump_all_ranges (stderr, htab);
+ }
+
+ /* Compute the value ranges and remove unreachable edges.
+ The main entry point of this file. */
+
+ bool
+ value_range_propagation ()
+ {
+ bool modified;
+ basic_block bb;
+ edge e;
+ int *rc_order;
+ int *bb_order;
+ int i;
+
+ /* Initialization. */
+ range_pool = create_alloc_pool ("range_t", sizeof (range_t), 250);
+ alloc_aux_for_blocks (sizeof (bb_info));
+ alloc_aux_for_edges (sizeof (edge_info));
+
+ FOR_EACH_BB (bb)
+ {
+ BBI (bb)->cond = get_condition (bb->end, NULL);
+ }
+ BBI (ENTRY_BLOCK_PTR)->cond = NULL;
+ BBI (EXIT_BLOCK_PTR)->cond = NULL;
+
+ FOR_ALL_BB (bb)
+ {
+ BBI (bb)->in = htab_create (5, range_hash, range_eq, range_del);
+ BBI (bb)->out = htab_create (5, range_hash, range_eq, range_del);
+ for (e = bb->succ; e; e = e->succ_next)
+ EI (e)->htab = htab_create (5, range_hash, range_eq, range_del);
+ }
+
+ /* Compute reverse completion order of depth first search of the CFG
+ so that the data-flow could possibly run faster. */
+ rc_order = (int *) xmalloc (n_basic_blocks * sizeof (int));
+ bb_order = (int *) xmalloc (last_basic_block * sizeof (int));
+ flow_depth_first_order_compute (NULL, rc_order);
+ for (i = 0; i < n_basic_blocks; i++)
+ bb_order[rc_order[i]] = i;
+ free (rc_order);
+
+ if (rtl_dump_file)
+ {
+ dump_flow_info (rtl_dump_file);
+ }
+
+ compute_ranges (bb_order);
+ free (bb_order);
+
+ if (rtl_dump_file)
+ {
+ FOR_EACH_BB (bb)
+ {
+ fprintf (rtl_dump_file, "Status at the end of BB %d:\n", bb->index);
+ dump_all_ranges (rtl_dump_file, BBI (bb)->out);
+ }
+
+ FOR_EACH_BB (bb)
+ {
+ for (e = bb->succ; e; e = e->succ_next)
+ {
+ fprintf (rtl_dump_file, "Status on edge %d -> %d:\n",
+ e->src->index, e->dest->index);
+ dump_all_ranges (rtl_dump_file, EI (e)->htab);
+ }
+ }
+ }
+
+ modified = redirect_edges ();
+
+ /* Cleanup. */
+ FOR_ALL_BB (bb)
+ {
+ htab_delete (BBI (bb)->in);
+ htab_delete (BBI (bb)->out);
+ for (e = bb->succ; e; e = e->succ_next)
+ htab_delete (EI (e)->htab);
+ }
+
+ free_aux_for_edges ();
+ free_aux_for_blocks ();
+ free_alloc_pool (range_pool);
+
+ return modified;
+ }
Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1074
diff -c -3 -p -r1.1074 Makefile.in
*** Makefile.in 7 Jun 2003 13:23:08 -0000 1.1074
--- Makefile.in 8 Jun 2003 11:40:43 -0000
*************** OBJS = alias.o bb-reorder.o bitmap.o bui
*** 821,827 ****
sibcall.o simplify-rtx.o sreal.o ssa.o ssa-ccp.o ssa-dce.o stmt.o \
stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \
tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \
! alloc-pool.o et-forest.o cgraph.o cgraphunit.o cfghooks.o \
$(GGC) $(out_object_file) $(EXTRA_OBJS) $(host_hook_obj)
BACKEND = main.o libbackend.a
--- 821,827 ----
sibcall.o simplify-rtx.o sreal.o ssa.o ssa-ccp.o ssa-dce.o stmt.o \
stor-layout.o stringpool.o timevar.o toplev.o tracer.o tree.o tree-dump.o \
tree-inline.o unroll.o varasm.o varray.o version.o vmsdbgout.o xcoffout.o \
! alloc-pool.o et-forest.o cgraph.o cgraphunit.o cfghooks.o vrp.o \
$(GGC) $(out_object_file) $(EXTRA_OBJS) $(host_hook_obj)
BACKEND = main.o libbackend.a
*************** lists.o: lists.c $(CONFIG_H) $(SYSTEM_H)
*** 1769,1774 ****
--- 1769,1777 ----
bb-reorder.o : bb-reorder.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(BASIC_BLOCK_H) flags.h output.h cfglayout.h $(FIBHEAP_H) \
$(TARGET_H)
+ vrp.o : vrp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
+ $(BASIC_BLOCK_H) hard-reg-set.h expr.h output.h alloc-pool.h sbitmap.h \
+ $(FIBHEAP_H) $(HASHTAB_H)
tracer.o : tracer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
$(BASIC_BLOCK_H) hard-reg-set.h output.h cfglayout.h flags.h \
$(PARAMS_H) $(COVERAGE_H)
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/toplev.c,v
retrieving revision 1.767
diff -c -3 -p -r1.767 toplev.c
*** toplev.c 7 Jun 2003 11:10:39 -0000 1.767
--- toplev.c 8 Jun 2003 11:40:44 -0000
*************** static void rest_of_handle_jump_bypass (
*** 147,152 ****
--- 147,153 ----
static void rest_of_handle_sibling_calls (rtx);
static void rest_of_handle_null_pointer (tree, rtx);
static void rest_of_handle_addresof (tree, rtx);
+ static void rest_of_handle_vrp (tree, rtx);
static void rest_of_handle_cfg (tree, rtx);
static void rest_of_handle_branch_prob (tree, rtx);
static void rest_of_handle_if_conversion (tree, rtx);
*************** enum dump_file_index
*** 271,276 ****
--- 272,278 ----
DFI_null,
DFI_cse,
DFI_addressof,
+ DFI_vrp,
DFI_gcse,
DFI_loop,
DFI_bypass,
*************** enum dump_file_index
*** 306,312 ****
Remaining -d letters:
" o q "
! " H JK OPQ TUV YZ"
*/
static struct dump_file_info dump_file[DFI_MAX] =
--- 308,314 ----
Remaining -d letters:
" o q "
! " H JK OPQ TU YZ"
*/
static struct dump_file_info dump_file[DFI_MAX] =
*************** static struct dump_file_info dump_file[D
*** 322,327 ****
--- 324,330 ----
{ "null", 'u', 0, 0, 0 },
{ "cse", 's', 0, 0, 0 },
{ "addressof", 'F', 0, 0, 0 },
+ { "vrp", 'V', 1, 0, 0 },
{ "gcse", 'G', 1, 0, 0 },
{ "loop", 'L', 1, 0, 0 },
{ "bypass", 'G', 1, 0, 0 }, /* Yes, duplicate enable switch. */
*************** static int flag_if_conversion;
*** 683,688 ****
--- 686,695 ----
static int flag_if_conversion2;
+ /* Nonzero means perform value range propagation. */
+
+ static int flag_value_range_propagation;
+
/* Nonzero means to use global dataflow analysis to eliminate
useless null pointer tests. */
*************** static const lang_independent_options f_
*** 1141,1146 ****
--- 1148,1155 ----
N_("Perform conversion of conditional jumps to branchless equivalents") },
{"if-conversion2", &flag_if_conversion2, 1,
N_("Perform conversion of conditional jumps to conditional execution") },
+ {"value-range-propagation", &flag_value_range_propagation, 1,
+ N_("Perform value range propagation and eliminate dead branches") },
{"rerun-cse-after-loop", &flag_rerun_cse_after_loop, 1,
N_("Run CSE pass after loop optimizations") },
{"rerun-loop-opt", &flag_rerun_loop_opt, 1,
*************** rest_of_handle_addresof (tree decl, rtx
*** 2878,2883 ****
--- 2887,2911 ----
close_dump_file (DFI_addressof, print_rtl, insns);
}
+ /* Perform value range propagation and remove edges dead because of the
+ conditions in code. */
+ static void
+ rest_of_handle_vrp (tree decl, rtx insns)
+ {
+ timevar_push (TV_VALUE_RANGE);
+ open_dump_file (DFI_vrp, decl);
+
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+ if (value_range_propagation ())
+ cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP);
+
+ if (rtl_dump_file)
+ dump_flow_info (rtl_dump_file);
+
+ close_dump_file (DFI_vrp, print_rtl_with_bb, insns);
+ timevar_pop (TV_VALUE_RANGE);
+ }
+
/* We may have potential sibling or tail recursion sites. Select one
(of possibly multiple) methods of performing the call. */
static void
*************** rest_of_compilation (tree decl)
*** 3686,3691 ****
--- 3714,3722 ----
if (optimize > 0)
{
+ if (flag_value_range_propagation)
+ rest_of_handle_vrp (decl, insns);
+
if (flag_gcse)
rest_of_handle_gcse (decl, insns);
*************** parse_options_and_default_flags (int arg
*** 5207,5212 ****
--- 5238,5244 ----
flag_crossjumping = 1;
flag_if_conversion = 1;
flag_if_conversion2 = 1;
+ flag_value_range_propagation = 1;
}
if (optimize >= 2)
Index: timevar.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/timevar.def,v
retrieving revision 1.18
diff -c -3 -p -r1.18 timevar.def
*** timevar.def 26 Feb 2003 11:09:33 -0000 1.18
--- timevar.def 8 Jun 2003 11:40:44 -0000
*************** DEFTIMEVAR (TV_LOOP , "
*** 68,73 ****
--- 68,74 ----
DEFTIMEVAR (TV_BYPASS , "bypass jumps")
DEFTIMEVAR (TV_TRACER , "tracer")
DEFTIMEVAR (TV_CSE2 , "CSE 2")
+ DEFTIMEVAR (TV_VALUE_RANGE , "value range propag.")
DEFTIMEVAR (TV_BRANCH_PROB , "branch prediction")
DEFTIMEVAR (TV_FLOW , "flow analysis")
DEFTIMEVAR (TV_COMBINE , "combiner")
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/rtl.h,v
retrieving revision 1.408
diff -c -3 -p -r1.408 rtl.h
*** rtl.h 6 Jun 2003 09:24:26 -0000 1.408
--- rtl.h 8 Jun 2003 11:40:45 -0000
*************** extern void tracer PARAMS ((void));
*** 2403,2406 ****
--- 2403,2409 ----
extern int flags_from_decl_or_type PARAMS ((tree));
+ /* In vrp.c */
+ extern bool value_range_propagation PARAMS ((void));
+
#endif /* ! GCC_RTL_H */
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/invoke.texi,v
retrieving revision 1.290
diff -c -3 -p -r1.290 invoke.texi
*** doc/invoke.texi 6 Jun 2003 04:33:01 -0000 1.290
--- doc/invoke.texi 8 Jun 2003 11:40:46 -0000
*************** in the following sections.
*** 283,288 ****
--- 283,289 ----
-fsched-spec-load-dangerous -fsched2-use-superblocks @gol
-fsched2-use-traces -fsignaling-nans @gol
-fsingle-precision-constant -fssa -fssa-ccp -fssa-dce @gol
+ -fvalue-range-propagation @gol
-fstrength-reduce -fstrict-aliasing -ftracer -fthread-jumps @gol
-funroll-all-loops -funroll-loops -fpeel-loops @gol
-funswitch-loops -fold-unroll-loops -fold-unroll-all-loops @gol
*************** sometimes follows CSE), to @file{@var{fi
*** 3195,3200 ****
--- 3196,3204 ----
@item u
@opindex du
Dump after null pointer elimination pass to @file{@var{file}.08.null}.
+ @item V
+ @opindex dV
+ Dump after value range propagation, to @file{@var{file}.11.vrp}.
@item w
@opindex dw
Dump after the second flow pass, to @file{@var{file}.28.flow2}.
*************** compilation time.
*** 3465,3470 ****
--- 3469,3475 ----
-fcrossjumping @gol
-fif-conversion @gol
-fif-conversion2 @gol
+ -fvalue-range-propagation @gol
-fdelayed-branch @gol
-fguess-branch-probability @gol
-fcprop-registers}
*************** Enabled at levels @option{-O}, @option{-
*** 3840,3845 ****
--- 3845,3866 ----
@opindex if-conversion2
Use conditional execution (where available) to transform conditional jumps into
branch-less equivalents.
+
+ Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
+
+ @item -fvalue-range-propagation
+ @opindex fvalue-range-propagation
+ Find out which range is a value of a register in. If the register may
+ have no value in some block the block is unreachable and is deleted.
+ For example a @code{printf} call in following code may be deleted:
+
+ @smallexample
+ if (i < 0)
+ @{
+ if (i > 5)
+ printf ("foo");
+ @}
+ @end smallexample
Enabled at levels @option{-O}, @option{-O2}, @option{-O3}, @option{-Os}.
Index: doc/passes.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/passes.texi,v
retrieving revision 1.25
diff -c -3 -p -r1.25 passes.texi
*** doc/passes.texi 31 May 2003 21:18:21 -0000 1.25
--- doc/passes.texi 8 Jun 2003 11:40:46 -0000
*************** The option @option{-ds} causes a debuggi
*** 299,304 ****
--- 299,317 ----
this pass. This dump file's name is made by appending @samp{.cse} to
the input file name.
+ @cindex value range propagation
+ @item
+ Value range propagation. This pass computes what ranges are values of
+ registers in. When some register may have no value in some basic block
+ it is unreachable because of conditions in the code. The edge which
+ the register may have no value on is redirected to the other which causes
+ removing of dead blocks.
+
+ @opindex dV
+ The option @option{-dV} causes a debugging dump of the RTL code after
+ this pass. This dump file's name is made by appending @samp{.vrp} to
+ the input file name.
+
@cindex global common subexpression elimination
@cindex constant propagation
@cindex copy propagation