diff mbox

Extend mode-switching to support toggle (2/2)

Message ID 534FDE90.7060709@st.com
State New
Headers show

Commit Message

Christian Bruel April 17, 2014, 2 p.m. UTC
and the toggle-support hookized....

many thanks,

Christian
diff mbox

Patch

2014-04-02  Christian Bruel  <christian.bruel@st.com>

	* target.def (mode_switching): New hook vector.
	(toggle_init, toggle_destroy, toggle_set, toggle_test):
	New mode toggle hooks.
	* targhooks.h (default_toggle_test): Declare.
	* basic-block.h (pre_edge_lcm_avs): Declare.
	* 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): Likewise.
	(add_mode_set): Likewise.
	(get_mode): Likewise.
	(commit_mode_sets): Likewise.
	(merge_modes): Likewise.
	(optimize_mode_switching): Support mode toggle.
	(default_priority_to_mode, default_toggle_test): Define.
	* doc/tm.texi.in (TARGET_MODE_TOGGLE_INIT, TARGET_MODE_TOGGLE_TEST)
	(TARGET_MODE_TOGGLE_DESTROY, TARGET_MODE_TOGGLE_SET):
	 New target hooks.
	* doc/tm.texi: Regenerate.
	* config/sh/sh.c (sh4_toggle_init, sh4_toggle_destroy): Add hook and define.
	(sh4_toggle_set, sh4_toggle_test): Likewise.
	(mode_in_flip, mode_out_flip): Add bitmap to compute mode flipping.
	(TARGET_MODE_EMIT): New toggle parameter.
	* 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.
	* config/i386/i386.c (ix86_emit_mode_set): Add bool unused parameter.
	* config/epiphany/epiphany.c (emit_set_fp_mode): Add bool unused parameter.

--- gcc/basic-block.h	2014-01-07 10:30:59.000000000 +0100
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/basic-block.h	2014-04-15 16:17:53.000000000 +0200
@@ -711,6 +711,9 @@ 
 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 **,
--- gcc/config/epiphany/epiphany.c	2014-04-17 13:23:48.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/epiphany/epiphany.c	2014-04-17 13:25:54.000000000 +0200
@@ -2529,7 +2529,8 @@ 
 }
 
 void
