> On 04/28/2014 10:08 AM, Christian Bruel wrote:
>>>>> Hello,
>>>>>
>>>>> I'd like to ping the following patches
>>>>>
>>>>> [Hookize mode-switching]
>>>>> http://gcc.gnu.org/ml/gcc-patches/2014-04/msg01003.html
>>>>>
>>>>> [Add new hooks to support toggle and SH4A fpchg instruction]
>>>>> http://gcc.gnu.org/ml/gcc-patches/2014-04/msg01005.html
>> Sorry, I only saw the first part and thought I' d need to wait till I
>> see the second part - and I somehow missed that.
>>
>> I think the previous known mode should be passed to the
>> TARGET_MODE_EMIT hook - no need to have extra hooks
>> for toggling, and, as I mentioned earlier, fixating on the toggle is
>> actually an SH artifact - other ports have multi-way
>> modes settings that can benefit from knowing the previous mode.
> OK I'll change the bool toggle parameter by the previous mode in
> TARGET_MODE_EMIT and remove the bool XOR hooks in the SH description.
>
Hello,

This is the interface for targets that could use the previous mode for
switching. TARGET_MODE_EMIT now takes a new prev_mode parameter. If the
previous mode cannot be determined, MODE_NONE (value depends on  the
entity) is used.

The implementation is less trivial than just supporting a boolean toggle
bit, as the previous modes information have to be carried along the
edges. For this I recycle the auxiliary edge field that is made
unnecessary by the removal of make_pred_opaque and a change in the
implementation to call LCM for every modes from every identity
simultaneously. This idea was suggested by Joern in PR29349.  Another
speed improvement is that we process the modes to no_mode instead of
max_num_modes for each entity.
Thanks to all this, the only additional data to support prev_mode is
that for each BB, the avin/avout lcm computation are cached inside the
bb_info mode_in/mode_out fields,  the xor toggle bit handling  could
have been removed.

bootstrapped/regtested for x86 and sh4, sh4a, sh4a-single,
epiphany build is OK. testsuite not ran.

Joern, is this new target macro interface OK with you ? Jeff, (or other
RTL maintainer) since this is a new implementation for
optimize_mode_switching I suppose your previous approval doesn't held
anymore... is this new one OK for trunk as well ?
No change for x86/sh4/2a interfaces.

Many thanks

Christian


2014-05-23  Christian Bruel  <christian.br...@st.com>

	* mode-switching.c (struct bb_info): Add mode_out, mode_in caches.
	(make_preds_opaque): Delete function.
	(clear_mode_bit, mode_bit_p, set_mode_bit): New macros.
	(add_mode_set, get_mode_set, alloc_mode_aux, free_modes_edges): New functions.
	(commit_mode_sets): New function.
	(optimize_mode_switching): Handle current_mode to mode_switching_emit.
	Process all modes at once. 
	* 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.
	* config/epiphany/epiphany.c (emit_set_fp_mode): Add prev_mode parameter.
	* config/epiphany/epiphany-protos.h (emit_set_fp_mode): Idem.
	* config/epiphany/resolve-sw-modes.c (pass_resolve_sw_modes::execute): Idem.
	* config/i386/i386.c (x96_emit_mode_set): Idem.
	* config/sh/sh.c (sh_emit_mode_set): Likewise. Handle PR toggle.
	* config/sh/sh.md (toggle_pr): 	Defined if TARGET_FPU_SINGLE.
	(fpscr_toggle) Disallow from delay slot.
	* target.def (emit_mode_set): Add prev_mode parameter.
	* doc/tm.texi: Regenerate.

2014-05-19  Christian Bruel  <christian.br...@st.com>

	* gcc.target/sh/fpchg.c: New test.

Index: gcc/basic-block.h
===================================================================
--- gcc/basic-block.h	(revision 210845)
+++ gcc/basic-block.h	(working copy)
@@ -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 **,
Index: gcc/config/epiphany/epiphany-protos.h
===================================================================
--- gcc/config/epiphany/epiphany-protos.h	(revision 210845)
+++ gcc/config/epiphany/epiphany-protos.h	(working copy)
@@ -40,7 +40,8 @@ extern int epiphany_initial_elimination_offset (in
 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, int prev_mode,
+			      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);
Index: gcc/config/epiphany/epiphany.c
===================================================================
--- gcc/config/epiphany/epiphany.c	(revision 210845)
+++ gcc/config/epiphany/epiphany.c	(working copy)
@@ -2542,7 +2542,8 @@ epiphany_mode_exit (int entity)
 }
 
 void
