diff mbox

[avr,4.9] Fix PR63633 ICEs for expanders colliding hard-regs

Message ID 544F8D44.1060000@gjlay.de
State New
Headers show

Commit Message

Georg-Johann Lay Oct. 28, 2014, 12:34 p.m. UTC
Middle-end might come up with hard registers as operands for expanders which 
clobber respective hard regs.  This patch uses freshly created pseudos for 
respective expander operands and emits pseudo <-> hard move insn.

Ok for 4.9.2?

It's not yet for trunk because avr trunk backend is currently broken.

Johann

gcc/
	PR63633
	* config/avr/avr-protos.h (regmask): New inline function.
	(avr_fix_inputs, avr_emit3_fix_outputs): New protos.
	* config/avr/avr.c (avr_fix_operands, avr_move_fixed_operands)
	(avr_fix_inputs, avr_emit3_fix_outputs): New functions.
	* config/avr/avr-fixed.md (mulqq3_nomul, muluqq3_nomul)
	(mul<ALL2QA>3, mul<ALL4A>3, <usdiv><ALL1Q>3, <usdiv><ALL2QA>3)
	(<usdiv><ALL4A>3, round<ALL124QA>3): Fix input operands.
	* config/avr/avr-dimode.md (add<ALL8>3, sub<ALL8>3)
	(<ss_addsub><ALL8S>3, <us_addsub><ALL8U>3, cbranch<ALL8>4)
	(<di_shifts><ALL8>3, <any_extend>mulsidi3): Fix input operands.
	* config/avr/avr.md (mulqi3_call, mulhi3_call, mulsi3, mulpsi3)
	(mulu<QIHI>si3, muls<QIHI>si3, mulohisi3, <any_extend>mulhisi3)
	(usmulhisi3, <any_extend>mulhi3_highpart, mulsqipsi3)
	(fmul, fmuls, fmulsu): Fix operands.  Turn insn into expander as
	needed.

gcc/testsuite/
	PR63633
	* gcc.target/avr/torture/pr63633-ice-mult.c: New test.

Comments

Georg-Johann Lay Oct. 28, 2014, 12:47 p.m. UTC | #1
Am 10/28/2014 01:34 PM, schrieb Georg-Johann Lay:
> Middle-end might come up with hard registers as operands for expanders which
> clobber respective hard regs.  This patch uses freshly created pseudos for
> respective expander operands and emits pseudo <-> hard move insn.
>
> Ok for 4.9.2?

p.s.: testsuite passes without new regressions


> It's not yet for trunk because avr trunk backend is currently broken.
>
> Johann
>
> gcc/
>      PR63633
>      * config/avr/avr-protos.h (regmask): New inline function.
>      (avr_fix_inputs, avr_emit3_fix_outputs): New protos.
>      * config/avr/avr.c (avr_fix_operands, avr_move_fixed_operands)
>      (avr_fix_inputs, avr_emit3_fix_outputs): New functions.
>      * config/avr/avr-fixed.md (mulqq3_nomul, muluqq3_nomul)
>      (mul<ALL2QA>3, mul<ALL4A>3, <usdiv><ALL1Q>3, <usdiv><ALL2QA>3)
>      (<usdiv><ALL4A>3, round<ALL124QA>3): Fix input operands.
>      * config/avr/avr-dimode.md (add<ALL8>3, sub<ALL8>3)
>      (<ss_addsub><ALL8S>3, <us_addsub><ALL8U>3, cbranch<ALL8>4)
>      (<di_shifts><ALL8>3, <any_extend>mulsidi3): Fix input operands.
>      * config/avr/avr.md (mulqi3_call, mulhi3_call, mulsi3, mulpsi3)
>      (mulu<QIHI>si3, muls<QIHI>si3, mulohisi3, <any_extend>mulhisi3)
>      (usmulhisi3, <any_extend>mulhi3_highpart, mulsqipsi3)
>      (fmul, fmuls, fmulsu): Fix operands.  Turn insn into expander as
>      needed.
>
> gcc/testsuite/
>      PR63633
>      * gcc.target/avr/torture/pr63633-ice-mult.c: New test.
Denis Chertykov Oct. 30, 2014, 6:48 a.m. UTC | #2
2014-10-28 15:47 GMT+03:00 Georg-Johann Lay <avr@gjlay.de>:
> Am 10/28/2014 01:34 PM, schrieb Georg-Johann Lay:
>>
>> Middle-end might come up with hard registers as operands for expanders
>> which
>> clobber respective hard regs.  This patch uses freshly created pseudos for
>> respective expander operands and emits pseudo <-> hard move insn.
>>
>> Ok for 4.9.2?
>
>

