diff mbox

[:,RL78] Disable interrupts during hardware multiplication routines

Message ID CAJA2CV2oxW7HCwcwQnkYW+sFLZ41k=rtvRkSAoYStddaoMLRTQ@mail.gmail.com
State New
Headers show

Commit Message

Kaushik M Phatak Jan. 12, 2016, 7:20 a.m. UTC
Hi Nick,
Thanks for your detailed review.
Please find an updated version of this patch here. I have tried to modify
it as per your suggestions.

> I would suggest:
>  static bool
Done.

> +      if (recog_memoized (insn) == CODE_FOR_udivmodsi4_g13
> have an attribute on these insns, and then test for that here.
Done. Added attribute 'is_g13_muldiv_insn' which gets tested in this function.

> +          return 1;
> If you change the function to "bool" then return "true" here.
> +  return 0;
Done.

>    if (rl78_is_naked_func ())
>     return;
> -
> +
> Why are you adding a extraneous space here ?
Done. This was a typo while extracting the patch.

> +  if (MUST_SAVE_MDUC_REGISTER && (!crtl->is_leaf || check_mduc_usage ()))
> It would be nice to update the stack size computation performed
I have tried to add code to update 'fs' while checking for 'framesize_locals'.
Please let me know if this is OK.

> +      emit_insn (gen_push (gen_rtx_REG (HImode, AX_REG)));
> it would be cleaner to use a for loop and a small structure containing
> the address, mode and volatility to be saved.
I have created simple structure for this, though the volatility is always set
and may not need to be a separate member.
Please let me know if this is OK.

> Hence I would suggest:
> fprintf (file, "\t; preserves MDUC registers\n");
Done.

> +This is the default.
> It is *not* the default!
Updated this. However, the registers are saved when -mmul=g13 and
-mg13 is passed.

> +@item -msave-mduc-in-interrupts
> +@item -mno-save-mduc-in-interrupts
> +@opindex msave-mduc-in-interrupts
> You should also mention that even if this option is enabled
Added information regarding registers being saved based on certain
conditions being met.

Kindly review the updated patch and let me know if it is OK.
This is regression tested for rl78 -msim and -mg13 + -msave-mduc-in-interrupts

Best Regards,
Kaushik

gcc/ChangeLog
2016-01-12  Kaushik Phatak  <kaushik.phatak@kpit.com>

* config/rl78/rl78.c (rl78_expand_prologue): Save the MDUC related
registers in all interrupt handlers if necessary.
(rl78_option_override): Add warning.
(MUST_SAVE_MDUC_REGISTER): New macro.
(rl78_expand_epilogue): Restore the MDUC registers if necessary.
* config/rl78/rl78.c (check_mduc_usage): New function.
* config/rl78/rl78.c (mduc_regs): New structure to hold MDUC register data.
* config/rl78/rl78.md (is_g13_muldiv_insn): New attribute.
* config/rl78/rl78.md (mulsi3_g13): Add is_g13_muldiv_insn attribute.
* config/rl78/rl78.md (udivmodsi4_g13): Add is_g13_muldiv_insn attribute.
* config/rl78/rl78.md (mulhi3_g13): Add is_g13_muldiv_insn attribute.
* config/rl78/rl78.opt (msave-mduc-in-interrupts): New option.
(mno-save-mduc-in-interrupts): New option.
* doc/invoke.texi (@item -msave-mduc-in-interrupts): New item.
(@item -mno-save-mduc-in-interrupts): New item

================== END of Patch ==============

-----Original Message-----
From: Nick Clifton [mailto:nickc@redhat.com]
Sent: Thursday, January 07, 2016 5:33 PM
To: Kaushik M Phatak <kphatak@gmail.com>; dj@redhat.com
Cc: Mike Stump <mikestump@comcast.net>; gcc-patches@gcc.gnu.org;
Kaushik Phatak <Kaushik.Phatak@kpit.com>
Subject: Re: [PATCH : RL78] Disable interrupts during hardware
multiplication routines

Hi Kaushik,

   Just a few comments on the patch itself:
Index: gcc/config/rl78/rl78.c
===================================================================
--- gcc/config/rl78/rl78.c	(revision 2871)
+++ gcc/config/rl78/rl78.c	(working copy)
@@ -83,6 +83,22 @@
   "sp", "ap", "psw", "es", "cs"
 };
 