-emit_set_fp_mode (int entity, int mode, HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
+emit_set_fp_mode (int entity, int mode, int prev_mode ATTRIBUTE_UNUSED,
+		  HARD_REG_SET regs_live ATTRIBUTE_UNUSED)
 {
   rtx save_cc, cc_reg, mask, src, src2;
   enum attr_fp_mode fp_mode;
Index: gcc/config/epiphany/resolve-sw-modes.c
===================================================================
--- gcc/config/epiphany/resolve-sw-modes.c	(revision 210845)
+++ gcc/config/epiphany/resolve-sw-modes.c	(working copy)
@@ -170,7 +170,7 @@ pass_resolve_sw_modes::execute (function *fun)
 	    }
 	  start_sequence ();
 	  emit_set_fp_mode (EPIPHANY_MSW_ENTITY_ROUND_UNKNOWN,
-			    jilted_mode, NULL);
+			    jilted_mode, FP_MODE_NONE, NULL);
 	  seq = get_insns ();
 	  end_sequence ();
 	  need_commit = true;
Index: gcc/config/i386/i386.c
===================================================================
--- gcc/config/i386/i386.c	(revision 210845)
+++ gcc/config/i386/i386.c	(working copy)
@@ -16440,7 +16440,8 @@ ix86_avx_emit_vzeroupper (HARD_REG_SET regs_live)
    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, int prev_mode ATTRIBUTE_UNUSED,
+		    HARD_REG_SET regs_live)
 {
   switch (entity)
     {
Index: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c	(revision 210845)
+++ gcc/config/sh/sh.c	(working copy)
@@ -202,7 +202,7 @@ static void push_regs (HARD_REG_SET *, int);
 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 sh_emit_mode_set (int, int, HARD_REG_SET);
+static void sh_emit_mode_set (int, int, int, HARD_REG_SET);
 static int sh_mode_needed (int, rtx);
 static int sh_mode_after (int, int, rtx);
 static int sh_mode_entry (int);
@@ -13582,9 +13582,17 @@ sh_try_omit_signzero_extend (rtx extended_op, rtx
 
 static void
 sh_emit_mode_set (int entity ATTRIBUTE_UNUSED, int mode,
-		  HARD_REG_SET regs_live)
+		  int prev_mode, HARD_REG_SET regs_live)
 {
-  fpscr_set_from_mem (mode, regs_live);
+  if ((TARGET_SH4A_FP || TARGET_SH4_300)
+      && prev_mode != FP_MODE_NONE && prev_mode != mode)
+    {
+      emit_insn (gen_toggle_pr ());
+      if (TARGET_FMOVD)
+	emit_insn (gen_toggle_sz ());
+    }
+  else
+    fpscr_set_from_mem (mode, regs_live);
 }
 
 static int
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md	(revision 210845)
+++ gcc/config/sh/sh.md	(working copy)
@@ -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")))
@@ -12239,15 +12240,12 @@ 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).
+;; Toggle FPU precision PR mode.
+
 (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"
   "fpchg"
   [(set_attr "type" "fpscr_toggle")])
 
Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	(revision 210845)
+++ gcc/doc/tm.texi	(working copy)
@@ -9737,12 +9737,12 @@ represented as numbers 0 @dots{} N @minus{} 1.  N
 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}, int @var{prev_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. @var{prev_moxde} indicates the mode to switch from. 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})
Index: gcc/lcm.c
===================================================================
--- gcc/lcm.c	(revision 210845)
+++ gcc/lcm.c	(working copy)
@@ -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.  */
 
Index: gcc/mode-switching.c
===================================================================
--- gcc/mode-switching.c	(revision 210845)
+++ gcc/mode-switching.c	(working copy)
@@ -80,23 +80,119 @@ struct bb_info
 {
   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 void add_seginfo (struct bb_info *, struct seginfo *);
 static void reg_dies (rtx, HARD_REG_SET *);
 static void reg_becomes_live (rtx, const_rtx, void *);
-static void make_preds_opaque (basic_block, int);
-
 
-/* This function will allocate a new BBINFO structure, initialized
-   with the MODE, INSN, and basic block BB parameters.
+/* 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)
+
+/* Record the MODE associated with edge EG for entity E.  */
+
+static void
+add_mode_set (int e, edge eg, int mode)
+{
+  ((int*)eg->aux)[e] = mode;
+}
+
+/* Return the mode needed on edge EG for entity E.  */
+
+static int
+get_mode_set (int e, edge eg)
+{
+  return eg->aux != NULL ? ((int*)eg->aux)[e] : -1;
+}
+
+/* Allocate the modes area on edge EG. Initialized with -1.  */
+
+static void
+alloc_mode_aux (edge eg, int n_entities, int *entity_map)
+{
+  eg->aux = (int *)xmalloc (sizeof (int) *  n_entities);
+
+  for (int j = n_entities - 1; j >= 0; j--)
+    add_mode_set (entity_map[j], eg,  -1);
+}
+
+static void
+free_modes_edges (struct edge_list *edge_list)
+{
+  for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
+    {
+      edge eg = INDEX_EDGE (edge_list, ed);
+
+      if (eg->aux)
+	{
+	  free (eg->aux);
+	  eg->aux = NULL;
+	}
+    }
+
+  free_edge_list (edge_list);
+}
+
+/* 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 = get_mode_set (e, eg)) != -1)
+	{
+	  HARD_REG_SET live_at_edge;
+	  basic_block src_bb = eg->src;
+	  int cur_mode = info[src_bb->index].mode_out;
+	  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 ();
+
+	  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_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;
+}
+
+/* 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.  */
@@ -137,30 +233,6 @@ add_seginfo (struct bb_info *head, struct seginfo
     }
 }
 
