2014-04-02 Christian Bruel <christian.bruel@st.com>
* basic-block.h (pre_edge_lcm_avs): Declare.
* doc/tm.texi (EMIT_MODE_TOGGLE): Document.
* doc/tm.texi.in (EMIT_MODE_TOGGLE): Idem.
* config/sh/sh.h (EMIT_MODE_TOGGLE): Define.
* config/sh/sh-protos.h (emit_fpu_toggle): Declare
* config/sh/sh.c (emit_fpu_toggle): New function.
* config/sh/sh.md (toggle_pr): Defined for TARGET_SH4_300 and
TARGET_SH4A_FP.
(in_delay_slot): fpscr_toggle don't go in delay slot.
* lcm.c (pre_edge_lcm_avs): Renamed from pre_edge_lcm.
Call clear_aux_for_edges. Fix comments.
(pre_edge_lcm): New wrapper function to call pre_edge_lcm_avs.
(pre_edge_rev_lcm): Idem.
* mode-switching.c (init_modes_infos): New function.
(free_modes_infos): Idem.
(init_modes_infos): Idem
(add_mode_set): Idem.
(get_mode): Idem.
(commit_mode_sets): Idem.
(merge_modes): Idem.
(set_flip_status): Idem
(test_flip_status): Idem.
(optimize_mode_switching): Add support to toggle modes.
2014-04-02 Christian Bruel <christian.bruel@st.com>
* gcc.target/sh/fpchg1.c: New test.
===================================================================
@@ -711,6 +711,9 @@ extern void bitmap_union_of_preds (sbitmap, sbitma
extern struct edge_list *pre_edge_lcm (int, sbitmap *, sbitmap *,
sbitmap *, sbitmap *, sbitmap **,
sbitmap **);
+extern struct edge_list *pre_edge_lcm_avs (int, sbitmap *, sbitmap *,
+ sbitmap *, sbitmap *, sbitmap *,
+ sbitmap *, sbitmap **, sbitmap **);
extern struct edge_list *pre_edge_rev_lcm (int, sbitmap *,
sbitmap *, sbitmap *,
sbitmap *, sbitmap **,
===================================================================
@@ -210,6 +210,7 @@ extern bool check_use_sfunc_addr (rtx, rtx);
#ifdef HARD_CONST
extern void fpscr_set_from_mem (int, HARD_REG_SET);
#endif
+extern bool emit_fpu_toggle (int);
extern void sh_pr_interrupt (struct cpp_reader *);
extern void sh_pr_trapa (struct cpp_reader *);
===================================================================
@@ -10042,6 +10042,17 @@ get_free_reg (HARD_REG_SET regs_live)
return gen_rtx_REG (Pmode, 7);
}
+/* This function switches the fpscr. */
+bool
+emit_fpu_toggle (int e ATTRIBUTE_UNUSED)
+{
+ emit_insn (gen_toggle_pr ());
+ if (TARGET_FMOVD)
+ emit_insn (gen_toggle_sz ());
+
+ return true;
+}
+
/* This function will set the fpscr from memory.
MODE is the mode we are setting it to. */
void
===================================================================
@@ -2259,6 +2259,9 @@ extern int current_function_interrupt;
#define MODE_PRIORITY_TO_MODE(ENTITY, N) \
((TARGET_FPU_SINGLE != 0) ^ (N) ? FP_MODE_SINGLE : FP_MODE_DOUBLE)
+#define EMIT_MODE_TOGGLE(ENTITY, MODE) \
+ ((TARGET_SH4A_FP || TARGET_SH4_300) ? emit_fpu_toggle (ENTITY) : false)
+
#define EMIT_MODE_SET(ENTITY, MODE, HARD_REGS_LIVE) \
fpscr_set_from_mem ((MODE), (HARD_REGS_LIVE))
===================================================================
@@ -504,6 +504,7 @@
(define_attr "in_delay_slot" "yes,no"
(cond [(eq_attr "type" "cbranch") (const_string "no")
(eq_attr "type" "pcload,pcload_si") (const_string "no")
+ (eq_attr "type" "fpscr_toggle") (const_string "no")
(eq_attr "needs_delay_slot" "yes") (const_string "no")
(eq_attr "length" "2") (const_string "yes")
] (const_string "no")))
@@ -12182,15 +12183,10 @@ label:
"fschg"
[(set_attr "type" "fpscr_toggle") (set_attr "fp_set" "unknown")])
-;; There's no way we can use it today, since optimize mode switching
-;; doesn't enable us to know from which mode we're switching to the
-;; mode it requests, to tell whether we can use a relative mode switch
-;; (like toggle_pr) or an absolute switch (like loading fpscr from
-;; memory).
(define_insn "toggle_pr"
[(set (reg:PSI FPSCR_REG)
(xor:PSI (reg:PSI FPSCR_REG) (const_int 524288)))]
- "TARGET_SH4A_FP && ! TARGET_FPU_SINGLE"
+ "(TARGET_SH4A_FP || TARGET_SH4_300)"
"fpchg"
[(set_attr "type" "fpscr_toggle")])
===================================================================
@@ -9723,6 +9723,11 @@ If you define this macro, you also have to define
are optional.
@end defmac
+@defmac EMIT_MODE_TOGGLE (@var{entity}, @var{mode})
+Generates one or more insns to toggle @var{entity}. Return true if entity supports
+toggle. EMIT_MODE_SET is used instead.
+@end defmac
+
@defmac NUM_MODES_FOR_MODE_SWITCHING
If you define @code{OPTIMIZE_MODE_SWITCHING}, you have to define this as
initializer for an array of integers. Each initializer element
===================================================================
@@ -7392,6 +7392,11 @@ If you define this macro, you also have to define
are optional.
@end defmac
+@defmac EMIT_MODE_TOGGLE (@var{entity}, @var{mode})
+Generates one or more insns to toggle @var{entity}. Return true if entity supports
+toggle. EMIT_MODE_SET is used instead.
+@end defmac
+
@defmac NUM_MODES_FOR_MODE_SWITCHING
If you define @code{OPTIMIZE_MODE_SWITCHING}, you have to define this as
initializer for an array of integers. Each initializer element
===================================================================
@@ -377,17 +377,17 @@ compute_insert_delete (struct edge_list *edge_list
}
}
-/* Given local properties TRANSP, ANTLOC, AVOUT, KILL return the insert and
- delete vectors for edge based LCM. Returns an edgelist which is used to
+/* Given local properties TRANSP, ANTLOC, AVLOC, KILL return the insert and
+ delete vectors for edge based LCM and return the AVIN, AVOUT bitmap.
map the insert vector to what edge an expression should be inserted on. */
struct edge_list *
-pre_edge_lcm (int n_exprs, sbitmap *transp,
+pre_edge_lcm_avs (int n_exprs, sbitmap *transp,
sbitmap *avloc, sbitmap *antloc, sbitmap *kill,
+ sbitmap *avin, sbitmap *avout,
sbitmap **insert, sbitmap **del)
{
sbitmap *antin, *antout, *earliest;
- sbitmap *avin, *avout;
sbitmap *later, *laterin;
struct edge_list *edge_list;
int num_edges;
@@ -413,10 +413,7 @@ struct edge_list *
#endif
/* Compute global availability. */
- avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_exprs);
- avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_exprs);
compute_available (avloc, kill, avout, avin);
- sbitmap_vector_free (avin);
/* Compute global anticipatability. */
antin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_exprs);
@@ -444,7 +441,6 @@ struct edge_list *
sbitmap_vector_free (antout);
sbitmap_vector_free (antin);
- sbitmap_vector_free (avout);
later = sbitmap_vector_alloc (num_edges, n_exprs);
@@ -485,6 +481,28 @@ struct edge_list *
return edge_list;
}
+/* Wrapper to allocate avin/avout and call pre_edge_lcm_avs. */
+
+struct edge_list *
+pre_edge_lcm (int n_exprs, sbitmap *transp,
+ sbitmap *avloc, sbitmap *antloc, sbitmap *kill,
+ sbitmap **insert, sbitmap **del)
+{
+ struct edge_list *edge_list;
+ sbitmap *avin, *avout;
+
+ avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_exprs);
+ avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_exprs);
+
+ edge_list = pre_edge_lcm_avs (n_exprs, transp, avloc, antloc, kill,
+ avin, avout, insert, del);
+
+ sbitmap_vector_free (avout);
+ sbitmap_vector_free (avin);
+
+ return edge_list;
+}
+
/* Compute the AVIN and AVOUT vectors from the AVLOC and KILL vectors.
Return the number of passes we performed to iterate to a solution. */
===================================================================
@@ -95,6 +95,178 @@ static void reg_becomes_live (rtx, const_rtx, void
static void make_preds_opaque (basic_block, int);
+/* To support mode switching, the algorithm cannot set the modes after
+ the insert and delete bitmaps are computed by pre_edge_lcm, because
+ 'avin' is computed iteratively for each possible modes for each entity.
+ The mode emission will be done after all mode are processed.
+ (see commit_mode_sets). */
+
+static int **modes_needed; /* modes needs to be inserted on this edge. */
+
+static const int num_modes[] = NUM_MODES_FOR_MODE_SWITCHING;
+
+#ifdef EMIT_MODE_TOGGLE
+
+/* Bitmap to compute mode flipping. */
+
+static sbitmap *mode_in_flip; /* flip in mode status for each basic blocks. */
+static sbitmap *mode_out_flip; /* flip out mode status for each basic blocks. */
+
+/* Test avin modes.
+ if 'out' is 1 we want to know if the mode out of the basic block
+ can be flipped. If 'in' is 1 we want to know if the mode entering the
+ basic block can be flipped.
+ If result is 0, we need to reset the mode. */
+
+static bool
+test_flip_status(int entity, basic_block bb, bool out)
+{
+ if (out)
+ return bitmap_bit_p (mode_out_flip[bb->index], entity);
+ else
+ return bitmap_bit_p (mode_in_flip[bb->index], entity);
+}
+
+/* Merges the avin modes. */
+
+static void
+set_flip_status (sbitmap *avin, sbitmap *avout)
+{
+ basic_block bb;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ int i = bb->index;
+
+ /* Merge modes for each entity for each bb.
+ If multiple avin modes are set for the same bb, they are not
+ exclusive and a flip may not be emitted. */
+ if (! bb_has_eh_pred (bb))
+ bitmap_xor (mode_in_flip[i], mode_in_flip[i], avin[i]);
+ bitmap_xor (mode_out_flip[i], mode_out_flip[i], avout[i]);
+ }
+}
+#endif /* EMIT_MODE_TOGGLE */
+
+/* Allocates and initializes modes_infos. */
+
+static void
+init_modes_infos (int n_entities, int *entity_map)
+{
+ int num_edges = 0;
+ basic_block bb;
+
+ FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR_FOR_FN (cfun),
+ EXIT_BLOCK_PTR_FOR_FN (cfun), next_bb)
+ num_edges += EDGE_COUNT (bb->succs);
+
+ modes_needed = XNEWVEC (int *, n_entities);
+
+ for (int j = 0; j < n_entities; j++)
+ {
+ int e = entity_map[j];
+ int no_mode = num_modes[e];
+
+ modes_needed[j] = XNEWVEC (int, num_edges);
+ for (int ed = 0; ed < num_edges; ed++)
+ modes_needed[j][ed] = no_mode;
+ }
+
+ /* Allocates bitmaps for modes. */
+#ifdef EMIT_MODE_TOGGLE
+ mode_in_flip = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities);
+ mode_out_flip = sbitmap_vector_alloc (last_basic_block_for_fn (cfun),
+ n_entities);
+ bitmap_vector_clear (mode_in_flip, last_basic_block_for_fn (cfun));
+ bitmap_vector_clear (mode_out_flip, last_basic_block_for_fn (cfun));
+#endif
+}
+
+/* frees memory used to hold the modes information. */
+
+static void
+free_modes_infos (int n_entities)
+{
+ int j;
+
+ for (j = 0; j < n_entities; j++)
+ free (modes_needed[j]);
+
+ free (modes_needed);
+#ifdef EMIT_MODE_TOGGLE
+ sbitmap_vector_free (mode_in_flip);
+ sbitmap_vector_free (mode_out_flip);
+#endif
+}
+
+/* records the mode associated with edge e for entity j. */
+
+static void
+add_mode_set (int j, int e, int mode)
+{
+ modes_needed[j][e] = mode;
+}
+
+/* returns the mode needed on edge e for entity j. -1 if none. */
+
+static int
+get_mode (int j, int e)
+{
+ return modes_needed[j][e];
+}
+
+/* Finally, after all the modes after been inserted after lcm, we can
+ process with the mode emission. */
+
+static bool
+commit_mode_sets (struct edge_list *edge_list, int j, int *entity_map)
+{
+ bool need_commit = false;
+
+ for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
+ {
+ int mode;
+ int e = entity_map[j];
+ int no_mode = num_modes[e];
+
+ if ((mode = get_mode (j, ed)) != no_mode)
+ {
+ HARD_REG_SET live_at_edge;
+ edge eg = INDEX_EDGE (edge_list, ed);
+ basic_block src_bb = eg->src;
+ rtx mode_set;
+
+ REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
+
+ rtl_profile_for_edge (eg);
+ start_sequence ();
+
+#ifdef EMIT_MODE_TOGGLE
+ if (! test_flip_status (j, src_bb, true) ||
+ ! EMIT_MODE_TOGGLE (entity_map[j], mode))
+#endif
+ EMIT_MODE_SET (entity_map[j], mode, live_at_edge);
+
+ mode_set = get_insns ();
+ end_sequence ();
+ default_rtl_profile ();
+
+ /* Do not bother to insert empty sequence. */
+ if (mode_set == NULL_RTX)
+ 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;
+}
+
/* This function will allocate a new BBINFO structure, initialized
with the MODE, INSN, and basic block BB parameters. */
@@ -201,7 +373,7 @@ reg_becomes_live (rtx reg, const_rtx setter ATTRIB
inserted before the exit block. Otherwise return null. */
static basic_block
-create_pre_exit (int n_entities, int *entity_map, const int *num_modes)
+create_pre_exit (int n_entities, int *entity_map)
{
edge eg;
edge_iterator ei;
@@ -455,10 +627,8 @@ optimize_mode_switching (void)
rtx insn;
int e;
basic_block bb;
- int need_commit = 0;
+ bool need_commit = false;
sbitmap *kill;
- struct edge_list *edge_list;
- 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];
@@ -467,6 +637,8 @@ optimize_mode_switching (void)
int max_num_modes = 0;
bool emitted ATTRIBUTE_UNUSED = false;
basic_block post_entry ATTRIBUTE_UNUSED, pre_exit ATTRIBUTE_UNUSED;
+ sbitmap *avin, *avout;
+ struct edge_list *edge_list = 0;
for (e = N_ENTITIES - 1, n_entities = 0; e >= 0; e--)
if (OPTIMIZE_MODE_SWITCHING (e))
@@ -494,7 +666,7 @@ optimize_mode_switching (void)
/* 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);
+ pre_exit = create_pre_exit (n_entities, entity_map);
#endif
df_analyze ();
@@ -504,6 +676,8 @@ optimize_mode_switching (void)
antic = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
transp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
comp = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
+ avin = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
+ avout = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
bitmap_vector_ones (transp, last_basic_block_for_fn (cfun));
@@ -610,6 +784,9 @@ optimize_mode_switching (void)
}
kill = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
+
+ init_modes_infos (n_entities, entity_map);
+
for (i = 0; i < max_num_modes; i++)
{
int current_mode[N_ENTITIES];
@@ -639,9 +816,14 @@ optimize_mode_switching (void)
FOR_EACH_BB_FN (bb, cfun)
bitmap_not (kill[bb->index], transp[bb->index]);
- edge_list = pre_edge_lcm (n_entities, transp, comp, antic,
- kill, &insert, &del);
+ edge_list = pre_edge_lcm_avs (n_entities, transp, comp, antic,
+ kill, avin, avout, &insert, &del);
+#ifdef EMIT_MODE_TOGGLE
+ /* Merge modes for all entities. */
+ set_flip_status (avin, avout);
+#endif
+
for (j = n_entities - 1; j >= 0; j--)
{
/* Insert all mode sets that have been inserted by lcm. */
@@ -657,10 +839,6 @@ optimize_mode_switching (void)
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;
eg->aux = 0;
@@ -669,27 +847,8 @@ optimize_mode_switching (void)
eg->aux = (void *)1;
- mode = current_mode[j];
- src_bb = eg->src;
-
- REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
-
- rtl_profile_for_edge (eg);
- start_sequence ();
- EMIT_MODE_SET (entity_map[j], mode, live_at_edge);
- mode_set = get_insns ();
- end_sequence ();
- default_rtl_profile ();
-
- /* Do not bother to insert empty sequence. */
- if (mode_set == NULL_RTX)
- continue;
-
- /* We should not get an abnormal edge here. */
- gcc_assert (! (eg->flags & EDGE_ABNORMAL));
-
- need_commit = 1;
- insert_insn_on_edge (mode_set, eg);
+ /* Remember we need to emit it. */
+ add_mode_set(j, e, current_mode[j]);
}
FOR_EACH_BB_REVERSE_FN (bb, cfun)
@@ -704,7 +863,10 @@ optimize_mode_switching (void)
sbitmap_vector_free (del);
sbitmap_vector_free (insert);
clear_aux_for_edges ();
- free_edge_list (edge_list);
+
+ /* Keep an edge_list for later. */
+ if (i != max_num_modes - 1)
+ free_edge_list (edge_list);
}
/* Now output the remaining mode sets in all the segments. */
@@ -712,9 +874,18 @@ optimize_mode_switching (void)
{
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, j, entity_map);
+
FOR_EACH_BB_REVERSE_FN (bb, cfun)
{
struct seginfo *ptr, *next;
+#ifdef EMIT_MODE_TOGGLE
+ bool toggle_p = test_flip_status (j, bb, false);
+#endif
+
for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next)
{
next = ptr->next;
@@ -724,7 +895,17 @@ optimize_mode_switching (void)
rtl_profile_for_bb (bb);
start_sequence ();
+
+#ifdef EMIT_MODE_TOGGLE
+ if (! toggle_p ||
+ ! EMIT_MODE_TOGGLE (entity_map[j], ptr->mode))
+#endif
EMIT_MODE_SET (entity_map[j], ptr->mode, ptr->regs_live);
+
+#ifdef EMIT_MODE_TOGGLE
+ toggle_p = true;
+#endif
+
mode_set = get_insns ();
end_sequence ();
@@ -748,11 +929,16 @@ optimize_mode_switching (void)
free (bb_info[j]);
}
+ free_edge_list (edge_list);
+ free_modes_infos (n_entities);
+
/* Finished. Free up all the things we've allocated. */
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 ();
===================================================================
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "" } { "-m4a" "-m4-300" } }
+
+/* Make sure that fpchg is used. */
+/* { dg-final { scan-assembler "fpchg" } } */
+/* { dg-final { scan-assembler-not "fpscr" } } */
+
+extern float c;
+
+void
+foo(int j)
+{
+ while (j--)
+ c++;
+
+}