Subject: [patch] MIPS/gcc: Revert removal of DImode shifts for 32-bit targets

From: "Maciej W. Rozycki" <[email-masked]>
Date: Mon, 19 Jul 2004 17:35:00 +0200 (CEST)

Hello Richard,

 Your change to gcc 3.5:

2004-05-17  Richard Sandiford  <[email-masked]>

	* config/mips/mips.h (MASK_DEBUG_G, TARGET_DEBUG_G_MODE): Delete.
	(TARGET_SWITCHES): Remove debugg.
	* config/mips/mips.md (adddi3, ashldi3, ashrdi3, lshrdi3): Only handle
	TARGET_64BIT.
	(subdi3): Replace the define_expand with a define_insn, the latter
	renamed from subdi3_internal_3.
	(negdi2): Likewise negdi2_internal_2.
	(adddi3_internal_[12], subdi3_internal, ashldi3_internal{,2,3})
	(ashrdi3_internal{,2,3}, lshrdi3_internal{,2,3}): Remove patterns
	and associated define_splits.
	(adddi3_internal): Renamed from adddi3_internal_3.
	(ashldi3_internal): Likewise ashldi3_internal4.
	(ashrdi3_internal): Likewise ashrdi3_internal4.
	(lshrdi3_internal): Likewise lshrdi3_internal4.

breaks 32-bit Linux builds.  Linux relies on simple operations
(addition/subtraction and shifts) on "long long" variables being
implemented inline without a call to libgcc, which isn't linked in.  
After your change Linux has unresolved references to external __ashldi3(),
__ashrdi3() and __lshrdi3() functions at the final link.

 Here is a complete revert of the relevant changes that works for me, but
please feel free to provide a replacement.  Either way, please make
ashldi3, ashrdi3 and lshrdi3 available for 32-bit targets again.

2004-07-19  Maciej W. Rozycki  <[email-masked]>

	* config/mips/mips.md (ashldi3, ashrdi3, lshrdi3): Handle 
	!TARGET_64BIT again, partially reverting the change from 
	2004-05-17.
	(ashldi3_internal{,2,3}, ashrdi3_internal{,2,3}, 
	lshrdi3_internal{,2,3}): Restore patterns and associated 
	define_splits.
	(ashldi3_internal4): Rename from ashldi3_internal.
	(ashrdi3_internal4): Likewise ashrdi3_internal.
	(lshrdi3_internal4): Likewise lshrdi3_internal.

  Maciej

