OSDN Git Service

.
authordj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 29 Nov 2011 21:36:43 +0000 (21:36 +0000)
committerdj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 29 Nov 2011 21:36:43 +0000 (21:36 +0000)
* configure.ac (rl78-*-*) New case.
* configure: Regenerate.
* MAINTAINERS: Add myself as RL78 maintainer.

libgcc
* config.host (rl78-*-elf): New case.
* config/rl78: New directory for the Renesas RL78.

gcc
* config.gcc (rl78-*-elf): New case.
* doc/extend.texi: Add RL78 documentation.
* doc/invoke.texi: Likewise.
* doc/md.texi: Likewise.
* doc/contrib.texi: Add RL78.
* doc/install.texi: Add rl78-*-elf.
* config/rl78: New directory for the Renesas RL78.

contrib
* config-list.mk (LIST): Add rl78-elf.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@181819 138bc75d-0d04-0410-961f-82ee72b054a4

38 files changed:
ChangeLog
MAINTAINERS
configure
configure.ac
contrib/ChangeLog
contrib/config-list.mk
gcc/ChangeLog
gcc/config.gcc
gcc/config/rl78/constraints.md [new file with mode: 0644]
gcc/config/rl78/predicates.md [new file with mode: 0644]
gcc/config/rl78/rl78-c.c [new file with mode: 0644]
gcc/config/rl78/rl78-expand.md [new file with mode: 0644]
gcc/config/rl78/rl78-opts.h [new file with mode: 0644]
gcc/config/rl78/rl78-protos.h [new file with mode: 0644]
gcc/config/rl78/rl78-real.md [new file with mode: 0644]
gcc/config/rl78/rl78-virt.md [new file with mode: 0644]
gcc/config/rl78/rl78.c [new file with mode: 0644]
gcc/config/rl78/rl78.h [new file with mode: 0644]
gcc/config/rl78/rl78.md [new file with mode: 0644]
gcc/config/rl78/rl78.opt [new file with mode: 0644]
gcc/config/rl78/t-rl78 [new file with mode: 0644]
gcc/doc/contrib.texi
gcc/doc/extend.texi
gcc/doc/install.texi
gcc/doc/invoke.texi
gcc/doc/md.texi
libgcc/ChangeLog
libgcc/config.host
libgcc/config/rl78/cmpsi2.S [new file with mode: 0644]
libgcc/config/rl78/lib2div.c [new file with mode: 0644]
libgcc/config/rl78/lib2mul.c [new file with mode: 0644]
libgcc/config/rl78/lib2shift.c [new file with mode: 0644]
libgcc/config/rl78/lshrsi3.S [new file with mode: 0644]
libgcc/config/rl78/mulsi3.S [new file with mode: 0644]
libgcc/config/rl78/rl78-divmod.h [new file with mode: 0644]
libgcc/config/rl78/rl78-mul.h [new file with mode: 0644]
libgcc/config/rl78/t-rl78 [new file with mode: 0644]
libgcc/config/rl78/trampoline.S [new file with mode: 0644]

index e70df0c..61bb4d5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2011-11-29  DJ Delorie  <dj@redhat.com>
+
+       * configure.ac (rl78-*-*) New case.
+       * configure: Regenerate.
+       * MAINTAINERS: Add myself as RL78 maintainer.
+
 2011-11-21  Eric Botcazou  <ebotcazou@libertysurf.fr>
 
        * MAINTAINERS: Add self as co-maintainer of the Ada front end.
index d6af32e..cad649c 100644 (file)
@@ -87,6 +87,7 @@ moxie port            Anthony Green           green@moxielogic.com
 pdp11 port             Paul Koning             ni1d@arrl.net
 picochip port          Hariharan Sandanagobalane       hariharan@picochip.com
 picochip port          Daniel Towner           dant@picochip.com
+rl78 port              DJ Delorie              dj@redhat.com
 rs6000 port            Geoff Keating           geoffk@geoffk.org
 rs6000 port            David Edelsohn          dje.gcc@gmail.com
 rs6000 vector extns    Aldy Hernandez          aldyh@redhat.com
index e68c402..c4a79dc 100755 (executable)
--- a/configure
+++ b/configure
@@ -3092,6 +3092,10 @@ case "${target}" in
   powerpc-*-aix* | rs6000-*-aix*)
     noconfigdirs="$noconfigdirs target-libssp"
     ;;
+  rl78-*-*)
+    # Dereferencing -1 is a compile-time error
+    noconfigdirs="$noconfigdirs target-libssp"
+    ;;
 esac
 
 # Disable libstdc++-v3 for some systems.
index 7e27de4..9fee563 100644 (file)
@@ -518,6 +518,12 @@ case "${target}" in
   powerpc-*-aix* | rs6000-*-aix*)
     noconfigdirs="$noconfigdirs target-libssp"
     ;;
+  rl78-*-*)
+    # libssp uses a misaligned load to trigger a fault, but the RL78
+    # doesn't fault for those - instead, it gives a build-time error
+    # for explicit misaligned loads.
+    noconfigdirs="$noconfigdirs target-libssp"
+    ;;
 esac
 
 # Disable libstdc++-v3 for some systems.
index abe317a..26d5959 100644 (file)
@@ -1,3 +1,7 @@
+2011-11-29  DJ Delorie  <dj@redhat.com>
+
+       * config-list.mk (LIST): Add rl78-elf.
+
 2011-11-21  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 
        * make_sunver.pl: Convert '?' in glob patterns to '.'.
index 31e0237..7d03cd3 100644 (file)
@@ -53,7 +53,7 @@ LIST = alpha-linux-gnu alpha-freebsd6 alpha-netbsd alpha-openbsd \
   powerpc-wrs-vxworks powerpc-wrs-vxworksae powerpc-lynxos powerpcle-elf \
   powerpcle-eabisim powerpcle-eabi rs6000-ibm-aix4.3 rs6000-ibm-aix5.1.0 \
   rs6000-ibm-aix5.2.0 rs6000-ibm-aix5.3.0 rs6000-ibm-aix6.0 \
-  rx-elf s390-linux-gnu s390x-linux-gnu s390x-ibm-tpf sh-elf \
+  rl78-elf rx-elf s390-linux-gnu s390x-linux-gnu s390x-ibm-tpf sh-elf \
   shle-linux sh-netbsdelf sh-superh-elf sh5el-netbsd sh64-netbsd sh64-linux \
   sh64-elfOPT-with-newlib sh-rtems sh-wrs-vxworks sparc-elf \
   sparc-leon-elf sparc-rtems sparc-linux-gnu \
index 3a80278..6ac5db9 100644 (file)
@@ -1,3 +1,13 @@
+2011-11-29  DJ Delorie  <dj@redhat.com>
+
+       * config.gcc (rl78-*-elf): New case.
+       * doc/extend.texi: Add RL78 documentation.
+       * doc/invoke.texi: Likewise.
+       * doc/md.texi: Likewise.
+       * doc/contrib.texi: Add RL78.
+       * doc/install.texi: Add rl78-*-elf.
+       * config/rl78: New directory for the Renesas RL78.
+
 2011-11-29  Jakub Jelinek  <jakub@redhat.com>
 
        PR tree-optimization/51247
index 45ba919..a477289 100644 (file)
@@ -2115,6 +2115,13 @@ rs6000-ibm-aix[6789].* | powerpc-ibm-aix[6789].*)
        use_gcc_stdint=wrap
        extra_headers=altivec.h
        ;;
+rl78-*-elf*)
+       tm_file="dbxelf.h elfos.h newlib-stdint.h ${tm_file}"
+       target_has_targetm_common=no
+       c_target_objs="rl78-c.o"
+       cxx_target_objs="rl78-c.o"
+       tmake_file="${tmake_file} rl78/t-rl78"
+       ;;
 rx-*-elf*)
        tm_file="dbxelf.h elfos.h newlib-stdint.h ${tm_file}"
        tmake_file="${tmake_file} rx/t-rx"
