Index: gcc-4.3.6/gcc/config/arm/cirrus.md
===================================================================
--- gcc-4.3.6.orig/gcc/config/arm/cirrus.md	2013-07-11 22:24:32.000000000 +0200
+++ gcc-4.3.6/gcc/config/arm/cirrus.md	2013-07-12 15:04:29.000000000 +0200
@@ -221,7 +221,15 @@
    (set_attr "cirrus" "normal")]
 )
 
-(define_insn "cirrus_ashl_const"
+; Shift instructions
+
+; Note: Both "gcc" *and* "as" for cfrshl{32,64} have the source and
+; destination operands swapped.
+; cfrshl* %V0, %V1, %s2 reads V0 and writes V1.
+
+; 32-bit shifts
+
+(define_insn "*cirrus_ashlsi_const"
   [(set (match_operand:SI            0 "cirrus_fp_register" "=v")
 	(ashift:SI (match_operand:SI 1 "cirrus_fp_register"  "v")
 		   (match_operand:SI 2 "const_cirrus_shift_operand"  "")))]
@@ -231,7 +239,7 @@
    (set_attr "cirrus" "normal")]
 )
 
-(define_insn "cirrus_ashiftrt_const"
+(define_insn "*cirrus_ashrsi_const"
   [(set (match_operand:SI	       0 "cirrus_fp_register" "=v")
 	(ashiftrt:SI (match_operand:SI 1 "cirrus_fp_register"  "v")
 		     (match_operand:SI 2 "const_cirrus_shiftrt_operand"  "")))]
@@ -241,7 +249,7 @@
    (set_attr "cirrus" "normal")]
 )
 
-(define_insn "cirrus_ashlsi3"
+(define_insn "*cirrus_ashlsi3"
   [(set (match_operand:SI            0 "cirrus_fp_register" "=v")
 	(ashift:SI (match_operand:SI 1 "cirrus_fp_register"  "v")
 		   (match_operand:SI 2 "register_operand"    "r")))]
@@ -251,36 +259,106 @@
    (set_attr "cirrus" "normal")]
 )
 
+; 64-bit shifts
+
+; Left shift DI
+
+; cfrshl64 truncates the shift count into the range -32 to 31,
+; so a shift by +48 gives -16.
+; Solution: do the 0-31 bit shift, then apply a 32-bit shift:
+; Special case is needed for a 64-bit shift, which should give 0.
+;    and	%s3, %s2, #31	/* First, shift by 0..31 bits	*/
+;    cfrshl64	%V1, %V0, %s3	/* (reads V1, writes V0)	*/
+;    nop			/* Avoid erratum 2		*/
+;    cmp	%s2,#64		/* If shift count >= 64		*/
+;    cfsub64ge	%V0,%V0,%V0	/* then result = 0.		*/
+;    ands	%s3, %s2, #32	/* Do the other 32 bits if the  */
+;    cfsh64ne	%V0, %V0, #16	/* shift count was 32..63.	*/
+;    cfsh64ne	%V0, %V0, #16
+; Even moving arg and result from and to ARM registers
+; this still averages 3/4 of the time of the ARM version.
+
 (define_insn "cirrus_ashldi3"
-  [(set (match_operand:DI            0 "cirrus_fp_register" "=v")
-	(ashift:DI (match_operand:DI 1 "cirrus_fp_register"  "v")
-		   (match_operand:SI 2 "register_operand"    "r")))]
+  [(parallel [
+     (set (match_operand:DI            0 "cirrus_fp_register" "=v")
+	  (ashift:DI (match_operand:DI 1 "cirrus_fp_register"  "v")
+		     (match_operand:SI 2 "register_operand"    "r")))
+     (clobber (match_scratch:SI 3 "=&r"))
+     (clobber (reg:CC CC_REGNUM))])]
   "TARGET_32BIT && TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI"
-  "cfrshl64%?\\t%V1, %V0, %s2"
+  "and\\t%s3, %s2, #31\;cfrshl64\\t%V1, %V0, %s3\;nop\;cmp\\t%s2,#64\;cfsub64ge\\t%V0,%V0,%V0\;ands\\t%s3, %s2, #32\;cfsh64ne\\t%V0, %V0, #16\;cfsh64ne\\t%V0, %V0, #16"
   [(set_attr "type" "farith")
+   (set_attr "conds" "clob")
+   (set_attr "length" "32")
    (set_attr "cirrus" "normal")]
 )
 