gcc-3.5.0-20040714-mips-xshxdi32.patch
diff -up --recursive --new-file gcc-3.5.0-20040714.macro/gcc/config/mips/mips.md gcc-3.5.0-20040714/gcc/config/mips/mips.md
--- gcc-3.5.0-20040714.macro/gcc/config/mips/mips.md	2004-07-13 04:59:54.000000000 +0000
+++ gcc-3.5.0-20040714/gcc/config/mips/mips.md	2004-07-17 19:00:14.000000000 +0000
@@ -4997,37 +4997,213 @@ dsrl\t%3,%3,1\n\
   { operands[2] = GEN_INT (INTVAL (operands[2]) - 8); })
 
 (define_expand "ashldi3"
-  [(set (match_operand:DI 0 "register_operand")
-	(ashift:DI (match_operand:DI 1 "register_operand")
-		   (match_operand:SI 2 "arith_operand")))]
-  "TARGET_64BIT"
+  [(parallel [(set (match_operand:DI 0 "register_operand")
+		   (ashift:DI (match_operand:DI 1 "register_operand")
+			      (match_operand:SI 2 "arith_operand")))
+	      (clobber (match_dup  3))])]
+  "TARGET_64BIT || !TARGET_MIPS16"
 {
-  /* On the mips16, a shift of more than 8 is a four byte
-     instruction, so, for a shift between 8 and 16, it is just as
-     fast to do two shifts of 8 or less.  If there is a lot of
-     shifting going on, we may win in CSE.  Otherwise combine will
-     put the shifts back together again.  This can be called by
-     function_arg, so we must be careful not to allocate a new
-     register if we've reached the reload pass.  */
-  if (TARGET_MIPS16
-      && optimize
-      && GET_CODE (operands[2]) == CONST_INT
-      && INTVAL (operands[2]) > 8
-      && INTVAL (operands[2]) <= 16
-      && ! reload_in_progress
-      && ! reload_completed)
+  if (TARGET_64BIT)
     {
-      rtx temp = gen_reg_rtx (DImode);
+      /* On the mips16, a shift of more than 8 is a four byte
+	 instruction, so, for a shift between 8 and 16, it is just as
+	 fast to do two shifts of 8 or less.  If there is a lot of
+	 shifting going on, we may win in CSE.  Otherwise combine will
+	 put the shifts back together again.  This can be called by
+	 function_arg, so we must be careful not to allocate a new
+	 register if we've reached the reload pass.  */
+      if (TARGET_MIPS16
+	  && optimize
+	  && GET_CODE (operands[2]) == CONST_INT
+	  && INTVAL (operands[2]) > 8
+	  && INTVAL (operands[2]) <= 16
+	  && ! reload_in_progress
+	  && ! reload_completed)
+	{
+	  rtx temp = gen_reg_rtx (DImode);
 
-      emit_insn (gen_ashldi3_internal (temp, operands[1], GEN_INT (8)));
-      emit_insn (gen_ashldi3_internal (operands[0], temp,
-				       GEN_INT (INTVAL (operands[2]) - 8)));
+	  emit_insn (gen_ashldi3_internal4 (temp, operands[1], GEN_INT (8)));
+	  emit_insn (gen_ashldi3_internal4 (operands[0], temp,
+					    GEN_INT (INTVAL (operands[2]) - 8)));
+	  DONE;
+	}
+
+      emit_insn (gen_ashldi3_internal4 (operands[0], operands[1],
+					operands[2]));
       DONE;
     }
+
+  operands[3] = gen_reg_rtx (SImode);
 })

 (define_insn "ashldi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=&d")