diff --git a/gcc/config/rl78/constraints.md b/gcc/config/rl78/constraints.md
new file mode 100644 (file)
index 0000000..a89e1a2
--- /dev/null
@@ -0,0 +1,266 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+; Constraints in use:
+
+; core:
+; V X g i m n o p r s < >
+; 0..9
+; I..Q - integers
+;   Int8 = 0..255
+;   Int3 = 1..7
+;   J = -255..0
+;   K = 1
+;   L = -1
+;   M = 0
+;   N = 2
+;   O = -2
+;   P = 1..15
+
+; E..H - float constants
+
+; RL78-specific
+; a x b c d e h l w - 8-bit regs
+; A B D T S - 16-bit regs
+; R = all regular registers (A-L)
+; Y - any valid memory
+; Wxx - various memory addressing modes
+; Qxx - conditionals
+; v = virtual registers
+; Zxx = specific virtual registers
+
+(define_constraint "Int8"
+  "Integer constant in the range 0 @dots{} 255."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 0, 255)")))
+
+(define_constraint "Int3"
+  "Integer constant in the range 1 @dots{} 7."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 1, 7)")))
+
+(define_constraint "J"
+  "Integer constant in the range -255 @dots{} 0"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, -255, 0)")))
+
+(define_constraint "K"
+  "Integer constant 1."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 1, 1)")))
+
+(define_constraint "L"
+  "Integer constant -1."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, -1, -1)")))
+
+(define_constraint "M"
+  "Integer constant 0."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 0, 0)")))
+
+(define_constraint "N"
+  "Integer constant 2."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 2, 2)")))
+
+(define_constraint "O"
+  "Integer constant -2."
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, -2, -2)")))
+
+(define_constraint "P"
+  "Integer constant 1..15"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (ival, 1, 15)")))
+
+(define_register_constraint "R" "QI_REGS"
+ "@code{A} through @code{L} registers.")
+
+(define_register_constraint "a" "AREG"
+ "The @code{A} register.")
+
+(define_register_constraint "x" "XREG"
+ "The @code{X} register.")
+
+(define_register_constraint "b" "BREG"
+ "The @code{B} register.")
+
+(define_register_constraint "c" "CREG"
+ "The @code{C} register.")
+
+(define_register_constraint "d" "DREG"
+ "The @code{D} register.")
+
+(define_register_constraint "e" "EREG"
+ "The @code{E} register.")
+
+(define_register_constraint "h" "HREG"
+ "The @code{H} register.")
+
+(define_register_constraint "l" "LREG"
+ "The @code{L} register.")
+
+(define_register_constraint "w" "PSWREG"
+ "The @code{PSW} register.")
+
+(define_register_constraint "A" "AXREG"
+ "The @code{AX} register.")
+
+(define_register_constraint "B" "BCREG"
+ "The @code{BC} register.")
+
+(define_register_constraint "D" "DEREG"
+ "The @code{DE} register.")
+
+; because H + L = T, assuming A=1.
+(define_register_constraint "T" "HLREG"
+ "The @code{HL} register.")
+
+(define_register_constraint "S" "SPREG"
+ "The @code{SP} register.")
+
+(define_register_constraint "v" "V_REGS"
+ "The virtual registers.")
+
+(define_register_constraint "Z08W" "R8W_REGS"
+ "The R8 register, HImode.")
+
+(define_register_constraint "Z10W" "R10W_REGS"
+ "The R10 register, HImode.")
+
+(define_register_constraint "Zint" "INT_REGS"
+ "The interrupt registers.")
+
+; All the memory addressing schemes the RL78 supports
+; of the form W {register} {bytes of offset}
+;          or W {register} {register}
+
+; absolute address
+(define_memory_constraint "Wab"
+  "[addr]"
+  (and (match_code "mem")
+       (ior (match_test "CONSTANT_P (XEXP (op, 0))")
+           (match_test "GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF"))
+           )
+  )
+
+(define_memory_constraint "Wbc"
+  "word16[BC]"
+  (and (match_code "mem")
+       (ior
+       (and (match_code "reg" "0")
+            (match_test "REGNO (XEXP (op, 0)) == BC_REG"))
+       (and (match_code "plus" "0")
+            (and (and (match_code "reg" "00")
+                      (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == BC_REG"))
+                      (match_test "uword_operand (XEXP (XEXP (op, 0), 1), VOIDmode)"))))
+       )
+  )
+
+(define_memory_constraint "Wde"
+  "[DE]"
+  (and (match_code "mem")
+       (and (match_code "reg" "0")
+           (match_test "REGNO (XEXP (op, 0)) == DE_REG")))
+  )
+
+(define_memory_constraint "Wca"
+  "[AX..HL] for calls"
+  (and (match_code "mem")
+       (and (match_code "reg" "0")
+           (match_test "REGNO (XEXP (op, 0)) <= HL_REG")))
+  )
+
+(define_memory_constraint "Wcv"
+  "[AX..HL,r8-r23] for calls"
+  (and (match_code "mem")
+       (and (match_code "reg" "0")
+           (match_test "REGNO (XEXP (op, 0)) < 24")))
+  )
+
+(define_memory_constraint "Wd2"
+  "word16[DE]"
+  (and (match_code "mem")
+       (ior
+       (and (match_code "reg" "0")
+            (match_test "REGNO (XEXP (op, 0)) == DE_REG"))
+       (and (match_code "plus" "0")
+            (and (and (match_code "reg" "00")
+                      (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == DE_REG"))
+                      (match_test "uword_operand (XEXP (XEXP (op, 0), 1), VOIDmode)"))))
+       )
+  )
+
+(define_memory_constraint "Whl"
+  "[HL]"
+  (and (match_code "mem")
+       (and (match_code "reg" "0")
+           (match_test "REGNO (XEXP (op, 0)) == HL_REG")))
+  )
+
+(define_memory_constraint "Wh1"
+  "byte8[HL]"
+  (and (match_code "mem")
+       (and (match_code "plus" "0")
+           (and (and (match_code "reg" "00")
+                     (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == HL_REG"))
+                     (match_test "ubyte_operand (XEXP (XEXP (op, 0), 1), VOIDmode)"))))
+  )
+
+(define_memory_constraint "Whb"
+  "[HL+B]"
+  (and (match_code "mem")
+       (match_test "rl78_hl_b_c_addr_p (XEXP (op, 0))"))
+  )
+
+(define_memory_constraint "Ws1"
+  "word8[SP]"
+  (and (match_code "mem")
+       (ior
+       (and (match_code "reg" "0")
+            (match_test "REGNO (XEXP (op, 0)) == SP_REG"))
+       (and (match_code "plus" "0")
+            (and (and (match_code "reg" "00")
+                      (match_test "REGNO (XEXP (XEXP (op, 0), 0)) == SP_REG"))
+                      (match_test "ubyte_operand (XEXP (XEXP (op, 0), 1), VOIDmode)"))))
+       )
+  )
+
+(define_memory_constraint "Wfr"
+  "ES/CS far pointer"
+  (and (match_code "mem")
+       (match_test "rl78_far_p (op)"))
+  )
+
+(define_memory_constraint "Y"
+  "any near legitimate memory access"
+  (and (match_code "mem")
+       (match_test "!rl78_far_p (op) && rl78_as_legitimate_address (VOIDmode, XEXP (op, 0), true, ADDR_SPACE_GENERIC)"))
+)
+
+
+(define_memory_constraint "Qbi"
+  "built-in compare types"
+  (match_code "eq,ne,gtu,ltu,geu,leu"))
+
+(define_memory_constraint "Qsc"
+  "synthetic compares"
+  (match_code "gt,lt,ge,le"))
diff --git a/gcc/config/rl78/predicates.md b/gcc/config/rl78/predicates.md
new file mode 100644 (file)
index 0000000..343cd8a
--- /dev/null
@@ -0,0 +1,60 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+(define_predicate "rl78_any_operand"
+  (ior (match_operand 0 "general_operand")
+       (match_code "mem,const_int,const_double,reg"))
+)
+
+(define_predicate "rl78_nonfar_operand"
+  (and (match_operand 0 "general_operand")
+       (not (match_test "rl78_far_p (op)")))
+)
+
+(define_predicate "rl78_nonfar_nonimm_operand"
+  (and (match_operand 0 "nonimmediate_operand")
+       (not (match_test "rl78_far_p (op)")))
+)
+
+(define_predicate "ubyte_operand"
+  (and (match_code "const_int")
+       (match_test "IN_RANGE (INTVAL (op), 0, 255)")))
+
+(define_predicate "rl78_24_operand"
+  (and (match_code "const_int")
+       (match_test "INTVAL (op) == 2 || INTVAL (op) == 4")))
+
+(define_predicate "uword_operand"
+  (ior (match_code "const")
+       (and (match_code "const_int")
+           (match_test "IN_RANGE (INTVAL (op), 0, 65536)"))))
+
+(define_predicate "rl78_cmp_operator_real"
+  (match_code "eq,ne,gtu,ltu,geu,leu"))
+(define_predicate "rl78_cmp_operator"
+  (match_code "eq,ne,gtu,ltu,geu,leu,gt,lt,ge,le"))
+
+(define_predicate "rl78_ax_operand"
+  (and (match_code "reg")
+       (match_test "REGNO (op) == AX_REG || REGNO (op) >= FIRST_PSEUDO_REGISTER")))
+
+(define_predicate "rl78_addw_operand"
+  (and (match_code "reg")
+       (match_test "REGNO (op) == AX_REG || REGNO (op) == SP_REG || REGNO (op) >= FIRST_PSEUDO_REGISTER")))
diff --git a/gcc/config/rl78/rl78-c.c b/gcc/config/rl78/rl78-c.c
new file mode 100644 (file)
index 0000000..98f704c
--- /dev/null
@@ -0,0 +1,43 @@
+/* RL78 C-specific support
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Red Hat, Inc.
+
+   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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "c-family/c-pragma.h"
+#include "c-family/c-common.h"
+#include "diagnostic-core.h"
+#include "cpplib.h"
+#include "hard-reg-set.h"
+#include "output.h"
+#include "rl78-protos.h"
+#include "function.h"
+#define MAX_RECOG_OPERANDS 10
+#include "reload.h"
+#include "target.h"
+
+/* Implements REGISTER_TARGET_PRAGMAS.  */
+void
+rl78_register_pragmas (void)
+{
+  c_register_addr_space ("__far", ADDR_SPACE_FAR);
+}
diff --git a/gcc/config/rl78/rl78-expand.md b/gcc/config/rl78/rl78-expand.md
new file mode 100644 (file)
index 0000000..9707e5b
--- /dev/null
@@ -0,0 +1,256 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+;;---------- Moving ------------------------
+
+(define_expand "movqi"
+  [(set (match_operand:QI 0 "nonimmediate_operand")
+       (match_operand:QI 1 "general_operand"))]
+  ""
+  {
+    if (MEM_P (operand0) && MEM_P (operand1))
+      operands[1] = copy_to_mode_reg (QImode, operand1);
+    if (rl78_far_p (operand0) && rl78_far_p (operand1))
+      operands[1] = copy_to_mode_reg (QImode, operand1);
+
+    /* FIXME: Not sure how GCC can generate (SUBREG (SYMBOL_REF)),
+       but it does.  Since this makes no sense, reject it here.  */
+    if (GET_CODE (operand1) == SUBREG
+        && GET_CODE (XEXP (operand1, 0)) == SYMBOL_REF)
+      FAIL;
+
+    if (CONST_INT_P (operand1) && ! IN_RANGE (INTVAL (operand1), (-1 << 8) + 1, (1 << 8) - 1))
+      gcc_unreachable();
+  }
+)
+
+(define_expand "movhi"
+  [(set (match_operand:HI 0 "nonimmediate_operand")
+       (match_operand:HI 1 "general_operand"))]
+  ""
+  {
+    if (MEM_P (operand0) && MEM_P (operand1))
+      operands[1] = copy_to_mode_reg (HImode, operand1);
+    if (rl78_far_p (operand0) && rl78_far_p (operand1))
+      operands[1] = copy_to_mode_reg (HImode, operand1);
+
+    /* FIXME: Not sure how GCC can generate (SUBREG (SYMBOL_REF)),
+       but it does.  Since this makes no sense, reject it here.  */
+    if (GET_CODE (operand1) == SUBREG
+        && GET_CODE (XEXP (operand1, 0)) == SYMBOL_REF)
+      FAIL;
+  }
+)
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "nonimmediate_operand")
+       (match_operand:SI 1 "general_operand"))]
+  ""
+  {
+    rl78_expand_movsi (operands);
+    DONE;
+  }
+)
+
+;;---------- Conversions ------------------------
+
+(define_expand "zero_extendqihi2"
+  [(set (match_operand:HI                 0 "nonimmediate_operand")
+       (zero_extend:HI (match_operand:QI 1 "general_operand")))]
+  ""
+  "if (rl78_force_nonfar_2 (operands, gen_zero_extendqihi2))
+     DONE;"
+  )
+
+(define_expand "extendqihi2"
+  [(set (match_operand:HI                 0 "nonimmediate_operand")
+       (sign_extend:HI (match_operand:QI 1 "general_operand")))]
+  ""
+  "if (rl78_force_nonfar_2 (operands, gen_extendqihi2))
+     DONE;"
+  )
+
+;;---------- Arithmetic ------------------------
+
+(define_expand "add<mode>3"
+  [(set (match_operand:QHI           0 "nonimmediate_operand")
+       (plus:QHI (match_operand:QHI 1 "general_operand")
+                 (match_operand:QHI 2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_add<mode>3))
+     DONE;"
+)
+
+(define_expand "sub<mode>3"
+  [(set (match_operand:QHI            0 "nonimmediate_operand")
+       (minus:QHI (match_operand:QHI 1 "general_operand")
+                  (match_operand:QHI 2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_sub<mode>3))
+     DONE;"
+)
+
+(define_expand "neg<mode>2"
+  [(set (match_operand:QHI            0 "nonimmediate_operand")
+       (minus:QHI (const_int 0)
+                  (match_operand:QHI 1 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_2 (operands, gen_neg<mode>2))
+     DONE;"
+)
+
+(define_expand "umulqihi3"
+  [(set (match_operand:HI 0 "register_operand")
+        (mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand"))
+                 (zero_extend:HI (match_operand:QI 2 "register_operand"))))]
+  ""
+  ""
+)
+
+(define_expand "andqi3"
+  [(set (match_operand:QI         0 "nonimmediate_operand")
+       (and:QI (match_operand:QI 1 "general_operand")
+               (match_operand:QI 2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_andqi3))
+     DONE;"
+)
+
+(define_expand "iorqi3"
+  [(set (match_operand:QI         0 "nonimmediate_operand")
+       (ior:QI (match_operand:QI 1 "general_operand")
+               (match_operand:QI 2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_iorqi3))
+     DONE;"
+)
+
+(define_expand "xorqi3"
+  [(set (match_operand:QI         0 "nonimmediate_operand")
+       (xor:QI (match_operand:QI 1 "general_operand")
+               (match_operand:QI 2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_xorqi3))
+     DONE;"
+)
+
+(define_expand "one_cmplqi2"
+  [(set (match_operand:QI         0 "nonimmediate_operand")
+       (xor:QI (match_operand:QI 1 "general_operand")
+               (const_int 255)))
+   ]
+  ""
+  "if (rl78_force_nonfar_2 (operands, gen_one_cmplqi2))
+     DONE;"
+)
+
+;;---------- Shifts ------------------------
+
+(define_expand "ashl<mode>3"
+  [(set (match_operand:QHI             0 "nonimmediate_operand")
+       (ashift:QHI (match_operand:QHI 1 "general_operand")
+                   (match_operand:QI  2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_ashl<mode>3))
+     DONE;"
+)
+
+(define_expand "ashr<mode>3"
+  [(set (match_operand:QHI               0 "nonimmediate_operand")
+       (ashiftrt:QHI (match_operand:QHI 1 "general_operand")
+                     (match_operand:QI  2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_ashr<mode>3))
+     DONE;"
+)
+
+(define_expand "lshr<mode>3"
+  [(set (match_operand:QHI               0 "nonimmediate_operand")
+       (lshiftrt:QHI (match_operand:QHI 1 "general_operand")
+                     (match_operand:QI  2 "general_operand")))
+   ]
+  ""
+  "if (rl78_force_nonfar_3 (operands, gen_lshr<mode>3))
+     DONE;"
+)
+
+(define_expand "ashrsi3"
+  [(set (match_operand:SI               0 "register_operand")
+       (ashiftrt:SI (match_operand:SI  1 "register_operand")
+                     (match_operand:SI 2 "immediate_operand")))
+   ]
+  ""
+  "if (GET_CODE (operands[2]) != CONST_INT)
+     FAIL;"
+)
+
+;;---------- Branching ------------------------
+
+(define_expand "indirect_jump"
+  [(set (pc)
+       (match_operand:HI 0 "nonimmediate_operand"))]
+  ""
+  ""
+)
+
+(define_expand "call"
+  [(call (match_operand:HI 0 "memory_operand")
+        (match_operand 1 ""))]
+  ""
+  ""
+)
+
+(define_expand "call_value"
+  [(set (match_operand          0 "register_operand")
+       (call (match_operand:HI 1 "memory_operand")
+             (match_operand    2 "")))]
+  ""
+  ""
+)
+
+(define_expand "cbranchqi4"
+  [(set (pc) (if_then_else
+             (match_operator                    0 "rl78_cmp_operator"
+                             [(match_operand:QI 1 "general_operand")
+                              (match_operand:QI 2 "general_operand")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  ""
+  "rl78_expand_compare (operands);"
+)
+
+(define_expand "cbranchhi4"
+  [(set (pc) (if_then_else
+             (match_operator                    0 "rl78_cmp_operator"
+                             [(match_operand:HI 1 "general_operand")
+                              (match_operand:HI 2 "general_operand")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  ""
+  "rl78_expand_compare (operands);"
+)
diff --git a/gcc/config/rl78/rl78-opts.h b/gcc/config/rl78/rl78-opts.h
new file mode 100644 (file)
index 0000000..c0caa50
--- /dev/null
@@ -0,0 +1,30 @@
+/* GCC option-handling definitions for the Renesas RL78 processor.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#ifndef RL78_OPTS_H
+#define RL78_OPTS_H
+
+enum rl78_mul_types
+{
+  MUL_NONE,
+  MUL_RL78,
+  MUL_G13
+};
+
+#endif
diff --git a/gcc/config/rl78/rl78-protos.h b/gcc/config/rl78/rl78-protos.h
new file mode 100644 (file)
index 0000000..f4ce186
--- /dev/null
@@ -0,0 +1,43 @@
+/* Prototypes for Renesas RL78 processors
+   Copyright (C) 2011 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/>.  */
+\f
+void           rl78_emit_eh_epilogue (rtx);
+void           rl78_expand_compare (rtx *);
+void           rl78_expand_movsi (rtx *);
+int            rl78_force_nonfar_2 (rtx *, rtx (*gen)(rtx,rtx));
+int            rl78_force_nonfar_3 (rtx *, rtx (*gen)(rtx,rtx,rtx));
+void           rl78_expand_eh_epilogue (rtx);
+void           rl78_expand_epilogue (void);
+void           rl78_expand_prologue (void);
+int            rl78_far_p (rtx x);
+int            rl78_hard_regno_mode_ok (int, enum machine_mode);
+int            rl78_hard_regno_nregs (int, enum machine_mode);
+bool           rl78_hl_b_c_addr_p (rtx);
+int            rl78_initial_elimination_offset (int, int);
+bool           rl78_as_legitimate_address (enum machine_mode, rtx,
+                                           bool, addr_space_t);
+int            rl78_legitimize_reload_address (rtx *, enum machine_mode, int,int, int);
+enum reg_class rl78_mode_code_base_reg_class (enum machine_mode, addr_space_t, int, int);
+bool           rl78_peep_movhi_p (rtx *);
+bool           rl78_real_insns_ok (void);
+void           rl78_register_pragmas (void);
+bool           rl78_regno_mode_code_ok_for_base_p (int, enum machine_mode, addr_space_t, int, int);
+void           rl78_setup_peep_movhi (rtx *);
+bool           rl78_virt_insns_ok (void);
diff --git a/gcc/config/rl78/rl78-real.md b/gcc/config/rl78/rl78-real.md
new file mode 100644 (file)
index 0000000..e13dc4e
--- /dev/null
@@ -0,0 +1,339 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+;; The insns in this file correspond to the actual opcodes the RL78
+;; can issue with real registers.  All insns in here should be
+;; conditional on rl78_real_insns_ok() returning true, and should
+;; allow virtual registers in their predicates - the reorg pass that
+;; allocates physical registers uses the constraints to select
+;; registers, but insns with virtual registers MUST match one of these
+;; patterns - other than the constraints - so that the operand info is
+;; properly set up for the alloc pass.
+
+;;---------- Moving ------------------------
+
+(define_insn "movqi_es"
+  [(set (reg:QI ES_REG)
+       (match_operand:QI 0 "register_operand" "a"))]
+  ""
+  "mov\tes, %0"
+)
+
+(define_insn "movqi_cs"
+  [(set (reg:QI CS_REG)
+       (match_operand:QI 0 "register_operand" "a"))]
+  ""
+  "mov\tcs, %0"
+)
+
+(define_insn "*movqi_real"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=g,RaxbcWab,RaxbcWab,a,                          bcx,R, WabWd2WhlWh1WhbWbcWs1v, bcx")
+       (match_operand    1 "general_operand"      "0,K,        M,       RInt8sJvWabWdeWd2WhlWh1WhbWbcWs1,Wab,aInt8J,a,                      R"))]
+  "rl78_real_insns_ok ()"
+  "@
+   ; mov\t%0, %1
+   oneb\t%0
+   clrb\t%0
+   mov\t%0, %1
+   mov\t%0, %1
+   mov\t%0, %1
+   mov\t%0, %1
+   mov\t%0, %S1"
+)
+
+(define_insn "*movhi_real"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=g,AB,AB,RSv,A,BDTvSWabWd2WdeWhlWh1WbcWs1, BDT,ABDT,v")
+       (match_operand:HI 1 "general_operand"      " 0,K, M, i,  BDTvSWabWd2WdeWh1WhlWbcWs1,A, BDT,vS,  ABDT"))]
+  "rl78_real_insns_ok ()"
+  "@
+   ; movw\t%0, %1
+   onew\t%0
+   clrw\t%0
+   movw\t%0, %1
+   movw\t%0, %1
+   movw\t%0, %1
+   movw\t%0, %S1
+   movw\t%0, %1
+   movw\t%0, %1"
+)
+
+;;---------- Conversions ------------------------
+
+(define_insn "*zero_extendqihi2_real"
+  [(set (match_operand:HI                 0 "nonimmediate_operand" "=rv,A")
+       (zero_extend:HI (match_operand:QI 1 "general_operand" "0,a")))]
+  "rl78_real_insns_ok ()"
+  "@
+   mov\t%Q0, #0
+   mov\tx, a \;mov\ta, #0"
+  )
+
+(define_insn "*extendqihi2_real"
+  [(set (match_operand:HI                 0 "nonimmediate_operand" "=A,A")
+       (sign_extend:HI (match_operand:QI 1 "general_operand" "x,a")))]
+  "rl78_real_insns_ok ()"
+  "@
+   shlw\t%0, 8 \;sarw\t%0, 8
+   sarw\t%0, 8"
+  )
+
+;;---------- Arithmetic ------------------------
+
+(define_insn "*addqi3_real"
+  [(set (match_operand:QI          0 "nonimmediate_operand"  "=rvWabWhlWh1,rvWabWhlWh1,a,*bcdehl")
+       (plus:QI (match_operand:QI 1 "general_operand"  "%0,0,0,0")
+                (match_operand:QI 2 "general_operand" "K,L,RWhlWh1i,a")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+    inc\t%0
+    dec\t%0
+    add\t%0, %2
+    add\t%0, %2"
+)
+
+(define_insn "*addhi3_real"
+  [(set (match_operand:HI          0 "nonimmediate_operand"  "=vABDTWh1Wab,vABDTWh1Wab,v,v,A,S,S,A")
+       (plus:HI (match_operand:HI 1 "general_operand"  "%0,0,0,0,0,0,0,S")
+                (match_operand:HI 2 "general_operand" "K,L,N,O,RWh1WhlWabiv,Int8,J,Ri")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   incw\t%0
+   decw\t%0
+   incw\t%0 \;incw\t%0
+   decw\t%0 \;decw\t%0
+   addw\t%0, %p2
+   addw\t%0, %2
+   subw\t%0, %m2
+   movw\t%0, %1 \;addw\t%0, %2"
+)
+
+(define_insn "*addqihi3a_real"
+  [(set (match_operand:HI          0 "register_operand"  "=r")
+       (plus:HI (zero_extend:HI (match_operand:QI 1 "register_operand"  "%r"))
+                (match_operand:HI 2 "register_operand" "r")))
+   ]
+  "rl78_real_insns_ok ()"
+  "add\t%q0, %q1 \;addc\t%Q0, #0"
+)
+
+(define_insn "*subqi3_real"
+  [(set (match_operand:QI           0 "nonimmediate_operand"  "=a,R,v")
+       (minus:QI (match_operand:QI 1 "general_operand"  "0,0,0")
+                 (match_operand:QI 2 "general_operand" "RiWabWhbWh1Whl,a,i")))
+   ]
+  "rl78_real_insns_ok ()"
+  "sub\t%0, %2"
+)
+
+(define_insn "*subhi3_real"
+  [(set (match_operand:HI           0 "nonimmediate_operand"  "=A,S")
+       (minus:HI (match_operand:HI 1 "general_operand"  "0,0")
+                 (match_operand:HI 2 "general_operand" "iBDTWabWh1v,i")))
+   ]
+  "rl78_real_insns_ok ()"
+  "subw\t%0, %2"
+)
+
+(define_insn "*umulhi3_shift_real"
+  [(set (match_operand:HI 0 "register_operand" "=A,A")
+        (mult:HI (match_operand:HI 1 "rl78_nonfar_operand" "0,0")
+                 (match_operand:HI 2 "rl78_24_operand" "N,i")))]
+  "rl78_real_insns_ok ()"
+  "@
+   shlw\t%0, 1
+   shlw\t%0, 2"
+)
+
+(define_insn "*umulqihi3_real"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=A")
+        (mult:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "%a"))
+                 (zero_extend:HI (match_operand:QI 2 "general_operand" "x"))))]
+  "rl78_real_insns_ok ()"
+  "mulu\t%2"
+)
+
+(define_insn "*andqi3_real"
+  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
+       (and:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
+               (match_operand:QI 2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+   ]
+  "rl78_real_insns_ok ()"
+  "and\t%0, %2"
+)
+
+(define_insn "*iorqi3_real"
+  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
+       (ior:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
+               (match_operand:QI 2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+   ]
+  "rl78_real_insns_ok ()"
+  "or\t%0, %2"
+)
+
+(define_insn "*xorqi3_real"
+  [(set (match_operand:QI         0 "nonimmediate_operand"  "=A,R,v")
+       (xor:QI (match_operand:QI 1 "general_operand"       "%0,0,0")
+               (match_operand    2 "general_operand"       "iRvWabWhbWh1Whl,A,i")))
+   ]
+  "rl78_real_insns_ok ()"
+  "xor\t%0, %2"
+)
+
+;;---------- Shifts ------------------------
+
+(define_insn "*ashlqi3_real"
+  [(set (match_operand:QI            0 "nonimmediate_operand"  "=abc,a,a")
+       (ashift:QI (match_operand:QI 1 "general_operand"  "0,0,0")
+                  (match_operand:QI 2 "general_operand" "Int3,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   shl\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: shl\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: shl\t%0, 1 \;dec %2 \;bnz $1b\;2:"
+)
+
+(define_insn "*ashlhi3_real"
+  [(set (match_operand:HI            0 "nonimmediate_operand"  "=AB,A,A")
+       (ashift:HI (match_operand:HI 1 "general_operand"  "0,0,0")
+                  (match_operand:QI 2 "general_operand" "P,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   shlw\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: shlw\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: shlw\t%0, 1 \;dec %2 \;bnz $1b\;2:"
+)
+
+;;----------
+
+(define_insn "*ashrqi3_real"
+  [(set (match_operand:QI              0 "nonimmediate_operand"  "=abc,a,a")
+       (ashiftrt:QI (match_operand:QI 1 "general_operand"  "0,0,0")
+                    (match_operand:QI 2 "general_operand" "Int3,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   sar\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: sar\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: sar\t%0, 1\;dec %2 \;bnz $1b\;2:"
+)
+
+(define_insn "*ashrhi3_real"
+  [(set (match_operand:HI              0 "nonimmediate_operand"  "=AB,A,A")
+       (ashiftrt:HI (match_operand:HI 1 "general_operand"  "0,0,0")
+                    (match_operand:QI 2 "general_operand" "P,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   sarw\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: sarw\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: sarw\t%0, 1\;dec %2\;bnz $1b\;2:"
+)
+
+;;----------
+
+(define_insn "*lshrqi3_real"
+  [(set (match_operand:QI              0 "nonimmediate_operand"  "=abc,a,a")
+       (lshiftrt:QI (match_operand:QI 1 "general_operand"  "0,0,0")
+                    (match_operand:QI 2 "general_operand" "Int3,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   shr\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: shr\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: shr\t%0, 1\;dec %2\;bnz $1b\;2:"
+)
+
+(define_insn "*lshrhi3_real"
+  [(set (match_operand:HI              0 "nonimmediate_operand"  "=AB,A,A")
+       (lshiftrt:HI (match_operand:HI 1 "general_operand"  "0,0,0")
+                    (match_operand:QI 2 "general_operand" "P,bc,dehl")))
+   ]
+  "rl78_real_insns_ok ()"
+  "@
+   shrw\t%0, %u2
+   cmp0 %2\; bz $2f\; 1: shrw\t%0, 1 \;dec %2 \;bnz $1b\;2:
+   inc %2\;dec %2\;bz $2f\;1: shrw\t%0, 1\;dec %2\;bnz $1b\;2:"
+)
+
+;;---------- Branching ------------------------
+
+(define_insn "*indirect_jump_real"
+  [(set (pc)
+       (match_operand:HI 0 "nonimmediate_operand" "A"))]
+  "rl78_real_insns_ok ()"
+  "br\t%0"
+)
+
+(define_insn "jump"
+  [(set (pc)
+       (label_ref (match_operand 0 "" "")))]
+  ""
+  ;; $rel8, $!rel16, !abs16, !!abs20
+  "br\t!!%0"
+)
+
+(define_insn "*call_real"
+  [(call (match_operand:HI 0 "memory_operand" "Wab,Wca")
+        (match_operand 1 "" ""))]
+  "rl78_real_insns_ok ()"
+  "@
+   call\t!!%A0
+   call\t%A0"
+  )
+
+(define_insn "*call_value_real"
+  [(set (match_operand 0 "register_operand" "=v,v")
+       (call (match_operand:HI 1 "memory_operand" "Wab,Wca")
+             (match_operand 2 "" "")))]
+  "rl78_real_insns_ok ()"
+  "@
+   call\t!!%A1
+   call\t%A1"
+  )
+
+(define_insn "*cbranchqi4_real"
+  [(set (pc) (if_then_else
+             (match_operator 0 "rl78_cmp_operator_real"
+                             [(match_operand:QI 1 "general_operand" "Wabvaxbc,a,          v,bcdehl")
+                              (match_operand:QI 2 "general_operand" "M,       irWhlWh1Whb,i,a")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  "rl78_real_insns_ok ()"
+  "@
+   cmp0\t%1 \;sk%c0 \;br\t!!%3
+   cmp\t%1, %2 \;sk%c0 \;br\t!!%3
+   cmp\t%1, %2 \;sk%c0 \;br\t!!%3
+   cmp\t%1, %2 \;sk%c0 \;br\t!!%3"
+  )
+
+(define_insn "*cbranchhi4_real"
+  [(set (pc) (if_then_else
+             (match_operator 0 "rl78_cmp_operator_real"
+                             [(match_operand:HI 1 "general_operand" "A")
+                              (match_operand:HI 2 "general_operand" "iBDTWhlWh1")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  "rl78_real_insns_ok ()"
+  "cmpw\t%1, %2 \;sk%c0 \;br\t!!%3"
+  )
diff --git a/gcc/config/rl78/rl78-virt.md b/gcc/config/rl78/rl78-virt.md
new file mode 100644 (file)
index 0000000..bd22416
--- /dev/null
@@ -0,0 +1,259 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+;; In this MD file, we define those insn patterns that involve
+;; registers, where such registers are virtual until allocated to a
+;; physical register.  All of these insns need to be conditional on
+;; rl78_virt_insns_ok () being true.
+
+;; This tells the physical register allocator what method to use to
+;; allocate registers.  Basically, this defines the template of the
+;; instruction - op1 is of the form "a = op(b)", op2 is "a = b op c"
+;; etc.
+
+(define_attr "valloc" "op1,op2,ro1,cmp,umul,macax"
+  (const_string "op2"))
+
+;;---------- Moving ------------------------
+
+(define_insn "*movqi_virt"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=vY,v,Wfr")
+       (match_operand    1 "general_operand" "vInt8JY,Wfr,vInt8J"))]
+  "rl78_virt_insns_ok ()"
+  "v.mov %0, %1"
+  [(set_attr "valloc" "op1")]
+)
+
+(define_insn "*movhi_virt"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=vYS,v,Wfr")
+       (match_operand:HI 1 "general_operand" "viYS,Wfr,v"))]
+  "rl78_virt_insns_ok ()"
+  "v.movw %0, %1"
+  [(set_attr "valloc" "op1")]
+)
+
+;;---------- Conversions ------------------------
+
+(define_insn "*zero_extendqihi2_virt"
+  [(set (match_operand:HI                 0 "rl78_nonfar_nonimm_operand" "=vm")
+       (zero_extend:HI (match_operand:QI 1 "general_operand" "vim")))]
+  "rl78_virt_insns_ok ()"
+  "v.zero_extend\t%0, %1"
+  [(set_attr "valloc" "op1")]
+  )
+
+(define_insn "*extendqihi2_virt"
+  [(set (match_operand:HI                 0 "rl78_nonfar_nonimm_operand" "=vm")
+       (sign_extend:HI (match_operand:QI 1 "general_operand" "vim")))]
+  "rl78_virt_insns_ok ()"
+  "v.sign_extend\t%0, %1"
+  [(set_attr "valloc" "op1")]
+  )
+
+;;---------- Arithmetic ------------------------
+
+(define_insn "*add<mode>3_virt"
+  [(set (match_operand:QHI           0 "rl78_nonfar_nonimm_operand" "=vY,S")
+       (plus:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "viY,0")
+                 (match_operand:QHI 2 "general_operand" "vim,i")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.add\t%0, %1, %2"
+)
+
+(define_insn "*sub<mode>3_virt"
+  [(set (match_operand:QHI            0 "rl78_nonfar_nonimm_operand" "=vm,S")
+       (minus:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "vim,0")
+                  (match_operand:QHI 2 "general_operand" "vim,i")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.sub\t%0, %1, %2"
+)
+
+(define_insn "*umulhi3_shift_virt"
+  [(set (match_operand:HI 0 "register_operand" "=vm")
+        (mult:HI (match_operand:HI 1 "rl78_nonfar_operand" "%vim")
+                 (match_operand:HI 2 "rl78_24_operand" "Ni")))]
+  "rl78_virt_insns_ok ()"
+  "v.mulu\t%0, %1, %2"
+  [(set_attr "valloc" "umul")]
+)
+
+(define_insn "*umulqihi3_virt"
+  [(set (match_operand:HI 0 "register_operand" "=vm")
+        (mult:HI (zero_extend:HI (match_operand:QI 1 "rl78_nonfar_operand" "%vim"))
+                 (zero_extend:HI (match_operand:QI 2 "general_operand" "vim"))))]
+  "rl78_virt_insns_ok ()"
+  "v.mulu\t%0, %2"
+  [(set_attr "valloc" "umul")]
+)
+
+(define_insn "*andqi3_virt"
+  [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=vm")
+       (and:QI (match_operand:QI 1 "rl78_nonfar_operand" "vim")
+               (match_operand:QI 2 "general_operand" "vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.and\t%0, %1, %2"
+)
+
+(define_insn "*iorqi3_virt"
+  [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=vm")
+       (ior:QI (match_operand:QI 1 "rl78_nonfar_operand" "vim")
+               (match_operand:QI 2 "general_operand" "vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.or\t%0, %1, %2"
+)
+
+(define_insn "*xor3_virt"
+  [(set (match_operand:QI         0 "rl78_nonfar_nonimm_operand" "=v,vm,m")
+       (xor:QI (match_operand:QI 1 "rl78_nonfar_operand" "%0,vm,vm")
+               (match_operand    2 "general_operand" "i,vm,vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.xor\t%0, %1, %2"
+)
+
+;;---------- Shifts ------------------------
+
+(define_insn "*ashl<mode>3_virt"
+  [(set (match_operand:QHI             0 "rl78_nonfar_nonimm_operand" "=vm")
+       (ashift:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "vim")
+                   (match_operand:QI  2 "general_operand" "vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.shl\t%0, %1, %2"
+)
+
+(define_insn "*ashr<mode>3_virt"
+  [(set (match_operand:QHI               0 "rl78_nonfar_nonimm_operand" "=vm")
+       (ashiftrt:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "vim")
+                     (match_operand:QI  2 "general_operand" "vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.sar\t%0, %1, %2"
+)
+
+(define_insn "*lshr<mode>3_virt"
+  [(set (match_operand:QHI               0 "rl78_nonfar_nonimm_operand" "=vm")
+       (lshiftrt:QHI (match_operand:QHI 1 "rl78_nonfar_operand" "vim")
+                     (match_operand:QI  2 "general_operand" "vim")))
+   ]
+  "rl78_virt_insns_ok ()"
+  "v.shr\t%0, %1, %2"
+)
+
+;; really a macro
+(define_insn "*ashrsi3_virt"
+  [(set (match_operand:SI               0 "register_operand" "=v,v,v")
+       (ashiftrt:SI (match_operand:SI  1 "register_operand" "0,v,0")
+                     (match_operand:SI 2 "immediate_operand" "M,K,i")))
+   ]
+  ""
+  "@
+   ; ashrsi %0, 0
+   movw\tax,%H1\;sarw\tax,1\;movw\t%H0,ax\;mov\ta,%Q1\;rorc\ta,1\;mov\t%Q0,a\;mov\ta,%q1\;rorc\ta,1\;mov\t%q0,a
+   mov\tb,%2\;1:\;movw\tax,%H1\;sarw\tax,1\;movw\t%H0,ax\;mov\ta,%Q1\;rorc\ta,1\;mov\t%Q0,a\;mov\ta,%q1\;rorc\ta,1\;mov\t%q0,a\;dec\tb\;bnz $1b"
+  [(set_attr "valloc" "macax")]
+)
+
+;;---------- Branching ------------------------
+
+(define_insn "*indirect_jump_virt"
+  [(set (pc)
+       (match_operand:HI 0 "nonimmediate_operand" "vm"))]
+  "rl78_virt_insns_ok ()"
+  "v.br\t%0"
+  [(set_attr "valloc" "ro1")]
+)
+
+(define_insn "*call_virt"
+  [(call (match_operand:HI 0 "memory_operand" "Wab,Wcv")
+        (match_operand 1 "" ""))]
+  "rl78_virt_insns_ok ()"
+  "v.call\t%0"
+  [(set_attr "valloc" "ro1")]
+  )
+
+(define_insn "*call_value_virt"
+  [(set (match_operand 0 "register_operand" "=v,v")
+       (call (match_operand:HI 1 "memory_operand" "Wab,Wcv")
+             (match_operand 2 "" "")))]
+  "rl78_virt_insns_ok ()"
+  "v.call\t%1"
+  [(set_attr "valloc" "op1")]
+  )
+
+(define_insn "*cbranchqi4_virt"
+  [(set (pc) (if_then_else
+             (match_operator 0 "rl78_cmp_operator_real"
+                             [(match_operand:QI 1 "general_operand" "vim")
+                              (match_operand:QI 2 "general_operand" "vim")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  "rl78_virt_insns_ok ()"
+  "v.cmp\t%1, %2\\n\tv.b%c0\t%3"
+  [(set_attr "valloc" "cmp")]
+  )
+
+(define_insn "*cbranchhi4_virt"
+  [(set (pc) (if_then_else
+             (match_operator 0 "rl78_cmp_operator_real"
+                             [(match_operand:HI 1 "general_operand" "vim")
+                              (match_operand:HI 2 "general_operand" "vim")])
+              (label_ref (match_operand 3 "" ""))
+             (pc)))]
+  "rl78_virt_insns_ok ()"
+  "v.cmpw\t%1, %2\\n\tv.b%c0\t%3"
+  [(set_attr "valloc" "cmp")]
+  )
+
+;;---------- Peepholes ------------------------
+
+(define_peephole2
+  [(set (match_operand:QI 0 "" "")
+       (match_operand:QI 1 "" ""))
+   (set (match_operand:QI 2 "" "")
+       (match_operand:QI 3 "" ""))]
+  "rl78_peep_movhi_p (operands)"
+  [(set (match_dup 4)
+       (match_dup 5))]
+  "rl78_setup_peep_movhi (operands);"
+  )
+
+(define_peephole2
+  [(set (reg:QI A_REG)
+       (match_operand:QI 1 "" ""))
+   (set (match_operand:QI 0 "" "")
+       (reg:QI A_REG))
+   (set (reg:QI A_REG)
+       (match_operand:QI 3 "" ""))
+   (set (match_operand:QI 2 "" "")
+       (reg:QI A_REG))
+   ]
+  "rl78_peep_movhi_p (operands)"
+  [(set (reg:HI AX_REG)
+       (match_dup 5))
+   (set (match_dup 4)
+       (reg:HI AX_REG))
+   ]
+  "rl78_setup_peep_movhi (operands);"
+  )
diff --git a/gcc/config/rl78/rl78.c b/gcc/config/rl78/rl78.c
new file mode 100644 (file)
index 0000000..460538e
--- /dev/null
@@ -0,0 +1,2705 @@
+/* Subroutines used for code generation on Renesas RL78 processors.
+   Copyright (C) 2011 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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "toplev.h"
+#include "reload.h"
+#include "df.h"
+#include "ggc.h"
+#include "tm_p.h"
+#include "debug.h"
+#include "target.h"
+#include "target-def.h"
+#include "langhooks.h"
+#include "rl78-protos.h"
+#include "tree-pass.h"
+\f
+static inline bool is_interrupt_func (const_tree decl);
+static inline bool is_brk_interrupt_func (const_tree decl);
+static void rl78_reorg (void);
+\f
+
+/* Debugging statements are tagged with DEBUG0 only so that they can
+   be easily enabled individually, by replacing the '0' with '1' as
+   needed.  */
+#define DEBUG0 0
+#define DEBUG1 1
+
+/* REGISTER_NAMES has the names for individual 8-bit registers, but
+   these have the names we need to use when referring to 16-bit
+   register pairs.  */
+static const char * const word_regnames[] =
+{
+  "ax", "AX", "bc", "BC", "de", "DE", "hl", "HL",
+  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+  "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+  "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+  "sp", "ap", "psw", "es", "cs"
+};
+
+struct GTY(()) machine_function
+{
+  /* If set, the rest of the fields have been computed.  */
+  int computed;
+  /* Which register pairs need to be pushed in the prologue.  */
+  int need_to_push [FIRST_PSEUDO_REGISTER / 2];
+
+  /* These fields describe the frame layout...  */
+  /* arg pointer */
+  /* 4 bytes for saved PC */
+  int framesize_regs;
+  /* frame pointer */
+  int framesize_locals;
+  int framesize_outgoing;
+  /* stack pointer */
+  int framesize;
+
+  /* If set, recog is allowed to match against the "real" patterns.  */
+  int real_insns_ok;
+  /* If set, recog is allowed to match against the "virtual" patterns.  */
+  int virt_insns_ok;
+  /* Set if the current function needs to clean up any trampolines.  */
+  int trampolines_used;
+};
+
+/* This is our init_machine_status, as set in
+   rl78_option_override.  */
+static struct machine_function *
+rl78_init_machine_status (void)
+{
+  struct machine_function *m;
+
+  m = ggc_alloc_cleared_machine_function ();
+  m->virt_insns_ok = 1;
+
+  return m;
+}
+
+/* Returns whether to run the devirtualization pass.  */
+static bool
+devirt_gate (void)
+{
+  return true;
+}
+
+/* Runs the devirtualization pass.  */
+static unsigned int
+devirt_pass (void)
+{
+  rl78_reorg ();
+  return 0;
+}
+
+/* This pass converts virtual instructions using virtual registers, to
+   real instructions using real registers.  Rather than run it as
+   reorg, we reschedule it before vartrack to help with debugging.  */
+static struct opt_pass rl78_devirt_pass =
+{
+  RTL_PASS,
+  "devirt",
+  devirt_gate,
+  devirt_pass,
+  NULL,
+  NULL,
+  212,
+  TV_MACH_DEP,
+  0, 0, 0,
+  0,
+  TODO_dump_func
+};
+
+static struct register_pass_info rl78_devirt_info =
+{
+  & rl78_devirt_pass,
+  "vartrack",
+  1,
+  PASS_POS_INSERT_BEFORE
+};
+
+#undef  TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START rl78_asm_file_start
+
+static void
+rl78_asm_file_start (void)
+{
+  int i;
+
+  for (i = 0; i < 8; i++)
+    {
+      fprintf (asm_out_file, "r%d\t=\t0x%x\n", 8 + i, 0xffef0 + i);
+      fprintf (asm_out_file, "r%d\t=\t0x%x\n", 16 + i, 0xffee8 + i);
+    }
+
+  register_pass (& rl78_devirt_info);
+}
+
+\f
+#undef  TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE         rl78_option_override
+
+static void
+rl78_option_override (void)
+{
+  flag_omit_frame_pointer = 1;
+  flag_no_function_cse = 1;
+  flag_split_wide_types = 0;
+
+  init_machine_status = rl78_init_machine_status;
+}
+
+/* Most registers are 8 bits.  Some are 16 bits because, for example,
+   gcc doesn't like dealing with $FP as a register pair.  This table
+   maps register numbers to size in bytes.  */
+static const int register_sizes[] =
+{
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 2, 1,
+  1, 1, 1, 1, 1, 1, 1, 1,
+  2, 2, 1, 1, 1
+};
+
+/* Predicates used in the MD patterns.  This one is true when virtual
+   insns may be matched, which typically means before (or during) the
+   devirt pass.  */
+bool
+rl78_virt_insns_ok (void)
+{
+  if (cfun)
+    return cfun->machine->virt_insns_ok;
+  return true;
+}
+
+/* Predicates used in the MD patterns.  This one is true when real
+   insns may be matched, which typically means after (or during) the
+   devirt pass.  */
+bool
+rl78_real_insns_ok (void)
+{
+  if (cfun)
+    return cfun->machine->real_insns_ok;
+  return false;
+}
+
+/* Implements HARD_REGNO_NREGS.  */
+int
+rl78_hard_regno_nregs (int regno, enum machine_mode mode)
+{
+  int rs = register_sizes[regno];
+  if (rs < 1)
+    rs = 1;
+  return ((GET_MODE_SIZE (mode) + rs - 1) / rs);
+}
+
+/* Implements HARD_REGNO_MODE_OK.  */
+int
+rl78_hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+  int s = GET_MODE_SIZE (mode);
+
+  if (s < 1)
+    return 0;
+  /* These are not to be used by gcc.  */
+  if (regno == 23 || regno == ES_REG || regno == CS_REG)
+    return 0;
+  /* $fp can alway sbe accessed as a 16-bit value.  */
+  if (regno == FP_REG && s == 2)
+    return 1;
+  if (regno < SP_REG)
+    {
+      /* Since a reg-reg move is really a reg-mem move, we must
+        enforce alignment.  */
+      if (s > 1 && (regno % 2))
+       return 0;
+      return 1;
+    }
+  if (s == CC_REGNUM)
+    return (mode == BImode);
+  /* All other registers must be accessed in their natural sizes.  */
+  if (s == register_sizes [regno])
+    return 1;
+  return 0;
+}
+
+/* Simplify_gen_subreg() doesn't handle memory references the way we
+   need it to below, so we use this function for when we must get a
+   valid subreg in a "natural" state.  */
+static rtx
+rl78_subreg (enum machine_mode mode, rtx r, enum machine_mode omode, int byte)
+{
+  if (GET_CODE (r) == MEM)
+    return adjust_address (r, mode, byte);
+  else
+    return simplify_gen_subreg (mode, r, omode, byte);
+}
+
+/* Used by movsi.  Split SImode moves into two HImode moves, using
+   appropriate patterns for the upper and lower halves of symbols.  */
+void
+rl78_expand_movsi (rtx *operands)
+{
+  rtx op00, op02, op10, op12;
+
+  op00 = rl78_subreg (HImode, operands[0], SImode, 0);
+  op02 = rl78_subreg (HImode, operands[0], SImode, 2);
+  if (GET_CODE (operands[1]) == CONST
+      || GET_CODE (operands[1]) == SYMBOL_REF)
+    {
+      op10 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (0));
+      op10 = gen_rtx_CONST (HImode, op10);
+      op12 = gen_rtx_ZERO_EXTRACT (HImode, operands[1], GEN_INT (16), GEN_INT (16));
+      op12 = gen_rtx_CONST (HImode, op12);
+    }
+  else
+    {
+      op10 = rl78_subreg (HImode, operands[1], SImode, 0);
+      op12 = rl78_subreg (HImode, operands[1], SImode, 2);
+    }
+
+  if (rtx_equal_p (operands[0], operands[1]))
+    ;
+  else if (rtx_equal_p (op00, op12))
+    {
+      emit_move_insn (op02, op12);
+      emit_move_insn (op00, op10);
+    }
+  else
+    {
+      emit_move_insn (op00, op10);
+      emit_move_insn (op02, op12);
+    }
+}
+
+/* Used by various two-operand expanders which cannot accept all
+   operands in the "far" namespace.  Force some such operands into
+   registers so that each pattern has at most one far operand.  */
+int
+rl78_force_nonfar_2 (rtx *operands, rtx (*gen)(rtx,rtx))
+{
+  int did = 0;
+  rtx temp_reg = NULL;
+
+  /* FIXME: in the future, be smarter about only doing this if the
+     other operand is also far, assuming the devirtualizer can also
+     handle that.  */
+  if (rl78_far_p (operands[0]))
+    {
+      temp_reg = operands[0];
+      operands[0] = gen_reg_rtx (GET_MODE (operands[0]));
+      did = 1;
+    }
+  if (!did)
+    return 0;
+
+  emit_insn (gen (operands[0], operands[1]));
+  if (temp_reg)
+    emit_move_insn (temp_reg, operands[0]);
+  return 1;
+}
+
+/* Likewise, but for three-operand expanders.  */
+int
+rl78_force_nonfar_3 (rtx *operands, rtx (*gen)(rtx,rtx,rtx))
+{
+  int did = 0;
+  rtx temp_reg = NULL;
+
+  /* FIXME: Likewise.  */
+  if (rl78_far_p (operands[1]))
+    {
+      rtx temp_reg = gen_reg_rtx (GET_MODE (operands[1]));
+      emit_move_insn (temp_reg, operands[1]);
+      operands[1] = temp_reg;
+      did = 1;
+    }
+  if (rl78_far_p (operands[0]))
+    {
+      temp_reg = operands[0];
+      operands[0] = gen_reg_rtx (GET_MODE (operands[0]));
+      did = 1;
+    }
+  if (!did)
+    return 0;
+
+  emit_insn (gen (operands[0], operands[1], operands[2]));
+  if (temp_reg)
+    emit_move_insn (temp_reg, operands[0]);
+  return 1;
+}
+
+#undef  TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE           rl78_can_eliminate
+
+static bool
+rl78_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+/* Returns nonzero if the given register needs to be saved by the
+   current function.  */
+static int
+need_to_save (int regno)
+{
+  if (is_interrupt_func (cfun->decl))
+    {
+      if (regno < 8)
+       return 1; /* don't know what devirt will need */
+      if (regno > 23)
+       return 0; /* don't need to save interrupt registers */
+      if (current_function_is_leaf)
+       {
+         return df_regs_ever_live_p (regno);
+       }
+      else
+       return 1;
+    }
+  if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)
+    return 1;
+  if (fixed_regs[regno])
+    return 0;
+  if (crtl->calls_eh_return)
+    return 1;
+  if (df_regs_ever_live_p (regno)
+      && !call_used_regs[regno])
+    return 1;
+  return 0;
+}
+
+/* We use this to wrap all emitted insns in the prologue.  */
+static rtx
+F (rtx x)
+{
+  RTX_FRAME_RELATED_P (x) = 1;
+  return x;
+}
+
+/* Compute all the frame-related fields in our machine_function
+   structure.  */
+static void
+rl78_compute_frame_info (void)
+{
+  int i;
+
+  cfun->machine->computed = 1;
+  cfun->machine->framesize_regs = 0;
+  cfun->machine->framesize_locals = get_frame_size ();
+  cfun->machine->framesize_outgoing = crtl->outgoing_args_size;
+
+  for (i = 0; i < 16; i ++)
+    if (need_to_save (i * 2) || need_to_save (i * 2 + 1))
+      {
+       cfun->machine->need_to_push [i] = 1;
+       cfun->machine->framesize_regs += 2;
+      }
+    else
+      cfun->machine->need_to_push [i] = 0;
+
+  if ((cfun->machine->framesize_locals + cfun->machine->framesize_outgoing) & 1)
+    cfun->machine->framesize_locals ++;
+
+  cfun->machine->framesize = (cfun->machine->framesize_regs
+                             + cfun->machine->framesize_locals
+                             + cfun->machine->framesize_outgoing);
+}
+\f
+/* Returns true if the provided function has the specified attribute.  */
+static inline bool
+has_func_attr (const_tree decl, const char * func_attr)
+{
+  if (decl == NULL_TREE)
+    decl = current_function_decl;
+
+  return lookup_attribute (func_attr, DECL_ATTRIBUTES (decl)) != NULL_TREE;
+}
+
+/* Returns true if the provided function has the "interrupt" attribute.  */
+static inline bool
+is_interrupt_func (const_tree decl)
+{
+  return has_func_attr (decl, "interrupt") || has_func_attr (decl, "brk_interrupt");
+}
+
+/* Returns true if the provided function has the "brk_interrupt" attribute.  */
+static inline bool
+is_brk_interrupt_func (const_tree decl)
+{
+  return has_func_attr (decl, "brk_interrupt");
+}
+
+/* Check "interrupt" attributes.  */
+static tree
+rl78_handle_func_attribute (tree * node,
+                         tree   name,
+                         tree   args,
+                         int    flags ATTRIBUTE_UNUSED,
+                         bool * no_add_attrs)
+{
+  gcc_assert (DECL_P (* node));
+  gcc_assert (args == NULL_TREE);
+
+  if (TREE_CODE (* node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
+      * no_add_attrs = true;
+    }
+
+  /* FIXME: We ought to check that the interrupt and exception
+     handler attributes have been applied to void functions.  */
+  return NULL_TREE;
+}
+
+#undef  TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE         rl78_attribute_table
+
+/* Table of RL78-specific attributes.  */
+const struct attribute_spec rl78_attribute_table[] =
+{
+  /* Name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
+     affects_type_identity.  */
+  { "interrupt",      0, 0, true, false, false, rl78_handle_func_attribute,
+    false },
+  { "brk_interrupt",  0, 0, true, false, false, rl78_handle_func_attribute,
+    false },
+  { NULL,             0, 0, false, false, false, NULL, false }
+};
+
+
+\f
+/* Break down an address RTX into its component base/index/addend
+   portions and return TRUE if the address is of a valid form, else
+   FALSE.  */
+static bool
+characterize_address (rtx x, rtx *base, rtx *index, rtx *addend)
+{
+  *base = NULL_RTX;
+  *index = NULL_RTX;
+  *addend = NULL_RTX;
+
+  if (GET_CODE (x) == REG)
+    {
+      *base = x;
+      return true;
+    }
+
+  /* We sometimes get these without the CONST wrapper */
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+      && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    {
+      *addend = x;
+      return true;
+    }
+
+  if (GET_CODE (x) == PLUS)
+    {
+      *base = XEXP (x, 0);
+      x = XEXP (x, 1);
+
+      if (GET_CODE (*base) != REG
+         && GET_CODE (x) == REG)
+       {
+         rtx tmp = *base;
+         *base = x;
+         x = tmp;
+       }
+
+      if (GET_CODE (*base) != REG)
+       return false;
+
+      if (GET_CODE (x) == ZERO_EXTEND
+         && GET_CODE (XEXP (x, 0)) == REG)
+       {
+         *index = XEXP (x, 0);
+         return false;
+       }
+    }
+
+  switch (GET_CODE (x))
+    {
+    case PLUS:
+      if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+         && GET_CODE (XEXP (x, 0)) == CONST_INT)
+       {
+         *addend = x;
+         return true;
+       }
+      /* fall through */
+    case MEM:
+    case REG:
+      return false;
+
+    case CONST:
+    case SYMBOL_REF:
+    case CONST_INT:
+      *addend = x;
+      return true;
+
+    default:
+      return false;
+    }
+
+  return false;
+}
+
+/* Used by the Whb constraint.  Match addresses that use HL+B or HL+C
+   addressing.  */
+bool
+rl78_hl_b_c_addr_p (rtx op)
+{
+  rtx hl, bc;
+
+  if (GET_CODE (op) != PLUS)
+    return false;
+  hl = XEXP (op, 0);
+  bc = XEXP (op, 1);
+  if (GET_CODE (hl) == ZERO_EXTEND)
+    {
+      rtx tmp = hl;
+      hl = bc;
+      bc = tmp;
+    }
+  if (GET_CODE (hl) != REG)
+    return false;
+  if (GET_CODE (bc) != ZERO_EXTEND)
+    return false;
+  bc = XEXP (bc, 0);
+  if (GET_CODE (bc) != REG)
+    return false;
+  if (REGNO (hl) != HL_REG)
+    return false;
+  if (REGNO (bc) != B_REG && REGNO (bc) != C_REG)
+    return false;
+
+  return true;
+}
+
+#define REG_IS(r, regno) (((r) == (regno)) || ((r) >= FIRST_PSEUDO_REGISTER && !(strict)))
+
+/* Used in various constraints and predicates to match operands in the
+   "far" address space.  */
+int
+rl78_far_p (rtx x)
+{
+  if (GET_CODE (x) != MEM)
+    return 0;
+#if DEBUG0
+  fprintf(stderr, "\033[35mrl78_far_p: "); debug_rtx(x);
+  fprintf(stderr, " = %d\033[0m\n", MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR);
+#endif
+  return MEM_ADDR_SPACE (x) == ADDR_SPACE_FAR;
+}
+
+/* Return the appropriate mode for a named address pointer.  */
+#undef TARGET_ADDR_SPACE_POINTER_MODE
+#define TARGET_ADDR_SPACE_POINTER_MODE rl78_addr_space_pointer_mode
+static enum machine_mode
+rl78_addr_space_pointer_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    case ADDR_SPACE_GENERIC:
+      return HImode;
+    case ADDR_SPACE_FAR:
+      return SImode;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return the appropriate mode for a named address address.  */
+#undef TARGET_ADDR_SPACE_ADDRESS_MODE
+#define TARGET_ADDR_SPACE_ADDRESS_MODE rl78_addr_space_address_mode
+static enum machine_mode
+rl78_addr_space_address_mode (addr_space_t addrspace)
+{
+  switch (addrspace)
+    {
+    case ADDR_SPACE_GENERIC:
+      return HImode;
+    case ADDR_SPACE_FAR:
+      return SImode;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+#undef  TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P           rl78_is_legitimate_constant
+
+static bool
+rl78_is_legitimate_constant (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x ATTRIBUTE_UNUSED)
+{
+  return true;
+}
+
+#undef  TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P
+#define TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P rl78_as_legitimate_address
+
+bool
+rl78_as_legitimate_address (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x,
+                           bool strict ATTRIBUTE_UNUSED, addr_space_t as ATTRIBUTE_UNUSED)
+{
+  rtx base, index, addend;
+
+  if (as == ADDR_SPACE_GENERIC
+      && GET_MODE (x) == SImode)
+    return false;
+
+  if (! characterize_address (x, &base, &index, &addend))
+    return false;
+
+  if (base && index)
+    {
+      int ir = REGNO (index);
+      int br = REGNO (base);
+
+#define OK(test, debug) if (test) { /*fprintf(stderr, "%d: OK %s\n", __LINE__, debug);*/ return true; }
+      OK (REG_IS (br, HL_REG) && REG_IS (ir, B_REG), "[hl+b]");
+      OK (REG_IS (br, HL_REG) && REG_IS (ir, C_REG), "[hl+c]");
+      return false;
+    }
+
+  if (strict && base && GET_CODE (base) == REG && REGNO (base) >= FIRST_PSEUDO_REGISTER)
+    return false;
+
+  return true;
+}
+
+/* Determine if one named address space is a subset of another.  */
+#undef  TARGET_ADDR_SPACE_SUBSET_P
+#define TARGET_ADDR_SPACE_SUBSET_P rl78_addr_space_subset_p
+static bool
+rl78_addr_space_subset_p (addr_space_t subset, addr_space_t superset)
+{
+  gcc_assert (subset == ADDR_SPACE_GENERIC || subset == ADDR_SPACE_FAR);
+  gcc_assert (superset == ADDR_SPACE_GENERIC || superset == ADDR_SPACE_FAR);
+
+  if (subset == superset)
+    return true;
+
+  else
+    return (subset == ADDR_SPACE_GENERIC && superset == ADDR_SPACE_FAR);
+}
+
+#undef  TARGET_ADDR_SPACE_CONVERT
+#define TARGET_ADDR_SPACE_CONVERT rl78_addr_space_convert
+/* Convert from one address space to another.  */
+static rtx
+rl78_addr_space_convert (rtx op, tree from_type, tree to_type)
+{
+  addr_space_t from_as = TYPE_ADDR_SPACE (TREE_TYPE (from_type));
+  addr_space_t to_as = TYPE_ADDR_SPACE (TREE_TYPE (to_type));
+  rtx result;
+
+  gcc_assert (from_as == ADDR_SPACE_GENERIC || from_as == ADDR_SPACE_FAR);
+  gcc_assert (to_as == ADDR_SPACE_GENERIC || to_as == ADDR_SPACE_FAR);
+
+  if (to_as == ADDR_SPACE_GENERIC && from_as == ADDR_SPACE_FAR)
+    {
+      /* This is unpredictable, as we're truncating off usable address
+        bits.  */
+
+      result = gen_reg_rtx (HImode);
+      emit_move_insn (result, simplify_subreg (HImode, op, SImode, 0));
+      return result;
+    }
+  else if (to_as == ADDR_SPACE_FAR && from_as == ADDR_SPACE_GENERIC)
+    {
+      /* This always works.  */
+      result = gen_reg_rtx (SImode);
+      debug_rtx(result);
+      debug_rtx(op);
+      emit_move_insn (rl78_subreg (HImode, result, SImode, 0), op);
+      emit_move_insn (rl78_subreg (HImode, result, SImode, 2), const0_rtx);
+      return result;
+    }
+  else
+    gcc_unreachable ();
+}
+
+/* Implements REGNO_MODE_CODE_OK_FOR_BASE_P.  */
+bool
+rl78_regno_mode_code_ok_for_base_p (int regno, enum machine_mode mode ATTRIBUTE_UNUSED,
+                                   addr_space_t address_space ATTRIBUTE_UNUSED,
+                                   int outer_code ATTRIBUTE_UNUSED, int index_code)
+{
+  if (regno < 24 && regno >= 16)
+    return true;
+  if (index_code == REG)
+    return (regno == HL_REG);
+  if (regno == C_REG || regno == B_REG || regno == E_REG || regno == L_REG)
+    return true;
+  return false;
+}
+
+/* Implements MODE_CODE_BASE_REG_CLASS.  */
+enum reg_class
+rl78_mode_code_base_reg_class (enum machine_mode mode ATTRIBUTE_UNUSED,
+                              addr_space_t address_space ATTRIBUTE_UNUSED,
+                              int outer_code ATTRIBUTE_UNUSED,
+                              int index_code ATTRIBUTE_UNUSED)
+{
+  return V_REGS;
+}
+
+/* Implements INITIAL_ELIMINATION_OFFSET.  The frame layout is
+   described in the machine_Function struct definition, above.  */
+int
+rl78_initial_elimination_offset (int from, int to)
+{
+  int rv = 0; /* as if arg to arg */
+
+  rl78_compute_frame_info ();
+
+  switch (to)
+    {
+    case STACK_POINTER_REGNUM:
+      rv += cfun->machine->framesize_outgoing;
+      rv += cfun->machine->framesize_locals;
+      /* Fall through.  */
+    case FRAME_POINTER_REGNUM:
+      rv += cfun->machine->framesize_regs;
+      rv += 4;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      rv -= 4;
+      rv -= cfun->machine->framesize_regs;
+    case ARG_POINTER_REGNUM:
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  return rv;
+}
+
+/* Expand the function prologue (from the prologue pattern).  */
+void
+rl78_expand_prologue (void)
+{
+  int i, fs;
+  rtx sp = gen_rtx_REG (HImode, STACK_POINTER_REGNUM);
+  int rb = 0;
+
+  if (!cfun->machine->computed)
+    rl78_compute_frame_info ();
+
+  for (i = 0; i < 16; i++)
+    if (cfun->machine->need_to_push [i])
+      {
+       int need_bank = i/4;
+       if (need_bank != rb)
+         {
+           emit_insn (gen_sel_rb (GEN_INT (need_bank)));
+           rb = need_bank;
+         }
+       F (emit_insn (gen_push (gen_rtx_REG (HImode, i*2))));
+      }
+  if (rb != 0)
+    emit_insn (gen_sel_rb (GEN_INT (0)));
+
+  if (frame_pointer_needed)
+    F (emit_move_insn (gen_rtx_REG (HImode, FRAME_POINTER_REGNUM),
+                      gen_rtx_REG (HImode, STACK_POINTER_REGNUM)));
+
+  fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+  while (fs > 0)
+    {
+      int fs_byte = (fs > 254) ? 254 : fs;
+      F (emit_insn (gen_subhi3 (sp, sp, GEN_INT (fs_byte))));
+      fs -= fs_byte;
+    }
+}
+
+/* Expand the function epilogue (from the epilogue pattern).  */
+void
+rl78_expand_epilogue (void)
+{
+  int i, fs;
+  rtx sp = gen_rtx_REG (HImode, STACK_POINTER_REGNUM);
+  int rb = 0;
+
+  if (frame_pointer_needed)
+    {
+      emit_move_insn (gen_rtx_REG (HImode, STACK_POINTER_REGNUM),
+                     gen_rtx_REG (HImode, FRAME_POINTER_REGNUM));
+    }
+  else
+    {
+      fs = cfun->machine->framesize_locals + cfun->machine->framesize_outgoing;
+      while (fs > 0)
+       {
+         int fs_byte = (fs > 254) ? 254 : fs;
+
+         emit_insn (gen_addhi3 (sp, sp, GEN_INT (fs_byte)));
+         fs -= fs_byte;
+       }
+    }
+
+  for (i = 15; i >= 0; i--)
+    if (cfun->machine->need_to_push [i])
+      {
+       int need_bank = i / 4;
+
+       if (need_bank != rb)
+         {
+           emit_insn (gen_sel_rb (GEN_INT (need_bank)));
+           rb = need_bank;
+         }
+       emit_insn (gen_pop (gen_rtx_REG (HImode, i * 2)));
+      }
+
+  if (rb != 0)
+    emit_insn (gen_sel_rb (GEN_INT (0)));
+
+  if (cfun->machine->trampolines_used)
+    emit_insn (gen_trampoline_uninit ());
+
+  if (is_brk_interrupt_func (cfun->decl))
+    emit_jump_insn (gen_brk_interrupt_return ());
+  else if (is_interrupt_func (cfun->decl))
+    emit_jump_insn (gen_interrupt_return ());
+  else
+    emit_jump_insn (gen_return ());
+}
+
+/* Likewise, for exception handlers.  */
+void
+rl78_expand_eh_epilogue (rtx x ATTRIBUTE_UNUSED)
+{
+  /* FIXME - replace this with an indirect jump with stack adjust.  */
+  emit_jump_insn (gen_return ());
+}
+
+#undef  TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE   rl78_start_function
+
+/* We don't use this to actually emit the function prologue.  We use
+   this to insert a comment in the asm file describing the
+   function.  */
+static void
+rl78_start_function (FILE *file, HOST_WIDE_INT hwi_local ATTRIBUTE_UNUSED)
+{
+  int i;
+
+  if (cfun->machine->framesize == 0)
+    return;
+  fprintf (file, "\t; start of function\n");
+
+  if (cfun->machine->framesize_regs)
+    {
+      fprintf (file, "\t; push %d:", cfun->machine->framesize_regs);
+      for (i = 0; i < 16; i ++)
+       if (cfun->machine->need_to_push[i])
+         fprintf (file, " %s", word_regnames[i*2]);
+      fprintf(file, "\n");
+    }
+
+  if (frame_pointer_needed)
+    fprintf (file, "\t; $fp points here (r22)\n");
+
+  if (cfun->machine->framesize_locals)
+    fprintf (file, "\t; locals: %d byte%s\n", cfun->machine->framesize_locals,
+            cfun->machine->framesize_locals == 1 ? "" : "s");
+
+  if (cfun->machine->framesize_outgoing)
+    fprintf (file, "\t; outgoing: %d byte%s\n", cfun->machine->framesize_outgoing,
+            cfun->machine->framesize_outgoing == 1 ? "" : "s");
+}
+
+/* Return an RTL describing where a function return value of type RET_TYPE
+   is held.  */
+
+#undef  TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE          rl78_function_value
+
+static rtx
+rl78_function_value (const_tree ret_type,
+                    const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+                    bool       outgoing ATTRIBUTE_UNUSED)
+{
+  enum machine_mode mode = TYPE_MODE (ret_type);
+
+  return gen_rtx_REG (mode, 8);
+}
+
+#undef  TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE rl78_promote_function_mode
+
+static enum machine_mode
+rl78_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
+                           enum machine_mode mode,
+                           int *punsignedp ATTRIBUTE_UNUSED,
+                           const_tree funtype ATTRIBUTE_UNUSED, int for_return ATTRIBUTE_UNUSED)
+{
+  return mode;
+}
+
+/* Return an RTL expression describing the register holding a function
+   parameter of mode MODE and type TYPE or NULL_RTX if the parameter should
+   be passed on the stack.  CUM describes the previous parameters to the
+   function and NAMED is false if the parameter is part of a variable
+   parameter list, or the last named parameter before the start of a
+   variable parameter list.  */
+
+#undef  TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG            rl78_function_arg
+
+static rtx
+rl78_function_arg (cumulative_args_t cum_v ATTRIBUTE_UNUSED,
+                  enum machine_mode mode ATTRIBUTE_UNUSED,
+                  const_tree type ATTRIBUTE_UNUSED,
+                  bool named ATTRIBUTE_UNUSED)
+{
+  return NULL_RTX;
+}
+
+#undef  TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE     rl78_function_arg_advance
+
+static void
+rl78_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode, const_tree type,
+                          bool named ATTRIBUTE_UNUSED)
+{
+  int rounded_size;
+  CUMULATIVE_ARGS * cum = get_cumulative_args (cum_v);
+
+  rounded_size = ((mode == BLKmode)
+                 ? int_size_in_bytes (type) : GET_MODE_SIZE (mode));
+  if (rounded_size & 1)
+    rounded_size ++;
+  (*cum) += rounded_size;
+}
+
+#undef  TARGET_FUNCTION_ARG_BOUNDARY
+#define        TARGET_FUNCTION_ARG_BOUNDARY rl78_function_arg_boundary
+
+static unsigned int
+rl78_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
+                           const_tree type ATTRIBUTE_UNUSED)
+{
+  return 16;
+}
+
+/* Supported modifier letters:
+
+   A - address of a MEM
+   S - SADDR form of a real register
+   v - real register corresponding to a virtual register
+   m - minus - negative of CONST_INT value.
+   c - inverse of a conditional (NE vs EQ for example)
+
+   h - bottom HI of an SI
+   H - top HI of an SI
+   q - bottom QI of an HI
+   Q - top QI of an HI
+   e - third QI of an SI (i.e. where the ES register gets values from)
+
+*/
+
+/* Implements the bulk of rl78_print_operand, below.  We do it this
+   way because we need to test for a constant at the top level and
+   insert the '#', but not test for it anywhere else as we recurse
+   down into the operand.  */
+static void
+rl78_print_operand_1 (FILE * file, rtx op, int letter)
+{
+  int need_paren;
+
+  switch (GET_CODE (op))
+    {
+    case MEM:
+      if (letter == 'A')
+       rl78_print_operand_1 (file, XEXP (op, 0), letter);
+      else
+       {
+         if (rl78_far_p (op))
+           fprintf(file, "es:");
+         if (letter == 'H')
+           {
+             op = adjust_address (op, HImode, 2);
+             letter = 0;
+           }
+         if (letter == 'h')
+           {
+             op = adjust_address (op, HImode, 0);
+             letter = 0;
+           }
+         if (letter == 'Q')
+           {
+             op = adjust_address (op, QImode, 1);
+             letter = 0;
+           }
+         if (letter == 'q')
+           {
+             op = adjust_address (op, QImode, 0);
+             letter = 0;
+           }
+         if (letter == 'e')
+           {
+             op = adjust_address (op, QImode, 2);
+             letter = 0;
+           }
+         if (CONSTANT_P (XEXP (op, 0)))
+           {
+             fprintf(file, "!");
+             rl78_print_operand_1 (file, XEXP (op, 0), letter);
+           }
+         else if (GET_CODE (XEXP (op, 0)) == PLUS
+                  && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF)
+           {
+             fprintf(file, "!");
+             rl78_print_operand_1 (file, XEXP (op, 0), letter);
+           }
+         else if (GET_CODE (XEXP (op, 0)) == PLUS
+                  && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
+                  && REGNO (XEXP (XEXP (op, 0), 0)) == 2)
+           {
+             rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 1), 'u');
+             fprintf(file, "[");
+             rl78_print_operand_1 (file, XEXP (XEXP (op, 0), 0), 0);
+             fprintf(file, "]");
+           }
+         else
+           {
+             fprintf(file, "[");
+             rl78_print_operand_1 (file, XEXP (op, 0), letter);
+             fprintf(file, "]");
+           }
+       }
+      break;
+
+    case REG:
+      if (letter == 'Q')
+       fprintf (file, "%s", reg_names [REGNO (op) | 1]);
+      else if (letter == 'H')
+       fprintf (file, "%s", reg_names [REGNO (op) + 2]);
+      else if (letter == 'q')
+       fprintf (file, "%s", reg_names [REGNO (op) & ~1]);
+      else if (letter == 'e')
+       fprintf (file, "%s", reg_names [REGNO (op) + 2]);
+      else if (letter == 'S')
+       fprintf (file, "0x%x", 0xffef8 + REGNO (op));
+      else if (GET_MODE (op) == HImode
+              && ! (REGNO (op) & ~0xfe))
+       {
+         if (letter == 'v')
+           fprintf (file, "%s", word_regnames [REGNO (op) % 8]);
+         else
+           fprintf (file, "%s", word_regnames [REGNO (op)]);
+       }
+      else
+       fprintf (file, "%s", reg_names [REGNO (op)]);
+      break;
+
+    case CONST_INT:
+      if (letter == 'Q')
+       fprintf (file, "%ld", INTVAL (op) >> 8);
+      else if (letter == 'H')
+       fprintf (file, "%ld", INTVAL (op) >> 16);
+      else if (letter == 'q')
+       fprintf (file, "%ld", INTVAL (op) & 0xff);
+      else if (letter == 'h')
+       fprintf (file, "%ld", INTVAL (op) & 0xffff);
+      else if (letter == 'e')
+       fprintf (file, "%ld", (INTVAL (op) >> 16) & 0xff);
+      else if (letter == 'm')
+       fprintf (file, "%ld", - INTVAL (op));
+      else
+       fprintf(file, "%ld", INTVAL (op));
+      break;
+
+    case CONST:
+      rl78_print_operand_1 (file, XEXP (op, 0), letter);
+      break;
+
+    case ZERO_EXTRACT:
+      {
+       int bits = INTVAL (XEXP (op, 1));
+       int ofs = INTVAL (XEXP (op, 2));
+       if (bits == 16 && ofs == 0)
+         fprintf (file, "%%lo16(");
+       else if (bits == 16 && ofs == 16)
+         fprintf (file, "%%hi16(");
+       else if (bits == 8 && ofs == 16)
+         fprintf (file, "%%hi8(");
+       else
+         gcc_unreachable ();
+       rl78_print_operand_1 (file, XEXP (op, 0), 0);
+       fprintf (file, ")");
+      }
+      break;
+
+    case ZERO_EXTEND:
+      if (GET_CODE (XEXP (op, 0)) == REG)
+       fprintf (file, "%s", reg_names [REGNO (XEXP (op, 0))]);
+      else
+       print_rtl (file, op);
+      break;
+
+    case PLUS:
+      need_paren = 0;
+      if (letter == 'H')
+       {
+         fprintf (file, "%%hi16(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'h')
+       {
+         fprintf (file, "%%lo16(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'e')
+       {
+         fprintf (file, "%%hi8(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'q' || letter == 'Q')
+       output_operand_lossage ("q/Q modifiers invalid for symbol references");
+
+      if (GET_CODE (XEXP (op, 0)) == ZERO_EXTEND)
+       {
+         rl78_print_operand_1 (file, XEXP (op, 1), letter);
+         fprintf (file, "+");
+         rl78_print_operand_1 (file, XEXP (op, 0), letter);
+       }
+      else
+       {
+         rl78_print_operand_1 (file, XEXP (op, 0), letter);
+         fprintf (file, "+");
+         rl78_print_operand_1 (file, XEXP (op, 1), letter);
+       }
+      if (need_paren)
+       fprintf (file, ")");
+      break;
+
+    case SYMBOL_REF:
+      need_paren = 0;
+      if (letter == 'H')
+       {
+         fprintf (file, "%%hi16(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'h')
+       {
+         fprintf (file, "%%lo16(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'e')
+       {
+         fprintf (file, "%%hi8(");
+         need_paren = 1;
+         letter = 0;
+       }
+      if (letter == 'q' || letter == 'Q')
+       output_operand_lossage ("q/Q modifiers invalid for symbol references");
+
+      output_addr_const (file, op);
+      if (need_paren)
+       fprintf (file, ")");
+      break;
+
+    case CODE_LABEL:
+    case LABEL_REF:
+      output_asm_label (op);
+      break;
+
+    case LTU:
+      fprintf (file, letter == 'c' ? "nc" : "c");
+      break;
+    case LEU:
+      fprintf (file, letter == 'c' ? "h" : "nh");
+      break;
+    case GEU:
+      fprintf (file, letter == 'c' ? "c" : "nc");
+      break;
+    case GTU:
+      fprintf (file, letter == 'c' ? "nh" : "h");
+      break;
+    case EQ:
+      fprintf (file, letter == 'c' ? "nz" : "z");
+      break;
+    case NE:
+      fprintf (file, letter == 'c' ? "z" : "nz");
+      break;
+
+    default:
+      fprintf (file, "(%s)", GET_RTX_NAME (GET_CODE (op)));
+      break;
+    }
+}
+
+#undef  TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND           rl78_print_operand
+
+static void
+rl78_print_operand (FILE * file, rtx op, int letter)
+{
+  if (CONSTANT_P (op) && letter != 'u')
+    fprintf (file, "#");
+  rl78_print_operand_1 (file, op, letter);
+}
+
+#undef  TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT rl78_trampoline_init
+
+/* Note that the RL78's addressing makes it very difficult to do
+   trampolines on the stack.  So, libgcc has a small pool of
+   trampolines from which one is allocated to this task.  */
+static void
+rl78_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain)
+{
+  rtx mov_addr, thunk_addr;
+  rtx function = XEXP (DECL_RTL (fndecl), 0);
+
+  mov_addr = adjust_address (m_tramp, HImode, 0);
+  thunk_addr = gen_reg_rtx (HImode);
+
+  function = force_reg (HImode, function);
+  static_chain = force_reg (HImode, static_chain);
+
+  emit_insn (gen_trampoline_init (thunk_addr, function, static_chain));
+  emit_move_insn (mov_addr, thunk_addr);
+
+  cfun->machine->trampolines_used = 1;
+}
+
+#undef  TARGET_TRAMPOLINE_ADJUST_ADDRESS
+#define TARGET_TRAMPOLINE_ADJUST_ADDRESS rl78_trampoline_adjust_address
+
+static rtx
+rl78_trampoline_adjust_address (rtx m_tramp)
+{
+  rtx x = gen_rtx_MEM (HImode, m_tramp);
+  return x;
+}
+\f
+/* Expander for cbranchqi4 and cbranchhi4.  RL78 is missing some of
+   the "normal" compares, specifically, it only has unsigned compares,
+   so we must synthesize the missing ones.  */
+void
+rl78_expand_compare (rtx *operands)
+{
+  /* RL78 does not have signed comparisons.  We must modify the
+     operands to be in the unsigned range, and emit an unsigned
+     comparison.  */
+
+  enum machine_mode mode;
+  rtx high_bit;
+  int i;
+  RTX_CODE new_cond;
+
+  switch (GET_CODE (operands[0]))
+    {
+    case GE:
+      new_cond = GEU;
+      break;
+    case LE:
+      new_cond = LEU;
+      break;
+    case GT:
+      new_cond = GTU;
+      break;
+    case LT:
+      new_cond = LTU;
+      break;
+    default:
+      return;
+    }
+
+#if DEBUG0
+  fprintf (stderr, "\033[38;5;129mrl78_expand_compare\n");
+  debug_rtx (operands[0]);
+  fprintf (stderr, "\033[0m");
+#endif
+
+  mode = GET_MODE (operands[1]);
+  if (mode == VOIDmode)
+    mode = GET_MODE (operands[2]);
+  high_bit = GEN_INT (~0 << (GET_MODE_BITSIZE (mode) - 1));
+
+  /* 0: conditional 1,2: operands */
+  for (i = 1; i <= 2; i ++)
+    {
+      rtx r = operands[i];
+
+      if (GET_CODE (r) == CONST_INT)
+       r = GEN_INT (INTVAL (r) ^ INTVAL (high_bit));
+      else
+       {
+         r = gen_rtx_PLUS (mode, operands[i], high_bit);
+         r = copy_to_mode_reg (mode, r);
+       }
+      operands[i] = r;
+    }
+
+  operands[0] = gen_rtx_fmt_ee (new_cond, GET_MODE (operands[0]), operands[1], operands[2]);
+
+#if DEBUG0
+  fprintf (stderr, "\033[38;5;142mrl78_expand_compare\n");
+  debug_rtx (operands[0]);
+  fprintf (stderr, "\033[0m");
+#endif
+}
+
+\f
+
+/* Define this to 1 if you are debugging the peephole optimizers.  */
+#define DEBUG_PEEP 0
+
+/* Predicate used to enable the peephole2 patterns in rl78-virt.md.
+   The default "word" size is a byte so we can effectively use all the
+   registers, but we want to do 16-bit moves whenever possible.  This
+   function determines when such a move is an option.  */
+bool
+rl78_peep_movhi_p (rtx *operands)
+{
+  int i;
+  rtx m, a;
+
+  /* (set (op0) (op1))
+     (set (op2) (op3)) */
+
+#if DEBUG_PEEP
+  fprintf (stderr, "\033[33m");
+  debug_rtx(operands[0]);
+  debug_rtx(operands[1]);
+  debug_rtx(operands[2]);
+  debug_rtx(operands[3]);
+  fprintf (stderr, "\033[0m");
+#endif
+
+  if (rtx_equal_p (operands[0], operands[3]))
+    {
+#if DEBUG_PEEP
+      fprintf (stderr, "no peep: overlapping\n");
+#endif
+      return false;
+    }
+
+  for (i = 0; i < 2; i ++)
+    {
+      if (GET_CODE (operands[i]) != GET_CODE (operands[i+2]))
+       {
+#if DEBUG_PEEP
+         fprintf (stderr, "no peep: different codes\n");
+#endif
+         return false;
+       }
+      if (GET_MODE (operands[i]) != GET_MODE (operands[i+2]))
+       {
+#if DEBUG_PEEP
+         fprintf (stderr, "no peep: different modes\n");
+#endif
+         return false;
+       }
+
+      switch (GET_CODE (operands[i]))
+       {
+       case REG:
+         /*   LSB                      MSB  */
+         if (REGNO (operands[i]) + 1 != REGNO (operands[i+2])
+             || GET_MODE (operands[i]) != QImode)
+           {
+#if DEBUG_PEEP
+             fprintf (stderr, "no peep: wrong regnos %d %d %d\n",
+                      REGNO (operands[i]), REGNO (operands[i+2]),
+                      i);
+#endif
+             return false;
+           }
+         if (! rl78_hard_regno_mode_ok (REGNO (operands[i]), HImode))
+           {
+#if DEBUG_PEEP
+             fprintf (stderr, "no peep: reg %d not HI\n", REGNO (operands[i]));
+#endif
+             return false;
+           }
+         break;
+
+       case CONST_INT:
+         break;
+
+       case MEM:
+         if (GET_MODE (operands[i]) != QImode)
+           return false;
+         if (MEM_ALIGN (operands[i]) < 16)
+           return false;
+         a = XEXP (operands[i], 0);
+         if (GET_CODE (a) == CONST)
+           a = XEXP (a, 0);
+         if (GET_CODE (a) == PLUS)
+           a = XEXP (a, 1);
+         if (GET_CODE (a) == CONST_INT
+             && INTVAL (a) & 1)
+           {
+#if DEBUG_PEEP
+             fprintf (stderr, "no peep: misaligned mem %d\n", i);
+             debug_rtx (operands[i]);
+#endif
+             return false;
+           }
+         m = adjust_address (operands[i], QImode, 1);
+         if (! rtx_equal_p (m, operands[i+2]))
+           {
+#if DEBUG_PEEP
+             fprintf (stderr, "no peep: wrong mem %d\n", i);
+             debug_rtx(m);
+             debug_rtx (operands[i+2]);
+#endif
+             return false;
+           }
+         break;
+
+       default:
+#if DEBUG_PEEP
+         fprintf (stderr, "no peep: wrong rtx %d\n", i);
+#endif
+         return false;
+       }
+    }
+#if DEBUG_PEEP
+  fprintf (stderr, "\033[32mpeep!\033[0m\n");
+#endif
+  return true;
+}
+
+/* Likewise, when a peephole is activated, this function helps compute
+   the new operands.  */
+void
+rl78_setup_peep_movhi (rtx *operands)
+{
+  int i;
+
+  for (i = 0; i < 2; i ++)
+    {
+      switch (GET_CODE (operands[i]))
+       {
+       case REG:
+         operands[i+4] = gen_rtx_REG (HImode, REGNO (operands[i]));
+         break;
+
+       case CONST_INT:
+         operands[i+4] = GEN_INT ((INTVAL (operands[i]) & 0xff) + ((char)INTVAL (operands[i+2])) * 256);
+         break;
+
+       case MEM:
+         operands[i+4] = adjust_address (operands[i], HImode, 0);
+         break;
+
+       default:
+         break;
+       }
+    }
+}
+\f
+/*
+       How Devirtualization works in the RL78 GCC port
+
+Background
+
+The RL78 is an 8-bit port with some 16-bit operations.  It has 32
+bytes of register space, in four banks, memory-mapped.  One bank is
+the "selected" bank and holds the registers used for primary
+operations.  Since the registers are memory mapped, often you can
+still refer to the unselected banks via memory accesses.
+
+Virtual Registers
+
+The GCC port uses bank 0 as the "selected" registers (A, X, BC, etc)
+and refers to the other banks via their memory addresses, although
+they're treated as regular registers internally.  These "virtual"
+registers are R8 through R23 (bank3 is reserved for asm-based
+interrupt handlers).
+
+There are four machine description files:
+
+rl78.md        - common register-independent patterns and definitions
+rl78-expand.md - expanders
+rl78-virt.md   - patterns that match BEFORE devirtualization
+rl78-real.md   - patterns that match AFTER devirtualization
+
+At least through register allocation and reload, gcc is told that it
+can do pretty much anything - but may only use the virtual registers.
+GCC cannot properly create the varying addressing modes that the RL78
+supports in an efficient way.
+
+Sometime after reload, the RL78 backend "devirtualizes" the RTL.  It
+uses the "valloc" attribute in rl78-virt.md for determining the rules
+by which it will replace virtual registers with real registers (or
+not) and how to make up addressing modes.  For example, insns tagged
+with "ro1" have a single read-only parameter, which may need to be
+moved from memory/constant/vreg to a suitable real register.  As part
+of devirtualization, a flag is toggled, disabling the rl78-virt.md
+patterns and enabling the rl78-real.md patterns.  The new patterns'
+constraints are used to determine the real registers used.  NOTE:
+patterns in rl78-virt.md essentially ignore the constrains and rely on
+predicates, where the rl78-real.md ones essentially ignore the
+predicates and rely on the constraints.
+
+The devirtualization pass is scheduled via the pass manager (despite
+being called "rl78_reorg") so it can be scheduled prior to var-track
+(the idea is to let gdb know about the new registers).  Ideally, it
+would be scheduled right after pro/epilogue generation, so the
+post-reload optimizers could operate on the real registers, but when I
+tried that there were some issues building the target libraries.
+
+During devirtualization, a simple register move optimizer is run.  It
+would be better to run a full CSE/propogation pass on it through, or
+re-run regmove, but that has not yet been attempted.
+
+ */
+#define DEBUG_ALLOC 0
+
+/* Rescans an insn to see if it's recognized again.  This is done
+   carefully to ensure that all the constraint information is accurate
+   for the newly matched insn.  */
+static bool
+insn_ok_now (rtx insn)
+{
+  INSN_CODE (insn) = -1;
+  if (recog (PATTERN (insn), insn, 0) > -1)
+    {
+      extract_insn (insn);
+      if (constrain_operands (1))
+       {
+#if DEBUG_ALLOC
+         fprintf (stderr, "\033[32m");
+         debug_rtx (insn);
+         fprintf (stderr, "\033[0m");
+#endif
+         return true;
+       }
+    }
+  else
+    {
+      fprintf (stderr, "\033[41;30m Unrecognized insn \033[0m\n");
+      debug_rtx (insn);
+      gcc_unreachable ();
+    }
+#if DEBUG_ALLOC
+  fprintf (stderr, "\033[31m");
+  debug_rtx (insn);
+  fprintf (stderr, "\033[0m");
+#endif
+  return false;
+}
+
+#if DEBUG_ALLOC
+#define WORKED fprintf (stderr, "\033[48;5;22m Worked at line %d \033[0m\n", __LINE__)
+#define FAILEDSOFAR fprintf (stderr, "\033[48;5;52m FAILED at line %d \033[0m\n", __LINE__)
+#define FAILED fprintf (stderr, "\033[48;5;52m FAILED at line %d \033[0m\n", __LINE__), gcc_unreachable()
+#define MAYBE_OK(insn) if (insn_ok_now (insn)) { WORKED; return; } else { FAILEDSOFAR; }
+#else
+#define WORKED
+#define FAILEDSOFAR
+#define FAILED gcc_unreachable ()
+#define MAYBE_OK(insn) if (insn_ok_now (insn)) return;
+#endif
+
+/* Registers into which we move the contents of virtual registers.  */
+#define X gen_rtx_REG (QImode, 0)
+#define A gen_rtx_REG (QImode, 1)
+#define C gen_rtx_REG (QImode, 2)
+#define B gen_rtx_REG (QImode, 3)
+#define E gen_rtx_REG (QImode, 4)
+#define D gen_rtx_REG (QImode, 5)
+#define L gen_rtx_REG (QImode, 6)
+#define H gen_rtx_REG (QImode, 7)
+
+#define AX gen_rtx_REG (HImode, 0)
+#define BC gen_rtx_REG (HImode, 2)
+#define DE gen_rtx_REG (HImode, 4)
+#define HL gen_rtx_REG (HImode, 6)
+
+#define OP(x) (*recog_data.operand_loc[x])
+
+/* Returns TRUE if R is a virtual register.  */
+static bool
+is_virtual_register (rtx r)
+{
+  return (GET_CODE (r) == REG
+         && REGNO (r) >= 8
+         && REGNO (r) < 24);
+}
+
+/* In all these alloc routines, we expect the following: the insn
+   pattern is unshared, the insn was previously recognized and failed
+   due to predicates or constraints, and the operand data is in
+   recog_data.  */
+
+static int virt_insn_was_frame;
+
+/* Hook for all insns we emit.  Re-mark them as FRAME_RELATED if
+   needed.  */
+static rtx
+EM2 (int line ATTRIBUTE_UNUSED, rtx r)
+{
+#if DEBUG_ALLOC
+  fprintf (stderr, "\033[36m%d: ", line);
+  debug_rtx(r);
+  fprintf (stderr, "\033[0m");
+#endif
+  /*SCHED_GROUP_P (r) = 1;*/
+  if (virt_insn_was_frame)
+    RTX_FRAME_RELATED_P (r) = 1;
+  return r;
+}
+
+#define EM(x) EM2 (__LINE__, x)
+
+/* Return a suitable RTX for the low half of a __far address.  */
+static rtx
+rl78_lo16 (rtx addr)
+{
+  if (GET_CODE (addr) == SYMBOL_REF
+      || GET_CODE (addr) == CONST)
+    {
+      rtx r = gen_rtx_ZERO_EXTRACT (HImode, addr, GEN_INT (16), GEN_INT (0));
+      r = gen_rtx_CONST (HImode, r);
+      return r;
+    }
+  return rl78_subreg (HImode, addr, SImode, 0);
+}
+
+/* Return a suitable RTX for the high half's lower byte of a __far address.  */
+static rtx
+rl78_hi8 (rtx addr)
+{
+  if (GET_CODE (addr) == SYMBOL_REF
+      || GET_CODE (addr) == CONST)
+    {
+      rtx r = gen_rtx_ZERO_EXTRACT (QImode, addr, GEN_INT (8), GEN_INT (16));
+      r = gen_rtx_CONST (QImode, r);
+      return r;
+    }
+  return rl78_subreg (QImode, addr, SImode, 2);
+}
+
+/* Copy any register values into real registers and return an RTX for
+   the same memory, now addressed by real registers.  Any needed insns
+   are emitted before BEFORE.  */
+static rtx
+transcode_memory_rtx (rtx m, rtx newbase, rtx before)
+{
+  rtx base, index, addendr;
+  int addend = 0;
+
+  if (GET_CODE (m) != MEM)
+    return m;
+
+  if (GET_MODE (XEXP (m, 0)) == SImode)
+    {
+      rtx seg = rl78_hi8 (XEXP (m, 0));
+#if DEBUG_ALLOC
+      fprintf (stderr, "setting ES:\n");
+      debug_rtx(seg);
+#endif
+      emit_insn_before (EM(gen_movqi (A, seg)), before);
+      emit_insn_before (EM(gen_movqi_es (A)), before);
+      m = change_address (m, GET_MODE (m), rl78_lo16 (XEXP (m, 0)));
+    }
+
+  characterize_address (XEXP (m, 0), &base, &index, &addendr);
+  gcc_assert (index == NULL_RTX);
+
+#if DEBUG_ALLOC
+  fprintf (stderr, "\033[33m"); debug_rtx(m); fprintf (stderr, "\033[0m");
+  debug_rtx (base);
+#endif
+  if (base == NULL_RTX)
+    return m;
+
+  if (addendr && GET_CODE (addendr) == CONST_INT)
+    addend = INTVAL (addendr);
+
+  if (REGNO (base) == SP_REG)
+    {
+      if (addend >= 0 && addend  <= 255)
+       return m;
+    }
+
+  /* BASE should be a virtual register.  We copy it to NEWBASE.  If
+     the addend is out of range for DE/HL, we use AX to compute the full
+     address.  */
+
+  if (addend < 0
+      || (addend > 255 && REGNO (newbase) != 2)
+      || (addendr && GET_CODE (addendr) != CONST_INT))
+    {
+      /* mov ax, vreg
+        add ax, #imm
+        mov hl, ax     */
+      EM (emit_insn_before (gen_movhi (AX, base), before));
+      EM (emit_insn_before (gen_addhi3 (AX, AX, addendr), before));
+      EM (emit_insn_before (gen_movhi (newbase, AX), before));
+      base = newbase;
+      addend = 0;
+    }
+  else
+    {
+      EM (emit_insn_before (gen_movhi (newbase, base), before));
+      base = newbase;
+    }
+
+  if (addend)
+    base = gen_rtx_PLUS (HImode, base, GEN_INT (addend));
+
+#if DEBUG_ALLOC
+  fprintf (stderr, "\033[33m");
+  debug_rtx (m);
+#endif
+  m = change_address (m, GET_MODE (m), base);
+#if DEBUG_ALLOC
+  debug_rtx (m);
+  fprintf (stderr, "\033[0m");
+#endif
+  return m;
+}
+
+/* Copy SRC to accumulator (A or AX), placing any generated insns
+   before BEFORE.  Returns accumulator RTX.  */
+
+static rtx
+move_to_acc (int opno, rtx before)
+{
+  rtx src = OP(opno);
+  enum machine_mode mode = GET_MODE (src);
+
+  if (GET_CODE (src) == REG
+      && REGNO (src) < 2)
+    return src;
+
+  if (mode == VOIDmode)
+    mode = recog_data.operand_mode[opno];
+
+  if (mode == QImode)
+    {
+      EM (emit_insn_before (gen_movqi (A, src), before));
+      return A;
+    }
+  else
+    {
+      EM (emit_insn_before (gen_movhi (AX, src), before));
+      return AX;
+    }
+}
+
+/* Copy accumulator (A or AX) to DEST, placing any generated insns
+   after AFTER.  Returns accumulator RTX.  */
+
+static rtx
+move_from_acc (rtx dest, rtx after)
+{
+  enum machine_mode mode = GET_MODE (dest);
+
+  if (REG_P (dest) && REGNO (dest) < 2)
+    return dest;
+
+  if (mode == QImode)
+    {
+      EM (emit_insn_after (gen_movqi (dest, A), after));
+      return A;
+    }
+  else
+    {
+      EM (emit_insn_after (gen_movhi (dest, AX), after));
+      return AX;
+    }
+}
+
+/* Copy accumulator (A or AX) to REGNO, placing any generated insns
+   before BEFORE.  Returns reg RTX.  */
+
+static rtx
+move_acc_to_reg (rtx acc, int regno, rtx before)
+{
+  enum machine_mode mode = GET_MODE (acc);
+  rtx reg;
+
+  reg = gen_rtx_REG (mode, regno);
+
+  if (mode == QImode)
+    {
+      EM (emit_insn_before (gen_movqi (reg, A), before));
+      return reg;
+    }
+  else
+    {
+      EM (emit_insn_before (gen_movhi (reg, AX), before));
+      return reg;
+    }
+}
+
+/* Copy SRC to X, placing any generated insns before BEFORE.
+   Returns X RTX.  */
+
+static rtx
+move_to_x (int opno, rtx before)
+{
+  rtx src = OP(opno);
+  enum machine_mode mode = GET_MODE (src);
+  rtx reg;
+
+  if (mode == VOIDmode)
+    mode = recog_data.operand_mode[opno];
+  reg = (mode == QImode) ? X : AX;
+
+  if (mode == QImode || ! is_virtual_register (OP (opno)))
+    {
+      OP(opno) = move_to_acc (opno, before);
+      OP(opno) = move_acc_to_reg (OP(opno), X_REG, before);
+      return reg;
+    }
+
+  if (mode == QImode)
+    EM (emit_insn_before (gen_movqi (reg, src), before));
+  else
+    EM (emit_insn_before (gen_movhi (reg, src), before));
+
+  return reg;
+}
+
+/* Copy OP(opno) to H or HL, placing any generated insns before BEFORE.
+   Returns H/HL RTX.  */
+
+static rtx
+move_to_hl (int opno, rtx before)
+{
+  rtx src = OP (opno);
+  enum machine_mode mode = GET_MODE (src);
+  rtx reg;
+
+  if (mode == VOIDmode)
+    mode = recog_data.operand_mode[opno];
+  reg = (mode == QImode) ? L : HL;
+
+  if (mode == QImode || ! is_virtual_register (OP (opno)))
+    {
+      OP (opno) = move_to_acc (opno, before);
+      OP (opno) = move_acc_to_reg (OP (opno), L_REG, before);
+      return reg;
+    }
+
+  if (mode == QImode)
+    EM (emit_insn_before (gen_movqi (reg, src), before));
+  else
+    EM (emit_insn_before (gen_movhi (reg, src), before));
+
+  return reg;
+}
+
+/* Copy OP(opno) to E or DE, placing any generated insns before BEFORE.
+   Returns E/DE RTX.  */
+
+static rtx
+move_to_de (int opno, rtx before)
+{
+  rtx src = OP (opno);
+  enum machine_mode mode = GET_MODE (src);
+  rtx reg;
+
+  if (mode == VOIDmode)
+    mode = recog_data.operand_mode[opno];
+
+  reg = (mode == QImode) ? E : DE;
+
+  if (mode == QImode || ! is_virtual_register (OP (opno)))
+    {
+      OP (opno) = move_to_acc (opno, before);
+      OP (opno) = move_acc_to_reg (OP (opno), E_REG, before);
+    }
+  else
+    {
+      rtx move = mode == QImode ? gen_movqi (reg, src) : gen_movhi (reg, src);
+
+      EM (emit_insn_before (move, before));
+    }
+
+  return reg;
+}
+
+/* Devirtualize an insn of the form (SET (op) (unop (op))).  */
+static void
+rl78_alloc_physical_registers_op1 (rtx insn)
+{
+  /* op[0] = func op[1] */
+
+  /* We first try using A as the destination, then copying it
+     back.  */
+  if (rtx_equal_p (OP(0), OP(1)))
+    {
+      OP(0) =
+      OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+    }
+  else
+    {
+      OP(0) = transcode_memory_rtx (OP(0), BC, insn);
+      OP(1) = transcode_memory_rtx (OP(1), HL, insn);
+    }
+
+  MAYBE_OK (insn);
+
+  OP(0) = move_from_acc (OP(0), insn);
+
+  MAYBE_OK (insn);
+
+  /* Try copying the src to acc first, then.  This is for, for
+     example, ZERO_EXTEND or NOT.  */
+  OP(1) = move_to_acc (1, insn);
+
+  MAYBE_OK (insn);
+
+  FAILED;
+}
+
+/* Devirtualize an insn of the form (SET (op) (unop (op) (op))).  */
+static void
+rl78_alloc_physical_registers_op2 (rtx insn)
+{
+  /* op[0] = op[1] func op[2] */
+  rtx prev = prev_nonnote_nondebug_insn (insn);
+  rtx first;
+  bool hl_used;
+
+  if (rtx_equal_p (OP(0), OP(1)))
+    {
+      OP(0) =
+      OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+      prev = next_nonnote_nondebug_insn (prev);
+      OP(2) = transcode_memory_rtx (OP(2), HL, insn);
+      prev = prev_nonnote_nondebug_insn (prev);
+    }
+  else if (rtx_equal_p (OP(0), OP(2)))
+    {
+      OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+      prev = next_nonnote_nondebug_insn (prev);
+      OP(0) =
+      OP(2) = transcode_memory_rtx (OP(2), HL, insn);
+      prev = prev_nonnote_nondebug_insn (prev);
+    }
+  else
+    {
+      OP(0) = transcode_memory_rtx (OP(0), BC, insn);
+      OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+      prev = next_nonnote_nondebug_insn (prev);
+      OP(2) = transcode_memory_rtx (OP(2), HL, insn);
+    }
+
+  MAYBE_OK (insn);
+
+  prev = prev_nonnote_nondebug_insn (insn);
+  if (recog_data.constraints[1][0] == '%'
+      && is_virtual_register (OP (1))
+      && ! is_virtual_register (OP (2))
+      && ! CONSTANT_P (OP (2)))
+    {
+      rtx tmp = OP (1);
+      OP (1) = OP (2);
+      OP (2) = tmp;
+    }
+
+  /* Make a note of wether (H)L is being used.  It matters
+     because if OP(2) alsoneeds reloading, then we must take
+     care not to corrupt HL.  */
+  hl_used = reg_mentioned_p (L, OP (0)) || reg_mentioned_p (L, OP (1));
+
+  OP(0) = move_from_acc (OP (0), insn);
+  OP(1) = move_to_acc (1, insn);
+
+  MAYBE_OK (insn);
+
+  /* We have to copy op2 to HL, but that involves AX, which
+     already has a live value.  Emit it before those insns.  */
+
+  if (prev)
+    first = next_nonnote_nondebug_insn (prev);
+  else
+    for (first = insn; prev_nonnote_nondebug_insn (first); first = prev_nonnote_nondebug_insn (first))
+      ;
+
+  OP (2) = hl_used ? move_to_de (2, first) : move_to_hl (2, first);
+  
+  MAYBE_OK (insn);
+  
+  FAILED;
+}
+
+/* Devirtualize an insn of the form (SET () (unop (op))).  */
+
+static void
+rl78_alloc_physical_registers_ro1 (rtx insn)
+{
+  /* (void) op[0] */
+  OP(0) = transcode_memory_rtx (OP(0), BC, insn);
+
+  MAYBE_OK (insn);
+
+  OP(0) = move_to_acc (0, insn);
+
+  MAYBE_OK (insn);
+
+  FAILED;
+}
+
+/* Devirtualize a compare insn.  */
+static void
+rl78_alloc_physical_registers_cmp (rtx insn)
+{
+  /* op[1] cmp_op[0] op[2] */
+  rtx prev = prev_nonnote_nondebug_insn (insn);
+  rtx first;
+
+  OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+  OP(2) = transcode_memory_rtx (OP(2), HL, insn);
+
+  MAYBE_OK (insn);
+
+  OP(1) = move_to_acc (1, insn);
+
+  MAYBE_OK (insn);
+
+  /* We have to copy op2 to HL, but that involves the acc, which
+     already has a live value.  Emit it before those insns.  */
+
+  if (prev)
+    first = next_nonnote_nondebug_insn (prev);
+  else
+    for (first = insn; prev_nonnote_nondebug_insn (first); first = prev_nonnote_nondebug_insn (first))
+      ;
+  OP(2) = move_to_hl (2, first);
+
+  MAYBE_OK (insn);
+
+  FAILED;
+}
+
+/* Like op2, but AX = A op X.  */
+static void
+rl78_alloc_physical_registers_umul (rtx insn)
+{
+  /* op[0] = op[1] func op[2] */
+  rtx prev = prev_nonnote_nondebug_insn (insn);
+  rtx first;
+
+  OP(0) = transcode_memory_rtx (OP(0), BC, insn);
+  OP(1) = transcode_memory_rtx (OP(1), DE, insn);
+  OP(2) = transcode_memory_rtx (OP(2), HL, insn);
+
+  MAYBE_OK (insn);
+
+  if (recog_data.constraints[1][0] == '%'
+      && is_virtual_register (OP(1))
+      && !is_virtual_register (OP(2))
+      && !CONSTANT_P (OP(2)))
+    {
+      rtx tmp = OP(1);
+      OP(1) = OP(2);
+      OP(2) = tmp;
+    }
+
+  OP(0) = move_from_acc (OP(0), insn);
+  OP(1) = move_to_acc (1, insn);
+
+  MAYBE_OK (insn);
+
+  /* We have to copy op2 to X, but that involves the acc, which
+     already has a live value.  Emit it before those insns.  */
+
+  if (prev)
+    first = next_nonnote_nondebug_insn (prev);
+  else
+    for (first = insn; prev_nonnote_nondebug_insn (first); first = prev_nonnote_nondebug_insn (first))
+      ;
+  OP(2) = move_to_x (2, first);
+
+  MAYBE_OK (insn);
+
+  FAILED;
+}
+
+/* Scan all insns and devirtualize them.  */
+static void
+rl78_alloc_physical_registers (void)
+{
+  /* During most of the compile, gcc is dealing with virtual
+     registers.  At this point, we need to assign physical registers
+     to the vitual ones, and copy in/out as needed.  */
+
+  rtx insn, curr;
+  enum attr_valloc valloc_method;
+
+  for (insn = get_insns (); insn; insn = curr)
+    {
+      int i;
+
+      curr = next_nonnote_nondebug_insn (insn);
+
+      if (INSN_P (insn)
+         && (GET_CODE (PATTERN (insn)) == SET
+             || GET_CODE (PATTERN (insn)) == CALL)
+         && INSN_CODE (insn) == -1)
+       {
+         if (GET_CODE (SET_SRC (PATTERN (insn))) == ASM_OPERANDS)
+           continue;
+         i = recog (PATTERN (insn), insn, 0);
+         if (i == -1)
+           {
+             debug_rtx (insn);
+             gcc_unreachable ();
+           }
+         INSN_CODE (insn) = i;
+       }
+    }
+
+  cfun->machine->virt_insns_ok = 0;
+  cfun->machine->real_insns_ok = 1;
+
+  for (insn = get_insns (); insn; insn = curr)
+    {
+      curr = insn ? next_nonnote_nondebug_insn (insn) : NULL;
+
+      if (!INSN_P (insn))
+       continue;
+      if (GET_CODE (PATTERN (insn)) != SET
+         && GET_CODE (PATTERN (insn)) != CALL)
+         continue;
+
+      if (GET_CODE (SET_SRC (PATTERN (insn))) == ASM_OPERANDS)
+       continue;
+
+      valloc_method = get_attr_valloc (insn);
+
+      PATTERN (insn)= copy_rtx_if_shared (PATTERN (insn));
+
+      if (insn_ok_now (insn))
+       continue;
+
+      INSN_CODE (insn) = -1;
+
+      if (RTX_FRAME_RELATED_P (insn))
+       virt_insn_was_frame = 1;
+      else
+       virt_insn_was_frame = 0;
+
+      switch (valloc_method)
+       {
+       case VALLOC_OP1:
+         rl78_alloc_physical_registers_op1 (insn);
+         break;
+       case VALLOC_OP2:
+         rl78_alloc_physical_registers_op2 (insn);
+         break;
+       case VALLOC_RO1:
+         rl78_alloc_physical_registers_ro1 (insn);
+         break;
+       case VALLOC_CMP:
+         rl78_alloc_physical_registers_cmp (insn);
+         break;
+       case VALLOC_UMUL:
+         rl78_alloc_physical_registers_umul (insn);
+         break;
+       case VALLOC_MACAX:
+         /* Macro that clobbers AX */
+         break;
+       }
+    }
+#if DEBUG_ALLOC
+  fprintf (stderr, "\033[0m");
+#endif
+}
+
+/* Add REG_DEAD notes using DEAD[reg] for rtx S which is part of INSN.
+   This function scans for uses of registers; the last use (i.e. first
+   encounter when scanning backwards) triggers a REG_DEAD note if the
+   reg was previously in DEAD[].  */
+static void
+rl78_note_reg_uses (char *dead, rtx s, rtx insn)
+{
+  const char *fmt;
+  int i, r;
+  enum rtx_code code;
+
+  if (!s)
+    return;
+
+  code = GET_CODE (s);
+
+  switch (code)
+    {
+      /* Compare registers by number.  */
+    case REG:
+      r = REGNO (s);
+      if (dump_file)
+       {
+         fprintf (dump_file, "note use reg %d size %d on insn %d\n",
+                  r, GET_MODE_SIZE (GET_MODE (s)), INSN_UID (insn));
+         print_rtl_single (dump_file, s);
+       }
+      if (dead [r])
+       add_reg_note (insn, REG_DEAD, gen_rtx_REG (GET_MODE (s), r));
+      for (i = 0; i < GET_MODE_SIZE (GET_MODE (s)); i ++)
+       dead [r + i] = 0;
+      return;
+
+      /* These codes have no constituent expressions
+        and are unique.  */
+    case SCRATCH:
+    case CC0:
+    case PC:
+      return;
+
+    case CONST_INT:
+    case CONST_VECTOR:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
+      /* These are kept unique for a given value.  */
+      return;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         int j;
+         for (j = XVECLEN (s, i) - 1; j >= 0; j--)
+           rl78_note_reg_uses (dead, XVECEXP (s, i, j), insn);
+       }
+      else if (fmt[i] == 'e')
+       rl78_note_reg_uses (dead, XEXP (s, i), insn);
+    }
+}
+
+/* Like the previous function, but scan for SETs instead.  */
+static void
+rl78_note_reg_set (char *dead, rtx d, rtx insn)
+{
+  int r, i;
+
+  if (GET_CODE (d) != REG)
+    return;
+
+  r = REGNO (d);
+  if (dead [r])
+    add_reg_note (insn, REG_UNUSED, gen_rtx_REG (GET_MODE (d), r));
+  if (dump_file)
+    fprintf (dump_file, "note set reg %d size %d\n", r, GET_MODE_SIZE (GET_MODE (d)));
+  for (i = 0; i < GET_MODE_SIZE (GET_MODE (d)); i ++)
+    dead [r + i] = 1;
+}
+
+/* This is a rather crude register death pass.  Death status is reset
+   at every jump or call insn.  */
+static void
+rl78_calculate_death_notes (void)
+{
+  char dead[FIRST_PSEUDO_REGISTER];
+  rtx insn, p, s, d;
+  int i;
+
+  memset (dead, 0, sizeof (dead));
+
+  for (insn = get_last_insn ();
+       insn;
+       insn = prev_nonnote_nondebug_insn (insn))
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, "\n--------------------------------------------------");
+         fprintf (dump_file, "\nDead:");
+         for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++)
+           if (dead[i])
+             fprintf(dump_file, " %s", reg_names[i]);
+         fprintf (dump_file, "\n");
+         print_rtl_single (dump_file, insn);
+       }
+
+      switch (GET_CODE (insn))
+       {
+       case INSN:
+         p = PATTERN (insn);
+         switch (GET_CODE (p))
+           {
+           case SET:
+             s = SET_SRC (p);
+             d = SET_DEST (p);
+             rl78_note_reg_set (dead, d, insn);
+             rl78_note_reg_uses (dead, s, insn);
+             break;
+
+           case USE:
+             rl78_note_reg_uses (dead, p, insn);
+             break;
+
+           default:
+             break;
+           }
+         break;
+
+       case JUMP_INSN:
+         if (INSN_CODE (insn) == CODE_FOR_return)
+           {
+             memset (dead, 1, sizeof (dead));
+             /* We expect a USE just prior to this, which will mark
+                the actual return registers.  The USE will have a
+                death note, but we aren't going to be modifying it
+                after this pass.  */
+             break;
+           }
+       case CALL_INSN:
+         memset (dead, 0, sizeof (dead));
+         break;
+
+       default:
+         break;
+       }
+      if (dump_file)
+       print_rtl_single (dump_file, insn);
+    }
+}
+
+/* Helper function to reset the origins in RP and the age in AGE for
+   all registers.  */
+static void
+reset_origins (int *rp, int *age)
+{
+  int i;
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      rp[i] = i;
+      age[i] = 0;
+    }
+}
+
+/* The idea behind this optimization is to look for cases where we
+   move data from A to B to C, and instead move from A to B, and A to
+   C.  If B is a virtual register or memory, this is a big win on its
+   own.  If B turns out to be unneeded after this, it's a bigger win.
+   For each register, we try to determine where it's value originally
+   came from, if it's propogated purely through moves (and not
+   computes).  The ORIGINS[] array has the regno for the "origin" of
+   the value in the [regno] it's indexed by.  */
+static void
+rl78_propogate_register_origins (void)
+{
+  int origins[FIRST_PSEUDO_REGISTER];
+  int age[FIRST_PSEUDO_REGISTER];
+  int i;
+  rtx insn, ninsn = NULL_RTX;
+  rtx pat;
+
+  reset_origins (origins, age);
+
+  for (insn = get_insns (); insn; insn = ninsn)
+    {
+      ninsn = next_nonnote_nondebug_insn (insn);
+
+      if (dump_file)
+       {
+         fprintf (dump_file, "\n");
+         fprintf (dump_file, "Origins:");
+         for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++)
+           if (origins[i] != i)
+             fprintf (dump_file, " r%d=r%d", i, origins[i]);
+         fprintf (dump_file, "\n");
+         print_rtl_single (dump_file, insn);
+       }
+
+      switch (GET_CODE (insn))
+       {
+       case CODE_LABEL:
+       case BARRIER:
+       case CALL_INSN:
+       case JUMP_INSN:
+         reset_origins (origins, age);
+         break;
+
+       default:
+         break;
+
+       case INSN:
+         pat = PATTERN (insn);
+
+         if (GET_CODE (pat) == PARALLEL)
+           {
+             rtx clobber = XVECEXP (pat, 0, 1);
+             pat = XVECEXP (pat, 0, 0);
+             if (GET_CODE (clobber) == CLOBBER)
+               {
+                 int cr = REGNO (XEXP (clobber, 0));
+                 int mb = GET_MODE_SIZE (GET_MODE (XEXP (clobber, 0)));
+                 if (dump_file)
+                   fprintf (dump_file, "reset origins of %d regs at %d\n", mb, cr);
+                 for (i = 0; i < mb; i++)
+                   {
+                     origins[cr + i] = cr + i;
+                     age[cr + i] = 0;
+                   }
+               }
+             else
+               break;
+           }
+
+         if (GET_CODE (pat) == SET)
+           {
+             rtx src = SET_SRC (pat);
+             rtx dest = SET_DEST (pat);
+             int mb = GET_MODE_SIZE (GET_MODE (dest));
+
+             if (GET_CODE (dest) == REG)
+               {
+                 int dr = REGNO (dest);
+
+                 if (GET_CODE (src) == REG)
+                   {
+                     int sr = REGNO (src);
+                     int same = 1;
+                     int best_age, best_reg;
+
+                     /* See if the copy is not needed.  */
+                     for (i = 0; i < mb; i ++)
+                       if (origins[dr + i] != origins[sr + i])
+                         same = 0;
+                     if (same)
+                       {
+                         if (dump_file)
+                           fprintf (dump_file, "deleting because dest already has correct value\n");
+                         delete_insn (insn);
+                         break;
+                       }
+
+                     if (dr < 8 || sr >= 8)
+                       {
+                         int ar;
+
+                         best_age = -1;
+                         best_reg = -1;
+                         /* See if the copy can be made from another
+                            bank 0 register instead, instead of the
+                            virtual src register.  */
+                         for (ar = 0; ar < 8; ar += mb)
+                           {
+                             same = 1;
+                             for (i = 0; i < mb; i ++)
+                               if (origins[ar + i] != origins[sr + i])
+                                 same = 0;
+
+                             /* The chip has some reg-reg move limitations.  */
+                             if (mb == 1 && dr > 3)
+                               same = 0;
+
+                             if (same)
+                               {
+                                 if (best_age == -1 || best_age > age[sr + i])
+                                   {
+                                     best_age = age[sr + i];
+                                     best_reg = sr;
+                                   }
+                               }
+                           }
+
+                         if (best_reg != -1)
+                           {
+                             /* FIXME: copy debug info too.  */
+                             SET_SRC (pat) = gen_rtx_REG (GET_MODE (src), best_reg);
+                             sr = best_reg;
+                           }
+                       }
+
+                     for (i = 0; i < mb; i++)
+                       {
+                         origins[dr + i] = origins[sr + i];
+                         age[dr + i] = age[sr + i] + 1;
+                       }
+                   }
+                 else
+                   {
+                     /* The destination is computed, its origin is itself.  */
+                     if (dump_file)
+                       fprintf (dump_file, "resetting origin of r%d for %d byte%s\n",
+                                dr, mb, mb == 1 ? "" : "s");
+                     for (i = 0; i < mb; i ++)
+                       {
+                         origins[dr + i] = dr + i;
+                         age[dr + i] = 0;
+                       }
+                   }
+
+                 /* Any registers marked with that reg as an origin are reset.  */
+                 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+                   if (origins[i] >= dr && origins[i] < dr + mb)
+                     {
+                       origins[i] = i;
+                       age[i] = 0;
+                     }
+               }
+
+             /* Special case - our ADDSI3 macro uses AX */
+             if (get_attr_valloc (insn) == VALLOC_MACAX)
+               {
+                 if (dump_file)
+                   fprintf (dump_file, "Resetting origin of AX for macro.\n");
+                 for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+                   if (i <= 1 || origins[i] <= 1)
+                     {
+                       origins[i] = i;
+                       age[i] = 0;
+                     }
+               }
+
+             if (GET_CODE (src) == ASHIFT
+                 || GET_CODE (src) == ASHIFTRT
+                 || GET_CODE (src) == LSHIFTRT)
+               {
+                 rtx count = XEXP (src, 1);
+                 if (GET_CODE (count) == REG)
+                   {
+                     /* Special case - our pattern clobbers the count register.  */
+                     int r = REGNO (count);
+                     if (dump_file)
+                       fprintf (dump_file, "Resetting origin of r%d for shift.\n", r);
+                     for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+                       if (i == r || origins[i] == r)
+                         {
+                           origins[i] = i;
+                           age[i] = 0;
+                         }
+                   }
+               }
+           }
+       }
+    }
+}
+
+/* Remove any SETs where the destination is unneeded.  */
+static void
+rl78_remove_unused_sets (void)
+{
+  rtx insn, ninsn = NULL_RTX;
+  rtx dest;
+
+  for (insn = get_insns (); insn; insn = ninsn)
+    {
+      ninsn = next_nonnote_nondebug_insn (insn);
+
+      if ((insn = single_set (insn)) == NULL_RTX)
+       continue;
+
+      dest = SET_DEST (insn);
+
+      if (REGNO (dest) > 23)
+       continue;
+
+      if (find_regno_note (insn, REG_UNUSED, REGNO (dest)))
+       delete_insn (insn);
+    }
+}
+
+#undef  xTARGET_MACHINE_DEPENDENT_REORG
+#define xTARGET_MACHINE_DEPENDENT_REORG  rl78_reorg
+
+/* This is the top of the devritualization pass.  */
+static void
+rl78_reorg (void)
+{
+  rl78_alloc_physical_registers ();
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\n================DEVIRT:=AFTER=ALLOC=PHYSICAL=REGISTERS================\n");
+      print_rtl_with_bb (dump_file, get_insns ());
+    }
+
+  rl78_propogate_register_origins ();
+  rl78_calculate_death_notes ();
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "\n================DEVIRT:=AFTER=PROPOGATION=============================\n");
+      print_rtl_with_bb (dump_file, get_insns ());
+      fprintf (dump_file, "\n======================================================================\n");
+    }
+
+  rl78_remove_unused_sets ();
+
+  /* The code after devirtualizing has changed so much that at this point
+     we might as well just rescan everything.  Note that
+     df_rescan_all_insns is not going to help here because it does not
+     touch the artificial uses and defs.  */
+  df_finish_pass (true);
+  if (optimize > 1)
+    df_live_add_problem ();
+  df_scan_alloc (NULL);
+  df_scan_blocks ();
+
+  if (optimize)
+    df_analyze ();
+}
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY rl78_return_in_memory
+
+static bool
+rl78_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+  const HOST_WIDE_INT size = int_size_in_bytes (type);
+  return (size == -1 || size > 8);
+}
+
+\f
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+#include "gt-rl78.h"
diff --git a/gcc/config/rl78/rl78.h b/gcc/config/rl78/rl78.h
new file mode 100644 (file)
index 0000000..5980c41
--- /dev/null
@@ -0,0 +1,462 @@
+/* GCC backend definitions for the Renesas RL78 processor.
+   Copyright (C) 2011 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/>.  */
+\f
+
+#define RL78_MUL_NONE  (rl78_mul_type == MUL_NONE)
+#define RL78_MUL_RL78  (rl78_mul_type == MUL_RL78)
+#define RL78_MUL_G13   (rl78_mul_type == MUL_G13)
+
+#define TARGET_CPU_CPP_BUILTINS()               \
+  do                                            \
+    {                                           \
+      builtin_define ("__RL78__");             \
+      builtin_assert ("cpu=RL78");             \
+      if (RL78_MUL_RL78)                       \
+       builtin_define ("__RL78_MUL_RL78__");   \
+      if (RL78_MUL_G13)                                \
+       builtin_define ("__RL78_MUL_G13__");    \
+    }                                           \
+  while (0)
+
+#undef  STARTFILE_SPEC
+#define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:crt0.o%s} crtbegin.o%s"
+
+#undef  ENDFILE_SPEC
+#define ENDFILE_SPEC "crtend.o%s crtn.o%s"
+
+#undef  LIB_SPEC
+#define LIB_SPEC "                                     \
+--start-group                                          \
+-lc                                                    \
+-lsim                                                  \
+%{fprofile-arcs|fprofile-generate|coverage:-lgcov}     \
+--end-group                                            \
+%{!T*: %{msim:%Trl78-sim.ld}%{!msim:%Trl78.ld}}                \
+"
+\f
+
+#define BITS_BIG_ENDIAN                0
+#define BYTES_BIG_ENDIAN               0
+#define WORDS_BIG_ENDIAN               0
+
+#ifdef IN_LIBGCC2
+/* This is to get correct SI and DI modes in libgcc2.c (32 and 64 bits).  */
+#define        UNITS_PER_WORD                  4
+/* We have a problem with libgcc2.  It only defines two versions of
+   each function, one for "int" and one for "long long".  Ie it assumes
+   that "sizeof (int) == sizeof (long)".  For the RL78 this is not true
+   and we need a third set of functions.  We explicitly define
+   LIBGCC2_UNITS_PER_WORD here so that it is clear that we are expecting
+   to get the SI and DI versions from the libgcc2.c sources, and we
+   provide our own set of HI functions, which is why this
+   definition is surrounded by #ifndef..#endif.  */
+#ifndef LIBGCC2_UNITS_PER_WORD
+#define LIBGCC2_UNITS_PER_WORD                 4
+#endif
+#else
+/* Actual width of a word, in units (bytes).  */
+#define        UNITS_PER_WORD                  1
+#endif
+
+#define SHORT_TYPE_SIZE                        16
+#define INT_TYPE_SIZE                  16
+#define LONG_TYPE_SIZE                 32
+#define LONG_LONG_TYPE_SIZE            64
+
+#define FLOAT_TYPE_SIZE                32
+#define DOUBLE_TYPE_SIZE               32 /*64*/
+#define LONG_DOUBLE_TYPE_SIZE          64 /*DOUBLE_TYPE_SIZE*/
+
+#define LIBGCC2_HAS_DF_MODE            1
+#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE   64
+
+#define DEFAULT_SIGNED_CHAR            0
+
+#define STRICT_ALIGNMENT               1
+#define FUNCTION_BOUNDARY              8
+#define BIGGEST_ALIGNMENT              16
+#define STACK_BOUNDARY                         16
+#define PARM_BOUNDARY                  16
+
+#define STACK_GROWS_DOWNWARD           1
+#define FRAME_GROWS_DOWNWARD           1
+#define FIRST_PARM_OFFSET(FNDECL)      0
+
+#define MAX_REGS_PER_ADDRESS           1
+
+#define Pmode                          HImode
+#define POINTER_SIZE                   16
+#undef  SIZE_TYPE
+#define SIZE_TYPE                      "unsigned int"
+#undef  PTRDIFF_TYPE
+#define PTRDIFF_TYPE                   "int"
+#undef  WCHAR_TYPE
+#define WCHAR_TYPE                     "long int"
+#undef  WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE                        BITS_PER_WORD
+#define POINTERS_EXTEND_UNSIGNED       1
+#define FUNCTION_MODE                  HImode
+#define CASE_VECTOR_MODE               Pmode
+#define WORD_REGISTER_OPERATIONS       0
+#define HAS_LONG_COND_BRANCH           0
+#define HAS_LONG_UNCOND_BRANCH         0
+
+#define MOVE_MAX                       2
+#define STARTING_FRAME_OFFSET          0
+
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC)   1
+
+#define ADDR_SPACE_FAR 1
+
+#define HAVE_PRE_DECCREMENT            0
+#define HAVE_POST_INCREMENT            0
+
+#define MOVE_RATIO(SPEED)              ((SPEED) ? 24 : 16)
+#define SLOW_BYTE_ACCESS               0
+
+#define STORE_FLAG_VALUE               1
+#define LOAD_EXTEND_OP(MODE)           ZERO_EXTEND
+#define SHORT_IMMEDIATES_SIGN_EXTEND   0
+\f
+
+/* The RL78 has four register banks.  Normal operation uses RB0 as
+   real registers, RB1 and RB2 as "virtual" registers (because we know
+   they'll be there, and not used as variables), and RB3 is reserved
+   for interrupt handlers.  The virtual registers are accessed as
+   SADDRs:
+
+   FFEE0-FFEE7 RB0
+   FFEE8-FFEEF RB1
+   FFEF0-FFEF7 RB2
+   FFEF8-FFEFF RB3
+*/
+#define REGISTER_NAMES                                         \
+  {                                                            \
+    "x", "a", "c", "b", "e", "d", "l", "h",                    \
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",      \
+    "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",    \
+    "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",    \
+      "sp", "ap", "psw", "es", "cs"                            \
+  }
+
+#define ADDITIONAL_REGISTER_NAMES      \
+{                                      \
+{ "ax", 0 }, \
+{ "bc", 2 }, \
+{ "de", 4 }, \
+{ "hl", 6 }, \
+{ "rp0", 0 }, \
+{ "rp1", 2 }, \
+{ "rp2", 4 }, \
+{ "rp3", 6 }, \
+{ "r0", 0 }, \
+{ "r1", 1 }, \
+{ "r2", 2 }, \
+{ "r3", 3 }, \
+{ "r4", 4 }, \
+{ "r5", 5 }, \
+{ "r6", 6 }, \
+{ "r7", 7 }, \
+}
+
+enum reg_class
+{
+  NO_REGS,                     /* No registers in set.  */
+  XREG,
+  AREG,
+  AXREG,
+  CREG,
+  BREG,
+  BCREG,
+  EREG,
+  DREG,
+  DEREG,
+  LREG,
+  HREG,
+  HLREG,
+  IDX_REGS,
+  QI_REGS,
+  SPREG,
+  R8W_REGS,
+  R10W_REGS,
+  INT_REGS,
+  V_REGS,                      /* Virtual registers.  */
+  GR_REGS,                     /* Integer registers.  */
+  PSWREG,
+  ALL_REGS,                    /* All registers.  */
+  LIM_REG_CLASSES              /* Max value + 1.  */
+};
+
+#define REG_CLASS_NAMES                                        \
+{                                                      \
+  "NO_REGS",                                           \
+  "XREG",                                              \
+  "AREG",                                              \
+  "AXREG",                                             \
+  "CREG",                                              \
+  "BREG",                                              \
+  "BCREG",                                             \
+  "EREG",                                              \
+  "DREG",                                              \
+  "DEREG",                                             \
+  "LREG",                                              \
+  "HREG",                                              \
+  "HLREG",                                             \
+  "IDX_REGS",                                          \
+  "QI_REGS",                                           \
+  "SPREG",                                             \
+  "R8W_REGS",                                          \
+  "R10W_REGS",                                         \
+  "INT_REGS",                                          \
+  "V_REGS",                                            \
+  "GR_REGS",                                           \
+  "PSWREG",                                            \
+  "ALL_REGS"                                           \
+}
+
+#define REG_CLASS_CONTENTS                             \
+{                                                      \
+  { 0x00000000, 0x00000000 },  /* No registers,  */            \
+  { 0x00000001, 0x00000000 }, \
+  { 0x00000002, 0x00000000 }, \
+  { 0x00000003, 0x00000000 }, \
+  { 0x00000004, 0x00000000 }, \
+  { 0x00000008, 0x00000000 }, \
+  { 0x0000000c, 0x00000000 }, \
+  { 0x00000010, 0x00000000 }, \
+  { 0x00000020, 0x00000000 }, \
+  { 0x00000030, 0x00000000 }, \
+  { 0x00000040, 0x00000000 }, \
+  { 0x00000080, 0x00000000 }, \
+  { 0x000000c0, 0x00000000 }, \
+  { 0x0000000c, 0x00000000 },  /* B and C - index regs.  */    \
+  { 0x000000ff, 0x00000000 },  /* all real registers.  */      \
+  { 0x00000000, 0x00000001 },  /* SP */                        \
+  { 0x00000300, 0x00000000 },  /* R8 - HImode */               \
+  { 0x00000c00, 0x00000000 },  /* R10 - HImode */              \
+  { 0xff000000, 0x00000000 },  /* INT - HImode */              \
+  { 0x007fff00, 0x00000000 },  /* Virtual registers.  */       \
+  { 0xff7fffff, 0x00000002 },  /* General registers.  */       \
+  { 0x04000000, 0x00000004 },  /* PSW.  */     \
+  { 0xff7fffff, 0x0000001f }   /* All registers.  */           \
+}
+
+#define SMALL_REGISTER_CLASSES                 1
+#define N_REG_CLASSES                  (int) LIM_REG_CLASSES
+#define CLASS_MAX_NREGS(CLASS, MODE)    ((GET_MODE_SIZE (MODE) \
+                                         + UNITS_PER_WORD - 1) \
+                                        / UNITS_PER_WORD)
+
+#define GENERAL_REGS                   GR_REGS
+#define BASE_REG_CLASS                 V_REGS
+#define INDEX_REG_CLASS                        V_REGS
+
+#define FIRST_PSEUDO_REGISTER          37
+
+#define REGNO_REG_CLASS(REGNO)          ((REGNO) < FIRST_PSEUDO_REGISTER \
+                                        ? GR_REGS : NO_REGS)
+
+#define FRAME_POINTER_REGNUM           22
+#define STACK_POINTER_REGNUM           32
+#define ARG_POINTER_REGNUM             33
+#define CC_REGNUM                       34
+#define FUNC_RETURN_REGNUM              8
+#define STATIC_CHAIN_REGNUM            14
+
+/* Trampolines are implemented with a separate data stack.  The memory
+   on stack only holds the function pointer for the chosen stub.
+ */
+
+#define TRAMPOLINE_SIZE                        4
+#define TRAMPOLINE_ALIGNMENT           16
+
+#define ELIMINABLE_REGS                                        \
+{{ ARG_POINTER_REGNUM,   STACK_POINTER_REGNUM },       \
+ { ARG_POINTER_REGNUM,   FRAME_POINTER_REGNUM },       \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }}
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)   \
+  (OFFSET) = rl78_initial_elimination_offset ((FROM), (TO))
+
+
+#define FUNCTION_ARG_REGNO_P(N)                0
+#define FUNCTION_VALUE_REGNO_P(N)      ((N) == 8)
+#define DEFAULT_PCC_STRUCT_RETURN      0
+
+#define FIXED_REGISTERS                                        \
+{                                                      \
+  1,1,1,1, 1,1,1,1,                                    \
+  0,0,0,0, 0,0,0,0,                                    \
+  0,0,0,0, 0,0,1,1,                                    \
+  1,1,1,1, 1,1,1,1,                                    \
+  0, 1, 0, 1, 1                                                \
+}
+
+#define CALL_USED_REGISTERS                            \
+{                                                      \
+  1,1,1,1, 1,1,1,1,                                    \
+  1,1,1,1, 1,1,1,1,                                    \
+  0,0,0,0, 0,0,1,1,                                    \
+  1,1,1,1, 1,1,1,1,                                    \
+  0, 1, 1, 1, 1                                                \
+}
+
+#define LIBCALL_VALUE(MODE)                            \
+  gen_rtx_REG ((MODE),                                 \
+              FUNC_RETURN_REGNUM)
+
+/* Order of allocation of registers.  */
+
+#define REG_ALLOC_ORDER                                        \
+  { 8, 9, 10, 11, 12, 13, 14, 15,                      \
+    16, 17, 18, 19, 20, 21, 22, 23,                    \
+    0, 1, 6, 7, 2, 3, 4, 5,                            \
+    24, 25, 26, 27, 28, 29, 30, 31,                    \
+    32, 33, 34                                         \
+}
+
+#define REGNO_IN_RANGE(REGNO, MIN, MAX)                        \
+  (IN_RANGE ((REGNO), (MIN), (MAX))                    \
+   || (reg_renumber != NULL                            \
+       && reg_renumber[(REGNO)] >= (MIN)               \
+       && reg_renumber[(REGNO)] <= (MAX)))
+
+#ifdef REG_OK_STRICT
+#define REGNO_OK_FOR_BASE_P(regno)      REGNO_IN_RANGE (regno, 16, 23)
+#else
+#define REGNO_OK_FOR_BASE_P(regno)     1
+#endif
+
+#define REGNO_OK_FOR_INDEX_P(regno)    REGNO_OK_FOR_BASE_P (regno)
+
+#define REGNO_MODE_CODE_OK_FOR_BASE_P(regno, mode, address_space, outer_code, index_code) \
+  rl78_regno_mode_code_ok_for_base_p (regno, mode, address_space, outer_code, index_code)
+
+#define MODE_CODE_BASE_REG_CLASS(mode, address_space, outer_code, index_code) \
+  rl78_mode_code_base_reg_class (mode, address_space, outer_code, index_code)
+
+#define RETURN_ADDR_RTX(COUNT, FRAMEADDR)                              \
+  ((COUNT) == 0                                                                \
+   ? gen_rtx_MEM (Pmode, gen_rtx_PLUS (HImode, arg_pointer_rtx, GEN_INT (-4))) \
+   : NULL_RTX)
+
+#define INCOMING_RETURN_ADDR_RTX       gen_rtx_MEM (Pmode, stack_pointer_rtx)
+
+#define ACCUMULATE_OUTGOING_ARGS       1
+
+typedef unsigned int CUMULATIVE_ARGS;
+
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
+  (CUM) = 0
+
+\f
+/* FIXME */
+#define NO_PROFILE_COUNTERS     1
+#define PROFILE_BEFORE_PROLOGUE 1
+
+#define FUNCTION_PROFILER(FILE, LABELNO)       \
+    fprintf (FILE, "\tbsr\t__mcount\n");
+\f
+
+#define HARD_REGNO_NREGS(REGNO, MODE)            \
+  rl78_hard_regno_nregs (REGNO, MODE)
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE)                        \
+  rl78_hard_regno_mode_ok (REGNO, MODE)
+
+#define MODES_TIEABLE_P(MODE1, MODE2)                          \
+  (   (   GET_MODE_CLASS (MODE1) == MODE_FLOAT                 \
+       || GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT)                \
+   == (   GET_MODE_CLASS (MODE2) == MODE_FLOAT                 \
+       || GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT))
+\f
+
+#define TEXT_SECTION_ASM_OP ".text"
+#define DATA_SECTION_ASM_OP ".data"
+#define BSS_SECTION_ASM_OP ".bss"
+#define CTORS_SECTION_ASM_OP ".section \".ctors\",\"a\""
+#define DTORS_SECTION_ASM_OP ".section \".dtors\",\"a\""
+
+#define ASM_COMMENT_START      " ;"
+#define ASM_APP_ON             ""
+#define ASM_APP_OFF            ""
+#define LOCAL_LABEL_PREFIX     ".L"
+#undef  USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX      "_"
+
+#define GLOBAL_ASM_OP          "\t.global\t"
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+  fprintf (FILE, "\t.long .L%d\n", VALUE)
+
+/* This is how to output an element of a case-vector that is relative.
+   Note: The local label referenced by the "3b" below is emitted by
+   the tablejump insn.  */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+  fprintf (FILE, "\t.long .L%d - 1b\n", VALUE)
+
+
+#define ASM_OUTPUT_ALIGN(STREAM, LOG)          \
+  do                                           \
+    {                                          \
+      if ((LOG) == 0)                          \
+        break;                                 \
+      fprintf (STREAM, "\t.balign %d\n", 1 << (LOG));  \
+    }                                          \
+  while (0)
+
+/* For PIC put jump tables into the text section so that the offsets that
+   they contain are always computed between two same-section symbols.  */
+#define JUMP_TABLES_IN_TEXT_SECTION    (flag_pic)
+\f
+/* This is a version of REG_P that also returns TRUE for SUBREGs.  */
+#define RL78_REG_P(rtl) (REG_P (rtl) || GET_CODE (rtl) == SUBREG)
+
+/* Like REG_P except that this macro is true for SET expressions.  */
+#define SET_P(rtl)    (GET_CODE (rtl) == SET)
+\f
+#undef  PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+#undef DWARF2_ADDR_SIZE
+#define        DWARF2_ADDR_SIZE                        4
+
+#define DWARF2_ASM_LINE_DEBUG_INFO             1
+
+#define EXIT_IGNORE_STACK                      0
+#define INCOMING_FRAME_SP_OFFSET               4
+\f
+
+#define BRANCH_COST(SPEED,PREDICT)       1
+#define REGISTER_MOVE_COST(MODE,FROM,TO) 2
+
+#define EH_RETURN_DATA_REGNO(N) (N < 2 ? (8+(N)*2) : INVALID_REGNUM)
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (HImode, 20)
+
+#define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) DW_EH_PE_udata4
+
+/* NOTE: defined but zero means dwarf2 debugging, but sjlj EH.  */
+#define DWARF2_UNWIND_INFO 0
+/*#define DONT_USE_BUILTIN_SETJMP 1*/
+#undef DONT_USE_BUILTIN_SETJMP
+#define JMP_BUF_SIZE (8*3+8)
+
+#define REGISTER_TARGET_PRAGMAS() rl78_register_pragmas()
diff --git a/gcc/config/rl78/rl78.md b/gcc/config/rl78/rl78.md
new file mode 100644 (file)
index 0000000..220ed99
--- /dev/null
@@ -0,0 +1,320 @@
+;;  Machine Description for Renesas RL78 processors
+;;  Copyright (C) 2011 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/>.
+\f
+(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 33)
+   (ES_REG 35)
+   (CS_REG 36)
+
+   (UNS_PROLOG 1)
+   (UNS_EPILOG 1)
+   (UNS_RETI   2)
+   (UNS_RETB   3)
+
+   (UNS_SET_RB 10)
+
+   (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")
+
+
+;; 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 "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)]
+  ""
+  "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 "" "vi") ;; target
+                         (match_operand 2 "" "vi") ;; sp
+                         (match_operand 3 "" "vi") ;; ?
+                         ] 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
+"
+  )
+
+;;======================================================================
+;;
+;; "macro" insns - cases where inline chunks of code are more
+;; efficient than anything else.
+
+(define_expand "addsi3"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+       (plus:SI (match_operand:SI 1 "nonmemory_operand" "vi")
+                (match_operand    2 "nonmemory_operand" "vi")))
+   ]
+  ""
+  "if (!nonmemory_operand (operands[1], SImode))
+     operands[1] = force_reg (SImode, operands[1]);
+   if (!nonmemory_operand (operands[1], SImode))
+     operands[2] = force_reg (SImode, operands[2]);"
+)
+
+(define_insn "addsi3_internal"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+       (plus:SI (match_operand:SI 1 "nonmemory_operand" "vi")
+                (match_operand:SI 2 "nonmemory_operand" "vi")))
+   ]
+  ""
+  "; addSI macro %0 = %1 + %2
+       movw    ax, %h1
+       addw    ax, %h2
+       movw    %h0, ax
+       movw    ax,%H1
+       sknc
+       incw    ax
+       addw    ax,%H2
+       movw    %H0,ax
+       ; end of addSI macro"
+  [(set_attr "valloc" "macax")]
+)
+
+(define_expand "mulsi3"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+       (mult:SI (match_operand:SI 1 "nonmemory_operand" "vi")
+                (match_operand:SI 2 "nonmemory_operand" "vi")))
+   ]
+  "! RL78_MUL_NONE"
+  ""
+)
+
+;; 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_rl78"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+       (mult:SI (match_operand:SI 1 "nonmemory_operand" "vi")
+                (match_operand:SI 2 "nonmemory_operand" "vi")))
+   ]
+  "RL78_MUL_RL78"
+  "; 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.
+;; 0xFFFF4 is MDBL.  0xFFFF6 is MDBH.
+;; 0xF00E0 is MDCL.  0xF00E2 is MDCH.
+;; 0xF00E8 is MDUC.
+;; Warning: this matches the documentation, not the silicon.
+(define_insn "mulsi3_g13"
+  [(set (match_operand:SI          0 "register_operand" "=&v")
+       (mult:SI (match_operand:SI 1 "nonmemory_operand" "vi")
+                (match_operand:SI 2 "nonmemory_operand" "vi")))
+   ]
+  "RL78_MUL_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, 0xffff4     ; MDBL
+       movw    %h0, ax
+
+       mov     a, #0x40
+       mov     !0xf00e8, a     ; MDUC
+       movw    ax, 0xffff6     ; 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
+       movw    ax, !0xf00e0    ; MDCL
+       movw    %H0, ax
+       ; end of mulsi macro"
+  [(set_attr "valloc" "macax")]
+  )
diff --git a/gcc/config/rl78/rl78.opt b/gcc/config/rl78/rl78.opt
new file mode 100644 (file)
index 0000000..d4858ba
--- /dev/null
@@ -0,0 +1,43 @@
+; Command line options for the Renesas RL78 port of GCC.
+; Copyright (C) 2011 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/>.
+;---------------------------------------------------
+
+HeaderInclude
+config/rl78/rl78-opts.h
+
+msim
+Target
+Use the simulator runtime.
+
+mmul=
+Target RejectNegative Joined Var(rl78_mul_type) Report Tolower Enum(rl78_mul_types) Init(MUL_NONE)
+Select hardware or software multiplication support.
+
+Enum
+Name(rl78_mul_types) Type(enum rl78_mul_types)
+
+EnumValue
+Enum(rl78_mul_types) String(none) Value(MUL_NONE)
+
+EnumValue
+Enum(rl78_mul_types) String(rl78) Value(MUL_RL78)
+
+EnumValue
+Enum(rl78_mul_types) String(g13) Value(MUL_G13)
diff --git a/gcc/config/rl78/t-rl78 b/gcc/config/rl78/t-rl78
new file mode 100644 (file)
index 0000000..9d08f1f
--- /dev/null
@@ -0,0 +1,22 @@
+# Makefile fragment for building GCC for the Renesas RL78 target.
+# Copyright (C) 2011 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/>.
+
+rl78-c.o: $(srcdir)/config/rl78/rl78-c.c $(RTL_H) $(TREE_H) $(CONFIG_H) $(TM_H)
+       $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
index ffefecc..37e24f0 100644 (file)
@@ -220,7 +220,7 @@ Mo DeJong for GCJ and libgcj bug fixes.
 
 @item
 DJ Delorie for the DJGPP port, build and libiberty maintenance,
-various bug fixes, and the M32C and MeP ports.
+various bug fixes, and the M32C, MeP, and RL78 ports.
 
 @item
 Arnaud Desitter for helping to debug GNU Fortran.
index d52f9a0..7f39a61 100644 (file)
@@ -1220,11 +1220,12 @@ Fixed-point types are supported by the DWARF2 debug information format.
 
 As an extension, the GNU C compiler supports named address spaces as
 defined in the N1275 draft of ISO/IEC DTR 18037.  Support for named
-address spaces in GCC will evolve as the draft technical report changes.
-Calling conventions for any target might also change.  At present, only
-the SPU and M32C targets support other address spaces.  On the SPU target, for
-example, variables may be declared as belonging to another address space
-by qualifying the type with the @code{__ea} address space identifier:
+address spaces in GCC will evolve as the draft technical report
+changes.  Calling conventions for any target might also change.  At
+present, only the SPU, M32C, and RL78 targets support other address
+spaces.  On the SPU target, for example, variables may be declared as
+belonging to another address space by qualifying the type with the
+@code{__ea} address space identifier:
 
 @smallexample
 extern int __ea i;
@@ -1244,6 +1245,11 @@ qualified with @code{__far} are accessed using 32-bit addresses in
 order to access memory beyond the first 64k bytes.  If @code{__far} is
 used with the M32CM or M32C cpu variants, it has no effect.
 
+On the RL78 target, variables qualified with @code{__far} are accessed
+with 32-bit pointers (20-bit addresses) rather than the default 16-bit
+addresses.  Non-far variables are assumed to appear in the topmost 64
+kB of the address space.
+
 @node Zero Length
 @section Arrays of Length Zero
 @cindex arrays of length zero
@@ -2553,7 +2559,7 @@ This attribute is ignored for R8C target.
 @item interrupt
 @cindex interrupt handler functions
 Use this attribute on the ARM, AVR, Epiphany, M32C, M32R/D, m68k, MeP, MIPS,
-RX and Xstormy16 ports to indicate that the specified function is an
+RL78, RX and Xstormy16 ports to indicate that the specified function is an
 interrupt handler.  The compiler will generate function entry and exit
 sequences suitable for use in an interrupt handler when this attribute
 is present.
@@ -2611,6 +2617,10 @@ void __attribute__ ((interrupt, use_shadow_register_set,
                      use_debug_exception_return)) v7 ();
 @end smallexample
 
+On RL78, use @code{brk_interrupt} instead of @code{interrupt} for
+handlers intended to be used with the @code{BRK} opcode (i.e.  those
+that must end with @code{RETB} instead of @code{RETI}).
+
 @item ifunc ("@var{resolver}")
 @cindex @code{ifunc} attribute
 The @code{ifunc} attribute is used to mark a function as an indirect
index 46c5824..da84758 100644 (file)
@@ -4140,6 +4140,13 @@ Embedded PowerPC system in little endian mode.
 @html
 <hr />
 @end html
+@heading @anchor{rl78-x-elf}rl78-*-elf
+The Renesas RL78 processor.
+This configuration is intended for embedded systems.
+
+@html
+<hr />
+@end html
 @heading @anchor{rx-x-elf}rx-*-elf
 The Renesas RX processor.  See
 @uref{http://eu.renesas.com/fmwk.jsp?cnt=rx600_series_landing.jsp&fp=/products/mpumcu/rx_family/rx600_series}
index 4e6edb9..265c8f0 100644 (file)
@@ -778,6 +778,9 @@ Objective-C and Objective-C++ Dialects}.
 @emph{PowerPC Options}
 See RS/6000 and PowerPC Options.
 
+@emph{RL78 Options}
+@gccoptlist{-msim -mmul=none -mmul=g13 -mmul=rl78}
+
 @emph{RS/6000 and PowerPC Options}
 @gccoptlist{-mcpu=@var{cpu-type} @gol
 -mtune=@var{cpu-type} @gol
@@ -10303,6 +10306,7 @@ platform.
 * PDP-11 Options::
 * picoChip Options::
 * PowerPC Options::
+* RL78 Options::
 * RS/6000 and PowerPC Options::
 * RX Options::
 * S/390 and zSeries Options::
@@ -15931,6 +15935,29 @@ the warning to be turned off.
 
 These are listed under @xref{RS/6000 and PowerPC Options}.
 
+@node RL78 Options
+@subsection RL78 Options
+@cindex RL78 Options
+
+@table @gcctabopt
+
+@item -msim
+@opindex msim
+Links in additional target libraries to support operation within a
+simulator.
+
+@item -mmul=none
+@itemx -mmul=g13
+@itemx -mmul=rl78
+@opindex mmul
+Specifies the type of hardware multiplication support to be used.  The
+default is @code{none}, which uses software multiplication functions.
+The @code{g13} option is for the hardware multiply/divide peripheral
+only on the RL78/G13 targets.  The @code{rl78} option is for the
+standard hardware multiplication defined in the RL78 software manual.
+
+@end table
+
 @node RS/6000 and PowerPC Options
 @subsection IBM RS/6000 and PowerPC Options
 @cindex RS/6000 and PowerPC Options
index 64ad833..dc87ca7 100644 (file)
@@ -2979,6 +2979,96 @@ A memory reference that is encoded within the opcode.
 
 @end table
 
+@item RL78---@file{config/rl78/constraints.md}
+@table @code
+
+@item Int3
+An integer constant in the range 1 @dots{} 7.
+@item Int8
+An integer constant in the range 0 @dots{} 255.
+@item J
+An integer constant in the range @minus{}255 @dots{} 0
+@item K
+The integer constant 1.
+@item L
+The integer constant -1.
+@item M
+The integer constant 0.
+@item N
+The integer constant 2.
+@item O
+The integer constant -2.
+@item P
+An integer constant in the range 1 @dots{} 15.
+@item Qbi
+The built-in compare types--eq, ne, gtu, ltu, geu, and leu.
+@item Qsc
+The synthetic compare types--gt, lt, ge, and le.
+@item Wab
+A memory reference with an absolute address.
+@item Wbc
+A memory reference using @code{BC} as a base register, with an optional offset.
+@item Wca
+A memory reference using @code{AX}, @code{BC}, @code{DE}, or @code{HL} for the address, for calls.
+@item Wcv
+A memory reference using any 16-bit register pair for the address, for calls.
+@item Wd2
+A memory reference using @code{DE} as a base register, with an optional offset.
+@item Wde
+A memory reference using @code{DE} as a base register, without any offset.
+@item Wfr
+Any memory reference to an address in the far address space.
+@item Wh1
+A memory reference using @code{HL} as a base register, with an optional one-byte offset.
+@item Whb
+A memory reference using @code{HL} as a base register, with @code{B} or @code{C} as the index register.
+@item Whl
+A memory reference using @code{HL} as a base register, without any offset.
+@item Ws1
+A memory reference using @code{SP} as a base register, with an optional one-byte offset.
+@item Y
+Any memory reference to an address in the near address space.
+@item A
+The @code{AX} register.
+@item B
+The @code{BC} register.
+@item D
+The @code{DE} register.
+@item R
+@code{A} through @code{L} registers.
+@item S
+The @code{SP} register.
+@item T
+The @code{HL} register.
+@item Z08W
+The 16-bit @code{R8} register.
+@item Z10W
+The 16-bit @code{R10} register.
+@item Zint
+The registers reserved for interrupts (@code{R24} to @code{R31}).
+@item a
+The @code{A} register.
+@item b
+The @code{B} register.
+@item c
+The @code{C} register.
+@item d
+The @code{D} register.
+@item e
+The @code{E} register.
+@item h
+The @code{H} register.
+@item l
+The @code{L} register.
+@item v
+The virtual registers.
+@item w
+The @code{PSW} register.
+@item x
+The @code{X} register.
+
+@end table
+
 @item RX---@file{config/rx/constraints.md}
 @table @code
 @item Q
index aca72b0..bf07e10 100644 (file)
@@ -1,3 +1,8 @@
+2011-11-29  DJ Delorie  <dj@redhat.com>
+
+       * config.host (rl78-*-elf): New case.
+       * config/rl78: New directory for the Renesas RL78.
+
 2011-11-29  Bernd Schmidt  <bernds@codesourcery.com>
 
        * config.host (tic6x-*-uclinux): Append to extra_parts.  Fix
index 961c69f..f15282c 100644 (file)
@@ -916,6 +916,9 @@ rs6000-ibm-aix[56789].* | powerpc-ibm-aix[56789].*)
        md_unwind_header=rs6000/aix-unwind.h
        tmake_file="t-fdpbit rs6000/t-ppc64-fp rs6000/t-ibm-ldouble rs6000/t-slibgcc-aix"
        ;;
+rl78-*-elf)
+       tmake_file="$tm_file t-fdpbit rl78/t-rl78"
+       ;;
 rx-*-elf)
        tmake_file="rx/t-rx t-fdpbit"
        tm_file="$tm_file rx/rx-abi.h rx/rx-lib.h"
diff --git a/libgcc/config/rl78/cmpsi2.S b/libgcc/config/rl78/cmpsi2.S
new file mode 100644 (file)
index 0000000..14fa3cf
--- /dev/null
@@ -0,0 +1,122 @@
+;   Copyright (C) 2011 Free Software Foundation, Inc.
+;   Contributed by Red Hat.
+; 
+; This file 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.
+; 
+; This file 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.
+; 
+; Under Section 7 of GPL version 3, you are granted additional
+; permissions described in the GCC Runtime Library Exception, version
+; 3.1, as published by the Free Software Foundation.
+;
+; You should have received a copy of the GNU General Public License and
+; a copy of the GCC Runtime Library Exception along with this program;
+; see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+; <http://www.gnu.org/licenses/>.
+
+
+; clobberable
+r8     =       0xffef0
+
+       .text
+
+       ;;   int __cmpsi2 (signed long A, signed long B)
+       ;;
+       ;; Performs a signed comparison of A and B.
+       ;; If A is less than B it returns 0.  If A is greater
+       ;; than B it returns 2.  If they are equal it returns 1.
+
+       .global ___cmpsi2
+        .type   ___cmpsi2, @function
+___cmpsi2:
+       ;; A is at [sp+4]
+       ;; B is at [sp+8]
+       ;; Result put in R8
+
+       ;; Initialise default return value.
+       onew    bc
+       
+       ;;  Compare the high words.
+       movw    ax, [sp + 10]
+       movw    de, ax
+       movw    ax, [sp + 6]
+       cmpw    ax, de
+       skz
+       br      !!.Lconvert_to_signed
+
+.Lcompare_bottom_words:        
+       ;; The top words are equal - compare the bottom words.
+       ;; Note - code from __ucmpsi2 branches into here.
+       movw   ax, [sp + 8]
+       movw   de, ax
+       movw   ax, [sp + 4]
+       cmpw   ax, de
+       sknz
+       br      !!.Lless_than_or_greater_than
+       ;; The words are equal - return 1.
+       ;; Note - we could branch to the return code at the end of the
+       ;; function but a branch instruction takes 4 bytes, and the
+       ;; return sequence itself is only 4 bytes long...
+       movw    ax, bc
+       movw    r8, ax
+       ret
+
+.Lconvert_to_signed:   
+       ;; The top words are different.  Unfortunately the comparison
+       ;; is always unsigned, so to get a signed result we XOR the CY
+       ;; flag with the top bits of AX and DE.
+       xor1    cy, a.7
+       mov     a, d
+       xor1    cy, a.7
+       ;; Fall through.
+
+.Lless_than_or_greater_than:
+       ;; We now have a signed less than/greater than result in CY.
+       ;; Return 0 for less than, 2 for greater than.
+       ;; Note - code from __ucmpsi2 branches into here.
+       incw    bc
+       sknc
+       clrw    bc
+
+       ;; Get the result value, currently in BC, into r8
+       movw    ax, bc
+       movw    r8, ax
+       ret
+
+       .size   ___cmpsi2, . - ___cmpsi2
+       
+       
+       ;;   int __ucmpsi2 (unsigned long A, unsigned long B)
+       ;;
+       ;; Performs an unsigned comparison of A and B.
+       ;; If A is less than B it returns 0.  If A is greater
+       ;; than B it returns 2.  If they are equal it returns 1.
+
+       .global ___ucmpsi2
+        .type   ___ucmpsi2, @function
+___ucmpsi2:
+       ;; A is at [sp+4]
+       ;; B is at [sp+8]
+       ;; Result put in R8..R9
+
+       ;; Initialise default return value.
+       onew    bc
+
+       ;;  Compare the high words.
+       movw    ax, [sp + 10]
+       movw    de, ax
+       movw    ax, [sp + 6]
+       cmpw    ax, de
+       skz
+       ;; Note: These branches go into the __cmpsi2 code!
+       br      !!.Lless_than_or_greater_than
+       br      !!.Lcompare_bottom_words
+
+       .size   ___ucmpsi2, . - ___ucmpsi2
+       
\ No newline at end of file
diff --git a/libgcc/config/rl78/lib2div.c b/libgcc/config/rl78/lib2div.c
new file mode 100644 (file)
index 0000000..679f26d
--- /dev/null
@@ -0,0 +1,81 @@
+/* libgcc routines for RL78
+   Copyright (C) 2005, 2009, 2011
+   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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+typedef          int  sint32_type   __attribute__ ((mode (SI)));
+typedef unsigned int  uint32_type   __attribute__ ((mode (SI)));
+typedef          int  sint16_type   __attribute__ ((mode (HI)));
+typedef unsigned int  uint16_type   __attribute__ ((mode (HI)));
+typedef          int  sint08_type   __attribute__ ((mode (QI)));
+typedef unsigned int  uint08_type   __attribute__ ((mode (QI)));
+typedef int           word_type     __attribute__ ((mode (__word__)));
+
+#define C3B(a,b,c) a##b##c
+#define C3(a,b,c) C3B(a,b,c)
+
+#define UINT_TYPE      uint32_type
+#define SINT_TYPE      sint32_type
+#define BITS_MINUS_1   31
+#define NAME_MODE      si
+
+#include "rl78-divmod.h"
+
+#undef UINT_TYPE
+#undef SINT_TYPE
+#undef BITS_MINUS_1
+#undef NAME_MODE
+
+#define UINT_TYPE      uint16_type
+#define SINT_TYPE      sint16_type
+#define BITS_MINUS_1   15
+#define NAME_MODE      hi
+
+#include "rl78-divmod.h"
+
+#undef UINT_TYPE
+#undef SINT_TYPE
+#undef BITS_MINUS_1
+#undef NAME_MODE
+
+#define UINT_TYPE      uint08_type
+#define SINT_TYPE      sint08_type
+#define BITS_MINUS_1   7
+#define NAME_MODE      qi
+
+#include "rl78-divmod.h"
+
+/* See the comment by the definition of LIBGCC2_UNITS_PER_WORD in
+   m32c.h for why we are creating extra versions of some of the
+   functions defined in libgcc2.c.  */
+
+#define LIBGCC2_UNITS_PER_WORD 2
+
+#define L_clzsi2
+#define L_ctzsi2
+#define L_ffssi2
+#define L_paritysi2
+#define L_popcountsi2
+
+#include "libgcc2.c"
diff --git a/libgcc/config/rl78/lib2mul.c b/libgcc/config/rl78/lib2mul.c
new file mode 100644 (file)
index 0000000..123690b
--- /dev/null
@@ -0,0 +1,49 @@
+/* libgcc routines for RL78
+   Copyright (C) 2005, 2009, 2011
+   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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+typedef unsigned int  uint32_type   __attribute__ ((mode (SI)));
+typedef unsigned int  uint16_type   __attribute__ ((mode (HI)));
+typedef unsigned int  uint08_type   __attribute__ ((mode (QI)));
+
+#define C3B(a,b,c) a##b##c
+#define C3(a,b,c) C3B(a,b,c)
+
+
+#define UINT_TYPE      uint16_type
+#define BITS_MINUS_1   15
+#define NAME_MODE      hi
+
+/*#include "rl78-mul.h"*/
+
+#undef UINT_TYPE
+#undef BITS_MINUS_1
+#undef NAME_MODE
+
+#define UINT_TYPE      uint08_type
+#define BITS_MINUS_1   7
+#define NAME_MODE      qi
+
+#include "rl78-mul.h"
diff --git a/libgcc/config/rl78/lib2shift.c b/libgcc/config/rl78/lib2shift.c
new file mode 100644 (file)
index 0000000..c9db85e
--- /dev/null
@@ -0,0 +1,113 @@
+/* Shift functions for the GCC support library for the Renesas RL78 processors.
+   Copyright (C) 2011 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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+\f
+typedef          int  sint32_type   __attribute__ ((mode (SI)));
+typedef unsigned int  uint32_type   __attribute__ ((mode (SI)));
+typedef          int  sint16_type   __attribute__ ((mode (HI)));
+typedef unsigned int  uint16_type   __attribute__ ((mode (HI)));
+
+uint32_type __ashlsi3 (uint32_type in, char bit);
+sint32_type __ashrsi3 (sint32_type in, char bit);
+int __clrsbhi2 (sint16_type x);
+extern int __clrsbsi2 (sint32_type x);
+
+typedef struct
+{
+  union
+  {
+    uint32_type u;
+    uint16_type h[2];
+  } u;
+} dd;
+
+uint32_type
+__ashlsi3 (uint32_type in, char bit)
+{
+  uint16_type h, l;
+  dd d;
+
+  if (bit > 32)
+    return 0;
+  if (bit < 0)
+    return in;
+
+  d.u.u = in;
+  h = d.u.h[1];
+  l = d.u.h[0];
+
+  if (bit > 15)
+    {
+      h = l;
+      l = 0;
+      bit -= 16;
+    }
+
+  while (bit)
+    {
+      h = (h << 1) | (l >> 15);
+      l <<= 1;
+      bit --;
+    }
+
+  d.u.h[1] = h;
+  d.u.h[0] = l;
+  return d.u.u;
+}
+
+sint32_type
+__ashrsi3 (sint32_type in, char bit)
+{
+  sint16_type h;
+  uint16_type l;
+  dd d;
+
+  if (bit > 32)
+    return 0;
+  if (bit < 0)
+    return in;
+
+  d.u.u = in;
+  h = d.u.h[1];
+  l = d.u.h[0];
+
+  while (bit)
+    {
+      l = (h << 15) | (l >> 1);
+      h >>= 1;
+      bit --;
+    }
+
+  d.u.h[1] = h;
+  d.u.h[0] = l;
+  return d.u.u;
+}
+
+int
+__clrsbhi2 (sint16_type x)
+{
+  if (x == 0)
+    return 15;
+  return __clrsbsi2 ((sint32_type) x) - 16;
+}
diff --git a/libgcc/config/rl78/lshrsi3.S b/libgcc/config/rl78/lshrsi3.S
new file mode 100644 (file)
index 0000000..8422284
--- /dev/null
@@ -0,0 +1,131 @@
+;   Copyright (C) 2011 Free Software Foundation, Inc.
+;   Contributed by Red Hat.
+; 
+; This file 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.
+; 
+; This file 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.
+; 
+; Under Section 7 of GPL version 3, you are granted additional
+; permissions described in the GCC Runtime Library Exception, version
+; 3.1, as published by the Free Software Foundation.
+;
+; You should have received a copy of the GNU General Public License and
+; a copy of the GCC Runtime Library Exception along with this program;
+; see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+; <http://www.gnu.org/licenses/>.
+
+r8     =       0xffef0
+r16    =       0xffee8
+r9     =       0xffef1
+r17    =       0xffee9
+r10    =       0xffef2
+r18    =       0xffeea
+r11    =       0xffef3
+r19    =       0xffeeb
+r12    =       0xffef4
+r20    =       0xffeec
+r13    =       0xffef5
+r21    =       0xffeed
+r14    =       0xffef6
+r22    =       0xffeee
+r15    =       0xffef7
+r23    =       0xffeef
+       
+       .text
+       .global ___lshrsi3
+       .type   ___lshrsi3, @function
+___lshrsi3:
+
+       ;; input:
+       ;; 
+       ;; [zero]
+       ;; [count]   <= $sp+8
+       ;; [in MSB]
+       ;; [in]
+       ;; [in]
+       ;; [in LSB]  <- $sp+4
+
+       ;; output:
+       ;; 
+       ;; [r8..r11] result
+
+       ;; registers:
+       ;;
+       ;; AX - temp for shift/rotate
+       ;; B  - count
+
+       mov     a, [sp+8]       ; A now contains the count
+
+       cmp     a, #0x20
+       bc      $.Lcount_is_normal
+
+       ;; count is out of bounds, just return zero.
+       movw    r8, #0
+       movw    r10, #0
+       ret
+
+.Lcount_is_normal:
+       cmp0    a
+       bnz     $.Lcount_is_nonzero
+
+       ;; count is zero, just copy IN to OUT
+       movw    ax, [sp+4]
+       movw    r8, ax
+       movw    ax, [sp+6]
+       movw    r10, ax
+       ret
+
+.Lcount_is_nonzero:
+       mov     b, a            ; B now contains the count also
+       bf      a.4, $.Lcount_lt_16
+
+       ;; count >= 16, shift 16 at a time.
+       movw    r10, #0
+       movw    ax, [sp+6]
+       movw    r8, ax
+       mov     a, b
+       and     a, #0x0f
+       sknz
+       ret
+
+       mov     b, a            ; B now contains the remaining count
+       inc     b
+       br      $.Lloop_top
+
+.Lcount_lt_16: 
+       ;; count is nonzero.  Do one 
+       movw    ax, [sp+6]
+       shrw    ax,1
+       movw    r10, ax
+       mov     a, [sp+5]
+       rorc    a,1
+       mov     r9, a
+       mov     a, [sp+4]
+       rorc    a,1
+       mov     r8, a
+
+       ;; we did one shift above; do as many more as we need now.
+.Lloop_top:    
+       dec     b
+       sknz
+       ret
+
+       movw    ax, r10
+       shrw    ax,1
+       movw    r10, ax
+       mov     a, r9
+       rorc    a,1
+       mov     r9, a
+       mov     a, r8
+       rorc    a,1
+       mov     r8, a
+
+       br      $.Lloop_top
+
+       .size   ___lshrsi3, .-___lshrsi3
diff --git a/libgcc/config/rl78/mulsi3.S b/libgcc/config/rl78/mulsi3.S
new file mode 100644 (file)
index 0000000..0bca43a
--- /dev/null
@@ -0,0 +1,235 @@
+;   Copyright (C) 2011 Free Software Foundation, Inc.
+;   Contributed by Red Hat.
+; 
+; This file 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.
+; 
+; This file 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.
+; 
+; Under Section 7 of GPL version 3, you are granted additional
+; permissions described in the GCC Runtime Library Exception, version
+; 3.1, as published by the Free Software Foundation.
+;
+; You should have received a copy of the GNU General Public License and
+; a copy of the GCC Runtime Library Exception along with this program;
+; see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+; <http://www.gnu.org/licenses/>.
+
+;; 32x32=32 multiply
+
+; real
+; GAS defines r0..r7 as aliases for real registers; we want the saddr
+; forms here.
+r_0    =       0xffef8
+r_1    =       0xffef9
+r_2    =       0xffefa
+r_3    =       0xffefb
+r_4    =       0xffefc
+r_5    =       0xffefd
+r_6    =       0xffefe
+r_7    =       0xffeff
+; clobberable
+r8     =       0xffef0
+r9     =       0xffef1
+r10    =       0xffef2
+r11    =       0xffef3
+r12    =       0xffef4
+r13    =       0xffef5
+r14    =       0xffef6
+r15    =       0xffef7
+; preserved
+r16    =       0xffee8
+r17    =       0xffee9
+r18    =       0xffeea
+r19    =       0xffeeb
+r20    =       0xffeec
+r21    =       0xffeed
+r22    =       0xffeee
+r23    =       0xffeef
+
+
+;----------------------------------------------------------------------
+
+; Register use:
+;      RB0     RB1     RB2
+; AX   op2L    res32L  res32H
+; BC   op2H    (resH)  op1
+; DE   count   (resL-tmp)
+; HL   [sp+4]
+
+       .text
+       nop
+       .global ___mulsi3               ; (USI a, USI b)
+___mulsi3:
+       ;; A is at [sp+4]
+       ;; B is at [sp+8]
+       ;; result is in R8..R11
+
+       movw    ax, sp
+       addw    ax, #4
+       movw    hl, ax
+
+       sel     rb2
+       push    ax
+       push    bc
+       sel     rb0
+
+       clrw    ax
+       movw    r8, ax
+       movw    r16, ax
+
+       movw    ax, [hl+6]
+       cmpw    ax, #0
+       bz      $1f
+       cmpw    ax, #0xffff
+       bnz     $2f
+       movw    ax, [hl]
+       sel     rb1
+       subw    ax, r_0
+       sel     rb0
+       br      $1f
+2:     
+       movw    bc, ax
+       movw    ax, [hl]
+       cmpw    ax, #0
+       skz
+       call    !.Lmul_hi
+1:     
+
+       movw    ax, [hl+2]
+       cmpw    ax, #0
+       bz      $1f
+       cmpw    ax, #0xffff
+       bnz     $2f
+       movw    ax, [hl+4]
+       sel     rb1
+       subw    ax, r_0
+       sel     rb0
+       br      $1f
+2:     
+       movw    bc, ax
+       movw    ax, [hl+4]
+       cmpw    ax, #0
+       skz
+       call    !.Lmul_hi
+1:     
+
+       movw    ax, r8
+       movw    r16, ax
+       clrw    ax
+       movw    r8, ax
+
+       ;; now do R16:R8 += op1L * op2L
+
+       ;; op1 is in AX.0 (needs to shrw)
+       ;; op2 is in BC.2 and BC.1 (bc can shlw/rolcw)
+       ;; res is in AX.2 and AX.1 (needs to addw)
+
+       movw    ax, [hl]
+       movw    r10, ax         ; BC.1
+       movw    ax, [hl+4]
+
+       cmpw    ax, r10
+       bc      $.Lmul_hisi_top
+       movw    bc, r10
+       movw    r10, ax
+       movw    ax, bc
+
+
+.Lmul_hisi_top:
+       movw    bc, #0
+
+.Lmul_hisi_loop:
+       shrw    ax, 1
+       bnc     $.Lmul_hisi_no_add
+       sel     rb1
+       addw    ax, bc
+       sel     rb2
+       sknc
+       incw    ax
+       addw    ax, r_2
+.Lmul_hisi_no_add:     
+       sel     rb1
+       shlw    bc, 1
+       sel     rb0
+       rolwc   bc, 1
+       cmpw    ax, #0
+       bz      $.Lmul_hisi_done
+
+       shrw    ax, 1
+       bnc     $.Lmul_hisi_no_add2
+       sel     rb1
+       addw    ax, bc
+       sel     rb2
+       sknc
+       incw    ax
+       addw    ax, r_2
+.Lmul_hisi_no_add2:
+       sel     rb1
+       shlw    bc, 1
+       sel     rb0
+       rolwc   bc, 1
+       cmpw    ax, #0
+       bnz     $.Lmul_hisi_loop
+
+.Lmul_hisi_done:
+
+       movw    ax, r16
+       movw    r10, ax
+       
+       sel     rb2
+       pop     bc
+       pop     ax
+       sel     rb0
+
+       ret
+
+;----------------------------------------------------------------------
+
+       ;; R8 += AX * BC
+.Lmul_hi:
+       cmpw    ax, bc
+       skc
+       xchw    ax, bc
+       br      $.Lmul_hi_loop
+       
+.Lmul_hi_top:
+       sel     rb1
+       addw    ax, r_2
+       sel     rb0
+.Lmul_hi_no_add:       
+       shlw    bc, 1
+.Lmul_hi_loop:
+       shrw    ax, 1
+       bc      $.Lmul_hi_top
+       cmpw    ax, #0
+       bz      $.Lmul_hi_done
+
+       shlw    bc, 1
+       shrw    ax, 1
+       bc      $.Lmul_hi_top
+       cmpw    ax, #0
+       bnz     $.Lmul_hi_no_add
+
+.Lmul_hi_done:
+       ret
+
+;----------------------------------------------------------------------
+
+       .global ___mulhi3
+___mulhi3:
+       sel     rb1
+       clrw    ax
+       sel     rb0
+       movw    ax, sp
+       addw    ax, #4
+       movw    hl, ax
+       movw    ax, [hl+2]
+       movw    bc, ax
+       movw    ax, [hl]
+       br      $.Lmul_hi
diff --git a/libgcc/config/rl78/rl78-divmod.h b/libgcc/config/rl78/rl78-divmod.h
new file mode 100644 (file)
index 0000000..8ea5e1e
--- /dev/null
@@ -0,0 +1,118 @@
+/* libgcc routines for RL78
+   Copyright (C) 2005, 2009, 2011
+   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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+UINT_TYPE C3(udivmod,NAME_MODE,4) (UINT_TYPE, UINT_TYPE, word_type);
+SINT_TYPE C3(__div,NAME_MODE,3)   (SINT_TYPE, SINT_TYPE);
+SINT_TYPE C3(__mod,NAME_MODE,3)   (SINT_TYPE, SINT_TYPE);
+UINT_TYPE C3(__udiv,NAME_MODE,3)  (UINT_TYPE, UINT_TYPE);
+UINT_TYPE C3(__umod,NAME_MODE,3)  (UINT_TYPE, UINT_TYPE);
+
+UINT_TYPE
+C3(udivmod,NAME_MODE,4) (UINT_TYPE num, UINT_TYPE den, word_type modwanted)
+{
+  UINT_TYPE bit = 1;
+  UINT_TYPE res = 0;
+
+  while (den < num && bit && !(den & (1L << BITS_MINUS_1)))
+    {
+      den <<= 1;
+      bit <<= 1;
+    }
+  while (bit)
+    {
+      if (num >= den)
+       {
+         num -= den;
+         res |= bit;
+       }
+      bit >>= 1;
+      den >>= 1;
+    }
+  if (modwanted)
+    return num;
+  return res;
+}
+
+SINT_TYPE
+C3(__div,NAME_MODE,3) (SINT_TYPE a, SINT_TYPE b)
+{
+  word_type neg = 0;
+  SINT_TYPE res;
+
+  if (a < 0)
+    {
+      a = -a;
+      neg = !neg;
+    }
+
+  if (b < 0)
+    {
+      b = -b;
+      neg = !neg;
+    }
+
+  res = C3(udivmod,NAME_MODE,4) (a, b, 0);
+
+  if (neg)
+    res = -res;
+
+  return res;
+}
+
+SINT_TYPE
+C3(__mod,NAME_MODE,3) (SINT_TYPE a, SINT_TYPE b)
+{
+  word_type neg = 0;
+  SINT_TYPE res;
+
+  if (a < 0)
+    {
+      a = -a;
+      neg = 1;
+    }
+
+  if (b < 0)
+    b = -b;
+
+  res = C3(udivmod,NAME_MODE,4) (a, b, 1);
+
+  if (neg)
+    res = -res;
+
+  return res;
+}
+
+UINT_TYPE
+C3(__udiv,NAME_MODE,3) (UINT_TYPE a, UINT_TYPE b)
+{
+  return C3(udivmod,NAME_MODE,4) (a, b, 0);
+}
+
+UINT_TYPE
+C3(__umod,NAME_MODE,3) (UINT_TYPE a, UINT_TYPE b)
+{
+  return C3(udivmod,NAME_MODE,4) (a, b, 1);
+}
diff --git a/libgcc/config/rl78/rl78-mul.h b/libgcc/config/rl78/rl78-mul.h
new file mode 100644 (file)
index 0000000..fc2cd6c
--- /dev/null
@@ -0,0 +1,43 @@
+/* libgcc routines for RL78
+   Copyright (C) 2005, 2009, 2011
+   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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+UINT_TYPE C3(__mul,NAME_MODE,3)   (UINT_TYPE, UINT_TYPE);
+UINT_TYPE
+C3(__mul,NAME_MODE,3) (UINT_TYPE a, UINT_TYPE b)
+{
+  UINT_TYPE rv = 0;
+
+  char bit;
+
+  for (bit=0; b && bit<sizeof(UINT_TYPE)*8; bit++)
+    {
+      if (b & 1)
+       rv += a;
+      a <<= 1;
+      b >>= 1;
+    }
+  return rv;
+}
diff --git a/libgcc/config/rl78/t-rl78 b/libgcc/config/rl78/t-rl78
new file mode 100644 (file)
index 0000000..d1a82a2
--- /dev/null
@@ -0,0 +1,28 @@
+# Makefile fragment for building LIBGCC for the Renesas RL78 target.
+# Copyright (C) 2011 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/>.
+
+LIB2ADD = \
+       $(srcdir)/config/rl78/trampoline.S \
+       $(srcdir)/config/rl78/lib2div.c \
+       $(srcdir)/config/rl78/lib2mul.c \
+       $(srcdir)/config/rl78/lib2shift.c \
+       $(srcdir)/config/rl78/lshrsi3.S \
+       $(srcdir)/config/rl78/mulsi3.S \
+       $(srcdir)/config/rl78/cmpsi2.S
diff --git a/libgcc/config/rl78/trampoline.S b/libgcc/config/rl78/trampoline.S
new file mode 100644 (file)
index 0000000..ef3aa6c
--- /dev/null
@@ -0,0 +1,139 @@
+/* libgcc routines for RL78
+   Copyright (C) 2011
+   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.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* RL78 Trampoline support
+
+  Since the RL78's RAM is not in the first 64k, we cannot "just" use a
+  function pointer to point to a trampoline on the stack.  So, we
+  create N fixed trampolines that read from an array, and allocate
+  them as needed.
+
+*/
+
+r8     =       0xffef0
+r10    =       0xffef2
+r14    =       0xffef6
+
+       .data
+       .p2align        1
+trampoline_array:
+
+       .macro stub n
+
+       .text
+trampoline_\n:
+        .type   trampoline_\n, @function
+       movw    ax, !trampoline_chain_\n
+       movw    r14, ax
+       movw    ax, !trampoline_addr_\n
+       br      ax
+       .size   trampoline_\n, .-trampoline_\n
+
+       .data
+trampoline_frame_\n:
+       .short  0
+trampoline_stub_\n:
+       .short  trampoline_\n
+trampoline_chain_\n:
+       .short  0
+trampoline_addr_\n:
+       .short  0
+
+#define TO_FRAME 0
+#define TO_STUB  2
+#define TO_CHAIN 4
+#define TO_ADDR  6
+#define TO_SIZE  8
+
+       .endm
+
+       stub    0
+       stub    1
+       stub    2
+       stub    3
+       stub    4
+       stub    5
+
+trampoline_array_end:
+
+/* Given the function pointer in R8 and the static chain
+   pointer in R10, allocate a trampoline and return its address in
+   R8. */
+
+       .text
+       .global ___trampoline_init
+        .type   ___trampoline_init, @function
+___trampoline_init:
+
+       movw    hl, #trampoline_array
+1:
+       movw    ax, [hl + TO_ADDR]
+       cmpw    ax, #0
+       bz      $2f
+
+       movw    ax, hl
+       addw    ax, #TO_SIZE
+       movw    hl, ax
+       cmpw    ax, #trampoline_array_end
+       bnz     $1b
+       brk                     ; no more slots?
+
+2:     movw    ax, r8
+       movw    [hl + TO_ADDR], ax
+       movw    ax, r10
+       movw    [hl + TO_CHAIN], ax
+       movw    ax, sp
+       movw    [hl + TO_FRAME], ax
+
+       movw    ax, [hl + TO_STUB]
+       movw    r8, ax
+
+       ret
+       .size   ___trampoline_init, . - ___trampoline_init
+
+       .global ___trampoline_uninit
+        .type   ___trampoline_uninit, @function
+___trampoline_uninit:
+       movw    hl, #trampoline_array
+       movw    ax, sp
+       movw    bc, ax
+1:
+       movw    ax, [hl + TO_FRAME]
+       cmpw    ax, bc
+       bc      $2f
+
+       clrw    ax
+       movw    [hl + TO_ADDR], ax
+
+2:
+       movw    ax, hl
+       addw    ax, #TO_SIZE
+       movw    hl, ax
+       cmpw    ax, #trampoline_array_end
+       bnz     $1b
+
+       ret
+       .size   ___trampoline_uninit, . - ___trampoline_uninit