Ok.
Please apply.

Denis.
Georg-Johann Lay Oct. 30, 2014, 10:41 a.m. UTC | #3
Am 10/30/2014 07:48 AM, schrieb Denis Chertykov:
>> Am 10/28/2014 01:34 PM, schrieb Georg-Johann Lay:
>>>
>>> Middle-end might come up with hard registers as operands for expanders
>>> which
>>> clobber respective hard regs.  This patch uses freshly created pseudos for
>>> respective expander operands and emits pseudo <-> hard move insn.
>>>
>>> Ok for 4.9.2?
>>
>
> Ok.
> Please apply.
>
> Denis.

I am also waiting for approval from release manager.

Johann
Jakub Jelinek Oct. 30, 2014, 10:52 a.m. UTC | #4
On Thu, Oct 30, 2014 at 11:41:26AM +0100, Georg-Johann Lay wrote:
> Am 10/30/2014 07:48 AM, schrieb Denis Chertykov:
> >>Am 10/28/2014 01:34 PM, schrieb Georg-Johann Lay:
> >>>
> >>>Middle-end might come up with hard registers as operands for expanders
> >>>which
> >>>clobber respective hard regs.  This patch uses freshly created pseudos for
> >>>respective expander operands and emits pseudo <-> hard move insn.
> >>>
> >>>Ok for 4.9.2?
> >>
> >
> >Ok.
> >Please apply.
> >
> >Denis.
> 
> I am also waiting for approval from release manager.

Too late, it has been already released.  The branch is open again, you don't
need any RM approval now.

	Jakub
diff mbox

Patch

Index: testsuite/gcc.target/avr/torture/pr63633-ice-mult.c
===================================================================
--- testsuite/gcc.target/avr/torture/pr63633-ice-mult.c	(revision 0)
+++ testsuite/gcc.target/avr/torture/pr63633-ice-mult.c	(revision 0)
@@ -0,0 +1,37 @@ 
+/* { dg-do compile } */
+
+void ice_mult32 (int x)
+{
+  register long reg __asm ("22");
+  __asm volatile (" " :: "r" (reg = 0x12345 * x));
+}
+
+void ice_mult24 (int x)
+{
+  register __int24 reg __asm ("20");
+  __asm volatile (" " :: "r" (reg = 0x12345 * x));
+}
+
+void ice_sh24 (__int24 x)
+{
+  register __int24 reg __asm ("20");
+  __asm volatile (" " :: "r" (reg = x << 3));
+}
+
+void ice_sh24b (__int24 x)
+{
+  register __int24 reg __asm ("20");
+  __asm volatile (" " :: "r" (reg = x << 22));
+}
+
+void ice_s16s16 (int x)
+{
+  register long reg __asm ("20");
+  __asm volatile (" " :: "r" (reg = (long) x*x));
+}
+
+void ice_u16s16 (int x)
+{
+  register long reg __asm ("20");
+  __asm volatile (" " :: "r" (reg = (long) x*0x1234u));
+}
Index: config/avr/avr-fixed.md
===================================================================
--- config/avr/avr-fixed.md	(revision 215212)
+++ config/avr/avr-fixed.md	(working copy)
@@ -231,7 +231,11 @@  (define_expand "mulqq3_nomul"
               (clobber (reg:HI 24))])
    (set (match_operand:QQ 0 "register_operand" "")
         (reg:QQ 23))]
