This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] support to flip modes in sh mode-switching


This patch allows to switch mode (instead of setting it) on architectures providing a modes-switching instruction, such as 'fpchg' on sh4-300.

The problem was described by Joern Rennecke in bugzilla #29439 (part 3).

In order to add this support, I needed to make the following changes for which I would like to have your recommendations:

1) In order to add this mode information on edges, I added an 'aux2' in the edge_def struct. The alternatives could have been:
- use the already existing 'aux' field. But it is broken by pre_edge_lcm and the cost of saving restoring it might be high (vector or size #of edges * # of entities).
- have a local edge vector. but that seems redundant.


there is a also ann additional memory cost for other targets.

2) the EMIT_MODE_SET macro was extended with a new parameter that is the actual mode, when known, or -1.
after rethought, I would prefer to have a new macro, EMIT_MODE_SWITCH that seems closer to the semantic. This macro could be use to #undef code, such as the 'aux2' field in edge_def.


the other local changes to mode-switching.c are
- avin is now a parameter to pre_edge_lcm
- EMIT_MODE_SET is now called lazily, since the insertion should wait for all modes to be processed and value know before emitting the rtl.


validated it on sh4-300 with gcc testsuite.

note: The patch is done against a gcc-4.3-20061209 snapshot.

an administrative question: I will latter have other contributions, usually related to the sh target. how can I require a write access (with approvals) to the subversion repository ?

Best Regards,

Christian


diff -rup gcc-4.3-20061209/gcc/basic-block.h gcc-4.3-20061209.mode-switch/gcc/basic-block.h
--- gcc-4.3-20061209/gcc/basic-block.h	2006-11-25 11:34:13.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/basic-block.h	2006-12-14 09:50:04.000000000 +0100
@@ -130,6 +130,9 @@ struct edge_def GTY(())
   /* Auxiliary info specific to a pass.  */
   PTR GTY ((skip (""))) aux;
 
+  /* Auxiliary info specific to a pass.  */
+  PTR GTY ((skip (""))) aux2;
+
   /* Location of any goto implicit in the edge, during tree-ssa.  */
   source_locus goto_locus;
 
@@ -861,8 +864,8 @@ extern void free_propagate_block_info (s
 
 /* In lcm.c */
 extern struct edge_list *pre_edge_lcm (int, sbitmap *, sbitmap *,
-				       sbitmap *, sbitmap *, sbitmap **,
-				       sbitmap **);
+				       sbitmap *, sbitmap *, sbitmap *,
+				       sbitmap **, sbitmap **);
 extern struct edge_list *pre_edge_rev_lcm (int, sbitmap *,
 					   sbitmap *, sbitmap *,
 					   sbitmap *, sbitmap **,
diff -rup gcc-4.3-20061209/gcc/config/i386/i386.h gcc-4.3-20061209.mode-switch/gcc/config/i386/i386.h
--- gcc-4.3-20061209/gcc/config/i386/i386.h	2006-12-08 19:20:25.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/config/i386/i386.h	2006-12-15 07:39:42.000000000 +0100
@@ -2252,7 +2252,7 @@ enum ix86_stack_slot
    is the set of hard registers live at the point where the insn(s)
    are to be inserted.  */
 
-#define EMIT_MODE_SET(ENTITY, MODE, HARD_REGS_LIVE) 			\
+#define EMIT_MODE_SET(ENTITY, MODE, ACTUALMODE, HARD_REGS_LIVE) 			\
   ((MODE) != I387_CW_ANY && (MODE) != I387_CW_UNINITIALIZED		\
    ? emit_i387_cw_initialization (MODE), 0				\
    : 0)
diff -rup gcc-4.3-20061209/gcc/config/sh/sh.c gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.c
--- gcc-4.3-20061209/gcc/config/sh/sh.c	2006-12-05 18:26:05.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.c	2006-12-12 11:16:51.000000000 +0100
@@ -8524,6 +8524,14 @@ get_free_reg (HARD_REG_SET regs_live)
   return gen_rtx_REG (Pmode, 7);
 }
 
