This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] support to flip modes in sh mode-switching
- From: Christian BRUEL <christian dot bruel at st dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Fri, 15 Dec 2006 17:26:00 +0100
- Subject: [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 ();