-  "!AVR_HAVE_MUL")
+  "!AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (QQmode, 24));
+  })
+
 
 (define_expand "muluqq3_nomul"
   [(set (reg:UQQ 22)
@@ -246,7 +250,10 @@  (define_expand "muluqq3_nomul"
               (clobber (reg:HI 22))])
    (set (match_operand:UQQ 0 "register_operand" "")
         (reg:UQQ 25))]
-  "!AVR_HAVE_MUL")
+  "!AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (UQQmode, 22));
+  })
 
 (define_insn "*mulqq3.call"
   [(set (reg:QQ 23)
@@ -274,7 +281,10 @@  (define_expand "mul<mode>3"
               (clobber (reg:HI 22))])
    (set (match_operand:ALL2QA 0 "register_operand" "")
         (reg:ALL2QA 24))]
-  "AVR_HAVE_MUL")
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 18));
+  })
 
 ;; "*mulhq3.call"  "*muluhq3.call"
 ;; "*mulha3.call"  "*muluha3.call"
@@ -302,7 +312,10 @@  (define_expand "mul<mode>3"
                     (reg:ALL4A 20)))
    (set (match_operand:ALL4A 0 "register_operand" "")
         (reg:ALL4A 24))]
-  "AVR_HAVE_MUL")
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 16));
+  })
 
 ;; "*mulsa3.call" "*mulusa3.call"
 (define_insn "*mul<mode>3.call"
@@ -330,7 +343,12 @@  (define_expand "<code><mode>3"
                                 (reg:ALL1Q 22)))
               (clobber (reg:QI 25))])
    (set (match_operand:ALL1Q 0 "register_operand" "")
-        (reg:ALL1Q 24))])
+        (reg:ALL1Q 24))]
+  ""
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 25));
+  })
+
 
 ;; "*divqq3.call" "*udivuqq3.call"
 (define_insn "*<code><mode>3.call"
@@ -356,7 +374,11 @@  (define_expand "<code><mode>3"
               (clobber (reg:HI 26))
               (clobber (reg:QI 21))])
    (set (match_operand:ALL2QA 0 "register_operand" "")
-        (reg:ALL2QA 24))])
+        (reg:ALL2QA 24))]
+  ""
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 26));
+  })
 
 ;; "*divhq3.call" "*udivuhq3.call"
 ;; "*divha3.call" "*udivuha3.call"
@@ -385,7 +407,11 @@  (define_expand "<code><mode>3"
               (clobber (reg:HI 26))
               (clobber (reg:HI 30))])
    (set (match_operand:ALL4A 0 "register_operand" "")
-        (reg:ALL4A 22))])
+        (reg:ALL4A 22))]
+  ""
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 24));
+  })
 
 ;; "*divsa3.call" "*udivusa3.call"
 (define_insn "*<code><mode>3.call"
@@ -435,6 +461,7 @@  (define_expand "round<mode>3"
 
     operands[3] = gen_rtx_REG (<MODE>mode, regno_out[(size_t) GET_MODE_SIZE (<MODE>mode)]);
     operands[4] = gen_rtx_REG (<MODE>mode,  regno_in[(size_t) GET_MODE_SIZE (<MODE>mode)]);
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, REGNO (operands[4])));
     operands[5] = simplify_gen_subreg (QImode, force_reg (HImode, operands[2]), HImode, 0);
     // $2 is no more needed, but is referenced for expand.
     operands[2] = const0_rtx;