-emit_set_fp_mode (int entity, int mode, HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
+emit_set_fp_mode (int entity, int mode, bool toggle ATTRIBUTE_UNUSED,
+		  HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
 {
   rtx save_cc, cc_reg, mask, src, src2;
   enum attr_fp_mode fp_mode;
--- gcc/config/epiphany/epiphany-protos.h	2014-04-17 11:10:36.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/epiphany/epiphany-protos.h	2014-04-17 11:22:02.000000000 +0200
@@ -40,7 +40,8 @@ 
 extern void epiphany_init_expanders (void);
 extern int hard_regno_mode_ok (int regno, enum machine_mode mode);
 #ifdef HARD_CONST
-extern void emit_set_fp_mode (int entity, int mode, HARD_REG_SET regs_live);
+extern void emit_set_fp_mode (int entity, int mode,
+			      bool toggle ATTRIBUTE_UNUSED, HARD_REG_SET regs_live);
 #endif
 extern void epiphany_insert_mode_switch_use (rtx insn, int, int);
 extern void epiphany_expand_set_fp_mode (rtx *operands);
--- gcc/config/epiphany/resolve-sw-modes.c	2014-04-17 11:10:36.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/epiphany/resolve-sw-modes.c	2014-04-17 11:21:07.000000000 +0200
@@ -147,7 +147,7 @@ 
 	    }
 	  start_sequence ();
 	  emit_set_fp_mode (EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN,
-			    jilted_mode, NULL);
+			    jilted_mode, false, NULL);
 	  seq = get_insns ();
 	  end_sequence ();
 	  need_commit = true;
--- gcc/config/i386/i386.c	2014-04-17 13:02:49.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/i386/i386.c	2014-04-17 13:04:18.000000000 +0200
@@ -16409,7 +16409,8 @@ 
    are to be inserted.  */
 
 static void
-ix86_emit_mode_set (int entity, int mode, HARD_REG_SET regs_live)
+ix86_emit_mode_set (int entity, int mode, bool toggle ATTRIBUTE_UNUSED,
+		    HARD_REG_SET regs_live)
 {
   switch (entity)
     {
--- gcc/config/sh/sh.c	2014-04-17 13:23:07.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/sh/sh.c	2014-04-17 13:25:27.000000000 +0200
@@ -202,7 +202,7 @@ 
 static int calc_live_regs (HARD_REG_SET *);
 static HOST_WIDE_INT rounded_frame_size (int);
 static bool sh_frame_pointer_required (void);
-static void sh4_emit_mode_set (int, int, HARD_REG_SET);
+static void sh4_emit_mode_set (int, int, bool, HARD_REG_SET);
 static int sh4_mode_needed (int, rtx);
 static int sh4_mode_after (int, int, rtx);
 static int sh4_mode_entry (int);
@@ -590,9 +590,21 @@ 
 #undef TARGET_MODE_EXIT
 #define TARGET_MODE_EXIT sh4_mode_exit
 
+#undef TARGET_MODE_TOGGLE_INIT
+#define TARGET_MODE_TOGGLE_INIT sh4_toggle_init
+
 #undef TARGET_MODE_PRIORITY
 #define TARGET_MODE_PRIORITY sh4_mode_priority
 
+#undef TARGET_MODE_TOGGLE_DESTROY
+#define TARGET_MODE_TOGGLE_DESTROY sh4_toggle_destroy
+
+#undef TARGET_MODE_TOGGLE_SET
+#define TARGET_MODE_TOGGLE_SET sh4_toggle_set
+
+#undef TARGET_MODE_TOGGLE_TEST
+#define TARGET_MODE_TOGGLE_TEST sh4_toggle_test
+
 /* Return regmode weight for insn.  */
 #define INSN_REGMODE_WEIGHT(INSN, MODE)\
   regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)]
@@ -13531,20 +13543,26 @@ 
 }
 
 static void
-sh4_emit_mode_set (int entity ATTRIBUTE_UNUSED, int mode,
-		   HARD_REG_SET regs_live)
+sh4_emit_mode_set (int ATTRIBUTE_UNUSED entity, int mode, bool toggle, HARD_REG_SET regs_live)
 {
-  fpscr_set_from_mem (mode, regs_live);
+  if ((TARGET_SH4A_FP || TARGET_SH4_300) && toggle)
+    {
+      emit_insn (gen_toggle_pr ());
+      if (TARGET_FMOVD)
+	emit_insn (gen_toggle_sz ());
+    }
+  else
+    fpscr_set_from_mem (mode, regs_live);
 }
 
 static int
-sh4_mode_needed (int entity ATTRIBUTE_UNUSED, rtx insn)
+sh4_mode_needed (int ATTRIBUTE_UNUSED entity, rtx insn)
 {
   return recog_memoized (insn) >= 0  ? get_attr_fp_mode (insn) : FP_MODE_NONE;
 }
 
 static int
-sh4_mode_after (int entity ATTRIBUTE_UNUSED, int mode, rtx insn)
+sh4_mode_after (int ATTRIBUTE_UNUSED entity, int mode, rtx insn)
 {
   if (TARGET_HITACHI && recog_memoized (insn) >= 0 &&
       get_attr_fp_set (insn) != FP_SET_NONE)
@@ -13571,4 +13589,62 @@ 
   return ((TARGET_FPU_SINGLE != 0) ^ (n) ? FP_MODE_SINGLE : FP_MODE_DOUBLE);
 }
 
+/* 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
+sh4_toggle_test (int entity, int index, bool out)
+{
+  if (out)
+    return bitmap_bit_p (mode_out_flip[index], entity);
+  else
+    return bitmap_bit_p (mode_in_flip[index], entity);
+}
+
+/* Merges the modes.  */
+
+static void
+sh4_toggle_set (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]);
+    }
+}
+
+static void
+sh4_toggle_init (int n_entities)
+{
+  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));
+}
+
+void
+sh4_toggle_destroy (void)
+{
+  sbitmap_vector_free (mode_in_flip);
+  sbitmap_vector_free (mode_out_flip);
+}
+
 #include "gt-sh.h"