-(define_insn "cirrus_ashldi_const"
+; If op[2]>62, we hope this will apply itself recursively.
+(define_insn_and_split "cirrus_ashldi_const"
   [(set (match_operand:DI            0 "cirrus_fp_register" "=v")
 	(ashift:DI (match_operand:DI 1 "cirrus_fp_register"  "v")
-		   (match_operand:SI 2 "const_cirrus_shift_operand"  "")))]
+		   (match_operand:SI 2 "const_int_operand"  "")))]
   "TARGET_32BIT && TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI"
-  "cfsh64%?\\t%V0, %V1, #%s2"
+    "cfsh64%?\\t%V0, %V1, #%s2"
+  "&& INTVAL (operands[2]) > 31"
+    [(set (match_dup 0)
+	  (ashift:DI (match_dup 1) (const_int 31)))
+     (set (match_dup 0)
+	  (ashift:DI (match_dup 0) (match_dup 3)))]
+    "{ operands[3] = GEN_INT (INTVAL (operands[2]) - 31); }"
+
   [(set_attr "type" "farith")
    (set_attr "cirrus" "normal")]
 )
 
-(define_insn "cirrus_ashiftrtdi_const"
-  [(set (match_operand:DI            0 "cirrus_fp_register" "=v")
+; Right shift DI
+
+; cfrshl64 can do right shifts as left shifts by 0 to -32 bits so
+; implement 64-bit shifts as one by 0..32 bits and one by 32.
+;
+; cfrshl64 right always does arithmetic shifts, duplicating the top bit,
+; even if you set the Unsigned Integer bit in the DSPSC register.
+;    negs	%s3, %s2, #31	/* 1..32 -> -1..-32, 0->0	*/
+;    orrne	%s3, #32	/* -63..-33 -> -31..-1, 0->0	*/
+;    cfrshl64	%V1, %V0, %s3	/* (reads V1, writes V0)	*/
+;    nop			/* Avoid Erratum 2		*/
+;    cmp	%s2, #32	/* If shift_count > 32		*/
+;    cfsh64gt	%V0, %V0, #-32	/* do the other 32 bits shift	*/ "
+
+(define_insn "cirrus_ashrdi3"
+  [(parallel [
+     (set (match_operand:DI              0 "cirrus_fp_register" "=v")
+	  (ashiftrt:DI (match_operand:DI 1 "cirrus_fp_register"  "v")
+		       (match_operand:SI 2 "register_operand"    "r")))
+     (clobber (match_scratch:SI 3 "=&r"))
+     (clobber (reg:CC CC_REGNUM))])]
+  "TARGET_32BIT && TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI"
+  "negs\\t%s3, %s2, #31\;orrne\\t%s3, #32\;cfrshl64\\t%V1, %V0, %s3\;nop\;cmp\\t%s2, #32\;cfsh64gt\\t%V0, %V0, #-32"
+  [(set_attr "type" "farith")
+   (set_attr "conds" "clob")
+   (set_attr "length" "24")
+   (set_attr "cirrus" "normal")]
+)
+
+(define_insn_and_split "cirrus_ashrdi_const"
+  [(set (match_operand:DI              0 "cirrus_fp_register" "=v")
 	(ashiftrt:DI (match_operand:DI 1 "cirrus_fp_register"  "v")
-		     (match_operand:SI 2 "const_cirrus_shiftrt_operand"  "")))]
+		     (match_operand:SI 2 "const_int_operand"  "")))]
   "TARGET_32BIT && TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI"
-  "cfsh64%?\\t%V0, %V1, #-%s2"
+    "cfsh64%?\\t%V0, %V1, #-%s2"
+  ; Handle larger constant right shifts as multiple <= 32-bit shifts
+  "&& INTVAL (operands[2]) > 32"
+    [(set (match_dup 0)
+	  (ashiftrt:DI (match_dup 1) (const_int 32)))
+     (set (match_dup 0)
+	  (ashiftrt:DI (match_dup 0) (match_dup 3)))]
+    "{ operands[3] = GEN_INT (INTVAL (operands[2]) - 32); }"
   [(set_attr "type" "farith")
    (set_attr "cirrus" "normal")]
 )
 