+	(ashift:DI (match_operand:DI 1 "register_operand" "d")
+		   (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16"
+  "sll\t%3,%2,26\;\
+bgez\t%3,1f%#\;\
+sll\t%M0,%L1,%2\;\
+%(b\t3f\;\
+move\t%L0,%.%)\
+\n\n\
+%~1:\;\
+%(beq\t%3,%.,2f\;\
+sll\t%M0,%M1,%2%)\
+\n\;\
+subu\t%3,%.,%2\;\
+srl\t%3,%L1,%3\;\
+or\t%M0,%M0,%3\n\
+%~2:\;\
+sll\t%L0,%L1,%2\n\
+%~3:"
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"SI")
+   (set_attr "length"	"48")])
+
+
+(define_insn "ashldi3_internal2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(ashift:DI (match_operand:DI 1 "register_operand" "d")
+		   (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 32) != 0"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+  return "sll\t%M0,%L1,%2\;move\t%L0,%.";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"8")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashift:DI (match_operand:DI 1 "register_operand")
+		   (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4) (ashift:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 0) (const_int 0))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashift:DI (match_operand:DI 1 "register_operand")
+		   (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0) (ashift:SI (subreg:SI (match_dup 1) 4) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 4) (const_int 0))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "ashldi3_internal3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(ashift:DI (match_operand:DI 1 "register_operand" "d")
+		   (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+{
+  int amount = INTVAL (operands[2]);
+
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+
+  return "sll\t%M0,%M1,%2\;srl\t%3,%L1,%4\;or\t%M0,%M0,%3\;sll\t%L0,%L1,%2";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"16")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashift:DI (match_operand:DI 1 "register_operand")
+		   (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4)
+	(ashift:SI (subreg:SI (match_dup 1) 4)
+		   (match_dup 2)))
+
+   (set (match_dup 3)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 0)
+		     (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(ior:SI (subreg:SI (match_dup 0) 4)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(ashift:SI (subreg:SI (match_dup 1) 0)
+		   (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashift:DI (match_operand:DI 1 "register_operand")
+		   (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0)
+	(ashift:SI (subreg:SI (match_dup 1) 0)
+		   (match_dup 2)))
+
+   (set (match_dup 3)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 4)
+		     (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(ior:SI (subreg:SI (match_dup 0) 0)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(ashift:SI (subreg:SI (match_dup 1) 4)
+		   (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_insn "ashldi3_internal4"
   [(set (match_operand:DI 0 "register_operand" "=d")
 	(ashift:DI (match_operand:DI 1 "register_operand" "d")
 		   (match_operand:SI 2 "arith_operand" "dI")))]
@@ -5157,33 +5333,208 @@ dsrl\t%3,%3,1\n\
   { operands[2] = GEN_INT (INTVAL (operands[2]) - 8); })
 
 (define_expand "ashrdi3"
-  [(set (match_operand:DI 0 "register_operand")
-	(ashiftrt:DI (match_operand:DI 1 "register_operand")
-		     (match_operand:SI 2 "arith_operand")))]
-  "TARGET_64BIT"
+  [(parallel [(set (match_operand:DI 0 "register_operand")
+		   (ashiftrt:DI (match_operand:DI 1 "register_operand")
+				(match_operand:SI 2 "arith_operand")))
+	      (clobber (match_dup  3))])]
+  "TARGET_64BIT || !TARGET_MIPS16"
 {
-  /* On the mips16, a shift of more than 8 is a four byte
-     instruction, so, for a shift between 8 and 16, it is just as
-     fast to do two shifts of 8 or less.  If there is a lot of
-     shifting going on, we may win in CSE.  Otherwise combine will
-     put the shifts back together again.  */
-  if (TARGET_MIPS16
-      && optimize
-      && GET_CODE (operands[2]) == CONST_INT
-      && INTVAL (operands[2]) > 8
-      && INTVAL (operands[2]) <= 16)
+  if (TARGET_64BIT)
     {
-      rtx temp = gen_reg_rtx (DImode);
+      /* On the mips16, a shift of more than 8 is a four byte
+	 instruction, so, for a shift between 8 and 16, it is just as
+	 fast to do two shifts of 8 or less.  If there is a lot of
+	 shifting going on, we may win in CSE.  Otherwise combine will
+	 put the shifts back together again.  */
+      if (TARGET_MIPS16
+	  && optimize
+	  && GET_CODE (operands[2]) == CONST_INT
+	  && INTVAL (operands[2]) > 8
+	  && INTVAL (operands[2]) <= 16)
+	{
+	  rtx temp = gen_reg_rtx (DImode);
+
+	  emit_insn (gen_ashrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+	  emit_insn (gen_ashrdi3_internal4 (operands[0], temp,
+					    GEN_INT (INTVAL (operands[2]) - 8)));
+	  DONE;
+	}
 
-      emit_insn (gen_ashrdi3_internal (temp, operands[1], GEN_INT (8)));
-      emit_insn (gen_ashrdi3_internal (operands[0], temp,
-				       GEN_INT (INTVAL (operands[2]) - 8)));
+      emit_insn (gen_ashrdi3_internal4 (operands[0], operands[1],
+					operands[2]));
       DONE;
     }
+
+  operands[3] = gen_reg_rtx (SImode);
 })

 (define_insn "ashrdi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=&d")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		     (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16"
+  "sll\t%3,%2,26\;\
+bgez\t%3,1f%#\;\
+sra\t%L0,%M1,%2\;\
+%(b\t3f\;\
+sra\t%M0,%M1,31%)\
+\n\n\
+%~1:\;\
+%(beq\t%3,%.,2f\;\
+srl\t%L0,%L1,%2%)\
+\n\;\
+subu\t%3,%.,%2\;\
+sll\t%3,%M1,%3\;\
+or\t%L0,%L0,%3\n\
+%~2:\;\
+sra\t%M0,%M1,%2\n\
+%~3:"
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"48")])
+
+
+(define_insn "ashrdi3_internal2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		     (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && (INTVAL (operands[2]) & 32) != 0"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+  return "sra\t%L0,%M1,%2\;sra\t%M0,%M1,31";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"8")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 4) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 4) (ashiftrt:SI (subreg:SI (match_dup 1) 4) (const_int 31)))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (const_int 31)))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "ashrdi3_internal3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		     (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+{
+  int amount = INTVAL (operands[2]);
+
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+
+  return "srl\t%L0,%L1,%2\;sll\t%3,%M1,%4\;or\t%L0,%L0,%3\;sra\t%M0,%M1,%2";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"16")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 0)
+		     (match_dup 2)))
+
+   (set (match_dup 3)
+	(ashift:SI (subreg:SI (match_dup 1) 4)
+		   (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(ior:SI (subreg:SI (match_dup 0) 0)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(ashiftrt:SI (subreg:SI (match_dup 1) 4)
+		     (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(ashiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 4)
+		     (match_dup 2)))
+
+   (set (match_dup 3)
+	(ashift:SI (subreg:SI (match_dup 1) 0)
+		   (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(ior:SI (subreg:SI (match_dup 0) 4)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(ashiftrt:SI (subreg:SI (match_dup 1) 0)
+		     (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_insn "ashrdi3_internal4"
   [(set (match_operand:DI 0 "register_operand" "=d")
 	(ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
 		     (match_operand:SI 2 "arith_operand" "dI")))]
@@ -5332,33 +5683,209 @@ dsrl\t%3,%3,1\n\
    (set_attr "length"	"16")])
 
 (define_expand "lshrdi3"
-  [(set (match_operand:DI 0 "register_operand")
-	(lshiftrt:DI (match_operand:DI 1 "register_operand")
-		     (match_operand:SI 2 "arith_operand")))]
-  "TARGET_64BIT"
+  [(parallel [(set (match_operand:DI 0 "register_operand")
+		   (lshiftrt:DI (match_operand:DI 1 "register_operand")
+				(match_operand:SI 2 "arith_operand")))
+	      (clobber (match_dup  3))])]
+  "TARGET_64BIT || !TARGET_MIPS16"
 {
-  /* On the mips16, a shift of more than 8 is a four byte
-     instruction, so, for a shift between 8 and 16, it is just as
-     fast to do two shifts of 8 or less.  If there is a lot of
-     shifting going on, we may win in CSE.  Otherwise combine will
-     put the shifts back together again.  */
-  if (TARGET_MIPS16
-      && optimize
-      && GET_CODE (operands[2]) == CONST_INT
-      && INTVAL (operands[2]) > 8
-      && INTVAL (operands[2]) <= 16)
+  if (TARGET_64BIT)
     {
-      rtx temp = gen_reg_rtx (DImode);
+      /* On the mips16, a shift of more than 8 is a four byte
+	 instruction, so, for a shift between 8 and 16, it is just as
+	 fast to do two shifts of 8 or less.  If there is a lot of
+	 shifting going on, we may win in CSE.  Otherwise combine will
+	 put the shifts back together again.  */
+      if (TARGET_MIPS16
+	  && optimize
+	  && GET_CODE (operands[2]) == CONST_INT
+	  && INTVAL (operands[2]) > 8
+	  && INTVAL (operands[2]) <= 16)
+	{
+	  rtx temp = gen_reg_rtx (DImode);
 
-      emit_insn (gen_lshrdi3_internal (temp, operands[1], GEN_INT (8)));
-      emit_insn (gen_lshrdi3_internal (operands[0], temp,
-				       GEN_INT (INTVAL (operands[2]) - 8)));
+	  emit_insn (gen_lshrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+	  emit_insn (gen_lshrdi3_internal4 (operands[0], temp,
+					    GEN_INT (INTVAL (operands[2]) - 8)));
+	  DONE;
+	}
+
+      emit_insn (gen_lshrdi3_internal4 (operands[0], operands[1],
+					operands[2]));
       DONE;
     }
+
+  operands[3] = gen_reg_rtx (SImode);
 })

 (define_insn "lshrdi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=&d")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		     (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16"
+  "sll\t%3,%2,26\;\
+bgez\t%3,1f%#\;\
+srl\t%L0,%M1,%2\;\
+%(b\t3f\;\
+move\t%M0,%.%)\
+\n\n\
+%~1:\;\
+%(beq\t%3,%.,2f\;\
+srl\t%L0,%L1,%2%)\
+\n\;\
+subu\t%3,%.,%2\;\
+sll\t%3,%M1,%3\;\
+or\t%L0,%L0,%3\n\
+%~2:\;\
+srl\t%M0,%M1,%2\n\
+%~3:"
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"48")])
+
+
+(define_insn "lshrdi3_internal2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		     (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 32) != 0"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+  return "srl\t%L0,%M1,%2\;move\t%M0,%.";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"8")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0) (lshiftrt:SI (subreg:SI (match_dup 1) 4) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 4) (const_int 0))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 32) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4) (lshiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+   (set (subreg:SI (match_dup 0) 0) (const_int 0))]
+
+  "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "lshrdi3_internal3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+		   (match_operand:SI 2 "small_int" "IJK")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+{
+  int amount = INTVAL (operands[2]);
+
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+
+  return "srl\t%L0,%L1,%2\;sll\t%3,%M1,%4\;or\t%L0,%L0,%3\;srl\t%M0,%M1,%2";
+}
+  [(set_attr "type"	"multi")
+   (set_attr "mode"	"DI")
+   (set_attr "length"	"16")])
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 0)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 0)
+		     (match_dup 2)))
+
+   (set (match_dup 3)
+	(ashift:SI (subreg:SI (match_dup 1) 4)
+		   (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(ior:SI (subreg:SI (match_dup 0) 0)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 4)
+		     (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand")
+	(lshiftrt:DI (match_operand:DI 1 "register_operand")
+		     (match_operand:SI 2 "small_int")))
+   (clobber (match_operand:SI 3 "register_operand"))]
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+   && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+   && (INTVAL (operands[2]) & 63) < 32
+   && (INTVAL (operands[2]) & 63) != 0"
+
+  [(set (subreg:SI (match_dup 0) 4)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 4)
+		     (match_dup 2)))
+
+   (set (match_dup 3)
+	(ashift:SI (subreg:SI (match_dup 1) 0)
+		   (match_dup 4)))
+
+   (set (subreg:SI (match_dup 0) 4)
+	(ior:SI (subreg:SI (match_dup 0) 4)
+		(match_dup 3)))
+
+   (set (subreg:SI (match_dup 0) 0)
+	(lshiftrt:SI (subreg:SI (match_dup 1) 0)
+		     (match_dup 2)))]
+{
+  int amount = INTVAL (operands[2]);
+  operands[2] = GEN_INT (amount & 31);
+  operands[4] = GEN_INT ((-amount) & 31);
+})
+
+
+(define_insn "lshrdi3_internal4"
   [(set (match_operand:DI 0 "register_operand" "=d")
 	(lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
 		     (match_operand:SI 2 "arith_operand" "dI")))]

---

From: Richard Sandiford <[email-masked]>
Date: Mon, 19 Jul 2004 17:59:11 +0100

"Maciej W. Rozycki" <[email-masked]> writes:
> Linux relies on simple operations (addition/subtraction and shifts) on
> "long long" variables being implemented inline without a call to
> libgcc, which isn't linked in.

Sorry, but I don't think this is a reasonable expection for 64-bit
shifts on 32-bit targets.  If linux insists on not using libgcc,

> __ashrdi3() and __lshrdi3() functions at the final link.

...these functions itself.

Richard

---

From: David Edelsohn <[email-masked]>
Date: Mon, 19 Jul 2004 13:32:51 -0400

>>>>> Richard Sandiford writes:

> shifts on 32-bit targets.  If linux insists on not using libgcc,

	Other targets provide those DImode operations.

	Part of the mission of GCC is to support GNU and GNU/Linux.

David

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Mon, 19 Jul 2004 19:33:14 +0200 (CEST)

> shifts on 32-bit targets.  If linux insists on not using libgcc,

 See e.g: "http://www.ussg.iu.edu/hypermail/linux/kernel/0009.2/0655.html"  
for a rationale behind that.

> ...these functions itself.

 Well, other targets, like the i386 (which didn't even have a 64-bit
variation till recently), do not force Linux to go through such
contortions.  I can't see a reason why MIPS should be different -- it's
not any harder to implement shifts for this processor than for an average
other platform.  Anyway, the patch works for me and it has been published
so that others can use it, thus I have no incentive to do anything else,
sorry.

  Maciej

---

From: Richard Sandiford <[email-masked]>
Date: Mon, 19 Jul 2004 18:37:08 +0100

"Maciej W. Rozycki" <[email-masked]> writes:
> contortions.

But arm targets (for instance) don't provide inline 64-bit shifts,
do they?  I don't think there's anything special in the MIPS ISA
that makes them easier for MIPS than they are for ARM.

Richard

---

From: Richard Henderson <[email-masked]>
Date: Mon, 19 Jul 2004 14:38:01 -0700

On Mon, Jul 19, 2004 at 07:33:14PM +0200, Maciej W. Rozycki wrote:
> variation till recently)...