+/* Structure for G13 MDUC registers.  */
+struct mduc_reg_type
+{
+  unsigned int address;
+  enum machine_mode mode;
+  bool is_volatile;
+};
+
+struct mduc_reg_type  mduc_regs[NUM_OF_MDUC_REGS] =
+  {{0xf00e8, QImode, true},
+   {0xffff0, HImode, true},
+   {0xffff2, HImode, true},
+   {0xf2224, HImode, true},
+   {0xf00e0, HImode, true},
+   {0xf00e2, HImode, true}};
+
 struct GTY(()) machine_function
 {
   /* If set, the rest of the fields have been computed.  */
@@ -342,6 +358,10 @@
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE		rl78_option_override
 
+#define MUST_SAVE_MDUC_REGISTERS                         \
+  (!TARGET_NO_SAVE_MDUC_REGISTERS                        \
+   && (is_interrupt_func (NULL_TREE)) && RL78_MUL_G13)
+
 static void
 rl78_option_override (void)
 {
@@ -366,6 +386,9 @@
     /* Address spaces are currently only supported by C.  */
     error ("-mes0 can only be used with C");
 
+  if (TARGET_SAVE_MDUC_REGISTERS && !(TARGET_G13 || RL78_MUL_G13))
+    warning (0, "mduc registers only saved for G13 target");
+
   switch (rl78_cpu_type)
     {
     case CPU_UNINIT:
@@ -1307,6 +1330,23 @@
   return (lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE);
 }
 
+/* Check if the block uses mul/div insns for G13 target.  */
+static bool
+check_mduc_usage ()
+{
+  rtx insn;
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, cfun)
+  {
+    FOR_BB_INSNS (bb, insn)
+    {
+      if (get_attr_is_g13_muldiv_insn (insn) == IS_G13_MULDIV_INSN_YES)
+        return true;
+    }
+  }
+  return false;
+}
+
 /* Expand the function prologue (from the prologue pattern).  */
 void
 rl78_expand_prologue (void)
@@ -1371,6 +1411,28 @@
       F (emit_insn (gen_push (ax)));
     }
 