+; Integer arithmetic
+
 (define_insn "*cirrus_absdi2"
   [(set (match_operand:DI         0 "cirrus_fp_register" "=v")
 	(abs:DI (match_operand:DI 1 "cirrus_fp_register"  "v")))]
Index: gcc-4.3.6/gcc/config/arm/arm.opt
===================================================================
--- gcc-4.3.6.orig/gcc/config/arm/arm.opt	2013-07-11 22:24:32.000000000 +0200
+++ gcc-4.3.6/gcc/config/arm/arm.opt	2013-07-11 22:24:32.000000000 +0200
@@ -65,7 +65,7 @@
 
 mcirrus-di
 Target Report Mask(CIRRUS_DI)
-Cirrus: Enable processing of 64-bit integers in the MaverickCrunch unit (buggy)
+Cirrus: Enable processing of 64-bit integers in the MaverickCrunch unit
 
 mcpu=
 Target RejectNegative Joined
Index: gcc-4.3.6/gcc/config/arm/arm.md
===================================================================
--- gcc-4.3.6.orig/gcc/config/arm/arm.md	2013-07-11 22:24:31.000000000 +0200
+++ gcc-4.3.6/gcc/config/arm/arm.md	2013-07-12 02:56:25.000000000 +0200
@@ -2966,6 +2966,19 @@
                    (match_operand:SI 2 "reg_or_int_operand" "")))]
   "TARGET_32BIT"
   "
+  if (TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI)
+    {
+      if (!cirrus_fp_register (operands[0], DImode))
+        operands[0] = force_reg (DImode, operands[0]);
+      if (!cirrus_fp_register (operands[1], DImode))
+        operands[1] = force_reg (DImode, operands[1]);
+      if (GET_CODE (operands[2]) == CONST_INT)
+	emit_insn (gen_cirrus_ashldi_const (operands[0], operands[1], operands[2]));
+      else
+        emit_insn (gen_cirrus_ashldi3 (operands[0], operands[1], operands[2]));
+      DONE;
+    }
+
   if (GET_CODE (operands[2]) == CONST_INT)
     {
       if ((HOST_WIDE_INT) INTVAL (operands[2]) == 1)
@@ -2979,8 +2992,7 @@
            values to iwmmxt regs and back.  */
         FAIL;
     }
-  else if (!TARGET_REALLY_IWMMXT
-	   && !(TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI))
+  else if (!TARGET_REALLY_IWMMXT)
     FAIL;
   "
 )
@@ -3026,6 +3038,19 @@
                      (match_operand:SI 2 "reg_or_int_operand" "")))]
   "TARGET_32BIT"
   "
+  if (TARGET_HARD_FLOAT && TARGET_MAVERICK && TARGET_CIRRUS_DI)
+    {
+      if (!cirrus_fp_register (operands[0], DImode))
+        operands[0] = force_reg (DImode, operands[0]);
+      if (!cirrus_fp_register (operands[1], DImode))
+        operands[1] = force_reg (DImode, operands[1]);
+      if (GET_CODE (operands[2]) == CONST_INT)
+	emit_insn (gen_cirrus_ashrdi_const (operands[0], operands[1], operands[2]));
+      else
+        emit_insn (gen_cirrus_ashrdi3 (operands[0], operands[1], operands[2]));
+      DONE;
+    }
+
   if (GET_CODE (operands[2]) == CONST_INT)
     {
       if ((HOST_WIDE_INT) INTVAL (operands[2]) == 1)