+/* This function flip the fpscr
+   MODE is the mode we are setting it to.  */
+void
+emit_fpu_flip ()
+{
+  emit_insn (gen_toggle_pr());
+}
+
 /* This function will set the fpscr from memory.
    MODE is the mode we are setting it to.  */
 void
diff -rup gcc-4.3-20061209/gcc/config/sh/sh.h gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.h
--- gcc-4.3-20061209/gcc/config/sh/sh.h	2006-12-08 17:37:42.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.h	2006-12-14 09:47:47.000000000 +0100
@@ -3418,8 +3418,11 @@ extern int current_function_interrupt;
 #define MODE_PRIORITY_TO_MODE(ENTITY, N) \
   ((TARGET_FPU_SINGLE != 0) ^ (N) ? FP_MODE_SINGLE : FP_MODE_DOUBLE)
 
-#define EMIT_MODE_SET(ENTITY, MODE, HARD_REGS_LIVE) \
-  fpscr_set_from_mem ((MODE), (HARD_REGS_LIVE))
+#define EMIT_MODE_SET(ENTITY, MODE, ACTUALMODE, HARD_REGS_LIVE) \
+  ((TARGET_SH4A_FP || TARGET_SH4_300)   \
+   && (ACTUALMODE) != -1 \
+   ? emit_fpu_flip()                    \
+   : fpscr_set_from_mem ((MODE), (HARD_REGS_LIVE)))
 
 #define MD_CAN_REDIRECT_BRANCH(INSN, SEQ) \
   sh_can_redirect_branch ((INSN), (SEQ))
diff -rup gcc-4.3-20061209/gcc/config/sh/sh.md gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.md
--- gcc-4.3-20061209/gcc/config/sh/sh.md	2006-11-29 15:35:38.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/config/sh/sh.md	2006-12-12 11:17:45.000000000 +0100
@@ -10066,15 +10066,10 @@ mov.l\\t1f,r0\\n\\
   "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")])
 
diff -rup gcc-4.3-20061209/gcc/gcse.c gcc-4.3-20061209.mode-switch/gcc/gcse.c
--- gcc-4.3-20061209/gcc/gcse.c	2006-11-10 22:42:04.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/gcse.c	2006-12-12 12:43:58.000000000 +0100
@@ -3821,6 +3821,7 @@ compute_pre_data (void)
   sbitmap trapping_expr;
   basic_block bb;
   unsigned int ui;
+  sbitmap *avin;
 
   compute_local_properties (transp, comp, antloc, &expr_hash_table);
   sbitmap_vector_zero (ae_kill, last_basic_block);
@@ -3862,13 +3863,16 @@ compute_pre_data (void)
       sbitmap_not (ae_kill[bb->index], ae_kill[bb->index]);
     }
 
+  avin = sbitmap_vector_alloc (last_basic_block, expr_hash_table.n_elems);
+
   edge_list = pre_edge_lcm (expr_hash_table.n_elems, transp, comp, antloc,
-			    ae_kill, &pre_insert_map, &pre_delete_map);
+			    ae_kill, avin, &pre_insert_map, &pre_delete_map);
   sbitmap_vector_free (antloc);
   antloc = NULL;
   sbitmap_vector_free (ae_kill);
   ae_kill = NULL;
   sbitmap_free (trapping_expr);
+  sbitmap_vector_free (avin);
 }
 
 /* PRE utilities */