-/* 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 || ! bitmap_bit_p (transp[pb->index], j))
-	continue;
-
-      bitmap_clear_bit (transp[pb->index], j);
-      make_preds_opaque (pb, j);
-    }
-}
-
 /* Record in LIVE that register REG died.  */
 
 static void
@@ -452,24 +524,26 @@ create_pre_exit (int n_entities, int *entity_map,
 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 emitted ATTRIBUTE_UNUSED = false;
   basic_block post_entry = 0;
   basic_block pre_exit = 0;
+  struct edge_list *edge_list = 0;
 
-  for (e = N_ENTITIES - 1, n_entities = 0; e >= 0; e--)
+  /* These bitmaps are used for the LCM algorithm.  */
+  sbitmap *kill, *del, *insert, *antic, *transp, *comp;
+  sbitmap *avin, *avout;
+
+  for (e = N_ENTITIES - 1; e >= 0; e--)
     if (OPTIMIZE_MODE_SWITCHING (e))
       {
 	int entry_exit_extra = 0;
@@ -491,9 +565,10 @@ optimize_mode_switching (void)
   if (! n_entities)
     return 0;
 
-  /* Make sure if MODE_ENTRY is defined the MODE_EXIT is defined and vice versa.  */
+  /* 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));
+	      || (!targetm.mode_switching.entry
+		  && !targetm.mode_switching.exit));
 
   if (targetm.mode_switching.entry && targetm.mode_switching.exit)
     {
@@ -506,18 +581,29 @@ optimize_mode_switching (void)
   df_analyze ();
 
   /* Create the bitmap vectors.  */
+  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);
 
-  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);
-
   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;
 
       /* 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.
@@ -529,16 +615,18 @@ optimize_mode_switching (void)
 	  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)
 	      {
 		rtx ins_pos = BB_HEAD (bb);
 		if (LABEL_P (ins_pos))
@@ -548,7 +636,8 @@ optimize_mode_switching (void)
 		  ins_pos = NEXT_INSN (ins_pos);
 		ptr = new_seginfo (no_mode, ins_pos, bb->index, live_now);
 		add_seginfo (info + bb->index, ptr);
-		bitmap_clear_bit (transp[bb->index], j);
+		for (i = 0; i < no_mode; i++)
+		  clear_mode_bit (transp[bb->index], j, i);
 	      }
 	  }
 
@@ -565,11 +654,13 @@ optimize_mode_switching (void)
 		      last_mode = mode;
 		      ptr = new_seginfo (mode, insn, bb->index, live_now);
 		      add_seginfo (info + bb->index, ptr);
-		      bitmap_clear_bit (transp[bb->index], j);
+		      for (i = 0; i < no_mode; i++)
+			clear_mode_bit (transp[bb->index], j, i);
 		    }
 
 		  if (targetm.mode_switching.after)
-		    last_mode = targetm.mode_switching.after (e, last_mode, insn);
+		    last_mode = targetm.mode_switching.after (e, last_mode,
+							      insn);
 
 		  /* Update LIVE_NOW.  */
 		  for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
@@ -593,13 +684,22 @@ optimize_mode_switching (void)
 	      ptr = new_seginfo (no_mode, BB_END (bb), bb->index, live_now);
 	      add_seginfo (info + bb->index, ptr);
 	      if (last_mode != no_mode)