--- gcc/config/sh/sh.md	2014-04-14 10:33:21.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/config/sh/sh.md	2014-04-15 16:17:48.000000000 +0200
@@ -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")))
@@ -12196,15 +12197,10 @@ 
   "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")])
 
--- gcc/doc/tm.texi	2014-04-17 15:17:11.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/doc/tm.texi	2014-04-17 15:09:51.000000000 +0200
@@ -9736,12 +9736,12 @@ 
 switch is needed / supplied.
 @end defmac
 
-@deftypefn {Target Hook} void TARGET_MODE_EMIT (int @var{entity}, int @var{mode}, HARD_REG_SET @var{regs_live})
-Generate one or more insns to set @var{entity} to @var{mode}. @var{hard_reg_live} is the set of hard registers live at the point where the insn(s) are to be inserted. Sets of a lower numbered entity will be emitted before sets of a higher numbered entity to a mode of the same or lower priority.
+@deftypefn {Target Hook} void TARGET_MODE_EMIT (int @var{entity}, int @var{mode}, bool @var{toggle}, HARD_REG_SET @var{regs_live})
+Generate one or more insns to set @var{entity} to @var{mode}. @var{hard_reg_live} is the set of hard registers live at the point where the insn(s) are to be inserted. @var{toggle} is a boolean to indicate that current mode is known an thus can be toggled. Sets of a lower numbered entity will be emitted before sets of a higher numbered entity to a mode of the same or lower priority.
 @end deftypefn
 
 @deftypefn {Target Hook} int TARGET_MODE_NEEDED (int @var{entity}, rtx @var{insn})
-@var{entity} is an integer specifying a mode-switched entity. If @code{OPTIMIZE_MODE_SWITCHING} is defined, you must define this macro to return an integer value not larger than the corresponding element in @code{NUM_MODES_FOR_MODE_SWITCHING}, to denote the mode that @var{entity} must be switched into prior to the execution of @var{insn}.
+@var{entity} is an integer specifying a mode-switched entity.  If @code{OPTIMIZE_MODE_SWITCHING} is defined, you must define this macro to return an integer value not larger than the corresponding element in @code{NUM_MODES_FOR_MODE_SWITCHING}, to denote the mode that @var{entity} must be switched into prior to the execution of @var{insn}.
 @end deftypefn
 
 @deftypefn {Target Hook} int TARGET_MODE_AFTER (int @var{entity}, int @var{mode}, rtx @var{insn})
@@ -9760,6 +9760,22 @@ 
 This macro specifies the order in which modes for @var{entity} are processed. 0 is the highest priority, @code{NUM_MODES_FOR_MODE_SWITCHING[@var{entity}] - 1} the lowest.  The value of the macro should be an integer designating a mode for @var{entity}.  For any fixed @var{entity}, @code{mode_priority} (@var{entity}, @var{n}) shall be a bijection in 0 @dots{} @code{num_modes_for_mode_switching[@var{entity}] - 1}.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_MODE_TOGGLE_INIT (int @var{n_entity})
+Initializes target-specific data used in maintain toggle information.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MODE_TOGGLE_DESTROY (void)
+Finalizes target-specific data used to maintain toggle information.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_MODE_TOGGLE_SET (sbitmap *@var{avin}, sbitmap *@var{avout})
+Hook called by the mode switching pass to record the modes needed for each entities in entry and exit of each basic block.
+@end deftypefn
+
+@deftypefn {Target Hook} bool TARGET_MODE_TOGGLE_TEST (int @var{entity}, int @var{bb}, bool @var{out})
+Hook used by mode switching pass to test if the current @var{entity}'s mode is available for basic block @var{bb} on all incoming edges (@var{out} false) or outgoing edges (@var{out} true). 
+@end deftypefn
+
 @node Target Attributes
 @section Defining target-specific uses of @code{__attribute__}
 @cindex target attributes
--- gcc/doc/tm.texi.in	2014-04-17 15:16:54.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/doc/tm.texi.in	2014-04-17 15:09:29.000000000 +0200
@@ -7417,6 +7417,14 @@ 
 
 @hook TARGET_MODE_PRIORITY
 
