Problems with sh mode switching code at -O0

Bernd Schmidt bernds@redhat.com
Wed Nov 15 05:00:00 GMT 2000


The sh port fails to compile some files correctly at -O0.  An example is
execute/930622-2.c; compilation flags to use are "-O0 -m4-single".

The problem is that we've started doing a bit of optimization even at -O0,
and this exposed a latent problem with the sh port.  Floating point
instructions on the sh depend on the value of a certain control register.
At -O0, the functions emit_sf_insn and emit_df_insn wrap each floating point
insn (even moves) with a save/restore sequence for the control register.
At higher optimization levels, we use a more clever approach using the
optimize_mode_switching pass.

The problem is that the movsf pattern expands into a multi-insn sequence,
and the last insn in it does not set the target of the movsf.  This causes
emit_libcall_block to emit bogus code.  This was harmless until we started
doing some optimizations at -O0; now it means that sometimes a libcall will
be discarded due to the incorrect REG_RETVAL/REG_LIBCALL notes.

There are a couple of alternatives how to fix it.  We could always run
optimize_mode_switching and get rid of emit_sf_insn/emit_df_insn.  This
is my preferred solution and implemented by patch #1 below.  However, I
don't know how fast that pass is; it may well be unacceptable at -O0.
Alternatively, we could try to make emit_libcall_block detect the situation.
This is patch #2.  There are more possibilities (e.g. don't make libcall
blocks at -O0), but these two are IMO the only ones that make sense.


Bernd

Patch #1:
	* toplev.c (rest_of_compilation): Run optimize_mode_switching even
	if not optimizing.
	* sh.c (emit_sf_insn, emit_df_insn): Just call emit_insn.
	
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/toplev.c,v
retrieving revision 1.395
diff -u -p -r1.395 toplev.c
--- toplev.c	2000/11/10 11:43:42	1.395
+++ toplev.c	2000/11/14 14:09:07
@@ -3340,20 +3340,17 @@ rest_of_compilation (decl)
     register_life_up_to_date = 0;
 
 #ifdef OPTIMIZE_MODE_SWITCHING
-  if (optimize)
-    {
-      timevar_push (TV_GCSE);
-
-      if (optimize_mode_switching (NULL_PTR))
-	{
-	  /* We did work, and so had to regenerate global life information.
-	     Take advantage of this and don't re-recompute register life
-	     information below.  */
-	  register_life_up_to_date = 1;
-	}
+  timevar_push (TV_GCSE);
 
-      timevar_pop (TV_GCSE);
+  if (optimize_mode_switching (NULL_PTR))
+    {
+      /* We did work, and so had to regenerate global life information.
+	 Take advantage of this and don't re-recompute register life
+	 information below.  */
+      register_life_up_to_date = 1;
     }
+
+  timevar_pop (TV_GCSE);
 #endif
 
 #ifdef INSN_SCHEDULING
Index: config/sh/sh.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/sh/sh.c,v
retrieving revision 1.70
diff -u -p -r1.70 sh.c
--- sh.c	2000/11/09 07:45:15	1.70
+++ sh.c	2000/11/14 14:09:09
@@ -5014,38 +5014,14 @@ void
 emit_sf_insn (pat)
      rtx pat;
 {
-  rtx addr;
-  /* When generating reload insns,  we must not create new registers.  FPSCR
-     should already have the correct value, so do nothing to change it.  */
-  if (! TARGET_FPU_SINGLE && ! reload_in_progress && optimize < 1)
-    {
-      addr = gen_reg_rtx (SImode);
-      emit_insn (gen_fpu_switch0 (addr));
-    }
   emit_insn (pat);
-  if (! TARGET_FPU_SINGLE && ! reload_in_progress && optimize < 1)
-    {
-      addr = gen_reg_rtx (SImode);
-      emit_insn (gen_fpu_switch1 (addr));
-    }
 }
 
 void
 emit_df_insn (pat)
      rtx pat;
 {
-  rtx addr;
-  if (TARGET_FPU_SINGLE && ! reload_in_progress && optimize < 1)
-    {
-      addr = gen_reg_rtx (SImode);
-      emit_insn (gen_fpu_switch0 (addr));
-    }
   emit_insn (pat);
-  if (TARGET_FPU_SINGLE && ! reload_in_progress && optimize < 1)
-    {
-      addr = gen_reg_rtx (SImode);
-      emit_insn (gen_fpu_switch1 (addr));
-    }
 }
 
 void


Patch #2:

	* optabs.c (emit_libcall_block): Try to detect if final move insn
	doesn't set TARGET and avoid making libcall notes if so.

Index: optabs.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/optabs.c,v
retrieving revision 1.84
diff -u -p -r1.84 optabs.c
--- optabs.c	2000/10/18 21:33:40	1.84
+++ optabs.c	2000/11/14 14:15:57
@@ -2873,6 +2873,16 @@ emit_libcall_block (insns, target, resul
     }
 
   last = emit_move_insn (target, result);
+
+  if (final_dest != target)
+    emit_move_insn (final_dest, target);
+
+  /* If the final move insn doesn't copy the target register, something
+     funny is going on.  Making a libcall block here would lead to
+     incorrect code.  */
+  if (! reg_overlap_mentioned_p (target, PATTERN (last)))
+    return;
+
   if (mov_optab->handlers[(int) GET_MODE (target)].insn_code
       != CODE_FOR_nothing)
     set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv));
@@ -2885,9 +2895,6 @@ emit_libcall_block (insns, target, resul
 	 "last".  */
       remove_note (last, find_reg_note (last, REG_EQUAL, NULL_RTX));
     }
-
-  if (final_dest != target)
-    emit_move_insn (final_dest, target);
 
   if (prev == 0)
     first = get_insns ();



More information about the Gcc-patches mailing list