diff options
author | DJ Delorie <dj@redhat.com> | 2011-11-29 16:36:43 -0500 |
---|---|---|
committer | DJ Delorie <dj@gcc.gnu.org> | 2011-11-29 16:36:43 -0500 |
commit | 85b8555ed3c45855b237b0f3a044eefb9382255c (patch) | |
tree | 65c814557c3c0e61f746e922c96ca834174464c4 /gcc/config | |
parent | c360c0fb8a1dd8ef61d986671d02071075d2c0b9 (diff) | |
download | gcc-85b8555ed3c45855b237b0f3a044eefb9382255c.zip gcc-85b8555ed3c45855b237b0f3a044eefb9382255c.tar.gz gcc-85b8555ed3c45855b237b0f3a044eefb9382255c.tar.bz2 |
.
* 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.
From-SVN: r181819
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/rl78/constraints.md | 266 | ||||
-rw-r--r-- | gcc/config/rl78/predicates.md | 60 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-c.c | 43 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-expand.md | 256 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-opts.h | 30 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-protos.h | 43 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-real.md | 339 | ||||
-rw-r--r-- | gcc/config/rl78/rl78-virt.md | 259 | ||||
-rw-r--r-- | gcc/config/rl78/rl78.c | 2705 | ||||
-rw-r--r-- | gcc/config/rl78/rl78.h | 462 | ||||
-rw-r--r-- | gcc/config/rl78/rl78.md | 320 | ||||
-rw-r--r-- | gcc/config/rl78/rl78.opt | 43 | ||||
-rw-r--r-- | gcc/config/rl78/t-rl78 | 22 |
13 files changed, 4848 insertions, 0 deletions
diff --git a/gcc/config/rl78/constraints.md b/gcc/config/rl78/constraints.md new file mode 100644 index 0000000..a89e1a2 --- /dev/null +++ b/gcc/config/rl78/constraints.md @@ -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/>. + +; 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 index 0000000..343cd8a --- /dev/null +++ b/gcc/config/rl78/predicates.md @@ -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/>. + +(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 index 0000000..98f704c --- /dev/null +++ b/gcc/config/rl78/rl78-c.c @@ -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 index 0000000..9707e5b --- /dev/null +++ b/gcc/config/rl78/rl78-expand.md @@ -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/>. + +;;---------- 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 index 0000000..c0caa50 --- /dev/null +++ b/gcc/config/rl78/rl78-opts.h @@ -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 index 0000000..f4ce186 --- /dev/null +++ b/gcc/config/rl78/rl78-protos.h @@ -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/>. */ + +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 index 0000000..e13dc4e --- /dev/null +++ b/gcc/config/rl78/rl78-real.md @@ -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/>. + +;; 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 index 0000000..bd22416 --- /dev/null +++ b/gcc/config/rl78/rl78-virt.md @@ -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/>. + +;; 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 index 0000000..460538e --- /dev/null +++ b/gcc/config/rl78/rl78.c @@ -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" + +static inline bool is_interrupt_func (const_tree decl); +static inline bool is_brk_interrupt_func (const_tree decl); +static void rl78_reorg (void); + + +/* 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); +} + + +#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); +} + +/* 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 } +}; + + + +/* 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; +} + +/* 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 +} + + + +/* 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; + } + } +} + +/* + 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); +} + + +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 index 0000000..5980c41 --- /dev/null +++ b/gcc/config/rl78/rl78.h @@ -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/>. */ + + +#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}} \ +" + + +#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 + + +/* 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 + + +/* FIXME */ +#define NO_PROFILE_COUNTERS 1 +#define PROFILE_BEFORE_PROLOGUE 1 + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tbsr\t__mcount\n"); + + +#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)) + + +#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) + +/* 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) + +#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 + + +#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 index 0000000..220ed99 --- /dev/null +++ b/gcc/config/rl78/rl78.md @@ -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/>. + +(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 index 0000000..d4858ba --- /dev/null +++ b/gcc/config/rl78/rl78.opt @@ -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 index 0000000..9d08f1f --- /dev/null +++ b/gcc/config/rl78/t-rl78 @@ -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) $< |