+@hook TARGET_MODE_TOGGLE_INIT
+
+@hook TARGET_MODE_TOGGLE_DESTROY
+
+@hook TARGET_MODE_TOGGLE_SET
+
+@hook TARGET_MODE_TOGGLE_TEST
+
 @node Target Attributes
 @section Defining target-specific uses of @code{__attribute__}
 @cindex target attributes
--- gcc/lcm.c	2014-01-20 10:07:28.000000000 +0100
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/lcm.c	2014-04-15 16:17:44.000000000 +0200
@@ -377,17 +377,17 @@ 
     }
 }
 
-/* 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 @@ 
 #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 @@ 
 
   sbitmap_vector_free (antout);
   sbitmap_vector_free (antin);
-  sbitmap_vector_free (avout);
 
   later = sbitmap_vector_alloc (num_edges, n_exprs);
 
@@ -485,6 +481,28 @@ 
   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.  */
 
--- gcc/mode-switching.c	2014-04-17 13:28:18.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/mode-switching.c	2014-04-17 13:28:30.000000000 +0200
@@ -95,6 +95,126 @@ 
 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;
+
+/* 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.  */
+  if (targetm.mode_switching.toggle_init)
+    targetm.mode_switching.toggle_init (n_entities);
+}
+
+/* frees memory used to hold the modes information.  */
+
+static void
+free_modes_infos (int n_entities)
+{
+  if (targetm.mode_switching.toggle_destroy)
+    targetm.mode_switching.toggle_destroy ();
+
+  for (int j = 0; j < n_entities; j++)
+    free (modes_needed[j]);
+
+  free (modes_needed);
+}
+
+/* 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;
+	  int prev_mode = targetm.mode_switching.toggle_test (j,
+							      src_bb->index,
+							      true);
+
+	  REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
+
+	  rtl_profile_for_edge (eg);
+	  start_sequence ();
+
+	  targetm.mode_switching.emit (entity_map[j], mode,
+				       prev_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.
    INSN may not be a NOTE_INSN_BASIC_BLOCK, unless it is an empty
@@ -200,7 +320,7 @@ 
    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 +575,8 @@ 
   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];
@@ -468,6 +586,8 @@ 
   bool emitted ATTRIBUTE_UNUSED = false;
   basic_block post_entry = 0;
   basic_block pre_exit = 0;
+  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))
@@ -500,7 +620,7 @@ 
       /* 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);
     }
 
   df_analyze ();
@@ -510,6 +630,8 @@ 
   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));
 
@@ -623,6 +745,9 @@ 
     }
 
   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];
@@ -653,8 +778,12 @@ 
 
       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);
+
+      /* Merge modes for all entities.  */
+      if (targetm.mode_switching.toggle_set)
+	targetm.mode_switching.toggle_set (avin, avout);
 
       for (j = n_entities - 1; j >= 0; j--)
 	{
@@ -671,10 +800,6 @@ 
 	  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;
 
@@ -683,27 +808,8 @@ 
 
 	      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 ();
-	      targetm.mode_switching.emit (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)
@@ -718,7 +824,10 @@ 
       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.  */
@@ -726,9 +835,18 @@ 
     {
       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;
+	  bool toggle_p = targetm.mode_switching.toggle_test (j,
+							      bb->index,
+							      false);
+
 	  for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next)
 	    {
 	      next = ptr->next;
@@ -738,9 +856,13 @@ 
 
 		  rtl_profile_for_bb (bb);
 		  start_sequence ();
-		  targetm.mode_switching.emit (entity_map[j],
-					       ptr->mode,
-					       ptr->regs_live);
+
+		  targetm.mode_switching.emit (entity_map[j], ptr->mode,
+					       toggle_p, ptr->regs_live);
+
+		  /* modes kill each other inside a basic block.  */
+		  toggle_p = true;
+
 		  mode_set = get_insns ();
 		  end_sequence ();
 
@@ -772,11 +894,16 @@ 
       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 ();
@@ -789,6 +916,13 @@ 
   return 1;
 }
 
+bool
+default_toggle_test (int ATTRIBUTE_UNUSED entity,
+			  int ATTRIBUTE_UNUSED bb, bool ATTRIBUTE_UNUSED out)
+{
+  return false;
+}
+
 #endif /* OPTIMIZE_MODE_SWITCHING */
 
 static bool
--- gcc/target.def	2014-04-17 15:04:30.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/target.def	2014-04-17 15:05:19.000000000 +0200
@@ -5352,12 +5352,12 @@ 
 
 DEFHOOK
 (emit,
- "Generate one or more insns to set @var{entity} to @var{mode}. @var{hard_reg_live} is the set of hard registers live at the point where the insn(s) are to be inserted. Sets of a lower numbered entity will be emitted before sets of a higher numbered entity to a mode of the same or lower priority.",
- void, (int entity, int mode, HARD_REG_SET regs_live), NULL)
+ "Generate one or more insns to set @var{entity} to @var{mode}. @var{hard_reg_live} is the set of hard registers live at the point where the insn(s) are to be inserted. @var{toggle} is a boolean to indicate that current mode is known an thus can be toggled. Sets of a lower numbered entity will be emitted before sets of a higher numbered entity to a mode of the same or lower priority.",
+ void, (int entity, int mode, bool toggle, HARD_REG_SET regs_live), NULL)
 
 DEFHOOK
 (needed,
- "@var{entity} is an integer specifying a mode-switched entity. If @code{OPTIMIZE_MODE_SWITCHING} is defined, you must define this macro to return an integer value not larger than the corresponding element in @code{NUM_MODES_FOR_MODE_SWITCHING}, to denote the mode that @var{entity} must be switched into prior to the execution of @var{insn}.",
+ "@var{entity} is an integer specifying a mode-switched entity.  If @code{OPTIMIZE_MODE_SWITCHING} is defined, you must define this macro to return an integer value not larger than the corresponding element in @code{NUM_MODES_FOR_MODE_SWITCHING}, to denote the mode that @var{entity} must be switched into prior to the execution of @var{insn}.",
  int, (int entity, rtx insn), NULL)
 
 DEFHOOK
