summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorrakdver <rakdver@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-20 00:46:37 +0000
committerrakdver <rakdver@138bc75d-0d04-0410-961f-82ee72b054a4>2006-01-20 00:46:37 +0000
commit3f37d4659f6f267e380af076f90439d7aceeea23 (patch)
treef0310ff5933484ccfcb5cd7fb312fb89350141a1 /gcc
parentdc83e3a13b917c0c92e71dbe6a384cff18ae9ad6 (diff)
downloadgcc-3f37d4659f6f267e380af076f90439d7aceeea23.tar.gz
* loop-iv.c: Include df.h and hashtab.h.
(enum iv_grd_result): New enum. (DF_REF_IV, DF_REF_IV_SET): New macros. (struct biv_entry): New. (df, bivs): New global variables. (struct insn_info, insn_info, last_def, bivs, max_insn_no, max_reg_no, assign_luids, mark_sets, kill_sets, mark_single_set, simple_set_p): Removed. (clear_iv_info, latch_dominating_def, record_iv, iv_analyze_expr, iv_analyze_result, iv_analyze_def, biv_hash, biv_eq, analyzed_for_bivness_p, record_biv): New functions. (iv_analysis_loop_init, iv_get_reaching_def, simple_reg_p, get_biv_step_1, get_biv_step, iv_analyze_biv, iv_analyze_op, iv_analyze, biv_p, iv_analysis_done): Work with df representation of UD chains. (iv_constant, iv_subreg, iv_extend, iv_mult, iv_shift): Do not set analysed. (iv_number_of_iterations): Use new interface to iv analysis. * loop-unroll.c: Do not include varray.h. (analyze_iv_to_split_insn): Use new interface to iv analysis. * loop-unswitch.c (may_unswitch_on): Ditto. * df.c (df_bitmaps_free): Only work for bbs for that structures are allocated. (df_bb_modify): Realloc tables to the new index. (df_find_use): New function. * df.h (df_find_use): Declare. * optabs.c (expand_unop): Make the mode of the REG_EQUAL node be outmode. * cfgloop.h (struct rtx_iv): Remove analysed field. (iv_get_reaching_def): Removed. (iv_analyze_result, iv_analyze_expr, iv_current_loop_df): Declare. * Makefile.in (loop-unroll.o): Remove VARRAY_H dependency. (loop-iv.o): Add df.h and hashtab.h dependency. * df-problems.c (df_ru_alloc, df_rd_alloc): Fix memory reallocation. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@110005 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog38
-rw-r--r--gcc/Makefile.in4
-rw-r--r--gcc/cfgloop.h7
-rw-r--r--gcc/df-problems.c12
-rw-r--r--gcc/loop-iv.c915
-rw-r--r--gcc/loop-unroll.c3
-rw-r--r--gcc/loop-unswitch.c5
-rw-r--r--gcc/optabs.c2
8 files changed, 506 insertions, 480 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 8aaec41158b..836da92b968 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,41 @@
+2006-01-20 Zdenek Dvorak <dvorakz@suse.cz>
+
+ * loop-iv.c: Include df.h and hashtab.h.
+ (enum iv_grd_result): New enum.
+ (DF_REF_IV, DF_REF_IV_SET): New macros.
+ (struct biv_entry): New.
+ (df, bivs): New global variables.
+ (struct insn_info, insn_info, last_def, bivs, max_insn_no, max_reg_no,
+ assign_luids, mark_sets, kill_sets, mark_single_set, simple_set_p):
+ Removed.
+ (clear_iv_info, latch_dominating_def, record_iv, iv_analyze_expr,
+ iv_analyze_result, iv_analyze_def, biv_hash, biv_eq,
+ analyzed_for_bivness_p, record_biv): New functions.
+ (iv_analysis_loop_init, iv_get_reaching_def, simple_reg_p,
+ get_biv_step_1, get_biv_step, iv_analyze_biv, iv_analyze_op,
+ iv_analyze, biv_p, iv_analysis_done): Work with df representation of
+ UD chains.
+ (iv_constant, iv_subreg, iv_extend, iv_mult, iv_shift): Do not set
+ analysed.
+ (iv_number_of_iterations): Use new interface to iv analysis.
+ * loop-unroll.c: Do not include varray.h.
+ (analyze_iv_to_split_insn): Use new interface to iv
+ analysis.
+ * loop-unswitch.c (may_unswitch_on): Ditto.
+ * df.c (df_bitmaps_free): Only work for bbs for that structures are
+ allocated.
+ (df_bb_modify): Realloc tables to the new index.
+ (df_find_use): New function.
+ * df.h (df_find_use): Declare.
+ * optabs.c (expand_unop): Make the mode of the REG_EQUAL node be
+ outmode.
+ * cfgloop.h (struct rtx_iv): Remove analysed field.
+ (iv_get_reaching_def): Removed.
+ (iv_analyze_result, iv_analyze_expr, iv_current_loop_df): Declare.
+ * Makefile.in (loop-unroll.o): Remove VARRAY_H dependency.
+ (loop-iv.o): Add df.h and hashtab.h dependency.
+ * df-problems.c (df_ru_alloc, df_rd_alloc): Fix memory reallocation.
+
2006-01-20 Alan Modra <amodra@bigpond.net.au>
* libgcc2.c (__floatdisf, __floatdidf): Don't use IBM Extended
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e1c6a1d84e3..fe983d49396 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2431,7 +2431,7 @@ struct-equiv.o : struct-equiv.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(REGS_H) $(EMIT_RTL_H)
loop-iv.o : loop-iv.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(BASIC_BLOCK_H) \
hard-reg-set.h $(CFGLOOP_H) $(EXPR_H) coretypes.h $(TM_H) $(OBSTACK_H) \
- output.h intl.h
+ output.h intl.h $(DF_H) $(HASHTAB_H)
loop-invariant.o : loop-invariant.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) \
$(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(EXPR_H) coretypes.h \
$(TM_H) $(TM_P_H) $(FUNCTION_H) $(FLAGS_H) $(DF_H) $(OBSTACK_H) output.h \
@@ -2447,7 +2447,7 @@ loop-unswitch.o : loop-unswitch.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \
output.h $(EXPR_H) coretypes.h $(TM_H) $(OBSTACK_H)
loop-unroll.o: loop-unroll.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TM_H) \
$(BASIC_BLOCK_H) hard-reg-set.h $(CFGLOOP_H) $(CFGLAYOUT_H) $(PARAMS_H) \
- output.h $(EXPR_H) coretypes.h $(TM_H) $(HASHTAB_H) $(RECOG_H) $(VARRAY_H) \
+ output.h $(EXPR_H) coretypes.h $(TM_H) $(HASHTAB_H) $(RECOG_H) \
$(OBSTACK_H)
dominance.o : dominance.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
hard-reg-set.h $(BASIC_BLOCK_H) et-forest.h $(OBSTACK_H) toplev.h
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index b5ac71caeab..3139ef70780 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -352,9 +352,6 @@ struct rtx_iv
/* The mode the variable iterates in. */
enum machine_mode mode;
- /* Whether we have already filled the remaining fields. */
- unsigned analysed : 1;
-
/* Whether the first iteration needs to be handled specially. */
unsigned first_special : 1;
};
@@ -404,12 +401,14 @@ struct niter_desc
};
extern void iv_analysis_loop_init (struct loop *);
-extern rtx iv_get_reaching_def (rtx, rtx);
extern bool iv_analyze (rtx, rtx, struct rtx_iv *);
+extern bool iv_analyze_result (rtx, rtx, struct rtx_iv *);
+extern bool iv_analyze_expr (rtx, rtx, enum machine_mode, struct rtx_iv *);
extern rtx get_iv_value (struct rtx_iv *, rtx);
extern bool biv_p (rtx, rtx);
extern void find_simple_exit (struct loop *, struct niter_desc *);
extern void iv_analysis_done (void);
+extern struct df *iv_current_loop_df (void);
extern struct niter_desc *get_simple_loop_desc (struct loop *loop);
extern void free_simple_loop_desc (struct loop *loop);
diff --git a/gcc/df-problems.c b/gcc/df-problems.c
index c17e048edad..2a7ec0d0ed9 100644
--- a/gcc/df-problems.c
+++ b/gcc/df-problems.c
@@ -369,12 +369,12 @@ df_ru_alloc (struct dataflow *dflow, bitmap blocks_to_rescan)
}
}
- if (problem_data->use_sites_size > reg_size)
+ if (problem_data->use_sites_size < reg_size)
{
problem_data->use_sites
- = xrealloc (problem_data->use_sites, reg_size *sizeof (bitmap));
- memset (problem_data->use_sites, 0,
- (reg_size - problem_data->use_sites_size) *sizeof (bitmap));
+ = xrealloc (problem_data->use_sites, reg_size * sizeof (bitmap));
+ memset (problem_data->use_sites + problem_data->use_sites_size, 0,
+ (reg_size - problem_data->use_sites_size) * sizeof (bitmap));
problem_data->use_sites_size = reg_size;
}
@@ -876,11 +876,11 @@ df_rd_alloc (struct dataflow *dflow, bitmap blocks_to_rescan)
}
}
- if (problem_data->def_sites_size > reg_size)
+ if (problem_data->def_sites_size < reg_size)
{
problem_data->def_sites
= xrealloc (problem_data->def_sites, reg_size *sizeof (bitmap));
- memset (problem_data->def_sites, 0,
+ memset (problem_data->def_sites + problem_data->def_sites_size, 0,
(reg_size - problem_data->def_sites_size) *sizeof (bitmap));
problem_data->def_sites_size = reg_size;
}
diff --git a/gcc/loop-iv.c b/gcc/loop-iv.c
index 8e915a04b23..e3ec78b7cfc 100644
--- a/gcc/loop-iv.c
+++ b/gcc/loop-iv.c
@@ -18,34 +18,35 @@ along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA. */
-/* This is just a very simplistic analysis of induction variables of the loop.
- The major use is for determining the number of iterations of a loop for
- loop unrolling, doloop optimization and branch prediction. For this we
- are only interested in bivs and a fairly limited set of givs that are
- needed in the exit condition. We also only compute the iv information on
- demand.
-
- The interesting registers are determined. A register is interesting if
-
- -- it is set only in the blocks that dominate the latch of the current loop
- -- all its sets are simple -- i.e. in the form we understand
-
- We also number the insns sequentially in each basic block. For a use of the
- interesting reg, it is now easy to find a reaching definition (there may be
- only one).
-
- Induction variable is then simply analyzed by walking the use-def
- chains.
-
- Usage:
-
- iv_analysis_loop_init (loop);
- insn = iv_get_reaching_def (where, reg);
- if (iv_analyze (insn, reg, &iv))
- {
- ...
- }
- iv_analysis_done (); */
+/* This is a simple analysis of induction variables of the loop. The major use
+ is for determining the number of iterations of a loop for loop unrolling,
+ doloop optimization and branch prediction. The iv information is computed
+ on demand.
+
+ Induction variable is analyzed by walking the use-def chains. When a biv
+ is found, it is cached in the bivs hash table. When register is proved
+ to be a giv, its description is stored to DF_REF_DATA of the def reference.
+
+ The analysis works always with one loop -- you must call
+ iv_analysis_loop_init (loop) for it. All the other functions then work with
+ this loop. When you need to work with another loop, just call
+ iv_analysis_loop_init for it. When you no longer need iv analysis, call
+ iv_analysis_done () to clean up the memory.
+
+ The available functions are:
+
+ iv_analyze (insn, reg, iv): Stores the description of the induction variable
+ corresponding to the use of register REG in INSN to IV. Returns true if
+ REG is an induction variable in INSN. false otherwise.
+ If use of REG is not found in INSN, following insns are scanned (so that
+ we may call this function on insn returned by get_condition).
+ iv_analyze_result (insn, def, iv): Stores to IV the description of the iv
+ corresponding to DEF, which is a register defined in INSN.
+ iv_analyze_expr (insn, rhs, mode, iv): Stores to IV the description of iv
+ corresponding to expression EXPR evaluated at INSN. All registers used bu
+ EXPR must also be used in INSN.
+ iv_current_loop_df (): Returns the dataflow object for the current loop used
+ by iv analysis. */
#include "config.h"
#include "system.h"
@@ -60,40 +61,61 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "intl.h"
#include "output.h"
#include "toplev.h"
+#include "df.h"
+#include "hashtab.h"
-/* The insn information. */
+/* Possible return values of iv_get_reaching_def. */
-struct insn_info
+enum iv_grd_result
{
- /* Id of the insn. */
- unsigned luid;
+ /* More than one reaching def, or reaching def that does not
+ dominate the use. */
+ GRD_INVALID,
- /* The previous definition of the register defined by the single
- set in the insn. */
- rtx prev_def;
+ /* The use is trivial invariant of the loop, i.e. is not changed
+ inside the loop. */
+ GRD_INVARIANT,
- /* The description of the iv. */
- struct rtx_iv iv;
+ /* The use is reached by initial value and a value from the
+ previous iteration. */
+ GRD_MAYBE_BIV,
+
+ /* The use has single dominating def. */
+ GRD_SINGLE_DOM
+};
+
+/* Information about a biv. */
+
+struct biv_entry
+{
+ unsigned regno; /* The register of the biv. */
+ struct rtx_iv iv; /* Value of the biv. */
};
-static struct insn_info *insn_info;
+/* Induction variable stored at the reference. */
+#define DF_REF_IV(REF) ((struct rtx_iv *) DF_REF_DATA (REF))
+#define DF_REF_IV_SET(REF, IV) DF_REF_DATA (REF) = (IV)
-/* The last definition of register. */
+/* The current loop. */
-static rtx *last_def;
+static struct loop *current_loop;
-/* The bivs. */
+/* Dataflow information for the current loop. */
-static struct rtx_iv *bivs;
+static struct df *df = NULL;
-/* Maximal insn number for that there is place in insn_info array. */
+/* Bivs of the current loop. */
-static unsigned max_insn_no;
+static htab_t bivs;
-/* Maximal register number for that there is place in bivs and last_def
- arrays. */
+/* Return the dataflow object for the current loop. */
+struct df *
+iv_current_loop_df (void)
+{
+ return df;
+}
-static unsigned max_reg_no;
+static bool iv_analyze_op (rtx, rtx, struct rtx_iv *);
/* Dumps information about IV to FILE. */
@@ -139,23 +161,6 @@ dump_iv_info (FILE *file, struct rtx_iv *iv)
fprintf (file, " (first special)");
}
-/* Assigns luids to insns in basic block BB. */
-
-static void
-assign_luids (basic_block bb)
-{
- unsigned i = 0, uid;
- rtx insn;
-
- FOR_BB_INSNS (bb, insn)
- {
- uid = INSN_UID (insn);
- insn_info[uid].luid = i++;
- insn_info[uid].prev_def = NULL_RTX;
- insn_info[uid].iv.analysed = false;
- }
-}
-
/* Generates a subreg to get the least significant part of EXPR (in mode
INNER_MODE) to OUTER_MODE. */
@@ -191,131 +196,45 @@ simple_reg_p (rtx reg)
if (GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT)
return false;
- if (last_def[r] == const0_rtx)
- return false;
-
return true;
}
-/* Checks whether assignment LHS = RHS is simple enough for us to process. */
+/* Clears the information about ivs stored in df. */
-static bool
-simple_set_p (rtx lhs, rtx rhs)
+static void
+clear_iv_info (void)
{
- rtx op0, op1;
-
- if (!REG_P (lhs)
- || !simple_reg_p (lhs))
- return false;
-
- if (CONSTANT_P (rhs))
- return true;
+ unsigned i, n_defs = DF_DEFS_SIZE (df);
+ struct rtx_iv *iv;
+ struct df_ref *def;
- switch (GET_CODE (rhs))
+ for (i = 0; i < n_defs; i++)
{
- case SUBREG:
- case REG:
- return simple_reg_p (rhs);
-
- case SIGN_EXTEND:
- case ZERO_EXTEND:
- case NEG:
- return simple_reg_p (XEXP (rhs, 0));
-
- case PLUS:
- case MINUS:
- case MULT:
- case ASHIFT:
- op0 = XEXP (rhs, 0);
- op1 = XEXP (rhs, 1);
-
- if (!simple_reg_p (op0)
- && !CONSTANT_P (op0))
- return false;
-
- if (!simple_reg_p (op1)
- && !CONSTANT_P (op1))
- return false;
-
- if (GET_CODE (rhs) == MULT
- && !CONSTANT_P (op0)
- && !CONSTANT_P (op1))
- return false;
-
- if (GET_CODE (rhs) == ASHIFT
- && CONSTANT_P (op0))
- return false;
-
- return true;
-
- default:
- return false;
+ def = DF_DEFS_GET (df, i);
+ iv = DF_REF_IV (def);
+ if (!iv)
+ continue;
+ free (iv);
+ DF_REF_IV_SET (def, NULL);
}
-}
-
-/* Mark single SET in INSN. */
-static rtx
-mark_single_set (rtx insn, rtx set)
-{
- rtx def = SET_DEST (set), src;
- unsigned regno, uid;
-
- src = find_reg_equal_equiv_note (insn);
- if (src)
- src = XEXP (src, 0);
- else
- src = SET_SRC (set);
-
- if (!simple_set_p (SET_DEST (set), src))
- return NULL_RTX;
-
- regno = REGNO (def);
- uid = INSN_UID (insn);
-
- bivs[regno].analysed = false;
- insn_info[uid].prev_def = last_def[regno];
- last_def[regno] = insn;
-
- return def;
+ htab_empty (bivs);
}
-/* Invalidate register REG unless it is equal to EXCEPT. */
+/* Returns hash value for biv B. */
-static void
-kill_sets (rtx reg, rtx by ATTRIBUTE_UNUSED, void *except)
+static hashval_t
+biv_hash (const void *b)
{
- if (GET_CODE (reg) == SUBREG)
- reg = SUBREG_REG (reg);
- if (!REG_P (reg))
- return;
- if (reg == except)
- return;
-
- last_def[REGNO (reg)] = const0_rtx;
+ return ((const struct biv_entry *) b)->regno;
}
-/* Marks sets in basic block BB. If DOM is true, BB dominates the loop
- latch. */
+/* Compares biv B and register R. */
-static void
-mark_sets (basic_block bb, bool dom)
+static int
+biv_eq (const void *b, const void *r)
{
- rtx insn, set, def;
-
- FOR_BB_INSNS (bb, insn)
- {
- if (!INSN_P (insn))
- continue;
-
- if (dom
- && (set = single_set (insn)))
- def = mark_single_set (insn, set);
- else
- def = NULL_RTX;
-
- note_stores (PATTERN (insn), kill_sets, def);
- }
+ return ((const struct biv_entry *) b)->regno == REGNO ((rtx) r);
}
/* Prepare the data for an induction variable analysis of a LOOP. */
@@ -323,97 +242,116 @@ mark_sets (basic_block bb, bool dom)
void
iv_analysis_loop_init (struct loop *loop)
{
- basic_block *body = get_loop_body_in_dom_order (loop);
- unsigned b;
+ basic_block *body = get_loop_body_in_dom_order (loop), bb;
+ bitmap blocks = BITMAP_ALLOC (NULL);
+ unsigned i;
+ bool first_time = (df == NULL);
- if ((unsigned) get_max_uid () >= max_insn_no)
- {
- /* Add some reserve for insns and registers produced in optimizations. */
- max_insn_no = get_max_uid () + 100;
- if (insn_info)
- free (insn_info);
- insn_info = xmalloc (max_insn_no * sizeof (struct insn_info));
- }
+ current_loop = loop;
- if ((unsigned) max_reg_num () >= max_reg_no)
+ /* Clear the information from the analysis of the previous loop. */
+ if (first_time)
{
- max_reg_no = max_reg_num () + 100;
- if (last_def)
- free (last_def);
- last_def = xmalloc (max_reg_no * sizeof (rtx));
- if (bivs)
- free (bivs);
- bivs = xmalloc (max_reg_no * sizeof (struct rtx_iv));
+ df = df_init (DF_HARD_REGS | DF_EQUIV_NOTES);
+ df_chain_add_problem (df, DF_UD_CHAIN);
+ bivs = htab_create (10, biv_hash, biv_eq, free);
}
+ else
+ clear_iv_info ();
- memset (last_def, 0, max_reg_num () * sizeof (rtx));
-
- for (b = 0; b < loop->num_nodes; b++)
+ for (i = 0; i < loop->num_nodes; i++)
{
- assign_luids (body[b]);
- mark_sets (body[b], just_once_each_iteration_p (loop, body[b]));
+ bb = body[i];
+ bitmap_set_bit (blocks, bb->index);
}
-
+ df_set_blocks (df, blocks);
+ df_analyze (df);
+ BITMAP_FREE (blocks);
free (body);
}
-/* Gets definition of REG reaching the INSN. If REG is not simple, const0_rtx
- is returned. If INSN is before the first def in the loop, NULL_RTX is
- returned. */
+/* Finds the definition of REG that dominates loop latch and stores
+ it to DEF. Returns false if there is not a single definition
+ dominating the latch. If REG has no definition in loop, DEF
+ is set to NULL and true is returned. */
-rtx
-iv_get_reaching_def (rtx insn, rtx reg)
+static bool
+latch_dominating_def (rtx reg, struct df_ref **def)
{
- unsigned regno, luid, auid;
- rtx ainsn;
- basic_block bb, abb;
+ struct df_ref *single_rd = NULL, *adef;
+ unsigned regno = REGNO (reg);
+ struct df_reg_info *reg_info = DF_REG_DEF_GET (df, regno);
+ struct df_rd_bb_info *bb_info = DF_RD_BB_INFO (df, current_loop->latch);
- if (GET_CODE (reg) == SUBREG)
+ for (adef = reg_info->reg_chain; adef; adef = adef->next_reg)
{
- if (!subreg_lowpart_p (reg))
- return const0_rtx;
- reg = SUBREG_REG (reg);
+ if (!bitmap_bit_p (bb_info->out, DF_REF_ID (adef)))
+ continue;
+
+ /* More than one reaching definition. */
+ if (single_rd)
+ return false;
+
+ if (!just_once_each_iteration_p (current_loop, DF_REF_BB (adef)))
+ return false;
+
+ single_rd = adef;
}
- if (!REG_P (reg))
- return NULL_RTX;
- regno = REGNO (reg);
- if (!last_def[regno]
- || last_def[regno] == const0_rtx)
- return last_def[regno];
+ *def = single_rd;
+ return true;
+}
- bb = BLOCK_FOR_INSN (insn);
- luid = insn_info[INSN_UID (insn)].luid;
+/* Gets definition of REG reaching its use in INSN and stores it to DEF. */
- ainsn = last_def[regno];
- while (1)
- {
- abb = BLOCK_FOR_INSN (ainsn);
+static enum iv_grd_result
+iv_get_reaching_def (rtx insn, rtx reg, struct df_ref **def)
+{
+ struct df_ref *use, *adef;
+ basic_block def_bb, use_bb;
+ rtx def_insn;
+ bool dom_p;
+
+ *def = NULL;
+ if (!simple_reg_p (reg))
+ return GRD_INVALID;
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+ gcc_assert (REG_P (reg));
- if (dominated_by_p (CDI_DOMINATORS, bb, abb))
- break;
+ use = df_find_use (df, insn, reg);
+ gcc_assert (use != NULL);
- auid = INSN_UID (ainsn);
- ainsn = insn_info[auid].prev_def;
+ if (!DF_REF_CHAIN (use))
+ return GRD_INVARIANT;
- if (!ainsn)
- return NULL_RTX;
- }
+ /* More than one reaching def. */
+ if (DF_REF_CHAIN (use)->next)
+ return GRD_INVALID;
- while (1)
- {
- abb = BLOCK_FOR_INSN (ainsn);
- if (abb != bb)
- return ainsn;
+ adef = DF_REF_CHAIN (use)->ref;
+ def_insn = DF_REF_INSN (adef);
+ def_bb = DF_REF_BB (adef);
+ use_bb = BLOCK_FOR_INSN (insn);
- auid = INSN_UID (ainsn);
- if (luid > insn_info[auid].luid)
- return ainsn;
+ if (use_bb == def_bb)
+ dom_p = (DF_INSN_LUID (df, def_insn) < DF_INSN_LUID (df, insn));
+ else
+ dom_p = dominated_by_p (CDI_DOMINATORS, use_bb, def_bb);
- ainsn = insn_info[auid].prev_def;
- if (!ainsn)
- return NULL_RTX;
+ if (dom_p)
+ {
+ *def = adef;
+ return GRD_SINGLE_DOM;
}
+
+ /* The definition does not dominate the use. This is still OK if
+ this may be a use of a biv, i.e. if the def_bb dominates loop
+ latch. */
+ if (just_once_each_iteration_p (current_loop, def_bb))
+ return GRD_MAYBE_BIV;
+
+ return GRD_INVALID;
}
/* Sets IV to invariant CST in MODE. Always returns true (just for
@@ -425,7 +363,6 @@ iv_constant (struct rtx_iv *iv, rtx cst, enum machine_mode mode)
if (mode == VOIDmode)
mode = GET_MODE (cst);
- iv->analysed = true;
iv->mode = mode;
iv->base = cst;
iv->step = const0_rtx;
@@ -653,20 +590,26 @@ iv_shift (struct rtx_iv *iv, rtx mby)
}
/* The recursive part of get_biv_step. Gets the value of the single value
- defined in INSN wrto initial value of REG inside loop, in shape described
+ defined by DEF wrto initial value of REG inside loop, in shape described
at get_biv_step. */
static bool
-get_biv_step_1 (rtx insn, rtx reg,
+get_biv_step_1 (struct df_ref *def, rtx reg,
rtx *inner_step, enum machine_mode *inner_mode,
enum rtx_code *extend, enum machine_mode outer_mode,
rtx *outer_step)
{
rtx set, rhs, op0 = NULL_RTX, op1 = NULL_RTX;
- rtx next, nextr, def_insn, tmp;
+ rtx next, nextr, tmp;
enum rtx_code code;
+ rtx insn = DF_REF_INSN (def);
+ struct df_ref *next_def;
+ enum iv_grd_result res;
set = single_set (insn);
+ if (!set)
+ return false;
+
rhs = find_reg_equal_equiv_note (insn);
if (rhs)
rhs = XEXP (rhs, 0);
@@ -742,11 +685,12 @@ get_biv_step_1 (rtx insn, rtx reg,
else
nextr = next;
- def_insn = iv_get_reaching_def (insn, nextr);
- if (def_insn == const0_rtx)
+ res = iv_get_reaching_def (insn, nextr, &next_def);
+
+ if (res == GRD_INVALID || res == GRD_INVARIANT)
return false;
- if (!def_insn)
+ if (res == GRD_MAYBE_BIV)
{
if (!rtx_equal_p (nextr, reg))
return false;
@@ -756,7 +700,7 @@ get_biv_step_1 (rtx insn, rtx reg,
*inner_mode = outer_mode;
*outer_step = const0_rtx;
}
- else if (!get_biv_step_1 (def_insn, reg,
+ else if (!get_biv_step_1 (next_def, reg,
inner_step, inner_mode, extend, outer_mode,
outer_step))
return false;
@@ -803,7 +747,7 @@ get_biv_step_1 (rtx insn, rtx reg,
break;
default:
- gcc_unreachable ();
+ return false;
}
return true;
@@ -813,16 +757,17 @@ get_biv_step_1 (rtx insn, rtx reg,
OUTER_STEP + EXTEND_{OUTER_MODE} (SUBREG_{INNER_MODE} (REG + INNER_STEP))
- If the operation cannot be described in this shape, return false. */
+ If the operation cannot be described in this shape, return false.
+ LAST_DEF is the definition of REG that dominates loop latch. */
static bool
-get_biv_step (rtx reg, rtx *inner_step, enum machine_mode *inner_mode,
- enum rtx_code *extend, enum machine_mode *outer_mode,
- rtx *outer_step)
+get_biv_step (struct df_ref *last_def, rtx reg, rtx *inner_step,
+ enum machine_mode *inner_mode, enum rtx_code *extend,
+ enum machine_mode *outer_mode, rtx *outer_step)
{
*outer_mode = GET_MODE (reg);
- if (!get_biv_step_1 (last_def[REGNO (reg)], reg,
+ if (!get_biv_step_1 (last_def, reg,
inner_step, inner_mode, extend, *outer_mode,
outer_step))
return false;
@@ -833,16 +778,54 @@ get_biv_step (rtx reg, rtx *inner_step, enum machine_mode *inner_mode,
return true;
}
+/* Records information that DEF is induction variable IV. */
+
+static void
+record_iv (struct df_ref *def, struct rtx_iv *iv)
+{
+ struct rtx_iv *recorded_iv = xmalloc (sizeof (struct rtx_iv));
+
+ *recorded_iv = *iv;
+ DF_REF_IV_SET (def, recorded_iv);
+}
+
+/* If DEF was already analyzed for bivness, store the description of the biv to
+ IV and return true. Otherwise return false. */
+
+static bool
+analyzed_for_bivness_p (rtx def, struct rtx_iv *iv)
+{
+ struct biv_entry *biv = htab_find_with_hash (bivs, def, REGNO (def));
+
+ if (!biv)
+ return false;
+
+ *iv = biv->iv;
+ return true;
+}
+
+static void
+record_biv (rtx def, struct rtx_iv *iv)
+{
+ struct biv_entry *biv = xmalloc (sizeof (struct biv_entry));
+ void **slot = htab_find_slot_with_hash (bivs, def, REGNO (def), INSERT);
+
+ biv->regno = REGNO (def);
+ biv->iv = *iv;
+ gcc_assert (!*slot);
+ *slot = biv;
+}
+
/* Determines whether DEF is a biv and if so, stores its description
to *IV. */
static bool
iv_analyze_biv (rtx def, struct rtx_iv *iv)
{
- unsigned regno;
rtx inner_step, outer_step;
enum machine_mode inner_mode, outer_mode;
enum rtx_code extend;
+ struct df_ref *last_def;
if (dump_file)
{
@@ -859,31 +842,24 @@ iv_analyze_biv (rtx def, struct rtx_iv *iv)
return iv_constant (iv, def, VOIDmode);
}
- regno = REGNO (def);
- if (last_def[regno] == const0_rtx)
+ if (!latch_dominating_def (def, &last_def))
{
if (dump_file)
fprintf (dump_file, " not simple.\n");
return false;
}
- if (last_def[regno] && bivs[regno].analysed)
+ if (!last_def)
+ return iv_constant (iv, def, VOIDmode);
+
+ if (analyzed_for_bivness_p (def, iv))
{
if (dump_file)
fprintf (dump_file, " already analysed.\n");
-
- *iv = bivs[regno];
return iv->base != NULL_RTX;
}
- if (!last_def[regno])
- {
- iv_constant (iv, def, VOIDmode);
- goto end;
- }
-
- iv->analysed = true;
- if (!get_biv_step (def, &inner_step, &inner_mode, &extend,
+ if (!get_biv_step (last_def, def, &inner_step, &inner_mode, &extend,
&outer_mode, &outer_step))
{
iv->base = NULL_RTX;
@@ -913,119 +889,154 @@ iv_analyze_biv (rtx def, struct rtx_iv *iv)
fprintf (dump_file, "\n");
}
- bivs[regno] = *iv;
-
+ record_biv (def, iv);
return iv->base != NULL_RTX;
}
-/* Analyzes operand OP of INSN and stores the result to *IV. */
+/* Analyzes expression RHS used at INSN and stores the result to *IV.
+ The mode of the induction variable is MODE. */
-static bool
-iv_analyze_op (rtx insn, rtx op, struct rtx_iv *iv)
+bool
+iv_analyze_expr (rtx insn, rtx rhs, enum machine_mode mode, struct rtx_iv *iv)
{
- rtx def_insn;
- unsigned regno;
- bool inv = CONSTANT_P (op);
-
- if (dump_file)
- {
- fprintf (dump_file, "Analyzing operand ");
- print_rtl (dump_file, op);
- fprintf (dump_file, " of insn ");
- print_rtl_single (dump_file, insn);
- }
-
- if (GET_CODE (op) == SUBREG)
- {
- if (!subreg_lowpart_p (op))
- return false;
+ rtx mby = NULL_RTX, tmp;
+ rtx op0 = NULL_RTX, op1 = NULL_RTX;
+ struct rtx_iv iv0, iv1;
+ enum rtx_code code = GET_CODE (rhs);
+ enum machine_mode omode = mode;
- if (!iv_analyze_op (insn, SUBREG_REG (op), iv))
- return false;
+ iv->mode = VOIDmode;
+ iv->base = NULL_RTX;
+ iv->step = NULL_RTX;
- return iv_subreg (iv, GET_MODE (op));
- }
+ gcc_assert (GET_MODE (rhs) == mode || GET_MODE (rhs) == VOIDmode);
- if (!inv)
+ if (CONSTANT_P (rhs)
+ || REG_P (rhs)
+ || code == SUBREG)
{
- regno = REGNO (op);
- if (!last_def[regno])
- inv = true;
- else if (last_def[regno] == const0_rtx)
+ if (!iv_analyze_op (insn, rhs, iv))
+ return false;
+
+ if (iv->mode == VOIDmode)
{
- if (dump_file)
- fprintf (dump_file, " not simple.\n");
- return false;
+ iv->mode = mode;
+ iv->extend_mode = mode;
}
+
+ return true;
}
- if (inv)
+ switch (code)
{
- iv_constant (iv, op, VOIDmode);
+ case REG:
+ op0 = rhs;
+ break;
- if (dump_file)
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ case NEG:
+ op0 = XEXP (rhs, 0);
+ omode = GET_MODE (op0);
+ break;
+
+ case PLUS:
+ case MINUS:
+ op0 = XEXP (rhs, 0);
+ op1 = XEXP (rhs, 1);
+ break;
+
+ case MULT:
+ op0 = XEXP (rhs, 0);
+ mby = XEXP (rhs, 1);
+ if (!CONSTANT_P (mby))
{
- fprintf (dump_file, " ");
- dump_iv_info (dump_file, iv);
- fprintf (dump_file, "\n");
+ tmp = op0;
+ op0 = mby;
+ mby = tmp;
}
- return true;
- }
+ if (!CONSTANT_P (mby))
+ return false;
+ break;
- def_insn = iv_get_reaching_def (insn, op);
- if (def_insn == const0_rtx)
- {
- if (dump_file)
- fprintf (dump_file, " not simple.\n");
+ case ASHIFT:
+ op0 = XEXP (rhs, 0);
+ mby = XEXP (rhs, 1);
+ if (!CONSTANT_P (mby))
+ return false;
+ break;
+
+ default:
return false;
}
- return iv_analyze (def_insn, op, iv);
-}
-
-/* Analyzes iv DEF defined in INSN and stores the result to *IV. */
-
-bool
-iv_analyze (rtx insn, rtx def, struct rtx_iv *iv)
-{
- unsigned uid;
- rtx set, rhs, mby = NULL_RTX, tmp;
- rtx op0 = NULL_RTX, op1 = NULL_RTX;
- struct rtx_iv iv0, iv1;
- enum machine_mode amode;
- enum rtx_code code;
+ if (op0
+ && !iv_analyze_expr (insn, op0, omode, &iv0))
+ return false;
- if (insn == const0_rtx)
+ if (op1
+ && !iv_analyze_expr (insn, op1, omode, &iv1))
return false;
- if (GET_CODE (def) == SUBREG)
+ switch (code)
{
- if (!subreg_lowpart_p (def))
+ case SIGN_EXTEND:
+ case ZERO_EXTEND:
+ if (!iv_extend (&iv0, code, mode))
+ return false;
+ break;
+
+ case NEG:
+ if (!iv_neg (&iv0))
return false;
+ break;
- if (!iv_analyze (insn, SUBREG_REG (def), iv))
+ case PLUS:
+ case MINUS:
+ if (!iv_add (&iv0, &iv1, code))
return false;
+ break;
- return iv_subreg (iv, GET_MODE (def));
+ case MULT:
+ if (!iv_mult (&iv0, mby))
+ return false;
+ break;
+
+ case ASHIFT:
+ if (!iv_shift (&iv0, mby))
+ return false;
+ break;
+
+ default:
+ break;
}
- if (!insn)
- return iv_analyze_biv (def, iv);
+ *iv = iv0;
+ return iv->base != NULL_RTX;
+}
+
+/* Analyzes iv DEF and stores the result to *IV. */
+
+static bool
+iv_analyze_def (struct df_ref *def, struct rtx_iv *iv)
+{
+ rtx insn = DF_REF_INSN (def);
+ rtx reg = DF_REF_REG (def);
+ rtx set, rhs;
if (dump_file)
{
- fprintf (dump_file, "Analyzing def of ");
- print_rtl (dump_file, def);
+ fprintf (dump_file, "Analysing def of ");
+ print_rtl (dump_file, reg);
fprintf (dump_file, " in insn ");
print_rtl_single (dump_file, insn);
}
- uid = INSN_UID (insn);
- if (insn_info[uid].iv.analysed)
+ if (DF_REF_IV (def))
{
if (dump_file)
fprintf (dump_file, " already analysed.\n");
- *iv = insn_info[uid].iv;
+ *iv = *DF_REF_IV (def);
return iv->base != NULL_RTX;
}
@@ -1034,146 +1045,129 @@ iv_analyze (rtx insn, rtx def, struct rtx_iv *iv)
iv->step = NULL_RTX;
set = single_set (insn);
+ if (!set || SET_DEST (set) != reg)
+ return false;
+
rhs = find_reg_equal_equiv_note (insn);
if (rhs)
rhs = XEXP (rhs, 0);
else
rhs = SET_SRC (set);
- code = GET_CODE (rhs);
- if (CONSTANT_P (rhs))
+ iv_analyze_expr (insn, rhs, GET_MODE (reg), iv);
+ record_iv (def, iv);
+
+ if (dump_file)
{
- op0 = rhs;
- amode = GET_MODE (def);
+ print_rtl (dump_file, reg);
+ fprintf (dump_file, " in insn ");
+ print_rtl_single (dump_file, insn);
+ fprintf (dump_file, " is ");
+ dump_iv_info (dump_file, iv);
+ fprintf (dump_file, "\n");
}
- else
- {
- switch (code)
- {
- case SUBREG:
- if (!subreg_lowpart_p (rhs))
- goto end;
- op0 = rhs;
- break;
-
- case REG:
- op0 = rhs;
- break;
- case SIGN_EXTEND:
- case ZERO_EXTEND:
- case NEG:
- op0 = XEXP (rhs, 0);
- break;
+ return iv->base != NULL_RTX;
+}
- case PLUS:
- case MINUS:
- op0 = XEXP (rhs, 0);
- op1 = XEXP (rhs, 1);
- break;
+/* Analyzes operand OP of INSN and stores the result to *IV. */
- case MULT:
- op0 = XEXP (rhs, 0);
- mby = XEXP (rhs, 1);
- if (!CONSTANT_P (mby))
- {
- gcc_assert (CONSTANT_P (op0));
- tmp = op0;
- op0 = mby;
- mby = tmp;
- }
- break;
+static bool
+iv_analyze_op (rtx insn, rtx op, struct rtx_iv *iv)
+{
+ struct df_ref *def = NULL;
+ enum iv_grd_result res;
- case ASHIFT:
- gcc_assert (!CONSTANT_P (XEXP (rhs, 0)));
- op0 = XEXP (rhs, 0);
- mby = XEXP (rhs, 1);
- break;
+ if (dump_file)
+ {
+ fprintf (dump_file, "Analysing operand ");
+ print_rtl (dump_file, op);
+ fprintf (dump_file, " of insn ");
+ print_rtl_single (dump_file, insn);
+ }
- default:
- gcc_unreachable ();
- }
+ if (CONSTANT_P (op))
+ res = GRD_INVARIANT;
+ else if (GET_CODE (op) == SUBREG)
+ {
+ if (!subreg_lowpart_p (op))
+ return false;
- amode = GET_MODE (rhs);
- }
+ if (!iv_analyze_op (insn, SUBREG_REG (op), iv))
+ return false;
- if (op0)
+ return iv_subreg (iv, GET_MODE (op));
+ }
+ else
{
- if (!iv_analyze_op (insn, op0, &iv0))
- goto end;
-
- if (iv0.mode == VOIDmode)
+ res = iv_get_reaching_def (insn, op, &def);
+ if (res == GRD_INVALID)
{
- iv0.mode = amode;
- iv0.extend_mode = amode;
+ if (dump_file)
+ fprintf (dump_file, " not simple.\n");
+ return false;
}
}
- if (op1)
+ if (res == GRD_INVARIANT)
{
- if (!iv_analyze_op (insn, op1, &iv1))
- goto end;
+ iv_constant (iv, op, VOIDmode);
- if (iv1.mode == VOIDmode)
+ if (dump_file)
{
- iv1.mode = amode;
- iv1.extend_mode = amode;
+ fprintf (dump_file, " ");
+ dump_iv_info (dump_file, iv);
+ fprintf (dump_file, "\n");
}
+ return true;
}
- switch (code)
- {
- case SIGN_EXTEND:
- case ZERO_EXTEND:
- if (!iv_extend (&iv0, code, amode))
- goto end;
- break;
+ if (res == GRD_MAYBE_BIV)
+ return iv_analyze_biv (op, iv);
- case NEG:
- if (!iv_neg (&iv0))
- goto end;
- break;
+ return iv_analyze_def (def, iv);
+}
- case PLUS:
- case MINUS:
- if (!iv_add (&iv0, &iv1, code))
- goto end;
- break;
+/* Analyzes value VAL at INSN and stores the result to *IV. */
- case MULT:
- if (!iv_mult (&iv0, mby))
- goto end;
- break;
+bool
+iv_analyze (rtx insn, rtx val, struct rtx_iv *iv)
+{
+ rtx reg;
- case ASHIFT:
- if (!iv_shift (&iv0, mby))
- goto end;
- break;
+ /* We must find the insn in that val is used, so that we get to UD chains.
+ Since the function is sometimes called on result of get_condition,
+ this does not necessarily have to be directly INSN; scan also the
+ following insns. */
+ if (simple_reg_p (val))
+ {
+ if (GET_CODE (val) == SUBREG)
+ reg = SUBREG_REG (val);
+ else
+ reg = val;
- default:
- break;
+ while (!df_find_use (df, insn, reg))
+ insn = NEXT_INSN (insn);
}
- *iv = iv0;
+ return iv_analyze_op (insn, val, iv);
+}
- end:
- iv->analysed = true;
- insn_info[uid].iv = *iv;
+/* Analyzes definition of DEF in INSN and stores the result to IV. */
- if (dump_file)
- {
- print_rtl (dump_file, def);
- fprintf (dump_file, " in insn ");
- print_rtl_single (dump_file, insn);
- fprintf (dump_file, " is ");
- dump_iv_info (dump_file, iv);
- fprintf (dump_file, "\n");
- }
+bool
+iv_analyze_result (rtx insn, rtx def, struct rtx_iv *iv)
+{
+ struct df_ref *adef;
- return iv->base != NULL_RTX;
+ adef = df_find_def (df, insn, def);
+ if (!adef)
+ return false;
+
+ return iv_analyze_def (adef, iv);
}
-/* Checks whether definition of register REG in INSN a basic induction
+/* Checks whether definition of register REG in INSN is a basic induction
variable. IV analysis must have been initialized (via a call to
iv_analysis_loop_init) for this function to produce a result. */
@@ -1181,14 +1175,22 @@ bool
biv_p (rtx insn, rtx reg)
{
struct rtx_iv iv;
+ struct df_ref *def, *last_def;
- if (!REG_P (reg))
+ if (!simple_reg_p (reg))
return false;
- if (last_def[REGNO (reg)] != insn)
+ def = df_find_def (df, insn, reg);
+ gcc_assert (def != NULL);
+ if (!latch_dominating_def (reg, &last_def))
+ return false;
+ if (last_def != def)
return false;
- return iv_analyze_biv (reg, &iv);
+ if (!iv_analyze_biv (reg, &iv))
+ return false;
+
+ return iv.step != const0_rtx;
}
/* Calculates value of IV at ITERATION-th iteration. */
@@ -1230,21 +1232,12 @@ get_iv_value (struct rtx_iv *iv, rtx iteration)
void
iv_analysis_done (void)
{
- max_insn_no = 0;
- max_reg_no = 0;
- if (insn_info)
- {
- free (insn_info);
- insn_info = NULL;
- }
- if (last_def)
- {
- free (last_def);
- last_def = NULL;
- }
- if (bivs)
+ if (df)
{
- free (bivs);
+ clear_iv_info ();
+ df_finish (df);
+ df = NULL;
+ htab_delete (bivs);
bivs = NULL;
}
}
@@ -1996,7 +1989,7 @@ static void
iv_number_of_iterations (struct loop *loop, rtx insn, rtx condition,
struct niter_desc *desc)
{
- rtx op0, op1, delta, step, bound, may_xform, def_insn, tmp, tmp0, tmp1;
+ rtx op0, op1, delta, step, bound, may_xform, tmp, tmp0, tmp1;
struct rtx_iv iv0, iv1, tmp_iv;
rtx assumption, may_not_xform;
enum rtx_code cond;
@@ -2038,15 +2031,13 @@ iv_number_of_iterations (struct loop *loop, rtx insn, rtx condition,
goto fail;
op0 = XEXP (condition, 0);
- def_insn = iv_get_reaching_def (insn, op0);
- if (!iv_analyze (def_insn, op0, &iv0))
+ if (!iv_analyze (insn, op0, &iv0))
goto fail;
if (iv0.extend_mode == VOIDmode)
iv0.mode = iv0.extend_mode = mode;
op1 = XEXP (condition, 1);
- def_insn = iv_get_reaching_def (insn, op1);
- if (!iv_analyze (def_insn, op1, &iv1))
+ if (!iv_analyze (insn, op1, &iv1))
goto fail;
if (iv1.extend_mode == VOIDmode)
iv1.mode = iv1.extend_mode = mode;
diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c
index 69b1eb675c0..3b4c8e5931c 100644
--- a/gcc/loop-unroll.c
+++ b/gcc/loop-unroll.c
@@ -33,7 +33,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
#include "expr.h"
#include "hashtab.h"
#include "recog.h"
-#include "varray.h"
/* This pass performs loop unrolling and peeling. We only perform these
optimizations on innermost loops (with single exception) because
@@ -1670,7 +1669,7 @@ analyze_iv_to_split_insn (rtx insn)
if (!biv_p (insn, dest))
return NULL;
- ok = iv_analyze (insn, dest, &iv);
+ ok = iv_analyze_result (insn, dest, &iv);
gcc_assert (ok);
if (iv.step == const0_rtx
diff --git a/gcc/loop-unswitch.c b/gcc/loop-unswitch.c
index 8a71f72fd53..c76f481e08e 100644
--- a/gcc/loop-unswitch.c
+++ b/gcc/loop-unswitch.c
@@ -172,7 +172,7 @@ unswitch_loops (struct loops *loops)
static rtx
may_unswitch_on (basic_block bb, struct loop *loop, rtx *cinsn)
{
- rtx test, at, insn, op[2], stest;
+ rtx test, at, op[2], stest;
struct rtx_iv iv;
unsigned i;
enum machine_mode mode;
@@ -205,8 +205,7 @@ may_unswitch_on (basic_block bb, struct loop *loop, rtx *cinsn)
if (CONSTANT_P (op[i]))
continue;
- insn = iv_get_reaching_def (at, op[i]);
- if (!iv_analyze (insn, op[i], &iv))
+ if (!iv_analyze (at, op[i], &iv))
return NULL_RTX;
if (iv.step != const0_rtx
|| iv.first_special)
diff --git a/gcc/optabs.c b/gcc/optabs.c
index da5251c554b..75d6894a977 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -2721,7 +2721,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
target = gen_reg_rtx (outmode);
emit_libcall_block (insns, target, value,
- gen_rtx_fmt_e (unoptab->code, mode, op0));
+ gen_rtx_fmt_e (unoptab->code, outmode, op0));
return target;
}