diff gcc/config/rl78/rl78.md @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/config/rl78/rl78.md	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,720 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011-2017 Free Software Foundation, Inc.
+;;  Contributed by Red Hat.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_constants
+  [
+   (AX_REG 0)
+   (X_REG 0)
+   (A_REG 1)
+   (BC_REG 2)
+   (C_REG 2)
+   (B_REG 3)
+   (DE_REG 4)
+   (E_REG 4)
+   (D_REG 5)
+   (HL_REG 6)
+   (L_REG 6)
+   (H_REG 7)
+
+   (FP_REG 22)
+   (SP_REG 32)
+   (CC_REG 34)
+   (ES_REG 35)
+   (CS_REG 36)
+
+   (UNS_PROLOG	1)
+   (UNS_EPILOG	1)
+   (UNS_RETI	2)
+   (UNS_RETB	3)
+
+   (UNS_SET_RB	10)
+   (UNS_ES_ADDR	11)
+
+   (UNS_TRAMPOLINE_INIT		20)
+   (UNS_TRAMPOLINE_UNINIT	21)
+   (UNS_NONLOCAL_GOTO		22)
+
+   ])
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop"
+  )
+
+(define_mode_iterator QHI [QI HI])
+
+(include "predicates.md")
+(include "constraints.md")
+(include "rl78-expand.md")
+(include "rl78-virt.md")
+(include "rl78-real.md")
+
+(define_attr "is_g13_muldiv_insn" "yes,no" (const_string "no"))
+
+;; Function Prologue/Epilogue Instructions
+
+(define_expand "prologue"
+  [(const_int 0)]
+  ""
+  "rl78_expand_prologue (); DONE;"
+)
+
+(define_expand "epilogue"
+  [(const_int 0)]
+  ""
+  "rl78_expand_epilogue (); DONE;"
+)
+
+(define_expand "sibcall_epilogue"
+  [(return)]
+  ""
+  "FAIL;"
+)
+
+(define_insn "rl78_return"
+  [(return)]
+  ""
+  "ret"
+)
+
+(define_insn "interrupt_return"
+  [(unspec_volatile [(return)] UNS_RETI) ]
+  ""
+  "reti"
+)
+
+(define_insn "brk_interrupt_return"
+  [(unspec_volatile [(return)] UNS_RETB) ]
+  ""
+  "retb"
+)
+
+(define_expand "eh_return"
+  [(match_operand:HI 0 "")]
+  ""
+  "rl78_expand_eh_epilogue (operands[0]);
+   emit_barrier ();
+   DONE;"
+)
+
+;; These are used only by prologue/epilogue so it's "safe" to pass
+;; virtual registers.
+(define_insn "push"
+  [(set (reg:HI SP_REG)
+	(plus:HI (reg:HI SP_REG)
+		  (const_int -2)))
+   (set (mem:HI (reg:HI SP_REG))
+	(match_operand:HI 0 "register_operand" "ABDT,vZint"))]
+  ""
+  "@
+   push\t%v0
+   push\t%v0 ; %0"
+)
+
+(define_insn "pop"
+  [(set (match_operand:HI 0 "register_operand" "=ABDT,vZint")
+	(mem:HI (reg:HI SP_REG)))
+   (set (reg:HI SP_REG)
+	(plus:HI (reg:HI SP_REG)
+		    (const_int 2)))]
+  ""
+  "@
+   pop\t%v0
+   pop\t%v0 ; %0"
+)
+
+(define_insn "sel_rb"
+  [(unspec_volatile [(match_operand 0 "immediate_operand" "")] UNS_SET_RB)]
+  "!TARGET_G10"
+  "sel\trb%u0"
+  )
+
+(define_insn "trampoline_init"
+  [(set (match_operand 0 "register_operand" "=Z08W")
+	(unspec_volatile [(match_operand 1 "register_operand" "Z08W")
+			  (match_operand 2 "register_operand" "Z10W")
+			  ] UNS_TRAMPOLINE_INIT))
+   ]
+  ""
+  "call !!___trampoline_init ; %0 <= %1 %2"
+  )
+
+(define_insn "trampoline_uninit"
+  [(unspec_volatile [(const_int 0)] UNS_TRAMPOLINE_UNINIT)
+   ]
+  ""
+  "call !!___trampoline_uninit"
+  )
+
+;; GCC restores $fp *before* using it to access values on the *old*
+;; frame.  So, we do it ourselves, to ensure this is not the case.
+;; Note that while %1 is usually a label_ref, we allow for a
+;; non-immediate as well.
+(define_expand "nonlocal_goto"
+  [(set (pc)
+	(unspec_volatile [(match_operand 0 "") ;; fp (ignore)
+			  (match_operand 1 "") ;; target
+			  (match_operand 2 "") ;; sp
+			  (match_operand 3 "") ;; ?
+			  ] UNS_NONLOCAL_GOTO))
+   ]
+  ""
+  "emit_jump_insn (gen_nonlocal_goto_insn (operands[0], operands[1], operands[2], operands[3]));
+   emit_barrier ();
+   DONE;"
+  )
+
+(define_insn "nonlocal_goto_insn"
+  [(set (pc)
+	(unspec_volatile [(match_operand 0 "" "") ;; fp (ignore)
+			  (match_operand 1 "" "vi") ;; target
+			  (match_operand 2 "" "vi") ;; sp
+			  (match_operand 3 "" "vi") ;; ?
+			  ] UNS_NONLOCAL_GOTO))
+   ]
+  ""
+  "; nonlocal goto
+	movw	ax, %3
+	movw	r22, ax
+	movw	ax, %2
+	movw	sp, ax
+	movw	ax, %1
+	br	ax
+"
+  )
+
+(define_expand "es_addr"
+  [(unspec:SI [(reg:QI ES_REG)
+	       (match_operand:HI 0 "")
+	       ] UNS_ES_ADDR)]
+  ""
+  ""
+)
+
+;;======================================================================
+;;
+;; "macro" insns - cases where inline chunks of code are more
+;; efficient than anything else.
+
+(define_expand "addsi3"
+  [(set (match_operand:SI          0 "nonimmediate_operand" "=&vm")
+	(plus:SI (match_operand:SI 1 "general_operand"      "vim")
+		 (match_operand    2 "general_operand"      "vim")))
+   ]
+  ""
+  "emit_insn (gen_addsi3_internal_virt (operands[0], operands[1], operands[2]));
+   DONE;"
+)
+
+(define_expand "adddi3"
+  [(set (match_operand:DI          0 "nonimmediate_operand" "")
+	(plus:DI (match_operand:DI 1 "general_operand"      "")
+		 (match_operand:DI 2 "general_operand"      "")))
+   ]
+  ""
+  "rl78_emit_libcall (\"__adddi3\", PLUS, DImode, DImode, 3, operands);
+   DONE;"
+)
+
+(define_insn "addsi3_internal_virt"
+  [(set (match_operand:SI          0 "nonimmediate_operand" "=v,&vm, vm")
+	(plus:SI (match_operand:SI 1 "general_operand"      "0, vim, vim")
+		 (match_operand    2 "general_operand"      "vim,vim,vim")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   ]
+  "rl78_virt_insns_ok ()"
+  ""
+  [(set_attr "valloc" "macax")]
+)
+
+(define_insn "addsi3_internal_real"
+  [(set (match_operand:SI          0 "nonimmediate_operand" "=v,&vU, vU")
+	(plus:SI (match_operand:SI 1 "general_operand"      "+0, viU, viU")
+		 (match_operand    2 "general_operand"      "viWabWhlWh1,viWabWhlWh1,viWabWhlWh1")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   ]
+  "rl78_real_insns_ok ()"
+  { return rl78_addsi3_internal (operands, which_alternative); }
+  [(set_attr "valloc" "macax")]
+)
+
+(define_expand "subsi3"
+  [(set (match_operand:SI           0 "nonimmediate_operand")
+	(minus:SI (match_operand:SI 1 "general_operand")
+		  (match_operand    2 "general_operand")))
+   ]
+  ""
+  "emit_insn (gen_subsi3_internal_virt (operands[0], operands[1], operands[2]));
+  DONE;"
+)
+
+(define_expand "subdi3"
+ [(set (match_operand:DI          0 "nonimmediate_operand" "")
+    (minus:DI (match_operand:DI 1 "general_operand"      "")
+         (match_operand:DI    2 "general_operand"      "")))
+   ]
+  ""
+  "rl78_emit_libcall (\"__subdi3\", MINUS, DImode, DImode, 3, operands);
+   DONE;"
+)
+
+(define_insn "subsi3_internal_virt"
+  [(set (match_operand:SI           0 "nonimmediate_operand" "=v,&vm, vm")
+	(minus:SI (match_operand:SI 1 "general_operand"      "0, vim, vim")
+		  (match_operand    2 "general_operand"      "vim,vim,vim")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   ]
+  "rl78_virt_insns_ok ()"
+  ""
+  [(set_attr "valloc" "macax")]
+)
+
+(define_insn "subsi3_internal_real"
+  [(set (match_operand:SI           0 "nonimmediate_operand" "=v,&vU, vU")
+	(minus:SI (match_operand:SI 1 "general_operand"      "+0, viU, viU")
+		  (match_operand    2 "general_operand"      "viWabWhlWh1,viWabWhlWh1,viWabWhlWh1")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   movw ax,%h1 \;subw ax,%h2 \;movw %h0, ax \;movw ax,%H1 \;sknc \;decw ax \;subw ax,%H2 \;movw %H0,ax
+   movw ax,%h1 \;subw ax,%h2 \;movw %h0, ax \;movw ax,%H1 \;sknc \;decw ax \;subw ax,%H2 \;movw %H0,ax
+   movw ax,%h1 \;subw ax,%h2 \;movw bc,  ax \;movw ax,%H1 \;sknc \;decw ax \;subw ax,%H2 \;movw %H0,ax \;movw ax,bc \;movw %h0, ax"
+  [(set_attr "valloc" "macax")]
+)
+
+(define_expand "mulqi3"
+  [(parallel
+    [(set (match_operand:QI            0 "register_operand")
+	   (mult:QI  (match_operand:QI 1 "general_operand")
+		     (match_operand:QI 2 "nonmemory_operand")))
+      (clobber (reg:HI AX_REG))
+    ])
+  ]
+  "" ; mulu supported by all targets
+  ""
+)
+
+(define_expand "mulhi3"
+  [(set (match_operand:HI          0 "register_operand")
+	(mult:HI (match_operand:HI 1 "general_operand")
+		 (match_operand:HI 2 "nonmemory_operand")))
+   ]
+  "! RL78_MUL_NONE"
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_mulhi3_g14 (operands[0], operands[1], operands[2]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_mulhi3_g13 (operands[0], operands[1], operands[2]));
+    DONE;
+  }
+)
+
+(define_expand "mulsi3"
+  [(set (match_operand:SI          0 "register_operand")
+	(mult:SI (match_operand:SI 1 "general_operand")
+		 (match_operand:SI 2 "nonmemory_operand")))
+   ]
+  "! RL78_MUL_NONE"
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_mulsi3_g14 (operands[0], operands[1], operands[2]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_mulsi3_g13 (operands[0], operands[1], operands[2]));
+    DONE;
+  }
+)
+
+(define_insn "*mulqi3_rl78"
+  [(set (match_operand:QI          0 "register_operand" "=&v")
+	(mult:QI (match_operand:QI 1 "general_operand" "viU")
+		 (match_operand:QI 2 "general_operand" "vi")))
+   (clobber (reg:HI AX_REG))
+  ]
+  "" ; mulu supported by all targets
+  "; mulqi macro %0 = %1 * %2
+	mov    a, %h1
+	mov    x, a
+	mov    a, %h2
+	mulu   x ; ax = a * x
+	mov    a, x
+	mov    %h0, a
+	; end of mulqi macro"
+  [(set_attr "valloc" "macax")]
+)
+
+(define_insn "mulhi3_g14"
+  [(set (match_operand:HI          0 "register_operand" "=&v")
+	(mult:HI (match_operand:HI 1 "general_operand" "viU")
+		 (match_operand:HI 2 "general_operand" "vi")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
+  "RL78_MUL_G14"
+  "; G14 mulhi macro %0 = %1 * %2
+	movw    ax, %h1
+	movw    bc, %h2
+	mulhu   ; bcax = bc * ax
+	movw    %h0, ax
+	; end of mulhi macro"
+  [(set_attr "valloc" "macax")]
+)
+
+(define_insn "mulhi3_g13"
+  [(set (match_operand:HI          0 "register_operand" "=&v")
+	(mult:HI (match_operand:HI 1 "general_operand" "viU")
+		 (match_operand:HI 2 "general_operand" "vi")))
+   (clobber (reg:HI AX_REG))
+  ]
+  "RL78_MUL_G13"
+  "; G13 mulhi macro %0 = %1 * %2
+	mov     a, #0x00
+	mov     !0xf00e8, a     ; MDUC
+	movw    ax, %h1
+	movw    0xffff0, ax     ; MDAL
+	movw    ax, %h2
+	movw    0xffff2, ax     ; MDAH
+	nop     ; mdb = mdal * mdah
+	movw    ax, 0xffff6     ; MDBL
+	movw    %h0, ax
+        ; end of mulhi macro"
+  [(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
+;; because we're only using the lower 16 bits (which is the upper 16
+;; bits of the result).
+(define_insn "mulsi3_g14"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+	(mult:SI (match_operand:SI 1 "general_operand" "viU")
+		 (match_operand:SI 2 "general_operand" "vi")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
+  "RL78_MUL_G14"
+  "; G14 mulsi macro %0 = %1 * %2
+	movw	ax, %h1
+	movw	bc, %h2
+	MULHU	; bcax = bc * ax
+	movw	%h0, ax
+	movw	ax, bc
+	movw	0xffff0, ax
+	movw	ax, %H1
+	movw	bc, %h2
+	MACHU	; MACR += bc * ax
+	movw	ax, %h1
+	movw	bc, %H2
+	MACHU	; MACR += bc * ax
+	movw	ax, 0xffff0
+	movw	%H0, ax
+	; end of mulsi macro"
+  [(set_attr "valloc" "macax")]
+  )
+
+;; 0xFFFF0 is MDAL.  0xFFFF2 is MDAH.
+;; 0xFFFF6 is MDBL.  0xFFFF4 is MDBH.
+;; 0xF00E0 is MDCL.  0xF00E2 is MDCH.
+;; 0xF00E8 is MDUC.
+;; Warning: this matches the silicon not the documentation.
+(define_insn "mulsi3_g13"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+	(mult:SI (match_operand:SI 1 "general_operand" "viU")
+		 (match_operand:SI 2 "general_operand" "viU")))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+  ]
+  "RL78_MUL_G13"
+  "; G13 mulsi macro %0 = %1 * %2
+	mov	a, #0x00
+	mov	!0xf00e8, a	; MDUC
+	movw	ax, %h1
+	movw	0xffff0, ax	; MDAL
+	movw	ax, %h2
+	movw	0xffff2, ax	; MDAH
+	nop	; mdb = mdal * mdah
+	movw	ax, 0xffff6	; MDBL
+	movw	%h0, ax
+
+	mov	a, #0x40
+	mov	!0xf00e8, a	; MDUC
+	movw	ax, 0xffff4	; MDBH
+	movw	!0xf00e0, ax	; MDCL
+	movw	ax, #0
+	movw	!0xf00e2, ax	; MDCL
+	movw	ax, %H1
+	movw	0xffff0, ax	; MDAL
+	movw	ax, %h2
+	movw	0xffff2, ax	; MDAH
+	nop	; mdc += mdal * mdah
+
+	mov	a, #0x40
+	mov	!0xf00e8, a	; MDUC
+	movw	ax, %h1
+	movw	0xffff0, ax	; MDAL
+	movw	ax, %H2
+	movw	0xffff2, ax	; MDAH
+	nop	; mdc += mdal * mdah
+	nop	; Additional nop for MAC
+	movw	ax, !0xf00e0	; MDCL
+	movw	%H0, ax
+	; end of mulsi macro"
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
+)
+
+(define_expand "udivmodhi4"
+  [(parallel
+    [(set (match_operand:HI          0 "register_operand")
+          (udiv:HI (match_operand:HI 1 "register_operand")
+                   (match_operand:HI 2 "register_operand")))
+     (set (match_operand:HI          3 "register_operand")
+          (umod:HI (match_dup 1) (match_dup 2)))
+     (clobber (reg:HI AX_REG))
+     (clobber (reg:HI DE_REG))
+    ])
+   ]
+  "RL78_MUL_G14"
+  ""
+)
+
+(define_insn "*udivmodhi4_g14"
+  [(set (match_operand:HI          0 "register_operand" "=v")
+	(udiv:HI (match_operand:HI 1 "register_operand" "v")
+		 (match_operand:HI 2 "register_operand" "v")))
+   (set (match_operand:HI          3 "register_operand" "=v")
+	(umod:HI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI DE_REG))
+  ]
+  "RL78_MUL_G14"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G14 udivhi macro %0 = %1 / %2 \n\
+	movw    ax, %h1 \n\
+	movw    de, %h2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divhu   	; ax = ax / de \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw    %h0, ax \n\
+	; end of udivhi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G14 umodhi macro %3 = %1 %% %2 \n\
+	movw    ax, %h1 \n\
+	movw    de, %h2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divhu   	; de = ax %% de \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw	ax, de \n\
+	movw    %h3, ax \n\
+	; end of umodhi macro";
+    else
+      return "; G14 udivmodhi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+	movw    ax, %h1 \n\
+	movw    de, %h2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divhu   	; ax = ax / de, de = ax %% de \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw    %h0, ax \n\
+	movw	ax, de \n\
+	movw    %h3, ax \n\
+	; end of udivmodhi macro";
+  }
+  [(set_attr "valloc" "divhi")]
+)
+
+(define_expand "udivmodsi4"
+  [(parallel
+    [(set (match_operand:SI          0 "register_operand")
+          (udiv:SI (match_operand:SI 1 "register_operand")
+                   (match_operand:SI 2 "register_operand")))
+     (set (match_operand:SI          3 "register_operand")
+          (umod:SI (match_dup 1) (match_dup 2)))
+    ])
+   ]
+  "! RL78_MUL_NONE && ! optimize_size"
+  {
+    if (RL78_MUL_G14)
+      emit_insn (gen_udivmodsi4_g14 (operands[0], operands[1], operands[2], operands[3]));
+    else /* RL78_MUL_G13 */
+      emit_insn (gen_udivmodsi4_g13 (operands[0], operands[1], operands[2], operands[3]));
+    DONE;
+  }
+)
+
+(define_insn "udivmodsi4_g14"
+  [(set (match_operand:SI          0 "register_operand" "=v")
+	(udiv:SI (match_operand:SI 1 "register_operand" "v")
+		 (match_operand:SI 2 "register_operand" "v")))
+   (set (match_operand:SI          3 "register_operand" "=v")
+	(umod:SI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+   (clobber (reg:HI BC_REG))
+   (clobber (reg:HI DE_REG))
+   (clobber (reg:HI HL_REG))
+  ]
+  "RL78_MUL_G14"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G14 udivsi macro %0 = %1 / %2 \n\
+	movw    ax, %h1 \n\
+	movw    bc, %H1 \n\
+	movw    de, %h2 \n\
+	movw    hl, %H2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divwu   	; bcax = bcax / hlde \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw    %h0, ax \n\
+	movw	ax, bc \n\
+	movw    %H0, ax \n\
+	; end of udivsi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G14 umodsi macro %3 = %1 %% %2 \n\
+	movw    ax, %h1 \n\
+	movw    bc, %H1 \n\
+	movw    de, %h2 \n\
+	movw    hl, %H2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divwu   	; hlde = bcax %% hlde \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw	ax, de \n\
+	movw    %h3, ax \n\
+	movw	ax, hl \n\
+	movw    %H3, ax \n\
+	; end of umodsi macro";
+    else
+      return "; G14 udivmodsi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+	movw    ax, %h1 \n\
+	movw    bc, %H1 \n\
+	movw    de, %h2 \n\
+	movw    hl, %H2 \n\
+	push	psw	; Save the current interrupt status \n\
+	di		; Disable interrupts. See Renesas Technical update TN-RL*-A025B/E \n\
+	divwu   	; bcax = bcax / hlde, hlde = bcax %% hlde \n\
+	pop	psw	; Restore saved interrupt status \n\
+	movw    %h0, ax \n\
+	movw	ax, bc \n\
+	movw    %H0, ax \n\
+	movw	ax, de \n\
+	movw    %h3, ax \n\
+	movw	ax, hl \n\
+	movw    %H3, ax \n\
+	; end of udivmodsi macro";
+  }
+  [(set_attr "valloc" "divsi")]
+)
+
+;; Warning: these values match the silicon not the documentation.
+;; 0xFFFF0 is MDAL.  0xFFFF2 is MDAH.
+;; 0xFFFF6 is MDBL.  0xFFFF4 is MDBH.
+;; 0xF00E0 is MDCL.  0xF00E2 is MDCH.
+;; 0xF00E8 is MDUC.
+
+(define_insn "udivmodsi4_g13"
+  [(set (match_operand:SI          0 "register_operand" "=v")
+	(udiv:SI (match_operand:SI 1 "register_operand" "v")
+		 (match_operand:SI 2 "register_operand" "v")))
+   (set (match_operand:SI          3 "register_operand" "=v")
+	(umod:SI (match_dup 1) (match_dup 2)))
+   (clobber (reg:HI AX_REG))
+  ]
+  "RL78_MUL_G13"
+  {
+    if (find_reg_note (insn, REG_UNUSED, operands[3]))
+      return "; G13 udivsi macro %0 = %1 / %2 \n\
+	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1 \n\
+	mov	!0xf00e8, a	; This preps the peripheral for division without interrupt generation \n\
+	movw	ax, %H1		\n\
+	movw	0xffff2, ax	; MDAH \n\
+	movw	ax, %h1		\n\
+	movw	0xffff0, ax	; MDAL \n\
+	movw	ax, %H2		\n\
+	movw	0xffff4, ax	; MDBH \n\
+	movw	ax, %h2		\n\
+	movw	0xffff6, ax	; MDBL \n\
+	mov	a, #0xC1	; Set the DIVST bit in MDUC \n\
+	mov	!0xf00e8, a	; This starts the division op \n\
+1:	mov	a, !0xf00e8	; Wait 16 clocks or until DIVST is clear \n\
+	bt	a.0, $1b	\n\
+	movw    ax, 0xffff0	; Read the quotient \n\
+	movw	%h0, ax		\n\
+	movw    ax, 0xffff2	\n\
+	movw	%H0, ax		\n\
+	; end of udivsi macro";
+    else if (find_reg_note (insn, REG_UNUSED, operands[0]))
+      return "; G13 umodsi macro %3 = %1 %% %2 \n\
+	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1 \n\
+	mov	!0xf00e8, a	; This preps the peripheral for division without interrupt generation \n\
+	movw	ax, %H1		\n\
+	movw	0xffff2, ax	; MDAH \n\
+	movw	ax, %h1		\n\
+	movw	0xffff0, ax	; MDAL \n\
+	movw	ax, %H2		\n\
+	movw	0xffff4, ax	; MDBH \n\
+	movw	ax, %h2		\n\
+	movw	0xffff6, ax	; MDBL \n\
+	mov	a, #0xC1	; Set the DIVST bit in MDUC \n\
+	mov	!0xf00e8, a	; This starts the division op \n\
+1:	mov	a, !0xf00e8	; Wait 16 clocks or until DIVST is clear \n\
+	bt	a.0, $1b	\n\
+  	movw	ax, !0xf00e0	; Read the remainder \n\
+	movw	%h3, ax		\n\
+	movw	ax, !0xf00e2	\n\
+	movw	%H3, ax		\n\
+	; end of umodsi macro";
+    else
+      return "; G13 udivmodsi macro %0 = %1 / %2 and %3 = %1 %% %2 \n\
+	mov	a, #0xC0	; Set DIVMODE=1 and MACMODE=1 \n\
+	mov	!0xf00e8, a	; This preps the peripheral for division without interrupt generation \n\
+	movw	ax, %H1		\n\
+	movw	0xffff2, ax	; MDAH \n\
+	movw	ax, %h1		\n\
+	movw	0xffff0, ax	; MDAL \n\
+	movw	ax, %H2		\n\
+	movw	0xffff4, ax	; MDBH \n\
+	movw	ax, %h2		\n\
+	movw	0xffff6, ax	; MDBL \n\
+	mov	a, #0xC1	; Set the DIVST bit in MDUC \n\
+	mov	!0xf00e8, a	; This starts the division op \n\
+1:	mov	a, !0xf00e8	; Wait 16 clocks or until DIVST is clear \n\
+	bt	a.0, $1b	\n\
+	movw    ax, 0xffff0	; Read the quotient \n\
+	movw	%h0, ax		\n\
+	movw    ax, 0xffff2	\n\
+	movw	%H0, ax		\n\
+  	movw	ax, !0xf00e0	; Read the remainder \n\
+	movw	%h3, ax		\n\
+	movw	ax, !0xf00e2	\n\
+	movw	%H3, ax		\n\
+	; end of udivmodsi macro";
+      }
+  [(set_attr "valloc" "macax")
+   (set_attr "is_g13_muldiv_insn" "yes")]
+)