@@ -5380,6 +5380,27 @@ 
  "This macro specifies the order in which modes for @var{entity} are processed. 0 is the highest priority, @code{NUM_MODES_FOR_MODE_SWITCHING[@var{entity}] - 1} the lowest.  The value of the macro should be an integer designating a mode for @var{entity}.  For any fixed @var{entity}, @code{mode_priority} (@var{entity}, @var{n}) shall be a bijection in 0 @dots{} @code{num_modes_for_mode_switching[@var{entity}] - 1}.",
  int, (int entity, int n), NULL)
 
+DEFHOOK
+(toggle_init,
+ "Initializes target-specific data used in maintain toggle information.",
+ void, (int n_entity), NULL)
+
+/* Function to delete target-specific cost modeling data.  */
+DEFHOOK
+(toggle_destroy,
+ "Finalizes target-specific data used to maintain toggle information.",
+ void, (void), NULL)
+
+DEFHOOK
+(toggle_set,
+ "Hook called by the mode switching pass to record the modes needed for each entities in entry and exit of each basic block.",
+ void, (sbitmap *avin, sbitmap *avout), NULL)
+
+DEFHOOK
+(toggle_test,
+ "Hook used by mode switching pass to test if the current @var{entity}'s mode is available for basic block @var{bb} on all incoming edges (@var{out} false) or outgoing edges (@var{out} true). ",
+ bool, (int entity, int bb, bool out), default_toggle_test)
+
 HOOK_VECTOR_END (mode_switching)
 
 /* Close the 'struct gcc_target' definition.  */
--- gcc/targhooks.h	2014-04-17 13:07:07.000000000 +0200
+++ /work1/bruel/superh_elf/gnu_trunk.devs/gcc/gcc/targhooks.h	2014-04-17 13:06:34.000000000 +0200
@@ -208,3 +208,7 @@ 
 extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
 extern bool can_use_doloop_if_innermost (double_int, double_int,
 					 unsigned int, bool);
+
+extern bool default_toggle_test (int, int, bool);
+extern void default_toggle_init (void);
+extern void default_toggle_destroy (void);