Except that 80386 has 64-bit shifts in hardware.

And in rebuttal to the "does not make linux jump through hoops"
argument, see arch/*/lib/ for arm, h8300, m68k, sparc, v850.

r~

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Fri, 23 Jul 2004 16:41:35 +0200 (CEST)

> Except that 80386 has 64-bit shifts in hardware.

 Indeed -- I tend to forget of these two, sigh...

> argument, see arch/*/lib/ for arm, h8300, m68k, sparc, v850.

 OK -- but then is there any way to convince gcc to embed a "static
inline" version of these functions instead of emitting a call?  Sometimes
putting these eight (or nine for ashrdi3) instructions inline would be a
performance win.

  Maciej

---

From: Richard Henderson <[email-masked]>
Date: Fri, 23 Jul 2004 13:27:03 -0700

On Fri, Jul 23, 2004 at 04:41:35PM +0200, Maciej W. Rozycki wrote:
> inline" version of these functions instead of emitting a call?

No.

> Sometimes putting these eight (or nine for ashrdi3) instructions
> inline would be a performance win.

Sometimes, maybe.  I suspect you'll find that in general it's
nothing but bloat.

r~

---

From: Ralf Baechle <[email-masked]>
Date: Fri, 23 Jul 2004 23:12:32 +0200

> Sometimes, maybe.  I suspect you'll find that in general it's

With a bit of hand waiving because haven't done benchmarks I guess Richard
might be right.  The subroutine calling overhead on modern processors is
rather low and smaller code means better cache hit rates ...

  Ralf

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Mon, 26 Jul 2004 13:56:47 +0200 (CEST)

> rather low and smaller code means better cache hit rates ...

 Well, I just worry the call may itself include at least the same number
of instructions as the callee if inlined.  There would be no way for it to
be faster.

 That may happen for a leaf function -- the call itself, plus $ra
saving/restoration is already four instructions.  Now it's sufficient for
two statics to be needed to preserve temporaries across such a call and
the size of the caller is already the same.  With three statics, you lose
even for a non-leaf function.  That's for a function containing a single
call to such a shift -- if there are more, then you may win (but is it
common?).

 So not only it may not be faster, but the resulting code may be bigger as
well.  That said, the current GCC's implementation of these operations is
not exactly optimal for current MIPS processors.  That's trivial to deal
with in Linux, but would it be possible to pick a different implementation
from libgcc based on the "-march=" setting, too?

  Maciej