+  /* Save MDUC registers inside interrupt routine.  */
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    {
+      rtx mem_mduc;
+
+      for (int i = 0; i <NUM_OF_MDUC_REGS; i++)
+      {
+	if (mduc_regs[i].mode == QImode)
+	{
+	  mem_mduc = gen_rtx_MEM (QImode, GEN_INT (mduc_regs[i].address));
+	  MEM_VOLATILE_P (mem_mduc) = 1;       
+	  emit_insn (gen_movqi (gen_rtx_REG (QImode, A_REG), mem_mduc));
+	}
+	else
+	{
+	  mem_mduc = gen_rtx_MEM (HImode, GEN_INT (mduc_regs[i].address));
+	  MEM_VOLATILE_P (mem_mduc) = 1;       
+	  emit_insn (gen_movqi (gen_rtx_REG (HImode, AX_REG), mem_mduc));
+	}
+	emit_insn (gen_push (gen_rtx_REG (HImode, AX_REG)));
+      }
+    }
   if (frame_pointer_needed)
     {
       F (emit_move_insn (ax, sp));
@@ -1378,6 +1440,8 @@
     }
 
   fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    fs = fs + NUM_OF_MDUC_REGS * 2;
   if (fs > 0)
     {
       /* If we need to subtract more than 254*3 then it is faster and
@@ -1426,6 +1490,8 @@
   else
     {
       fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+      if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+	fs = fs + NUM_OF_MDUC_REGS * 2;
       if (fs > 254 * 3)
 	{
 	  emit_move_insn (ax, sp);
@@ -1444,6 +1510,29 @@
 	}
     }
 
+  /* Restore MDUC registers from interrupt routine.  */
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    {
+      rtx mem_mduc;
+
+      for (int i = NUM_OF_MDUC_REGS-1; i >= 0; i--)
+      {
+	emit_insn (gen_pop (gen_rtx_REG (HImode, AX_REG)));
+        if (mduc_regs[i].mode == QImode)
+        {
+          mem_mduc = gen_rtx_MEM (QImode, GEN_INT (mduc_regs[i].address));
+          MEM_VOLATILE_P (mem_mduc) = 1;
+	  emit_insn (gen_movqi (mem_mduc, gen_rtx_REG (QImode, A_REG)));
+        }
+        else
+        {
+          mem_mduc = gen_rtx_MEM (HImode, GEN_INT (mduc_regs[i].address));
+          MEM_VOLATILE_P (mem_mduc) = 1;
+	  emit_insn (gen_movhi (mem_mduc, gen_rtx_REG (HImode, AX_REG)));
+        }
+      }
+    }
+
   /* Save ES register inside interrupt functions.  */
   if (is_interrupt_func (cfun->decl) && cfun->machine->uses_es)
     {
@@ -1599,6 +1688,9 @@
 
   if (cfun->machine->uses_es)
     fprintf (file, "\t; uses ES register\n");
+
+  if (MUST_SAVE_MDUC_REGISTERS)
+    fprintf (file, "\t; preserves MDUC registers\n");
 }
 
 /* Return an RTL describing where a function return value of type RET_TYPE
Index: gcc/config/rl78/rl78.h
===================================================================
--- gcc/config/rl78/rl78.h	(revision 2871)
+++ gcc/config/rl78/rl78.h	(working copy)
@@ -28,6 +28,7 @@
 #define TARGET_G14	(rl78_cpu_type == CPU_G14)
 
 
+#define NUM_OF_MDUC_REGS 6
 
 #define TARGET_CPU_CPP_BUILTINS()		    \
   do                                                \
Index: gcc/config/rl78/rl78.md
===================================================================
--- gcc/config/rl78/rl78.md	(revision 2871)
+++ gcc/config/rl78/rl78.md	(working copy)
@@ -78,6 +78,7 @@
 (include "rl78-virt.md")
 (include "rl78-real.md")
 
+(define_attr "is_g13_muldiv_insn" "yes,no" (const_string "no"))
 
 ;; Function Prologue/Epilogue Instructions
 
@@ -416,7 +417,8 @@
 	movw    ax, 0xffff6     ; MDBL
 	movw    %h0, ax
         ; end of mulhi macro"
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )
 
 ;; 0xFFFF0 is MACR(L).  0xFFFF2 is MACR(H) but we don't care about it
@@ -496,7 +498,8 @@
 	movw	ax, !0xf00e0	; MDCL
 	movw	%H0, ax
 	; end of mulsi macro"
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )
 
 ;; start-sanitize-rl78
@@ -731,7 +734,8 @@
 	movw	%H3, ax		\n\
 	; end of udivmodsi macro";
       }
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )
 (define_insn "mov1"
   [(set (match_operand:QI         0 "rl78_nonimmediate_operand"   "=vU")
Index: gcc/config/rl78/rl78.opt
===================================================================
--- gcc/config/rl78/rl78.opt	(revision 2871)
+++ gcc/config/rl78/rl78.opt	(working copy)
@@ -103,4 +103,10 @@
 Target Mask(ES0)
 Assume ES is zero throughout program execution, use ES: for read-only data.
 
+msave-mduc-in-interrupts
+Target Mask(SAVE_MDUC_REGISTERS)
+Stores the MDUC registers in interrupt handlers for G13 target.
 
+mno-save-mduc-in-interrupts
+Target RejectNegative Mask(NO_SAVE_MDUC_REGISTERS)
+Does not save the MDUC registers in interrupt handlers for G13 target.
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 2871)
+++ gcc/doc/invoke.texi	(working copy)
@@ -18901,6 +18901,20 @@
 registers @code{r24..r31} are reserved for use in interrupt handlers.
 With this option enabled these registers can be used in ordinary
 functions as well.
+
+@item -msave-mduc-in-interrupts
+@item -mno-save-mduc-in-interrupts
+@opindex msave-mduc-in-interrupts
+@opindex mno-save-mduc-in-interrupts
+Specifies that interrupt handler functions should preserve the
+MDUC registers.  This is only necessary if normal code might use
+the MDUC registers, for example because it performs multiplication
+and division operations. The default is to ignore the MDUC registers
+as this makes the interrupt handlers faster. The target option -mg13
+needs to be passed for this to work as this feature is only available
+on the G13 target (S2 core). The option will not have any effect if
+the target does not have multiply hardware, or if the interrupt
+function does not call any other function.
 @end table
 
 @node RS/6000 and PowerPC Options

Comments

Nick Clifton Jan. 12, 2016, 12:20 p.m. UTC | #1
Hi Kaushik,

> +/* Structure for G13 MDUC registers.  */
> +struct mduc_reg_type
> +{
> +  unsigned int address;
> +  enum machine_mode mode;
> +  bool is_volatile;
> +};
> +
> +struct mduc_reg_type  mduc_regs[NUM_OF_MDUC_REGS] =
> +  {{0xf00e8, QImode, true},
> +   {0xffff0, HImode, true},
> +   {0xffff2, HImode, true},
> +   {0xf2224, HImode, true},
> +   {0xf00e0, HImode, true},
> +   {0xf00e2, HImode, true}};

If the is_volatile field is true for all members of this array, why 
bother having it at all ?  (If I remember correctly in your previous 
patch only some of these addresses were being treated as volatile 
registers, not all of them).


> +/* Check if the block uses mul/div insns for G13 target.  */
> +static bool
> +check_mduc_usage ()

Add a void type to the declaration.  Ie:

   check mduc_usage (void)


> +{
> +  rtx insn;
> +  basic_block bb;
> +  FOR_EACH_BB_FN (bb, cfun)
> +  {

You should have a blank line between the end of the variable 
declarations and the start of the code.


> +    FOR_BB_INSNS (bb, insn)
> +    {
> +      if (get_attr_is_g13_muldiv_insn (insn) == IS_G13_MULDIV_INSN_YES)
> +        return true;

I am not sure - but it might be safer to check INSN_P(insn) first 
before checking for the muldiv attribute.


> +      for (int i = 0; i <NUM_OF_MDUC_REGS; i++)
> +      {
> +if (mduc_regs[i].mode == QImode)
> +{

Indentation.


> +  mem_mduc = gen_rtx_MEM (QImode, GEN_INT (mduc_regs[i].address));
> +  MEM_VOLATILE_P (mem_mduc) = 1;
> +  emit_insn (gen_movqi (gen_rtx_REG (QImode, A_REG), mem_mduc));
> +}
> +else
> +{
> +  mem_mduc = gen_rtx_MEM (HImode, GEN_INT (mduc_regs[i].address));
> +  MEM_VOLATILE_P (mem_mduc) = 1;
> +  emit_insn (gen_movqi (gen_rtx_REG (HImode, AX_REG), mem_mduc));
> +}

In the else case you are using gen_movqi to move an HImode value...

Also you could simplify the above code like this:

   for (int i = 0; i < NUM_OF_MDUC_REGS; i++)
     {
        mduc_reg_type *reg = mduc_regs + i;
        rtx mem_mduc = gen_rtx_MEM (reg->mode, GEN_INT (reg->address));

        MEM_VOLATILE_P (mem_mduc) = reg->is_volatile;
        if (reg->mode == QImode)
          emit_insn (gen_movqi (gen_rtx_REG (QImode, A_REG), mem_mduc));
        else
          emit_insn (gen_movhi (gen_rtx_REG (HImode, AX_REG), mem_mduc));
        emit_insn (gen_push (gen_rtx_REG (HImode, AX_REG)));
     }

>     fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
> +  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
> +    fs = fs + NUM_OF_MDUC_REGS * 2;
>     if (fs > 0)
>       {
>         /* If we need to subtract more than 254*3 then it is faster and
> @@ -1426,6 +1490,8 @@
>     else
>       {
>         fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
> +      if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
> +fs = fs + NUM_OF_MDUC_REGS * 2;
>         if (fs > 254 * 3)

No - this is wrong.  "fs" is the amount of extra space needed in the 
stack frame to hold local variables and outgoing variables.  It should 
not include the stack space used for already pushed registers.


> Index: gcc/config/rl78/rl78.h
> ===================================================================
> --- gcc/config/rl78/rl78.h(revision 2871)
> +++ gcc/config/rl78/rl78.h(working copy)
> @@ -28,6 +28,7 @@
>   #define TARGET_G14(rl78_cpu_type == CPU_G14)
>
>
> +#define NUM_OF_MDUC_REGS 6

Why define this here ?  It is only ever used in rl78,c and it can be 
computed automatically by applying the ARRAY_SIZE macro to the mduc_regs 
array.



> Index: gcc/config/rl78/rl78.opt
> ===================================================================
> --- gcc/config/rl78/rl78.opt(revision 2871)
> +++ gcc/config/rl78/rl78.opt(working copy)
> @@ -103,4 +103,10 @@
>   Target Mask(ES0)
>   Assume ES is zero throughout program execution, use ES: for read-only data.
>
> +msave-mduc-in-interrupts
> +Target Mask(SAVE_MDUC_REGISTERS)
> +Stores the MDUC registers in interrupt handlers for G13 target.
>
> +mno-save-mduc-in-interrupts
> +Target RejectNegative Mask(NO_SAVE_MDUC_REGISTERS)
> +Does not save the MDUC registers in interrupt handlers for G13 target.

This looks wrong.  Surely you only need the msave-mduc-in-interrupts 
definition.  That will automatically allow -mno-save-mduc-in-interrupts, 
since it does not have the RejectNegative attribute.  Also these is no 
need to have two separate target mask bits.  Just SAVE_MDUC_REGISTERS 
will do.




> Index: gcc/doc/invoke.texi
> ===================================================================
> --- gcc/doc/invoke.texi(revision 2871)
> +++ gcc/doc/invoke.texi(working copy)

You should also add the name of the new option to the Machine Dependent 
Options section of the manual.  (Approx line 896 in invoke.texi)


> +@item -msave-mduc-in-interrupts
> +@item -mno-save-mduc-in-interrupts
> +@opindex msave-mduc-in-interrupts
> +@opindex mno-save-mduc-in-interrupts
> +Specifies that interrupt handler functions should preserve the
> +MDUC registers.  This is only necessary if normal code might use
> +the MDUC registers, for example because it performs multiplication
> +and division operations. The default is to ignore the MDUC registers
> +as this makes the interrupt handlers faster. The target option -mg13
> +needs to be passed for this to work as this feature is only available
> +on the G13 target (S2 core). The option will not have any effect if
> +the target does not have multiply hardware, or if the interrupt
> +function does not call any other function.

Still not quite right.  The last sentence should be:

   The MDUC registers will only be saved if the interrupt handler 
performs a multiplication or division operation or it calls another 
function.

Cheers
   Nick
Mike Stump Jan. 12, 2016, 3:52 p.m. UTC | #2
On Jan 11, 2016, at 11:20 PM, Kaushik M Phatak <kphatak@gmail.com> wrote:
> Kindly review the updated patch and let me know if it is OK.

My review comment is still outstanding.
diff mbox

Patch

Index: gcc/config/rl78/rl78.c
===================================================================
--- gcc/config/rl78/rl78.c(revision 2871)
+++ gcc/config/rl78/rl78.c(working copy)
@@ -83,6 +83,22 @@ 
   "sp", "ap", "psw", "es", "cs"
 };

+/* Structure for G13 MDUC registers.  */
+struct mduc_reg_type
+{
+  unsigned int address;
+  enum machine_mode mode;
+  bool is_volatile;
+};
+
+struct mduc_reg_type  mduc_regs[NUM_OF_MDUC_REGS] =
+  {{0xf00e8, QImode, true},
+   {0xffff0, HImode, true},
+   {0xffff2, HImode, true},
+   {0xf2224, HImode, true},
+   {0xf00e0, HImode, true},
+   {0xf00e2, HImode, true}};
+
 struct GTY(()) machine_function
 {
   /* If set, the rest of the fields have been computed.  */
@@ -342,6 +358,10 @@ 
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDErl78_option_override

+#define MUST_SAVE_MDUC_REGISTERS                         \
+  (!TARGET_NO_SAVE_MDUC_REGISTERS                        \
+   && (is_interrupt_func (NULL_TREE)) && RL78_MUL_G13)
+
 static void
 rl78_option_override (void)
 {
@@ -366,6 +386,9 @@ 
     /* Address spaces are currently only supported by C.  */
     error ("-mes0 can only be used with C");

+  if (TARGET_SAVE_MDUC_REGISTERS && !(TARGET_G13 || RL78_MUL_G13))
+    warning (0, "mduc registers only saved for G13 target");
+
   switch (rl78_cpu_type)
     {
     case CPU_UNINIT:
@@ -1307,6 +1330,23 @@ 
   return (lookup_attribute ("naked", DECL_ATTRIBUTES
(current_function_decl)) != NULL_TREE);
 }

+/* Check if the block uses mul/div insns for G13 target.  */
+static bool
+check_mduc_usage ()
+{
+  rtx insn;
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, cfun)
+  {
+    FOR_BB_INSNS (bb, insn)
+    {
+      if (get_attr_is_g13_muldiv_insn (insn) == IS_G13_MULDIV_INSN_YES)
+        return true;
+    }
+  }
+  return false;
+}
+
 /* Expand the function prologue (from the prologue pattern).  */
 void
 rl78_expand_prologue (void)
@@ -1371,6 +1411,28 @@ 
       F (emit_insn (gen_push (ax)));
     }

+  /* Save MDUC registers inside interrupt routine.  */
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    {
+      rtx mem_mduc;
+
+      for (int i = 0; i <NUM_OF_MDUC_REGS; i++)
+      {
+if (mduc_regs[i].mode == QImode)
+{
+  mem_mduc = gen_rtx_MEM (QImode, GEN_INT (mduc_regs[i].address));
+  MEM_VOLATILE_P (mem_mduc) = 1;
+  emit_insn (gen_movqi (gen_rtx_REG (QImode, A_REG), mem_mduc));
+}
+else
+{
+  mem_mduc = gen_rtx_MEM (HImode, GEN_INT (mduc_regs[i].address));
+  MEM_VOLATILE_P (mem_mduc) = 1;
+  emit_insn (gen_movqi (gen_rtx_REG (HImode, AX_REG), mem_mduc));
+}
+emit_insn (gen_push (gen_rtx_REG (HImode, AX_REG)));
+      }
+    }
   if (frame_pointer_needed)
     {
       F (emit_move_insn (ax, sp));
@@ -1378,6 +1440,8 @@ 
     }

   fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    fs = fs + NUM_OF_MDUC_REGS * 2;
   if (fs > 0)
     {
       /* If we need to subtract more than 254*3 then it is faster and
@@ -1426,6 +1490,8 @@ 
   else
     {
       fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+      if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+fs = fs + NUM_OF_MDUC_REGS * 2;
       if (fs > 254 * 3)
 {
   emit_move_insn (ax, sp);
@@ -1444,6 +1510,29 @@ 
 }
     }

+  /* Restore MDUC registers from interrupt routine.  */
+  if (MUST_SAVE_MDUC_REGISTERS && (!crtl->is_leaf || check_mduc_usage ()))
+    {
+      rtx mem_mduc;
+
+      for (int i = NUM_OF_MDUC_REGS-1; i >= 0; i--)
+      {
+emit_insn (gen_pop (gen_rtx_REG (HImode, AX_REG)));
+        if (mduc_regs[i].mode == QImode)
+        {
+          mem_mduc = gen_rtx_MEM (QImode, GEN_INT (mduc_regs[i].address));
+          MEM_VOLATILE_P (mem_mduc) = 1;
+  emit_insn (gen_movqi (mem_mduc, gen_rtx_REG (QImode, A_REG)));
+        }
+        else
+        {
+          mem_mduc = gen_rtx_MEM (HImode, GEN_INT (mduc_regs[i].address));
+          MEM_VOLATILE_P (mem_mduc) = 1;
+  emit_insn (gen_movhi (mem_mduc, gen_rtx_REG (HImode, AX_REG)));
+        }
+      }
+    }
+
   /* Save ES register inside interrupt functions.  */
   if (is_interrupt_func (cfun->decl) && cfun->machine->uses_es)
     {
@@ -1599,6 +1688,9 @@ 

   if (cfun->machine->uses_es)
     fprintf (file, "\t; uses ES register\n");
+
+  if (MUST_SAVE_MDUC_REGISTERS)
+    fprintf (file, "\t; preserves MDUC registers\n");
 }

 /* Return an RTL describing where a function return value of type RET_TYPE
Index: gcc/config/rl78/rl78.h
===================================================================
--- gcc/config/rl78/rl78.h(revision 2871)
+++ gcc/config/rl78/rl78.h(working copy)
@@ -28,6 +28,7 @@ 
 #define TARGET_G14(rl78_cpu_type == CPU_G14)


+#define NUM_OF_MDUC_REGS 6

 #define TARGET_CPU_CPP_BUILTINS()    \
   do                                                \
Index: gcc/config/rl78/rl78.md
===================================================================
--- gcc/config/rl78/rl78.md(revision 2871)
+++ gcc/config/rl78/rl78.md(working copy)
@@ -78,6 +78,7 @@ 
 (include "rl78-virt.md")
 (include "rl78-real.md")

+(define_attr "is_g13_muldiv_insn" "yes,no" (const_string "no"))

 ;; Function Prologue/Epilogue Instructions

@@ -416,7 +417,8 @@ 
 movw    ax, 0xffff6     ; MDBL
 movw    %h0, ax
         ; end of mulhi macro"
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )

 ;; 0xFFFF0 is MACR(L).  0xFFFF2 is MACR(H) but we don't care about it
@@ -496,7 +498,8 @@ 
 movwax, !0xf00e0; MDCL
 movw%H0, ax
 ; end of mulsi macro"
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )

 ;; start-sanitize-rl78
@@ -731,7 +734,8 @@ 
 movw%H3, ax\n\
 ; end of udivmodsi macro";
       }
-  [(set_attr "valloc" "macax")]
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
 )
 (define_insn "mov1"
   [(set (match_operand:QI         0 "rl78_nonimmediate_operand"   "=vU")
Index: gcc/config/rl78/rl78.opt
===================================================================
--- gcc/config/rl78/rl78.opt(revision 2871)
+++ gcc/config/rl78/rl78.opt(working copy)
@@ -103,4 +103,10 @@ 
 Target Mask(ES0)
 Assume ES is zero throughout program execution, use ES: for read-only data.

+msave-mduc-in-interrupts
+Target Mask(SAVE_MDUC_REGISTERS)
+Stores the MDUC registers in interrupt handlers for G13 target.

+mno-save-mduc-in-interrupts
+Target RejectNegative Mask(NO_SAVE_MDUC_REGISTERS)
+Does not save the MDUC registers in interrupt handlers for G13 target.
Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi(revision 2871)
+++ gcc/doc/invoke.texi(working copy)
@@ -18901,6 +18901,20 @@ 
 registers @code{r24..r31} are reserved for use in interrupt handlers.
 With this option enabled these registers can be used in ordinary
 functions as well.
+
+@item -msave-mduc-in-interrupts
+@item -mno-save-mduc-in-interrupts
+@opindex msave-mduc-in-interrupts
+@opindex mno-save-mduc-in-interrupts
+Specifies that interrupt handler functions should preserve the
+MDUC registers.  This is only necessary if normal code might use
+the MDUC registers, for example because it performs multiplication
+and division operations. The default is to ignore the MDUC registers
+as this makes the interrupt handlers faster. The target option -mg13
+needs to be passed for this to work as this feature is only available
+on the G13 target (S2 core). The option will not have any effect if
+the target does not have multiply hardware, or if the interrupt
+function does not call any other function.
 @end table

@node RS/6000 and PowerPC Options