On 06/11/2014 02:00 PM, Christian Bruel wrote:
> On 06/11/2014 06:17 AM, Joern Rennecke wrote:
>>>> Joern, is this new target macro interface OK with you ?
>> Yes, this interface should allow me to do switches between rounding
>> and truncating
>> floating-point modes with an add/subtract immediate.
>>
>> However, the implentation, as posted, doesn't work - it causes memory
>> corruption.
>>
>> It appears to work with the attached amendment patch.
>>
> Indeed,  thanks for pointing out the bad reusing of the aux field
> between multiple entities.
>
> In fact rereading this part of the implementation, I find the allocation
> of aux*n_entities awkward. A simpler setting in the entity loop to carry
> the mode directly into eg->aux is possible without array allocation
> (which also fixes a memory leak by the way).
>

Here is the revised version fixing the aforementioned issue found by
Joern on Epiphany. It also simplifies the allocation of the aux edges
field to carry the modes.

Now that everyone agrees on the interface, is this OK for trunk ?

bootstrapped/regtested for X86 and SH4a.

thanks,

Christian






2014-06-12  Christian Bruel  <christian.br...@st.com>

	* mode-switching.c (struct bb_info): Add mode_out, mode_in caches.
	(make_preds_opaque): Delete.
	(clear_mode_bit, mode_bit_p, set_mode_bit): New macros.
	(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-06-12  Christian Bruel  <christian.br...@st.com>

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

Index: gcc/basic-block.h
===================================================================
--- gcc/basic-block.h	(revision 211436)
+++ 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 211436)
+++ 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 211436)
+++ gcc/config/epiphany/epiphany.c	(working copy)
@@ -2543,7 +2543,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 211436)
+++ 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 211436)
+++ gcc/config/i386/i386.c	(working copy)
@@ -16447,7 +16447,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 211436)
+++ gcc/config/sh/sh.c	(working copy)
@@ -203,7 +203,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 211436)
+++ 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 211436)
+++ gcc/doc/tm.texi	(working copy)
@@ -9595,12 +9595,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 211436)
+++ 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 211436)
+++ gcc/mode-switching.c	(working copy)
@@ -80,23 +80,75 @@ 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)
+
+/* 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 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 +189,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 +480,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 +521,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 +537,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 +571,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 +592,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 +610,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 +640,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 +664,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 +677,109 @@ 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;
+	  eg->aux = (void *)(intptr_t)-1;
 
-	      mode = current_mode[j];
-	      src_bb = eg->src;
+	  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;
+		}
+	    }
+	}
 
-	      REG_SET_TO_HARD_REG_SET (live_at_edge, df_get_live_out (src_bb));
+      FOR_EACH_BB_FN (bb, cfun)
+	{
+	  struct bb_info *info = bb_info[j];
+	  int last_mode = no_mode;
 
-	      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 ();
+	  /* 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;
 
-	      /* 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);
-	    }
-
-	  FOR_EACH_BB_REVERSE_FN (bb, cfun)
-	    if (bitmap_bit_p (del[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 (del);
-      sbitmap_vector_free (insert);
+      /* Now output the remaining mode sets in all the segments.  */
+
+      /* 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 ();
-      free_edge_list (edge_list);
-    }
 
-  /* 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]];
-
-      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 +789,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 +826,17 @@ optimize_mode_switching (void)
       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 ();
Index: gcc/target.def
===================================================================
--- gcc/target.def	(revision 211436)
+++ gcc/target.def	(working copy)
@@ -5365,12 +5365,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