diff -rup gcc-4.3-20061209/gcc/lcm.c gcc-4.3-20061209.mode-switch/gcc/lcm.c
--- gcc-4.3-20061209/gcc/lcm.c	2006-02-06 19:20:47.000000000 +0100
+++ gcc-4.3-20061209.mode-switch/gcc/lcm.c	2006-12-13 18:01:32.000000000 +0100
@@ -377,11 +377,11 @@ compute_insert_delete (struct edge_list 
 
 struct edge_list *
 pre_edge_lcm (int n_exprs, sbitmap *transp,
-	      sbitmap *avloc, sbitmap *antloc, sbitmap *kill,
+	      sbitmap *avloc, sbitmap *antloc, sbitmap *kill, sbitmap *avin,
 	      sbitmap **insert, sbitmap **delete)
 {
   sbitmap *antin, *antout, *earliest;
-  sbitmap *avin, *avout;
+  sbitmap *avout;
   sbitmap *later, *laterin;
   struct edge_list *edge_list;
   int num_edges;
@@ -403,10 +403,8 @@ pre_edge_lcm (int n_exprs, sbitmap *tran
 #endif
 
   /* Compute global availability.  */
-  avin = sbitmap_vector_alloc (last_basic_block, n_exprs);
   avout = sbitmap_vector_alloc (last_basic_block, n_exprs);
   compute_available (avloc, kill, avout, avin);
-  sbitmap_vector_free (avin);
 
   /* Compute global anticipatability.  */
   antin = sbitmap_vector_alloc (last_basic_block, n_exprs);
diff -rup gcc-4.3-20061209/gcc/mode-switching.c gcc-4.3-20061209.mode-switch/gcc/mode-switching.c
--- gcc-4.3-20061209/gcc/mode-switching.c	2006-09-05 23:41:23.000000000 +0200
+++ gcc-4.3-20061209.mode-switch/gcc/mode-switching.c	2006-12-15 10:23:59.000000000 +0100
@@ -84,11 +84,121 @@ struct bb_info
   int computing;
 };
 
+/* we need to wait before EMIT_MODE_SET to know if all incoming edges have a 
+  known edge values or not. Real mode creation is done in commit_mode_set.
+  eg->aux2 is used to hold the mode value for avin. */
+static int *edge_mode_inserts;
+
+static void
+init_mode_sets (void)
+{
+  struct edge_list* edge_list = create_edge_list ();
+  int n = NUM_EDGES (edge_list);
+
+  edge_mode_inserts = xcalloc (n, sizeof (int));
+  memset (edge_mode_inserts, -1, n * sizeof (int));
+}
+
+static void
+add_mode_set (int e, int mode)
+{
+  edge_mode_inserts[e] = mode;
+}
+
+static void
+free_mode_set (void)
+{
+  free (edge_mode_inserts);
+}
+
+static void
+init_edge_auxes (int n_entities)
+{
+  basic_block bb;
+
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+    {
+      edge_iterator ei;
+      edge eg;
+      FOR_EACH_EDGE (eg, ei, bb->succs)
+	{
+	  eg->aux2 = xcalloc (n_entities, sizeof(int));
+	  memset (eg->aux2, -1, n_entities * sizeof(int));
+	}
+  }
+}
+
+static void
+free_edge_auxes (void)
+{
+  basic_block bb;
+
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+    {
+      edge_iterator ei;
+      edge eg;
+      FOR_EACH_EDGE (eg, ei, bb->succs)
+	{
+	  free (eg->aux2);
+	  eg->aux2 = NULL;
+	}
+    }
+}
+
+static int
+commit_mode_sets (int n_entities)
+{
+  struct edge_list* edge_list = create_edge_list ();
+  int n = NUM_EDGES (edge_list);
+  int j;
+  int need_commit = 0;
+
+  for (j = n_entities - 1; j >= 0; j--)
+    {
+      int e;
+      for (e = n - 1; e >= 0; e--)
+	{
+	  HARD_REG_SET live_at_edge;
+	  edge eg = INDEX_EDGE (edge_list, e);
+	  basic_block src_bb = eg->src;
+	  int mode = edge_mode_inserts[e];
+	  rtx mode_set;
+
+	  if (mode == -1)
+	    continue;
+
+	  REG_SET_TO_HARD_REG_SET (live_at_edge, src_bb->il.rtl->global_live_at_end);
+
+	  start_sequence ();
+	  EMIT_MODE_SET (entity_map[j], mode, ((int *)eg->aux2)[j], live_at_edge);
+
+	  mode_set = get_insns ();
+	  end_sequence ();      
+
+	  /* 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);
+	}
+    }
+
+  free_edge_list (edge_list);
+  free_mode_set();
+
+  return need_commit;
+}
+
 /* These bitmaps are used for the LCM algorithm.  */
 
 static sbitmap *antic;
 static sbitmap *transp;
 static sbitmap *comp;
+static sbitmap *avin;
 
 static struct seginfo * new_seginfo (int, rtx, int, HARD_REG_SET);
 static void add_seginfo (struct bb_info *, struct seginfo *);
@@ -379,6 +489,67 @@ create_pre_exit (int n_entities, int *en
 }
 #endif
 
+static void
+set_edge_modes_internal(basic_block bb, sbitmap visited, struct bb_info *info, int j, int no_mode)
+{
+  edge_iterator ei;
+  edge e;  
+
+  SET_BIT (visited, bb->index);
+
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      basic_block bs = e->dest;
+      
+      if (info[bb->index].computing != no_mode)
+	((int *)e->aux2)[j] = info[bb->index].computing;
+    
+      else if (TEST_BIT(transp[bb->index], j) && TEST_BIT (avin[bb->index], j))
+	{
+	  edge_iterator e2i;
+	  edge e2;
+
+	  FOR_EACH_EDGE (e2, e2i, bb->preds)
+	    ((int *)e->aux2)[j] = ((int *)e2->aux2)[j];
+	}
+
+      if (bs->index >= 0 && !TEST_BIT (visited, bs->index))
+	set_edge_modes_internal (bs, visited, info, j, no_mode);
+    }
+}
+
+static int
+known_mode (basic_block bb, int j)
+{
+  edge_iterator ei;
+  edge e;  
+  int mode = -2;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if (mode == -2)
+	mode = ((int *)e->aux2)[j];
+      else if (mode != ((int *)e->aux2)[j])
+	{
+	  mode = -1;
+	  break;
+	}
+    }
+
+  return mode;
+}
+
+static void
+set_edge_modes (struct bb_info *info, int j, int no_mode)
+{
+  sbitmap visited = sbitmap_alloc (last_basic_block);
+  sbitmap_zero (visited);
+
+  set_edge_modes_internal(cfun->cfg->x_entry_block_ptr->next_bb, visited, info, j, no_mode);
+
+  sbitmap_free (visited);
+}
+
 /* Find all insns that need a particular mode setting, and insert the
    necessary mode switches.  Return true if we did work.  */
 
@@ -390,7 +561,6 @@ optimize_mode_switching (void)
   basic_block bb;
   int need_commit = 0;
   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];
@@ -436,6 +606,7 @@ optimize_mode_switching (void)
   antic = sbitmap_vector_alloc (last_basic_block, n_entities);
   transp = sbitmap_vector_alloc (last_basic_block, n_entities);
   comp = sbitmap_vector_alloc (last_basic_block, n_entities);
+  avin = sbitmap_vector_alloc (last_basic_block, n_entities);
 
   sbitmap_vector_ones (transp, last_basic_block);
 
@@ -538,15 +709,22 @@ optimize_mode_switching (void)
     }
 
   kill = sbitmap_vector_alloc (last_basic_block, n_entities);
+
+  init_edge_auxes(n_entities);
+  init_mode_sets();
+
   for (i = 0; i < max_num_modes; i++)
     {
       int current_mode[N_ENTITIES];
+      struct edge_list *edge_list;
       sbitmap *delete;
       sbitmap *insert;
 
-      /* Set the anticipatable and computing arrays.  */
+      /* Set the anticipatable, avin and computing arrays.  */
       sbitmap_vector_zero (antic, last_basic_block);
+      sbitmap_vector_zero (avin, last_basic_block);
       sbitmap_vector_zero (comp, last_basic_block);
+
       for (j = n_entities - 1; j >= 0; j--)
 	{
 	  int m = current_mode[j] = MODE_PRIORITY_TO_MODE (entity_map[j], i);
@@ -568,7 +746,7 @@ optimize_mode_switching (void)
       FOR_EACH_BB (bb)
 	sbitmap_not (kill[bb->index], transp[bb->index]);
       edge_list = pre_edge_lcm (n_entities, transp, comp, antic,
-				kill, &insert, &delete);
+				kill, avin, &insert, &delete);
 
       for (j = n_entities - 1; j >= 0; j--)
 	{
@@ -587,36 +765,29 @@ optimize_mode_switching (void)
 	      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;
+	      src_bb = eg->src;
+
+	      eg->aux = (void *)0;
 
 	      if (! TEST_BIT (insert[e], j))
-		continue;
+		{
+		  /* the mode was already set and is now available in so we can
+		     just switch it */
+		  if (eg->src != ENTRY_BLOCK_PTR
+		      && !(eg->flags & EDGE_ABNORMAL)
+		      && TEST_BIT (avin[src_bb->index], j))
+		    ((int *)eg->aux2)[j] = current_mode[j];
+		  continue;
+		}
 
 	      eg->aux = (void *)1;
 
 	      mode = current_mode[j];
-	      src_bb = eg->src;
-
-	      REG_SET_TO_HARD_REG_SET (live_at_edge,
-				       src_bb->il.rtl->global_live_at_end);
-
-	      start_sequence ();
-	      EMIT_MODE_SET (entity_map[j], mode, live_at_edge);
-	      mode_set = get_insns ();
-	      end_sequence ();
-
-	      /* 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));
+	      ((int *)eg->aux2)[j] = mode;
 
-	      need_commit = 1;
-	      insert_insn_on_edge (mode_set, eg);
+	      add_mode_set(e, mode);
 	    }
 
 	  FOR_EACH_BB_REVERSE (bb)
@@ -634,14 +805,20 @@ optimize_mode_switching (void)
       free_edge_list (edge_list);
     }
 
+  need_commit = commit_mode_sets (n_entities);
+
   /* 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]];
 
+      set_edge_modes(bb_info[j], j, no_mode);
+
       FOR_EACH_BB_REVERSE (bb)
 	{
 	  struct seginfo *ptr, *next;
+	  int last_mode = known_mode(bb, j);
+
 	  for (ptr = bb_info[j][bb->index].seginfo; ptr; ptr = next)
 	    {
 	      next = ptr->next;
@@ -650,10 +827,14 @@ optimize_mode_switching (void)
 		  rtx mode_set;
 
 		  start_sequence ();
-		  EMIT_MODE_SET (entity_map[j], ptr->mode, ptr->regs_live);
+
+		  EMIT_MODE_SET (entity_map[j], ptr->mode, last_mode, ptr->regs_live);
+
 		  mode_set = get_insns ();
 		  end_sequence ();
 
+		  last_mode = ptr->mode;
+
 		  /* Insert MODE_SET only if it is nonempty.  */
 		  if (mode_set != NULL_RTX)
 		    {
@@ -671,6 +852,7 @@ optimize_mode_switching (void)
 	    }
 	}
 
+      clear_aux_for_edges ();
       free (bb_info[j]);
     }
 
@@ -680,10 +862,13 @@ optimize_mode_switching (void)
   sbitmap_vector_free (antic);
   sbitmap_vector_free (transp);
   sbitmap_vector_free (comp);
+  sbitmap_vector_free (avin);
 
   if (need_commit)
     commit_edge_insertions ();
 
+  free_edge_auxes ();
+
 #if defined (MODE_ENTRY) && defined (MODE_EXIT)
   cleanup_cfg (CLEANUP_NO_INSN_DEL);
 #else
diff -rup gcc-4.3-20061209/gcc/see.c gcc-4.3-20061209.mode-switch/gcc/see.c
--- gcc-4.3-20061209/gcc/see.c	2006-05-30 09:53:02.000000000 +0200
+++ gcc-4.3-20061209.mode-switch/gcc/see.c	2006-12-12 12:46:04.000000000 +0100
@@ -2098,6 +2098,7 @@ see_execute_LCM (void)
 {
   size_t pre_extension_num = htab_elements (see_pre_extension_hash);
   int i = 0;
+  sbitmap *avin;
 
   if (dump_file)
     fprintf (dump_file,
@@ -2129,8 +2130,11 @@ see_execute_LCM (void)
   add_noreturn_fake_exit_edges ();
 
   /* Run the LCM.  */
+  avin = sbitmap_vector_alloc (last_basic_block, pre_extension_num);
+
   edge_list = pre_edge_lcm (pre_extension_num, transp, comp, antloc,
-  			    ae_kill, &pre_insert_map, &pre_delete_map);
+  			    ae_kill, avin, &pre_insert_map, &pre_delete_map);
+  sbitmap_vector_free (avin);
 
   /* Remove the fake edges.  */
   remove_fake_exit_edges ();

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]