-		bitmap_clear_bit (transp[bb->index], j);
+		for (i = 0; i < no_mode; i++)
+		  clear_mode_bit (transp[bb->index], j, i);
 	    }
 	}
       if (targetm.mode_switching.entry && targetm.mode_switching.exit)
 	{
 	  int mode = targetm.mode_switching.entry (e);
 
+	  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;
+	    }
+
 	  if (mode != no_mode)
 	    {
 	      bb = post_entry;
@@ -608,7 +708,8 @@ optimize_mode_switching (void)
 		 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.  */
-	      bitmap_clear_bit (transp[bb->index], j);
+	      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
@@ -620,115 +721,107 @@ optimize_mode_switching (void)
 		  targetm.mode_switching.exit (e);
 	    }
 	}
-    }
 
-  kill = sbitmap_vector_alloc (last_basic_block_for_fn (cfun), n_entities);
-  for (i = 0; i < max_num_modes; i++)
-    {
-      int current_mode[N_ENTITIES];
-      sbitmap *del;
-      sbitmap *insert;
-
       /* Set the anticipatable and computing arrays.  */
-      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--)
+      for (i = 0; i < no_mode; i++)
 	{
-	  int m = current_mode[j] =
-	    targetm.mode_switching.priority (entity_map[j], i);
-	  struct bb_info *info = bb_info[j];
+	  int m = targetm.mode_switching.priority (entity_map[j], i);
 
 	  FOR_EACH_BB_FN (bb, cfun)
 	    {
 	      if (info[bb->index].seginfo->mode == m)
-		bitmap_set_bit (antic[bb->index], j);
+		set_mode_bit (antic[bb->index], j, m);
 
 	      if (info[bb->index].computing == m)
-		bitmap_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.  */
+  /* Calculate the optimal locations for the
+     placement mode switches to modes with priority I.  */
 
-      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);
+  FOR_EACH_BB_FN (bb, cfun)
+    bitmap_not (kill[bb->index], transp[bb->index]);
 
-      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]];
+  edge_list = pre_edge_lcm_avs (n_entities * max_num_modes, transp, comp, antic,
+				kill, avin, avout, &insert, &del);
 
-	  /* 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 (j = n_entities - 1; j >= 0; j--)
+    {
+      int no_mode = num_modes[entity_map[j]];
 
-	      eg->aux = 0;
+      /* Insert all mode sets that have been inserted by lcm.  */
 
-	      if (! bitmap_bit_p (insert[e], j))
-		continue;
+      for (int ed = NUM_EDGES (edge_list) - 1; ed >= 0; ed--)
+	{
+	  edge eg = INDEX_EDGE (edge_list, ed);
 
-	      eg->aux = (void *)1;
+	  for (i = 0; i < no_mode; i++)
+	    {
+	      int m = targetm.mode_switching.priority (entity_map[j], i);
 
-	      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)
+	      if (! mode_bit_p (insert[ed], j, m))
 		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.  */
+	      if (!eg->aux)
+		alloc_mode_aux (eg, n_entities, entity_map);
+	      add_mode_set (entity_map[j], eg, m);
 	    }
+	}
 
-	  FOR_EACH_BB_REVERSE_FN (bb, cfun)
-	    if (bitmap_bit_p (del[bb->index], j))
+      FOR_EACH_BB_FN (bb, cfun)
+	{
+	  struct bb_info *info = bb_info[j];
+	  int last_mode = no_mode;
+
+	  /* intialize mode in availability for bb.  */
+	  for (i = 0; i < no_mode; i++)
+	    if (mode_bit_p (avout[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_out = last_mode;
+
+	  /* 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))
+	      {
+		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 (del);
-      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]);
 
-      FOR_EACH_BB_REVERSE_FN (bb, cfun)
+      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;
@@ -738,12 +831,15 @@ optimize_mode_switching (void)
 
 		  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,
+					       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)
 		    {
@@ -772,11 +868,17 @@ optimize_mode_switching (void)
       free (bb_info[j]);
     }
 
+  free_modes_edges (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 ();
Index: gcc/target.def
===================================================================
--- gcc/target.def	(revision 210845)
+++ gcc/target.def	(working copy)
@@ -5366,12 +5366,12 @@ HOOK_VECTOR (TARGET_TOGGLE_, mode_switching)
 
 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{prev_moxde} indicates the mode to switch from. 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, int prev_mode, 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
Index: gcc/testsuite/gcc.target/sh/fpchg.c
===================================================================
--- gcc/testsuite/gcc.target/sh/fpchg.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/fpchg.c	(working copy)
@@ -0,0 +1,16 @@
+/* Check that fpchg is used to switch precision.  */
+
+/* { dg-do compile } */
+/* { dg-final { scan-assembler "fpchg" } } */
+/* { dg-final { scan-assembler-not "fpscr" } } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m4a" } } */
+
+extern float c;
+
+void
+foo(int j)
+{
+  while (j--)
+    c++;
+
+}

Reply via email to