Index: config/avr/avr-dimode.md
===================================================================
--- config/avr/avr-dimode.md	(revision 215212)
+++ config/avr/avr-dimode.md	(working copy)
@@ -68,6 +68,7 @@  (define_expand "add<mode>3"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
 
     if (DImode == <MODE>mode
@@ -145,6 +146,7 @@  (define_expand "sub<mode>3"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
 
     if (const_operand (operands[2], GET_MODE (operands[2])))
@@ -201,6 +203,7 @@  (define_expand "<code_stdname><mode>3"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
 
     if (const_operand (operands[2], GET_MODE (operands[2])))
@@ -249,6 +252,7 @@  (define_expand "<code_stdname><mode>3"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
 
     if (const_operand (operands[2], GET_MODE (operands[2])))
@@ -338,6 +342,7 @@  (define_expand "cbranch<mode>4"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
 
     if (s8_operand (operands[2], VOIDmode))
@@ -424,6 +429,7 @@  (define_expand "<code_stdname><mode>3"
   {
     rtx acc_a = gen_rtx_REG (<MODE>mode, ACC_A);
 
+    avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, ACC_A));
     emit_move_insn (acc_a, operands[1]);
     emit_move_insn (gen_rtx_REG (QImode, 16), operands[2]);
     emit_insn (gen_<code_stdname><mode>3_insn ());
@@ -457,6 +463,7 @@  (define_expand "<extend_u>mulsidi3"
               (clobber (any_extend:SI (match_dup 1)))])]
   "avr_have_dimode"
   {
+    avr_fix_inputs (operands, 1 << 2, regmask (SImode, 22));
     emit_move_insn (gen_rtx_REG (SImode, 22), operands[1]);
     emit_move_insn (gen_rtx_REG (SImode, 18), operands[2]);
     emit_insn (gen_<extend_u>mulsidi3_insn());
Index: config/avr/avr.md
===================================================================
--- config/avr/avr.md	(revision 215212)
+++ config/avr/avr.md	(working copy)
@@ -1482,7 +1482,11 @@  (define_expand "mulqi3_call"
    (set (reg:QI 22) (match_operand:QI 2 "register_operand" ""))
    (parallel [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22)))
               (clobber (reg:QI 22))])
-   (set (match_operand:QI 0 "register_operand" "") (reg:QI 24))])
+   (set (match_operand:QI 0 "register_operand" "") (reg:QI 24))]
+  ""
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (QImode, 24));
+  })
 
 (define_insn "*mulqi3_call"
   [(set (reg:QI 24) (mult:QI (reg:QI 24) (reg:QI 22)))
@@ -2210,7 +2214,13 @@  (define_expand "mulhi3_call"
    (parallel [(set (reg:HI 24) (mult:HI (reg:HI 24) (reg:HI 22)))
               (clobber (reg:HI 22))
               (clobber (reg:QI 21))])
-   (set (match_operand:HI 0 "register_operand" "") (reg:HI 24))])
+   (set (match_operand:HI 0 "register_operand" "")
+        (reg:HI 24))]
+  ""
+  {
+    avr_fix_inputs (operands, (1 << 2), regmask (HImode, 24));
+  })
+
 
 (define_insn "*mulhi3_call"
   [(set (reg:HI 24) (mult:HI (reg:HI 24) (reg:HI 22)))
@@ -2248,6 +2258,10 @@  (define_expand "mulsi3"
         emit_insn (gen_mulohisi3 (operands[0], operands[2], operands[1]));
         DONE;
       }
+
+    if (avr_emit3_fix_outputs (gen_mulsi3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
   })
 
 (define_insn_and_split "*mulsi3"
@@ -2287,7 +2301,23 @@  (define_insn_and_split "*mulsi3"
 
 ;; "muluqisi3"
 ;; "muluhisi3"
-(define_insn_and_split "mulu<mode>si3"
+(define_expand "mulu<mode>si3"
+  [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "")
+                   (mult:SI (zero_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" ""))
+                            (match_operand:SI 2 "pseudo_register_or_const_int_operand" "")))
+              (clobber (reg:HI 26))
+              (clobber (reg:DI 18))])]
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, (1 << 1) | (1 << 2), -1u);
+    if (avr_emit3_fix_outputs (gen_mulu<mode>si3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
+
+;; "*muluqisi3"
+;; "*muluhisi3"
+(define_insn_and_split "*mulu<mode>si3"
   [(set (match_operand:SI 0 "pseudo_register_operand"                           "=r")
         (mult:SI (zero_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" "r"))
                  (match_operand:SI 2 "pseudo_register_or_const_int_operand"      "rn")))
@@ -2323,7 +2353,23 @@  (define_insn_and_split "mulu<mode>si3"
 
 ;; "mulsqisi3"
 ;; "mulshisi3"
-(define_insn_and_split "muls<mode>si3"
+(define_expand "muls<mode>si3"
+  [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "")
+                   (mult:SI (sign_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" ""))
+                            (match_operand:SI 2 "pseudo_register_or_const_int_operand" "")))
+              (clobber (reg:HI 26))
+              (clobber (reg:DI 18))])]
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, (1 << 1) | (1 << 2), -1u);
+    if (avr_emit3_fix_outputs (gen_muls<mode>si3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
+
+;; "*mulsqisi3"
+;; "*mulshisi3"
+(define_insn_and_split "*muls<mode>si3"
   [(set (match_operand:SI 0 "pseudo_register_operand"                           "=r")
         (mult:SI (sign_extend:SI (match_operand:QIHI 1 "pseudo_register_operand" "r"))
                  (match_operand:SI 2 "pseudo_register_or_const_int_operand"      "rn")))
@@ -2366,7 +2412,22 @@  (define_insn_and_split "muls<mode>si3"
 
 ;; One-extend operand 1
 
-(define_insn_and_split "mulohisi3"
+(define_expand "mulohisi3"
+  [(parallel [(set (match_operand:SI 0 "pseudo_register_operand" "")
+                   (mult:SI (not:SI (zero_extend:SI
+                                     (not:HI (match_operand:HI 1 "pseudo_register_operand" ""))))
+                            (match_operand:SI 2 "pseudo_register_or_const_int_operand" "")))
+              (clobber (reg:HI 26))
+              (clobber (reg:DI 18))])]
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, (1 << 1) | (1 << 2), -1u);
+    if (avr_emit3_fix_outputs (gen_mulohisi3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
+
+(define_insn_and_split "*mulohisi3"
   [(set (match_operand:SI 0 "pseudo_register_operand"                          "=r")
         (mult:SI (not:SI (zero_extend:SI
                           (not:HI (match_operand:HI 1 "pseudo_register_operand" "r"))))
@@ -2394,7 +2455,12 @@  (define_expand "<extend_u>mulhisi3"
                             (any_extend:SI (match_operand:HI 2 "register_operand" ""))))
               (clobber (reg:HI 26))
               (clobber (reg:DI 18))])]
-  "AVR_HAVE_MUL")
+  "AVR_HAVE_MUL"
+  {
+    if (avr_emit3_fix_outputs (gen_<extend_u>mulhisi3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
 
 (define_expand "usmulhisi3"
   [(parallel [(set (match_operand:SI 0 "register_operand" "")
@@ -2402,7 +2468,12 @@  (define_expand "usmulhisi3"
                             (sign_extend:SI (match_operand:HI 2 "register_operand" ""))))
               (clobber (reg:HI 26))
               (clobber (reg:DI 18))])]
-  "AVR_HAVE_MUL")
+  "AVR_HAVE_MUL"
+  {
+    if (avr_emit3_fix_outputs (gen_usmulhisi3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
 
 ;; "*uumulqihisi3" "*uumulhiqisi3" "*uumulhihisi3" "*uumulqiqisi3"
 ;; "*usmulqihisi3" "*usmulhiqisi3" "*usmulhihisi3" "*usmulqiqisi3"
@@ -2474,7 +2545,10 @@  (define_expand "<extend_su>mulhi3_highpa
               (clobber (reg:HI 22))])
    (set (match_operand:HI 0 "register_operand" "")
         (reg:HI 24))]
-  "AVR_HAVE_MUL")
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, 1 << 2, regmask (HImode, 18));
+  })
 
 
 (define_insn "*mulsi3_call"
@@ -2697,6 +2771,10 @@  (define_expand "mulpsi3"
         emit_insn (gen_mulsqipsi3 (operands[0], reg, operands[1]));
         DONE;
       }
+
+    if (avr_emit3_fix_outputs (gen_mulpsi3, operands, 1u << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
   })
 
 (define_insn "*umulqihipsi3"
@@ -2729,7 +2807,21 @@  (define_insn "*umulhiqipsi3"
   [(set_attr "length" "7")
    (set_attr "cc" "clobber")])
 
-(define_insn_and_split "mulsqipsi3"
+(define_expand "mulsqipsi3"
+  [(parallel [(set (match_operand:PSI 0 "pseudo_register_operand" "")
+                   (mult:PSI (sign_extend:PSI (match_operand:QI 1 "pseudo_register_operand" ""))
+                             (match_operand:PSI 2 "pseudo_register_or_const_int_operand""")))
+              (clobber (reg:HI 26))
+              (clobber (reg:DI 18))])]
+  "AVR_HAVE_MUL"
+  {
+    avr_fix_inputs (operands, (1 << 1) | (1 << 2), -1u);
+    if (avr_emit3_fix_outputs (gen_mulsqipsi3, operands, 1 << 0,
+                               regmask (DImode, 18) | regmask (HImode, 26)))
+      DONE;
+  })
+
+(define_insn_and_split "*mulsqipsi3"
   [(set (match_operand:PSI 0 "pseudo_register_operand"                          "=r")
         (mult:PSI (sign_extend:PSI (match_operand:QI 1 "pseudo_register_operand" "r"))
                   (match_operand:PSI 2 "pseudo_register_or_const_int_operand"    "rn")))
@@ -6064,6 +6156,7 @@  (define_expand "fmul"
         emit_insn (gen_fmul_insn (operand0, operand1, operand2));
         DONE;
       }
+    avr_fix_inputs (operands, 1 << 2, regmask (QImode, 24));
   })
 
 (define_insn "fmul_insn"
@@ -6107,6 +6200,7 @@  (define_expand "fmuls"
         emit_insn (gen_fmuls_insn (operand0, operand1, operand2));
         DONE;
       }
+    avr_fix_inputs (operands, 1 << 2, regmask (QImode, 24));
   })
 
 (define_insn "fmuls_insn"
@@ -6150,6 +6244,7 @@  (define_expand "fmulsu"
         emit_insn (gen_fmulsu_insn (operand0, operand1, operand2));
         DONE;
       }
+    avr_fix_inputs (operands, 1 << 2, regmask (QImode, 24));
   })
 
 (define_insn "fmulsu_insn"
Index: config/avr/avr-protos.h
===================================================================
--- config/avr/avr-protos.h	(revision 215212)
+++ config/avr/avr-protos.h	(working copy)
@@ -124,6 +124,15 @@  extern bool avr_mem_memx_p (rtx);
 extern bool avr_load_libgcc_p (rtx);
 extern bool avr_xload_libgcc_p (enum machine_mode);
 
+static inline unsigned
+regmask (enum machine_mode mode, unsigned regno)
+{
+  return ((1u << GET_MODE_SIZE (mode)) - 1) << regno;
+}
+
+extern void avr_fix_inputs (rtx*, unsigned, unsigned);
+extern bool avr_emit3_fix_outputs (rtx (*)(rtx,rtx,rtx), rtx*, unsigned, unsigned);
+
 extern rtx lpm_reg_rtx;
 extern rtx lpm_addr_reg_rtx;
 extern rtx tmp_reg_rtx;
Index: config/avr/avr.c
===================================================================
--- config/avr/avr.c	(revision 215212)
+++ config/avr/avr.c	(working copy)
@@ -11118,6 +11118,115 @@  avr_convert_to_type (tree type, tree exp
 }
 
 
+/* PR63633: The middle-end might come up with hard regs as input operands.
+
+   RMASK is a bit mask representing a subset of hard registers R0...R31:
+   Rn is an element of that set iff bit n of RMASK is set.
+   OPMASK describes a subset of OP[]:  If bit n of OPMASK is 1 then
+   OP[n] has to be fixed; otherwise OP[n] is left alone.
+
+   For each element of OPMASK which is a hard register overlapping RMASK,
+   replace OP[n] with a newly created pseudo register
+
+   HREG == 0:  Also emit a move insn that copies the contents of that
+               hard register into the new pseudo.
+
+   HREG != 0:  Also set HREG[n] to the hard register.  */
+
+static void
+avr_fix_operands (rtx *op, rtx *hreg, unsigned opmask, unsigned rmask)
+{
+  for (; opmask; opmask >>= 1, op++)
+    {
+      rtx reg = *op;
+
+      if (hreg)
+        *hreg = NULL_RTX;
+
+      if ((opmask & 1)
+          && REG_P (reg)
+          && REGNO (reg) < FIRST_PSEUDO_REGISTER
+          // This hard-reg overlaps other prohibited hard regs?
+          && (rmask & regmask (GET_MODE (reg), REGNO (reg))))
+        {
+          *op = gen_reg_rtx (GET_MODE (reg));
+          if (hreg == NULL)
+            emit_move_insn (*op, reg);
+          else
+            *hreg = reg;
+        }
+
+      if (hreg)
+        hreg++;
+    }
+}
+
+
+void
+avr_fix_inputs (rtx *op, unsigned opmask, unsigned rmask)
+{
+  avr_fix_operands (op, NULL, opmask, rmask);
+}
+
+
+/* Helper for the function below:  If bit n of MASK is set and
+   HREG[n] != NULL, then emit a move insn to copy OP[n] to HREG[n].
+   Otherwise do nothing for that n.  Return TRUE.  */
+
+static bool
+avr_move_fixed_operands (rtx *op, rtx *hreg, unsigned mask)
+{
+  for (; mask; mask >>= 1, op++, hreg++)
+    if ((mask & 1)
+        && *hreg)
+      emit_move_insn (*hreg, *op);
+
+  return true;
+}
+
+
+/* PR63633: The middle-end might come up with hard regs as output operands.
+
+   GEN is a sequence generating function like gen_mulsi3 with 3 operands OP[].
+   RMASK is a bit mask representing a subset of hard registers R0...R31:
+   Rn is an element of that set iff bit n of RMASK is set.
+   OPMASK describes a subset of OP[]:  If bit n of OPMASK is 1 then
+   OP[n] has to be fixed; otherwise OP[n] is left alone.
+
+   Emit the insn sequence as generated by GEN() with all elements of OPMASK
+   which are hard registers overlapping RMASK replaced by newly created
+   pseudo registers.  After the sequence has been emitted, emit insns that
+   move the contents of respective pseudos to their hard regs.  */
+
+bool
+avr_emit3_fix_outputs (rtx (*gen)(rtx,rtx,rtx), rtx *op,
+                       unsigned opmask, unsigned rmask)
+{
+  const int n = 3;
+  rtx hreg[n];
+
+  /* It is letigimate for GEN to call this function, and in order not to
+     get self-recursive we use the following static kludge.  This is the
+     only way not to duplicate all expanders and to avoid ugly and
+     hard-to-maintain C-code instead of the much more appreciated RTL
+     representation as supplied by define_expand.  */
+  static bool lock = false;
+
+  gcc_assert (opmask < (1u << n));
+
+  if (lock)
+    return false;
+
+  avr_fix_operands (op, hreg, opmask, rmask);
+
+  lock = true;
+  emit_insn (gen (op[0], op[1], op[2]));
+  lock = false;
+
+  return avr_move_fixed_operands (op, hreg, opmask);
+}
+
+
 /* Worker function for movmemhi expander.
    XOP[0]  Destination as MEM:BLK
    XOP[1]  Source      "     "