/* CPU mode switching
- Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
- Free Software Foundation, Inc.
+ Copyright (C) 1998-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
-#include "real.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "basic-block.h"
-#include "output.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "function.h"
+#include "regs.h"
+#include "emit-rtl.h"
+#include "cfgrtl.h"
+#include "cfganal.h"
+#include "lcm.h"
+#include "cfgcleanup.h"
#include "tree-pass.h"
-#include "timevar.h"
-#include "df.h"
/* We want target macros for the mode switching code to be able to refer
to instruction attribute values. */
and finding all the insns which require a specific mode. Each insn gets
a unique struct seginfo element. These structures are inserted into a list
for each basic block. For each entity, there is an array of bb_info over
- the flow graph basic blocks (local var 'bb_info'), and contains a list
+ the flow graph basic blocks (local var 'bb_info'), which contains a list
of all insns within that basic block, in the order they are encountered.
For each entity, any basic block WITHOUT any insns requiring a specific
- mode are given a single entry, without a mode. (Each basic block
- in the flow graph must have at least one entry in the segment table.)
+ mode are given a single entry without a mode (each basic block in the
+ flow graph must have at least one entry in the segment table).
The LCM algorithm is then run over the flow graph to determine where to
- place the sets to the highest-priority value in respect of first the first
+ place the sets to the highest-priority mode with respect to the first
insn in any one block. Any adjustments required to the transparency
vectors are made, then the next iteration starts for the next-lower
priority mode, till for each entity all modes are exhausted.
- More details are located in the code for optimize_mode_switching(). */
+ More details can be found in the code of optimize_mode_switching. */
\f
/* This structure contains the information for each insn which requires
either single or double mode to be set.
struct seginfo
{
int mode;
- rtx insn_ptr;
+ rtx_insn *insn_ptr;
int bbnum;
struct seginfo *next;
HARD_REG_SET regs_live;
{
struct seginfo *seginfo;
int computing;
+ int mode_out;
+ int mode_in;
};
-/* These bitmaps are used for the LCM algorithm. */
-
-static sbitmap *antic;
-static sbitmap *transp;
-static sbitmap *comp;
-
-static struct seginfo * new_seginfo (int, rtx, int, HARD_REG_SET);
+static struct seginfo * new_seginfo (int, rtx_insn *, int, HARD_REG_SET);
static void add_seginfo (struct bb_info *, struct seginfo *);
static void reg_dies (rtx, HARD_REG_SET *);
-static void reg_becomes_live (rtx, rtx, void *);
-static void make_preds_opaque (basic_block, int);
-\f
+static void reg_becomes_live (rtx, const_rtx, void *);
+
+/* Clear ode I from entity J in bitmap B. */
+#define clear_mode_bit(b, j, i) \
+ bitmap_clear_bit (b, (j * max_num_modes) + i)
+
+/* Test mode I from entity J in bitmap B. */
+#define mode_bit_p(b, j, i) \
+ bitmap_bit_p (b, (j * max_num_modes) + i)
+
+/* Set mode I from entity J in bitmal B. */
+#define set_mode_bit(b, j, i) \
+ bitmap_set_bit (b, (j * max_num_modes) + i)
+
+/* Emit modes segments from EDGE_LIST associated with entity E.
+ INFO gives mode availability for each mode. */
+
+static bool
+commit_mode_sets (struct edge_list *edge_list, int e, struct bb_info *info)
+{
+ bool need_commit = false;
+
+ for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
+ {
+ edge eg = INDEX_EDGE (edge_list, ed);
+ int mode;
+
+ if ((mode = (int)(intptr_t)(eg->aux)) != -1)
+ {
+ HARD_REG_SET live_at_edge;
+ basic_block src_bb = eg->src;
+ int cur_mode = info[src_bb->index].mode_out;
+ rtx_insn *mode_set;
+
+ REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
-/* This function will allocate a new BBINFO structure, initialized
- with the MODE, INSN, and basic block BB parameters. */
+ rtl_profile_for_edge (eg);
+ start_sequence ();
+
+ targetm.mode_switching.emit (e, mode, cur_mode, live_at_edge);
+
+ mode_set = get_insns ();
+ end_sequence ();
+ default_rtl_profile ();
+
+ /* Do not bother to insert empty sequence. */
+ if (mode_set == NULL)
+ continue;
+
+ /* We should not get an abnormal edge here. */
+ gcc_assert (! (eg->flags & EDGE_ABNORMAL));
+
+ need_commit = true;
+ insert_insn_on_edge (mode_set, eg);
+ }
+ }
+
+ return need_commit;
+}
+
+/* Allocate a new BBINFO structure, initialized with the MODE, INSN,
+ and basic block BB parameters.
+ INSN may not be a NOTE_INSN_BASIC_BLOCK, unless it is an empty
+ basic block; that allows us later to insert instructions in a FIFO-like
+ manner. */
static struct seginfo *
-new_seginfo (int mode, rtx insn, int bb, HARD_REG_SET regs_live)
+new_seginfo (int mode, rtx_insn *insn, int bb, HARD_REG_SET regs_live)
{
struct seginfo *ptr;
+
+ gcc_assert (!NOTE_INSN_BASIC_BLOCK_P (insn)
+ || insn == BB_END (NOTE_BASIC_BLOCK (insn)));
ptr = XNEW (struct seginfo);
ptr->mode = mode;
ptr->insn_ptr = insn;
}
}
-/* Make all predecessors of basic block B opaque, recursively, till we hit
- some that are already non-transparent, or an edge where aux is set; that
- denotes that a mode set is to be done on that edge.
- J is the bit number in the bitmaps that corresponds to the entity that
- we are currently handling mode-switching for. */
-
-static void
-make_preds_opaque (basic_block b, int j)
-{
- edge e;
- edge_iterator ei;
-
- FOR_EACH_EDGE (e, ei, b->preds)
- {
- basic_block pb = e->src;
-
- if (e->aux || ! TEST_BIT (transp[pb->index], j))
- continue;
-
- RESET_BIT (transp[pb->index], j);
- make_preds_opaque (pb, j);
- }
-}
-
/* Record in LIVE that register REG died. */
static void
This is called via note_stores. */
static void
-reg_becomes_live (rtx reg, rtx setter ATTRIBUTE_UNUSED, void *live)
+reg_becomes_live (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, void *live)
{
int regno;
add_to_hard_reg_set ((HARD_REG_SET *) live, GET_MODE (reg), regno);
}
-/* Make sure if MODE_ENTRY is defined the MODE_EXIT is defined
- and vice versa. */
-#if defined (MODE_ENTRY) != defined (MODE_EXIT)
- #error "Both MODE_ENTRY and MODE_EXIT must be defined"
-#endif
-
-#if defined (MODE_ENTRY) && defined (MODE_EXIT)
/* Split the fallthrough edge to the exit block, so that we can note
that there NORMAL_MODE is required. Return the new block if it's
inserted before the exit block. Otherwise return null. */
fallthrough edge; there can be at most one, but there could be
none at all, e.g. when exit is called. */
pre_exit = 0;
- FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR->preds)
+ FOR_EACH_EDGE (eg, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
if (eg->flags & EDGE_FALLTHRU)
{
basic_block src_bb = eg->src;
- rtx last_insn, ret_reg;
+ rtx_insn *last_insn;
+ rtx ret_reg;
gcc_assert (!pre_exit);
/* If this function returns a value at the end, we have to
insert the final mode switch before the return value copy
- to its hard register. */
- if (EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 1
+ to its hard register.
+
+ x86 targets use mode-switching infrastructure to
+ conditionally insert vzeroupper instruction at the exit
+ from the function where there is no need to switch the
+ mode before the return value copy. The vzeroupper insertion
+ pass runs after reload, so use !reload_completed as a stand-in
+ for x86 to skip the search for the return value copy insn.
+
+ N.b.: the code below assumes that the return copy insn
+ immediately precedes its corresponding use insn. This
+ assumption does not hold after reload, since sched1 pass
+ can schedule the return copy insn away from its
+ corresponding use insn. */
+ if (!reload_completed
+ && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 1
&& NONJUMP_INSN_P ((last_insn = BB_END (src_bb)))
&& GET_CODE (PATTERN (last_insn)) == USE
&& GET_CODE ((ret_reg = XEXP (PATTERN (last_insn), 0))) == REG)
{
int ret_start = REGNO (ret_reg);
- int nregs = hard_regno_nregs[ret_start][GET_MODE (ret_reg)];
+ int nregs = REG_NREGS (ret_reg);
int ret_end = ret_start + nregs;
- int short_block = 0;
- int maybe_builtin_apply = 0;
- int forced_late_switch = 0;
- rtx before_return_copy;
+ bool short_block = false;
+ bool multi_reg_return = false;
+ bool forced_late_switch = false;
+ rtx_insn *before_return_copy;
do
{
- rtx return_copy = PREV_INSN (last_insn);
+ rtx_insn *return_copy = PREV_INSN (last_insn);
rtx return_copy_pat, copy_reg;
int copy_start, copy_num;
int j;
- if (INSN_P (return_copy))
+ if (NONDEBUG_INSN_P (return_copy))
{
+ /* When using SJLJ exceptions, the call to the
+ unregister function is inserted between the
+ clobber of the return value and the copy.
+ We do not want to split the block before this
+ or any other call; if we have not found the
+ copy yet, the copy must have been deleted. */
+ if (CALL_P (return_copy))
+ {
+ short_block = true;
+ break;
+ }
return_copy_pat = PATTERN (return_copy);
switch (GET_CODE (return_copy_pat))
{
case USE:
- /* Skip __builtin_apply pattern. */
+ /* Skip USEs of multiple return registers.
+ __builtin_apply pattern is also handled here. */
if (GET_CODE (XEXP (return_copy_pat, 0)) == REG
- && (FUNCTION_VALUE_REGNO_P
+ && (targetm.calls.function_value_regno_p
(REGNO (XEXP (return_copy_pat, 0)))))
{
- maybe_builtin_apply = 1;
+ multi_reg_return = true;
last_insn = return_copy;
continue;
}
&& GET_CODE (SUBREG_REG (copy_reg)) == REG)
copy_start = REGNO (SUBREG_REG (copy_reg));
else
- break;
- if (copy_start >= FIRST_PSEUDO_REGISTER)
- break;
- copy_num
- = hard_regno_nregs[copy_start][GET_MODE (copy_reg)];
+ {
+ /* When control reaches end of non-void function,
+ there are no return copy insns at all. This
+ avoids an ice on that invalid function. */
+ if (ret_start + nregs == ret_end)
+ short_block = true;
+ break;
+ }
+ if (!targetm.calls.function_value_regno_p (copy_start))
+ copy_num = 0;
+ else
+ copy_num = hard_regno_nregs (copy_start,
+ GET_MODE (copy_reg));
/* If the return register is not likely spilled, - as is
the case for floating point on SH4 - then it might
for (j = n_entities - 1; j >= 0; j--)
{
int e = entity_map[j];
- int mode = MODE_NEEDED (e, return_copy);
+ int mode =
+ targetm.mode_switching.needed (e, return_copy);
- if (mode != num_modes[e] && mode != MODE_EXIT (e))
+ if (mode != num_modes[e]
+ && mode != targetm.mode_switching.exit (e))
break;
}
if (j >= 0)
{
+ /* __builtin_return emits a sequence of loads to all
+ return registers. One of them might require
+ another mode than MODE_EXIT, even if it is
+ unrelated to the return value, so we want to put
+ the final mode switch after it. */
+ if (multi_reg_return
+ && targetm.calls.function_value_regno_p
+ (copy_start))
+ forced_late_switch = true;
+
/* For the SH4, floating point loads depend on fpscr,
thus we might need to put the final mode switch
after the return value copy. That is still OK,
if (copy_start >= ret_start
&& copy_start + copy_num <= ret_end
&& OBJECT_P (SET_SRC (return_copy_pat)))
- forced_late_switch = 1;
+ forced_late_switch = true;
break;
}
+ if (copy_num == 0)
+ {
+ last_insn = return_copy;
+ continue;
+ }
if (copy_start >= ret_start
&& copy_start + copy_num <= ret_end)
nregs -= copy_num;
- else if (!maybe_builtin_apply
- || !FUNCTION_VALUE_REGNO_P (copy_start))
+ else if (!multi_reg_return
+ || !targetm.calls.function_value_regno_p
+ (copy_start))
break;
last_insn = return_copy;
}
isolated use. */
if (return_copy == BB_HEAD (src_bb))
{
- short_block = 1;
+ short_block = true;
break;
}
last_insn = return_copy;
}
while (nregs);
-
+
/* If we didn't see a full return value copy, verify that there
is a plausible reason for this. If some, but not all of the
return register is likely spilled, we can expect that there
gcc_assert (!nregs
|| forced_late_switch
|| short_block
- || !(CLASS_LIKELY_SPILLED_P
+ || !(targetm.class_likely_spilled_p
(REGNO_REG_CLASS (ret_start)))
- || (nregs
- != hard_regno_nregs[ret_start][GET_MODE (ret_reg)])
+ || nregs != REG_NREGS (ret_reg)
/* For multi-hard-register floating point
values, sometimes the likely-spilled part
is ordinarily copied first, then the other
failures, so let it pass. */
|| (GET_MODE_CLASS (GET_MODE (ret_reg)) != MODE_INT
&& nregs != 1));
-
- if (INSN_P (last_insn))
+
+ if (!NOTE_INSN_BASIC_BLOCK_P (last_insn))
{
before_return_copy
= emit_note_before (NOTE_INSN_DELETED, last_insn);
require a different mode than MODE_EXIT, so if we might
have such instructions, keep them in a separate block
from pre_exit. */
- if (last_insn != BB_HEAD (src_bb))
- src_bb = split_block (src_bb,
- PREV_INSN (before_return_copy))->dest;
+ src_bb = split_block (src_bb,
+ PREV_INSN (before_return_copy))->dest;
}
else
before_return_copy = last_insn;
return pre_exit;
}
-#endif
/* Find all insns that need a particular mode setting, and insert the
necessary mode switches. Return true if we did work. */
static int
optimize_mode_switching (void)
{
- rtx insn;
int e;
basic_block bb;
- int need_commit = 0;
- sbitmap *kill;
- struct edge_list *edge_list;
+ bool need_commit = false;
static const int num_modes[] = NUM_MODES_FOR_MODE_SWITCHING;
#define N_ENTITIES ARRAY_SIZE (num_modes)
int entity_map[N_ENTITIES];
struct bb_info *bb_info[N_ENTITIES];
int i, j;
- int n_entities;
+ int n_entities = 0;
int max_num_modes = 0;
- bool emited = false;
- basic_block post_entry ATTRIBUTE_UNUSED, pre_exit ATTRIBUTE_UNUSED;
+ bool emitted ATTRIBUTE_UNUSED = false;
+ basic_block post_entry = 0;
+ basic_block pre_exit = 0;
+ struct edge_list *edge_list = 0;
+
+ /* These bitmaps are used for the LCM algorithm. */
+ sbitmap *kill, *del, *insert, *antic, *transp, *comp;
+ sbitmap *avin, *avout;
- for (e = N_ENTITIES - 1, n_entities = 0; e >= 0; e--)
+ for (e = N_ENTITIES - 1; e >= 0; e--)
if (OPTIMIZE_MODE_SWITCHING (e))
{
int entry_exit_extra = 0;
/* Create the list of segments within each basic block.
If NORMAL_MODE is defined, allow for two extra
blocks split from the entry and exit block. */
-#if defined (MODE_ENTRY) && defined (MODE_EXIT)
- entry_exit_extra = 3;
-#endif
+ if (targetm.mode_switching.entry && targetm.mode_switching.exit)
+ entry_exit_extra = 3;
+
bb_info[n_entities]
- = XCNEWVEC (struct bb_info, last_basic_block + entry_exit_extra);
+ = XCNEWVEC (struct bb_info,
+ last_basic_block_for_fn (cfun) + entry_exit_extra);
entity_map[n_entities++] = e;
if (num_modes[e] > max_num_modes)
max_num_modes = num_modes[e];
if (! n_entities)
return 0;
-#if defined (MODE_ENTRY) && defined (MODE_EXIT)
- /* Split the edge from the entry block, so that we can note that
- there NORMAL_MODE is supplied. */
- post_entry = split_edge (single_succ_edge (ENTRY_BLOCK_PTR));
- pre_exit = create_pre_exit (n_entities, entity_map, num_modes);
-#endif
+ /* Make sure if MODE_ENTRY is defined MODE_EXIT is defined. */
+ gcc_assert ((targetm.mode_switching.entry && targetm.mode_switching.exit)
+ || (!targetm.mode_switching.entry
+ && !targetm.mode_switching.exit));
+
+ if (targetm.mode_switching.entry && targetm.mode_switching.exit)
+ {
+ /* Split the edge from the entry block, so that we can note that
+ there NORMAL_MODE is supplied. */
+ post_entry = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+ pre_exit = create_pre_exit (n_entities, entity_map, num_modes);
+ }
df_analyze ();
/* Create the bitmap vectors. */
-
- antic = sbitmap_vector_alloc (last_basic_block, n_entities);
- transp = sbitmap_vector_alloc (last_basic_block, n_entities);
- comp = sbitmap_vector_alloc (last_basic_block, n_entities);
-
- sbitmap_vector_ones (transp, last_basic_block);
+ antic = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+ transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+ comp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+ avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+ avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+ kill = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities * max_num_modes);
+
+ bitmap_vector_ones (transp, last_basic_block_for_fn (cfun));
+ bitmap_vector_clear (antic, last_basic_block_for_fn (cfun));
+ bitmap_vector_clear (comp, last_basic_block_for_fn (cfun));
for (j = n_entities - 1; j >= 0; j--)
{
int e = entity_map[j];
int no_mode = num_modes[e];
struct bb_info *info = bb_info[j];
+ rtx_insn *insn;
/* Determine what the first use (if any) need for a mode of entity E is.
This will be the mode that is anticipatable for this block.
Also compute the initial transparency settings. */
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
struct seginfo *ptr;
int last_mode = no_mode;
+ bool any_set_required = false;
HARD_REG_SET live_now;
+ info[bb->index].mode_out = info[bb->index].mode_in = no_mode;
+
REG_SET_TO_HARD_REG_SET (live_now, df_get_live_in (bb));
/* Pretend the mode is clobbered across abnormal edges. */
{
edge_iterator ei;
- edge e;
- FOR_EACH_EDGE (e, ei, bb->preds)
- if (e->flags & EDGE_COMPLEX)
+ edge eg;
+ FOR_EACH_EDGE (eg, ei, bb->preds)
+ if (eg->flags & EDGE_COMPLEX)
break;
- if (e)
+ if (eg)
{
- ptr = new_seginfo (no_mode, BB_HEAD (bb), bb->index, live_now);
+ rtx_insn *ins_pos = BB_HEAD (bb);
+ if (LABEL_P (ins_pos))
+ ins_pos = NEXT_INSN (ins_pos);
+ gcc_assert (NOTE_INSN_BASIC_BLOCK_P (ins_pos));
+ if (ins_pos != BB_END (bb))
+ ins_pos = NEXT_INSN (ins_pos);
+ ptr = new_seginfo (no_mode, ins_pos, bb->index, live_now);
add_seginfo (info + bb->index, ptr);
- RESET_BIT (transp[bb->index], j);
+ for (i = 0; i < no_mode; i++)
+ clear_mode_bit (transp[bb->index], j, i);
}
}
- for (insn = BB_HEAD (bb);
- insn != NULL && insn != NEXT_INSN (BB_END (bb));
- insn = NEXT_INSN (insn))
+ FOR_BB_INSNS (bb, insn)
{
if (INSN_P (insn))
{
- int mode = MODE_NEEDED (e, insn);
+ int mode = targetm.mode_switching.needed (e, insn);
rtx link;
if (mode != no_mode && mode != last_mode)
{
+ any_set_required = true;
last_mode = mode;
ptr = new_seginfo (mode, insn, bb->index, live_now);
add_seginfo (info + bb->index, ptr);
- RESET_BIT (transp[bb->index], j);
+ for (i = 0; i < no_mode; i++)
+ clear_mode_bit (transp[bb->index], j, i);
}
-#ifdef MODE_AFTER
- last_mode = MODE_AFTER (last_mode, insn);
-#endif
+
+ if (targetm.mode_switching.after)
+ last_mode = targetm.mode_switching.after (e, last_mode,
+ insn);
+
/* Update LIVE_NOW. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD)
}
info[bb->index].computing = last_mode;
- /* Check for blocks without ANY mode requirements. */
- if (last_mode == no_mode)
+ /* Check for blocks without ANY mode requirements.
+ N.B. because of MODE_AFTER, last_mode might still
+ be different from no_mode, in which case we need to
+ mark the block as nontransparent. */
+ if (!any_set_required)
{
ptr = new_seginfo (no_mode, BB_END (bb), bb->index, live_now);
add_seginfo (info + bb->index, ptr);
+ if (last_mode != no_mode)
+ for (i = 0; i < no_mode; i++)
+ clear_mode_bit (transp[bb->index], j, i);
}
}
-#if defined (MODE_ENTRY) && defined (MODE_EXIT)
- {
- int mode = MODE_ENTRY (e);
-
- if (mode != no_mode)
- {
- bb = post_entry;
-
- /* By always making this nontransparent, we save
- an extra check in make_preds_opaque. We also
- need this to avoid confusing pre_edge_lcm when
- antic is cleared but transp and comp are set. */
- RESET_BIT (transp[bb->index], j);
-
- /* Insert a fake computing definition of MODE into entry
- blocks which compute no mode. This represents the mode on
- entry. */
- info[bb->index].computing = mode;
+ if (targetm.mode_switching.entry && targetm.mode_switching.exit)
+ {
+ int mode = targetm.mode_switching.entry (e);
- if (pre_exit)
- info[pre_exit->index].seginfo->mode = MODE_EXIT (e);
- }
- }
-#endif /* NORMAL_MODE */
- }
+ info[post_entry->index].mode_out =
+ info[post_entry->index].mode_in = no_mode;
+ if (pre_exit)
+ {
+ info[pre_exit->index].mode_out =
+ info[pre_exit->index].mode_in = no_mode;
+ }
- kill = sbitmap_vector_alloc (last_basic_block, n_entities);
- for (i = 0; i < max_num_modes; i++)
- {
- int current_mode[N_ENTITIES];
- sbitmap *delete;
- sbitmap *insert;
+ if (mode != no_mode)
+ {
+ bb = post_entry;
+
+ /* By always making this nontransparent, we save
+ an extra check in make_preds_opaque. We also
+ need this to avoid confusing pre_edge_lcm when
+ antic is cleared but transp and comp are set. */
+ for (i = 0; i < no_mode; i++)
+ clear_mode_bit (transp[bb->index], j, i);
+
+ /* Insert a fake computing definition of MODE into entry
+ blocks which compute no mode. This represents the mode on
+ entry. */
+ info[bb->index].computing = mode;
+
+ if (pre_exit)
+ info[pre_exit->index].seginfo->mode =
+ targetm.mode_switching.exit (e);
+ }
+ }
/* Set the anticipatable and computing arrays. */
- sbitmap_vector_zero (antic, last_basic_block);
- sbitmap_vector_zero (comp, last_basic_block);
- for (j = n_entities - 1; j >= 0; j--)
+ for (i = 0; i < no_mode; i++)
{
- int m = current_mode[j] = MODE_PRIORITY_TO_MODE (entity_map[j], i);
- struct bb_info *info = bb_info[j];
+ int m = targetm.mode_switching.priority (entity_map[j], i);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
if (info[bb->index].seginfo->mode == m)
- SET_BIT (antic[bb->index], j);
+ set_mode_bit (antic[bb->index], j, m);
if (info[bb->index].computing == m)
- SET_BIT (comp[bb->index], j);
+ set_mode_bit (comp[bb->index], j, m);
}
}
+ }
- /* Calculate the optimal locations for the
- placement mode switches to modes with priority I. */
-
- FOR_EACH_BB (bb)
- sbitmap_not (kill[bb->index], transp[bb->index]);
- edge_list = pre_edge_lcm (n_entities, transp, comp, antic,
- kill, &insert, &delete);
+ /* Calculate the optimal locations for the
+ placement mode switches to modes with priority I. */
- for (j = n_entities - 1; j >= 0; j--)
- {
- /* Insert all mode sets that have been inserted by lcm. */
- int no_mode = num_modes[entity_map[j]];
-
- /* Wherever we have moved a mode setting upwards in the flow graph,
- the blocks between the new setting site and the now redundant
- computation ceases to be transparent for any lower-priority
- mode of the same entity. First set the aux field of each
- insertion site edge non-transparent, then propagate the new
- non-transparency from the redundant computation upwards till
- we hit an insertion site or an already non-transparent block. */
- for (e = NUM_EDGES (edge_list) - 1; e >= 0; e--)
- {
- edge eg = INDEX_EDGE (edge_list, e);
- int mode;
- basic_block src_bb;
- HARD_REG_SET live_at_edge;
- rtx mode_set;
+ FOR_EACH_BB_FN (bb, cfun)
+ bitmap_not (kill[bb->index], transp[bb->index]);
- eg->aux = 0;
+ edge_list = pre_edge_lcm_avs (n_entities * max_num_modes, transp, comp, antic,
+ kill, avin, avout, &insert, &del);
- if (! TEST_BIT (insert[e], j))
- continue;
-
- eg->aux = (void *)1;
+ for (j = n_entities - 1; j >= 0; j--)
+ {
+ int no_mode = num_modes[entity_map[j]];
- mode = current_mode[j];
- src_bb = eg->src;
+ /* Insert all mode sets that have been inserted by lcm. */
- REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
+ for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
+ {
+ edge eg = INDEX_EDGE (edge_list, ed);
- start_sequence ();
- EMIT_MODE_SET (entity_map[j], mode, live_at_edge);
- mode_set = get_insns ();
- end_sequence ();
+ eg->aux = (void *)(intptr_t)-1;
- /* Do not bother to insert empty sequence. */
- if (mode_set == NULL_RTX)
- continue;
+ for (i = 0; i < no_mode; i++)
+ {
+ int m = targetm.mode_switching.priority (entity_map[j], i);
+ if (mode_bit_p (insert[ed], j, m))
+ {
+ eg->aux = (void *)(intptr_t)m;
+ break;
+ }
+ }
+ }
- /* We should not get an abnormal edge here. */
- gcc_assert (! (eg->flags & EDGE_ABNORMAL));
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ struct bb_info *info = bb_info[j];
+ int last_mode = no_mode;
- need_commit = 1;
- insert_insn_on_edge (mode_set, eg);
- }
+ /* intialize mode in availability for bb. */
+ for (i = 0; i < no_mode; i++)
+ if (mode_bit_p (avout[bb->index], j, i))
+ {
+ if (last_mode == no_mode)
+ last_mode = i;
+ if (last_mode != i)
+ {
+ last_mode = no_mode;
+ break;
+ }
+ }
+ info[bb->index].mode_out = last_mode;
- FOR_EACH_BB_REVERSE (bb)
- if (TEST_BIT (delete[bb->index], j))
+ /* intialize mode out availability for bb. */
+ last_mode = no_mode;
+ for (i = 0; i < no_mode; i++)
+ if (mode_bit_p (avin[bb->index], j, i))
{
- make_preds_opaque (bb, j);
- /* Cancel the 'deleted' mode set. */
- bb_info[j][bb->index].seginfo->mode = no_mode;
+ if (last_mode == no_mode)
+ last_mode = i;
+ if (last_mode != i)
+ {
+ last_mode = no_mode;
+ break;
+ }
}
+ info[bb->index].mode_in = last_mode;
+
+ for (i = 0; i < no_mode; i++)
+ if (mode_bit_p (del[bb->index], j, i))
+ info[bb->index].seginfo->mode = no_mode;
}
- sbitmap_vector_free (delete);
- sbitmap_vector_free (insert);
- clear_aux_for_edges ();
- free_edge_list (edge_list);
- }
+ /* Now output the remaining mode sets in all the segments. */
- /* Now output the remaining mode sets in all the segments. */
- for (j = n_entities - 1; j >= 0; j--)
- {
- int no_mode = num_modes[entity_map[j]];
+ /* In case there was no mode inserted. the mode information on the edge
+ might not be complete.
+ Update mode info on edges and commit pending mode sets. */
+ need_commit |= commit_mode_sets (edge_list, entity_map[j], bb_info[j]);
+
+ /* Reset modes for next entity. */
+ clear_aux_for_edges ();
- FOR_EACH_BB_REVERSE (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
struct seginfo *ptr, *next;
+ int cur_mode = bb_info[j][bb->index].mode_in;
+
for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next)
{
next = ptr->next;
if (ptr->mode != no_mode)
{
- rtx mode_set;
+ rtx_insn *mode_set;
+ rtl_profile_for_bb (bb);
start_sequence ();
- EMIT_MODE_SET (entity_map[j], ptr->mode, ptr->regs_live);
+
+ targetm.mode_switching.emit (entity_map[j], ptr->mode,
+ cur_mode, ptr->regs_live);
mode_set = get_insns ();
end_sequence ();
+ /* modes kill each other inside a basic block. */
+ cur_mode = ptr->mode;
+
/* Insert MODE_SET only if it is nonempty. */
if (mode_set != NULL_RTX)
{
- emited = true;
+ emitted = true;
if (NOTE_INSN_BASIC_BLOCK_P (ptr->insn_ptr))
- emit_insn_after (mode_set, ptr->insn_ptr);
+ /* We need to emit the insns in a FIFO-like manner,
+ i.e. the first to be emitted at our insertion
+ point ends up first in the instruction steam.
+ Because we made sure that NOTE_INSN_BASIC_BLOCK is
+ only used for initially empty basic blocks, we
+ can achieve this by appending at the end of
+ the block. */
+ emit_insn_after
+ (mode_set, BB_END (NOTE_BASIC_BLOCK (ptr->insn_ptr)));
else
emit_insn_before (mode_set, ptr->insn_ptr);
}
+
+ default_rtl_profile ();
}
free (ptr);
free (bb_info[j]);
}
+ free_edge_list (edge_list);
+
/* Finished. Free up all the things we've allocated. */
+ sbitmap_vector_free (del);
+ sbitmap_vector_free (insert);
sbitmap_vector_free (kill);
sbitmap_vector_free (antic);
sbitmap_vector_free (transp);
sbitmap_vector_free (comp);
+ sbitmap_vector_free (avin);
+ sbitmap_vector_free (avout);
if (need_commit)
commit_edge_insertions ();
-#if defined (MODE_ENTRY) && defined (MODE_EXIT)
- cleanup_cfg (CLEANUP_NO_INSN_DEL);
-#else
- if (!need_commit && !emited)
+ if (targetm.mode_switching.entry && targetm.mode_switching.exit)
+ {
+ free_dominance_info (CDI_DOMINATORS);
+ cleanup_cfg (CLEANUP_NO_INSN_DEL);
+ }
+ else if (!need_commit && !emitted)
return 0;
-#endif
return 1;
}
#endif /* OPTIMIZE_MODE_SWITCHING */
\f
-static bool
-gate_mode_switching (void)
+namespace {
+
+const pass_data pass_data_mode_switching =
{
+ RTL_PASS, /* type */
+ "mode_sw", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_MODE_SWITCH, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_df_finish, /* todo_flags_finish */
+};
+
+class pass_mode_switching : public rtl_opt_pass
+{
+public:
+ pass_mode_switching (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_mode_switching, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ /* The epiphany backend creates a second instance of this pass, so we need
+ a clone method. */
+ opt_pass * clone () { return new pass_mode_switching (m_ctxt); }
+ virtual bool gate (function *)
+ {
#ifdef OPTIMIZE_MODE_SWITCHING
- return true;
+ return true;
#else
- return false;
+ return false;
#endif
-}
+ }
-static unsigned int
-rest_of_handle_mode_switching (void)
-{
+ virtual unsigned int execute (function *)
+ {
#ifdef OPTIMIZE_MODE_SWITCHING
- optimize_mode_switching ();
+ optimize_mode_switching ();
#endif /* OPTIMIZE_MODE_SWITCHING */
- return 0;
-}
+ return 0;
+ }
+
+}; // class pass_mode_switching
+} // anon namespace
-struct tree_opt_pass pass_mode_switching =
+rtl_opt_pass *
+make_pass_mode_switching (gcc::context *ctxt)
{
- "mode-sw", /* name */
- gate_mode_switching, /* gate */
- rest_of_handle_mode_switching, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_MODE_SWITCH, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_df_finish |
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
-};
+ return new pass_mode_switching (ctxt);
+}