From cc7232b999b8336cf4e261407ed9289c77bed1f0 Mon Sep 17 00:00:00 2001 From: Jojo Date: Fri, 17 Aug 2018 19:03:27 +0000 Subject: [PATCH] C-SKY port: Backend implementation 2018-08-17 Jojo Huibin Wang Sandra Loosemore Chung-Lin Tang C-SKY port: Backend implementation gcc/ * config/csky/*: New. * common/config/csky/*: New. Co-Authored-By: Chung-Lin Tang Co-Authored-By: Huibin Wang Co-Authored-By: Sandra Loosemore From-SVN: r263628 --- gcc/ChangeLog | 10 + gcc/common/config/csky/csky-common.c | 42 + gcc/config/csky/constraints.md | 174 + gcc/config/csky/csky-elf.h | 81 + gcc/config/csky/csky-linux-elf.h | 132 + gcc/config/csky/csky-protos.h | 71 + gcc/config/csky/csky.c | 6795 +++++++++++++++++++++++ gcc/config/csky/csky.h | 1054 ++++ gcc/config/csky/csky.md | 3798 +++++++++++++ gcc/config/csky/csky.opt | 173 + gcc/config/csky/csky_cores.def | 199 + gcc/config/csky/csky_genopt.sh | 97 + gcc/config/csky/csky_insn_dsp.md | 95 + gcc/config/csky/csky_insn_fpu.md | 567 ++ gcc/config/csky/csky_isa.def | 59 + gcc/config/csky/csky_isa.h | 47 + gcc/config/csky/csky_opts.h | 63 + gcc/config/csky/csky_pipeline_ck801.md | 54 + gcc/config/csky/csky_pipeline_ck802.md | 77 + gcc/config/csky/csky_pipeline_ck803.md | 64 + gcc/config/csky/csky_pipeline_ck810.md | 34 + gcc/config/csky/csky_tables.opt | 230 + gcc/config/csky/predicates.md | 298 + gcc/config/csky/print-sysroot-suffix.sh | 147 + gcc/config/csky/t-csky | 29 + gcc/config/csky/t-csky-elf | 107 + gcc/config/csky/t-csky-linux | 52 + gcc/config/csky/t-sysroot-suffix | 28 + 28 files changed, 14577 insertions(+) create mode 100644 gcc/common/config/csky/csky-common.c create mode 100644 gcc/config/csky/constraints.md create mode 100644 gcc/config/csky/csky-elf.h create mode 100644 gcc/config/csky/csky-linux-elf.h create mode 100644 gcc/config/csky/csky-protos.h create mode 100644 gcc/config/csky/csky.c create mode 100644 gcc/config/csky/csky.h create mode 100644 gcc/config/csky/csky.md create mode 100644 gcc/config/csky/csky.opt create mode 100644 gcc/config/csky/csky_cores.def create mode 100644 gcc/config/csky/csky_genopt.sh create mode 100644 gcc/config/csky/csky_insn_dsp.md create mode 100644 gcc/config/csky/csky_insn_fpu.md create mode 100644 gcc/config/csky/csky_isa.def create mode 100644 gcc/config/csky/csky_isa.h create mode 100644 gcc/config/csky/csky_opts.h create mode 100644 gcc/config/csky/csky_pipeline_ck801.md create mode 100644 gcc/config/csky/csky_pipeline_ck802.md create mode 100644 gcc/config/csky/csky_pipeline_ck803.md create mode 100644 gcc/config/csky/csky_pipeline_ck810.md create mode 100644 gcc/config/csky/csky_tables.opt create mode 100644 gcc/config/csky/predicates.md create mode 100644 gcc/config/csky/print-sysroot-suffix.sh create mode 100644 gcc/config/csky/t-csky create mode 100644 gcc/config/csky/t-csky-elf create mode 100644 gcc/config/csky/t-csky-linux create mode 100644 gcc/config/csky/t-sysroot-suffix diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b1367f752b2..7b8ce718363 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2018-08-17 Jojo + Huibin Wang + Sandra Loosemore + Chung-Lin Tang + + C-SKY port: Backend implementation + + * config/csky/*: New. + * common/config/csky/*: New. + 2018-08-17 Jojo Huibin Wang Sandra Loosemore diff --git a/gcc/common/config/csky/csky-common.c b/gcc/common/config/csky/csky-common.c new file mode 100644 index 00000000000..39095bf2581 --- /dev/null +++ b/gcc/common/config/csky/csky-common.c @@ -0,0 +1,42 @@ +/* Common hooks for CSKY. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "common/common-target.h" +#include "common/common-target-def.h" + +/* Set default optimization options. */ +static const struct default_options csky_option_optimization_table[] = + { + /* Enable section anchors by default at -O1 or higher. */ + { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT + +#undef TARGET_OPTION_OPTIMIZATION_TABLE +#define TARGET_OPTION_OPTIMIZATION_TABLE csky_option_optimization_table + +struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER; diff --git a/gcc/config/csky/constraints.md b/gcc/config/csky/constraints.md new file mode 100644 index 00000000000..e08f7f8bde3 --- /dev/null +++ b/gcc/config/csky/constraints.md @@ -0,0 +1,174 @@ +;; Constraints for C-SKY. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +;; Register constraints. + +(define_register_constraint "a" "MINI_REGS" "r0 - r7") +(define_register_constraint "b" "LOW_REGS" "r0 - r15") +(define_register_constraint "c" "C_REGS" "C register") +(define_register_constraint "y" "HILO_REGS" "HI and LO registers") +(define_register_constraint "l" "LO_REGS" "LO register") +(define_register_constraint "h" "HI_REGS" "HI register") +(define_register_constraint "v" "V_REGS" "vector registers") +(define_register_constraint "z" "SP_REGS" "SP register") + + +;; Memory and misc constraints. + +(define_memory_constraint "Q" + "Memory operands with base register, index register and short displacement for FPUV2" + (match_test "csky_valid_fpuv2_mem_operand (op)")) + +(define_constraint "R" + "Memory operands whose address is a label_ref" + (and (match_code "mem") + (match_test "GET_CODE (XEXP (op, 0)) == LABEL_REF"))) + +(define_constraint "S" + "Symbol reference with optional offset" + (match_test "csky_symbolic_address_p (op)")) + + +;; Constant integer constraints. + +(define_constraint "I" + "Constant in range [0, 65535]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_I (ival)"))) + +(define_constraint "J" + "Constant in range [1, 32]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_J (ival)"))) + +(define_constraint "K" + "Constant in range [0, 31]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_K (ival)"))) + +(define_constraint "L" + "Constant in range [1, 8]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_L (ival)"))) + +(define_constraint "M" + "Constant in range [1, 4096]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_M (ival)"))) + +(define_constraint "N" + "Constant in range [1, 256]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_N (ival)"))) + +(define_constraint "O" + "Constant in range [0, 4095]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_O (ival)"))) + +(define_constraint "P" + "Constant in range [4, 508] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_P (ival)"))) + +(define_constraint "T" + "Constant in range [-256, -1]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_T (ival)"))) + +(define_constraint "Ua" + "Constant 0" + (and (match_code "const_int") + (match_test "ival == 0"))) + +(define_constraint "Ub" + "Unsigned int that is an exact power of 2" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Ub (ival)"))) + +(define_constraint "Uc" + "Unsigned int X such that X+1 is an exact power of 2" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uc (ival)"))) + +(define_constraint "Ud" + "64-bit int whose high/low words separately satisfy I, Ub, or Uc" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Ud (ival)"))) + +(define_constraint "Ug" + "Constant in range [-508, -4] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Ug (ival)"))) + +(define_constraint "Uh" + "Constant in range [-31, 0]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uh (ival)"))) + +(define_constraint "Uj" + "Constant in range [4, 1024] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uj (ival)"))) + +(define_constraint "Uk" + "Constant in range [1, 65536]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uk (ival)"))) + +(define_constraint "Ul" + "Constant in range [-1024, -4] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Ul (ival)"))) + +(define_constraint "Um" + "Constant in range [-4096, -1]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Um (ival)"))) + +(define_constraint "Un" + "Constant whose low 16 bits are all zeros" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_MOVIH (ival)"))) + +(define_constraint "Uo" + "Constant that can be synthesized with an extra instruction" + (and (match_code "const_int") + (match_test "csky_inlinable_constant (ival)"))) + +(define_constraint "Up" + "Constant in range [0, 255]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_N (ival + 1)"))) + +(define_constraint "Uq" + "Constant in range [0, 1020] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uj (ival + 4)"))) + +(define_constraint "Ur" + "Constant in range [-1020, -4] that is divisible by 4" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_Uj (-ival + 4)"))) + +(define_constraint "Us" + "Constant in range [-8, -1]" + (and (match_code "const_int") + (match_test "CSKY_CONST_OK_FOR_US (ival)"))) diff --git a/gcc/config/csky/csky-elf.h b/gcc/config/csky/csky-elf.h new file mode 100644 index 00000000000..822caed2f37 --- /dev/null +++ b/gcc/config/csky/csky-elf.h @@ -0,0 +1,81 @@ +/* Declarations for bare-metal C-SKY targets. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + + +/****************************************************************** + * Run-time Target Specification * + ******************************************************************/ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "crt0.o%s crti.o%s crtbegin.o%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +#undef CC1_SPEC +#define CC1_SPEC \ + "%{EB:-EB} \ + %{EL:-EL} \ + %{fpic|fPIC:-DPIC} \ + %{march=ck803s:-march=ck803} \ + " + +#undef ASM_SPEC +#define ASM_SPEC \ + "%{mbig-endian:-mbig-endian} \ + %{EB:-EB} \ + %{EL:-EL} \ + %{fpic|fPIC:-pic} \ + %{mcpu=*:-mcpu=%*} \ + %{march=*:-march=%*} \ + %{mhard-float:-mhard-float} \ + %{melrw:-melrw} \ + %{mno-elrw:-mno-elrw} \ + %{mistack:-mistack} \ + %{mno-istack:-mno-istack} \ + %{mmp:-mmp} \ + %{mcp:-mcp} \ + %{mcache:-mcache} \ + %{msecurity|mmac:-msecurity} \ + %{mtrust:-mtrust} \ + %{mdsp:-mdsp} \ + %{medsp:-medsp} \ + %{mvdsp:-mvdsp} \ + " + +#undef LINK_SPEC +#define LINK_SPEC \ +"%{mbig-endian:-EB} \ + %{EB:-EB} \ + %{EL:-EL} -X" + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{pthread:-lpthread} -lc %{mccrt:-lcc-rt}" +/* FIXME add this to LIB_SPEC when need */ +/* %{!shared:%{profile:-lc_p}%{!profile:-lc}}" */ + + +#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" + +/* Disable features only for Linux toolchains. */ +#undef TARGET_POSIX_IO +#define TARGET_CSKY_LINUX 0 diff --git a/gcc/config/csky/csky-linux-elf.h b/gcc/config/csky/csky-linux-elf.h new file mode 100644 index 00000000000..19a553cb08e --- /dev/null +++ b/gcc/config/csky/csky-linux-elf.h @@ -0,0 +1,132 @@ +/* Declarations for C-SKY targets running Linux. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +/****************************************************************** + * Run-time Target Specification * + ******************************************************************/ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: %{pie:Scrt1.o%s;:crt1.o%s}} \ + crti.o%s %{static:crtbeginT.o%s;shared|pie:crtbeginS.o%s;:crtbegin.o%s}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s" + +#undef CC1_SPEC +#define CC1_SPEC \ + "%{EB:-EB} \ + %{EL:-EL} \ + " + +#undef ASM_SPEC +#define ASM_SPEC \ + "%{mbig-endian:-mbig-endian} \ + %{EB:-EB} \ + %{EL:-EL} \ + %{fpic|fPIC:-pic} \ + %{mcpu=*:-mcpu=%*} \ + %{march=*:-march=%*} \ + %{mhard-float:-mhard-float} \ + %{melrw:-melrw} \ + %{mno-elrw:-mno-elrw} \ + %{mistack:-mistack} \ + %{mno-istack:-mno-istack} \ + %{mmp:-mmp} \ + %{mcp:-mcp} \ + %{mcache:-mcache} \ + %{msecurity|mmac:-msecurity} \ + %{mtrust:-mtrust} \ + %{mdsp:-mdsp} \ + %{medsp:-medsp} \ + %{mvdsp:-mvdsp} \ + " + +#define LINUX_DYNAMIC_LINKER "/lib/ld.so.1" + +#define LINUX_TARGET_LINK_SPEC "%{h*} %{version:-v} \ + %{b} \ + %{static:-Bstatic} \ + %{shared:-shared} \ + %{symbolic:-Bsymbolic} \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!shared:-dynamic-linker " LINUX_DYNAMIC_LINKER "}} \ + -X \ + %{mbig-endian:-EB} %{mlittle-endian:-EL} \ + %{EB:-EB} %{EL:-EL}" + + +#undef LINK_SPEC +#define LINK_SPEC LINUX_TARGET_LINK_SPEC + + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{pthread:-lpthread} -lc %{mccrt:-lcc-rt}" +/* FIXME add this to LIB_SPEC when need */ +/* %{!shared:%{profile:-lc_p}%{!profile:-lc}}" */ + +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + GNU_USER_TARGET_OS_CPP_BUILTINS (); \ + } \ + while (0) + +/* In crtstuff.c to control section in where code resides. + We have to write it as asm code. */ +#ifdef __PIC__ +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP "\n" \ + "\tgrs\tr3, .Lgetpc_"#FUNC"\n\t" \ + ".Lgetpc_"#FUNC":\n\t" \ + "\tlrw\tr2,\t.Lgetpc_"#FUNC"@GOTPC\n\t" \ + "\taddu\tr3, r2\n\t" \ + "\tlrw\tr2, "#FUNC"@GOTOFF\n\t" \ + "\taddu\tr2, r3\n\t" \ + "\tjsr\tr2\n\t"); \ + FORCE_CODE_SECTION_ALIGN \ + asm (TEXT_SECTION_ASM_OP); +#endif + +#undef CPP_SPEC +#define CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}" + +#undef FUNCTION_PROFILER +#define SAVE_LR \ + "push\tlr" +#define FUNCTION_PROFILER(file, labelno) \ + fprintf (file, "\t%s\n\tjbsr\t_mcount\n", SAVE_LR); +#define NO_PROFILE_COUNTERS 1 + +/* Enable features only for Linux toolchains. */ +#define TARGET_CSKY_LINUX 1 + +/* Clear the instruction cache from `BEG' to `END'. */ +#define CLEAR_INSN_CACHE(BEG, END) \ + cacheflush (BEG, END-BEG, 3) + +/* For __clear_cache in libgcc2.c. The declaration is copied from + . */ +#ifdef IN_LIBGCC2 +extern int cacheflush (void *__addr, const int __nbytes, const int __op); +#endif diff --git a/gcc/config/csky/csky-protos.h b/gcc/config/csky/csky-protos.h new file mode 100644 index 00000000000..c10267a91ab --- /dev/null +++ b/gcc/config/csky/csky-protos.h @@ -0,0 +1,71 @@ +/* Prototype declarations for the C-SKY back end. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +#ifndef GCC_CSKY_PROTOS_H +#define GCC_CSKY_PROTOS_H + +extern bool csky_simple_addr_operand_p (rtx); +extern bool csky_symbolic_address_p (rtx); +extern bool csky_legitimate_pic_operand_p (rtx); + +extern void csky_cpu_cpp_builtins (cpp_reader *); + +extern bool csky_inlinable_constant (HOST_WIDE_INT value); +extern bool csky_shifted_imm8_constant (unsigned HOST_WIDE_INT, + unsigned int *, unsigned int *); +extern bool csky_valid_fpuv2_mem_operand (rtx); + +extern bool csky_minipool_load_p (rtx_insn *); +extern const char *csky_output_move (rtx insn, rtx *, machine_mode); +extern const char *csky_output_movedouble (rtx *, machine_mode); +extern const char *csky_output_ck801_move (rtx, rtx *, machine_mode); +extern const char *csky_output_ck801_movedouble (rtx *, machine_mode); +extern char *csky_output_call (rtx *, int); +extern const char *csky_output_casesi (rtx *); + +extern bool csky_split_and (rtx *); +extern bool csky_split_ior (rtx *); +extern bool csky_split_xor (rtx *); + +#ifdef RTX_CODE +extern bool csky_emit_compare (enum rtx_code, rtx, rtx); +extern bool csky_emit_compare_float (enum rtx_code, rtx, rtx); +#endif /* RTX_CODE */ + +extern rtx csky_return_addr (int, rtx); +extern void csky_init_expanders (void); +extern HOST_WIDE_INT csky_initial_elimination_offset (int, int); +extern void csky_expand_prologue (void); +extern void csky_expand_epilogue (void); +extern const char *csky_output_return_instruction (void); +extern void csky_set_eh_return_address (rtx, rtx); + +extern bool csky_symbol_mentioned_p (rtx); +extern bool csky_label_mentioned_p (rtx); +extern rtx csky_legitimize_pic_address (rtx, rtx, bool); + +extern bool csky_tls_referenced_p (rtx); +extern rtx csky_legitimize_tls_address (rtx, rtx); + +extern int csky_compute_pushpop_length (rtx *); + +extern int csky_default_branch_cost (bool, bool); +extern bool csky_default_logical_op_non_short_circuit (void); +#endif /* GCC_CSKY_PROTOS_H */ diff --git a/gcc/config/csky/csky.c b/gcc/config/csky/csky.c new file mode 100644 index 00000000000..a9e196ba3f1 --- /dev/null +++ b/gcc/config/csky/csky.c @@ -0,0 +1,6795 @@ +/* GCC backend functions for C-SKY targets. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +#define IN_TARGET_CODE 1 + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "memmodel.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "cfghooks.h" +#include "df.h" +#include "tm_p.h" +#include "stringpool.h" +#include "attribs.h" +#include "optabs.h" +#include "regs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "cgraph.h" +#include "c-family/c-common.h" +#include "cpplib.h" +#include "diagnostic-core.h" +#include "alias.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "calls.h" +#include "varasm.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "reload.h" +#include "explow.h" +#include "expr.h" +#include "cfgrtl.h" +#include "sched-int.h" +#include "common/common-target.h" +#include "langhooks.h" +#include "intl.h" +#include "libfuncs.h" +#include "params.h" +#include "opts.h" +#include "dumpfile.h" +#include "target-globals.h" +#include "builtins.h" +#include "tm-constrs.h" +#include "rtl-iter.h" +#include "pass_manager.h" +#include "tree-pass.h" +#include "context.h" + +/* This file should be included last. */ +#include "target-def.h" + +/* Stack and register size macros. */ + +#define CSKY_NUM_WORDS(SIZE) \ + (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) +#define CSKY_NUM_REGS(MODE) \ + CSKY_NUM_WORDS (GET_MODE_SIZE (MODE)) +#define CSKY_STACK_ALIGN(SIZE) \ + (CSKY_NUM_WORDS (SIZE) * UNITS_PER_WORD) + +/* Offsets and range macros. */ + +#define CSKY_LD16_MAX_OFFSET(MODE) \ + (31 * GET_MODE_SIZE (MODE)) +#define CSKY_LD32_MAX_OFFSET(MODE) \ + (4095 * GET_MODE_SIZE (MODE)) +#define CSKY_LD16_OFFSET_MASK(MODE) \ + (CSKY_LD16_MAX_OFFSET (MODE) + GET_MODE_SIZE (MODE) - 1) + +#define CSKY_ADDI16_MAX_IMM 256 +#define CSKY_SUBI16_MAX_IMM 256 + +#define CSKY_CONSTPOOL_LABEL_PREFIX "LCP" + +/* Array of the smallest class containing reg number REGNO, indexed by + REGNO. Used by REGNO_REG_CLASS. */ +enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] = +{ + /* Registers r0-r7. */ + MINI_REGS, MINI_REGS, MINI_REGS, MINI_REGS, + MINI_REGS, MINI_REGS, MINI_REGS, MINI_REGS, + /* Registers r8-r15. */ + LOW_REGS, LOW_REGS, LOW_REGS, LOW_REGS, + LOW_REGS, LOW_REGS, SP_REGS, LOW_REGS, + /* Registers r16-r31. */ + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + /* Reserved. */ + RESERVE_REGS, + /* CC,HI,LO registers. */ + C_REGS, HI_REGS, LO_REGS, + /* Reserved. */ + RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, + RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, + RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, + RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, RESERVE_REGS, + /* Vec registers. */ + V_REGS, V_REGS, V_REGS, V_REGS, + V_REGS, V_REGS, V_REGS, V_REGS, + V_REGS, V_REGS, V_REGS, V_REGS, + V_REGS, V_REGS, V_REGS, V_REGS, + /* Reserved. */ + RESERVE_REGS, RESERVE_REGS, + /* Register epc. */ + OTHER_REGS +}; + +/* Arrays that map GCC register numbers to debugger register numbers, + '-1' means that is INVALID_REGNUM. + TODO: which rules according to here ? */ +const int csky_dbx_regno[FIRST_PSEUDO_REGISTER] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + -1, -1, 36, 37, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, -1, -1, 72 +}; + +/* Table of machine attributes. */ +static tree csky_handle_fndecl_attribute (tree *, tree, tree, int, bool *); +static tree csky_handle_isr_attribute (tree *, tree, tree, int, bool *); +static const struct attribute_spec csky_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, + affects_type_identity, handler, exclude } */ + { "naked", 0, 0, true, false, false, false, csky_handle_fndecl_attribute, NULL }, + /* Interrupt Service Routines have special prologue and epilogue requirements. */ + { "interrupt", 0, 1, false, false, false, false, csky_handle_isr_attribute, NULL }, + { "isr", 0, 1, false, false, false, false, csky_handle_isr_attribute, NULL }, + { NULL, 0, 0, false, false, false, false, NULL, NULL } +}; + +/* A C structure for machine-specific, per-function data. + This is added to the cfun structure. */ +typedef struct GTY(()) machine_function +{ + /* Records if LR has to be saved for far jumps. */ + int far_jump_used; + /* Records the type of the current function. */ + unsigned long func_type; + /* Record if the function has a variable argument list. */ + int uses_anonymous_args; + + /* Stack frame layout information. If frame_init_p is true, + these fields have been initialized and don't need to be + recomputed. */ + unsigned int reg_mask; /* non-volatile reg saves */ + int arg_size; /* stdarg spills (bytes) */ + int reg_size; /* non-volatile reg saves (bytes) */ + int local_size; /* locals */ + int outbound_size; /* arg overflow on calls out */ + int frame_size; /* total static size of stack frame */ + int local_offset; + int reg_offset; + int arg_offset; + int frame_init_p; + +} machine_function; + +/* These macros are for the func_type values above. */ +#define CSKY_FT_TYPE_MASK ((1 << 3) - 1) +#define CSKY_FT_UNKNOWN 0 /* Type not been determined */ +#define CSKY_FT_NORMAL 1 /* Normal function */ +#define CSKY_FT_ISR 4 /* Interrupt service routine */ +#define CSKY_FT_FIQ 5 /* Fast interrupt service routine */ +#define CSKY_FT_EXCEPTION 6 /* Exception handler */ +#define CSKY_FT_INTERRUPT (1 << 2) /* overlap CSKY_FT_ISR */ +#define CSKY_FT_NAKED (1 << 3) /* No prologue and epilogue */ +#define CSKY_FUNCTION_TYPE(t) ((t) & CSKY_FT_TYPE_MASK) +#define CSKY_FUNCTION_IS_INTERRUPT(t) ((t) & CSKY_FT_INTERRUPT) +#define CSKY_FUNCTION_IS_NAKED(t) ((t) & CSKY_FT_NAKED) + +struct csky_processors +{ + const char *const name; + enum csky_processor_type core; + const char *arch; + enum csky_base_architecture base_arch; + enum csky_isa_feature isa_bits[CSKY_ISA_FEATURE_GET (max)]; +}; + +static struct csky_processors all_cores[] = +{ +#undef CSKY_CORE +#define CSKY_CORE(NAME, CORE, X, ARCH, ISA) \ + {NAME, TARGET_CPU_##CORE, #ARCH, CSKY_BASE_ARCH_##ARCH, \ + {ISA CSKY_ISA_FEATURE_GET (none)}}, +#include "csky_cores.def" +#undef CSKY_CORE + {NULL, TARGET_CPU_csky_none, NULL, CSKY_BASE_ARCH_NONE, \ + {CSKY_ISA_FEATURE_GET (none)}} +}; + +static struct csky_processors all_architectures[] = +{ +#undef CSKY_ARCH +#define CSKY_ARCH(NAME, CORE, ARCH, ISA) \ + {NAME, TARGET_CPU_##CORE, #ARCH, CSKY_BASE_ARCH_##ARCH, \ + {ISA CSKY_ISA_FEATURE_GET (none)}}, +#include "csky_cores.def" +#undef CSKY_ARCH + {NULL, TARGET_CPU_csky_none, NULL, CSKY_BASE_ARCH_NONE, \ + {CSKY_ISA_FEATURE_GET (none)}} +}; + +struct csky_fpu_desc +{ + const char *name; + enum csky_isa_feature isa_bits[CSKY_ISA_FEATURE_GET (max)]; +}; + +static const struct csky_fpu_desc all_fpus[] = +{ +#undef CSKY_FPU +#define CSKY_FPU(NAME, CNAME, ISA) \ + {NAME, {ISA CSKY_ISA_FEATURE_GET (none)}}, +#include "csky_cores.def" +#undef CSKY_FPU +}; + +/* Active target architecture. */ +struct csky_build_target +{ + /* Name of the target CPU, if known, or NULL if the target CPU was not + specified by the user (and inferred from the -march option). */ + const char *core_name; + /* Name of the target ARCH. NULL if there is a selected CPU. */ + const char *arch_name; + /* Preprocessor substring (never NULL). */ + const char *arch_pp_name; + /* CPU identifier for the core we're compiling for (architecturally). */ + enum csky_processor_type arch_core; + /* The base architecture value. */ + enum csky_base_architecture base_arch; + /* Bitmap encapsulating the isa_bits for the target environment. */ + sbitmap isa; +}; + +struct csky_build_target csky_active_target; + +/* The following are used in the .md file as equivalents to bits. */ +int csky_arch_isa_features[CSKY_ISA_FEATURE_GET (max)] = {0}; + +/* The highest CSKY architecture version supported by the target. */ +enum csky_base_architecture csky_base_arch = CSKY_TARGET_ARCH_GET (NONE); + +/* Forward definitions of types. */ +typedef struct minipool_node Mnode; +typedef struct minipool_fixup Mfix; + +static GTY(()) int tls_labelno; + + +/* Maximum constant offset that can be added/subtracted from SP in a + single instruction. For ck801, this is for addsp/subsp, otherwise + it is the range of addi/subi. */ +#define CSKY_MAX_SP_ADJUST \ + (CSKY_TARGET_ARCH (CK801) ? 508 : 4096) + + +/* Implement TARGET_CPU_CPP_BUILTINS. */ + +#define builtin_define(MACRO) cpp_define (pfile, MACRO) + +void +csky_cpu_cpp_builtins (cpp_reader *pfile) +{ + const char *arch_name = csky_active_target.arch_pp_name; + char *pp_name = (char *) alloca (1 + strlen (arch_name) + 4); + sprintf (pp_name, "__%s__", arch_name); + builtin_define (pp_name); + + builtin_define ("__csky__=2"); + builtin_define ("__CSKY__=2"); + builtin_define ("__ckcore__=2"); + builtin_define ("__CKCORE__=2"); + + builtin_define ("__CSKYABIV2__"); + builtin_define ("__cskyabiv2__"); + builtin_define ("__CSKYABI__=2"); + builtin_define ("__cskyabi__=2"); + + if (TARGET_BIG_ENDIAN) + { + builtin_define ("__ckcoreBE__"); + builtin_define ("__cskyBE__"); + builtin_define ("__cskybe__"); + builtin_define ("__CSKYBE__"); + } + else + { + builtin_define ("__ckcoreLE__"); + builtin_define ("__cskyLE__"); + builtin_define ("__cskyle__"); + builtin_define ("__CSKYLE__"); + } + + if (TARGET_HARD_FLOAT) + { + builtin_define ("__csky_hard_float__"); + builtin_define ("__CSKY_HARD_FLOAT__"); + } + else + { + builtin_define ("__csky_soft_float__"); + builtin_define ("__CSKY_SOFT_FLOAT__"); + } + + if (CSKY_ISA_FEATURE (fpv2_sf)) + { + builtin_define ("__csky_fpuv2__"); + builtin_define ("__CSKY_FPUV2__"); + } + + if (TARGET_ELRW) + { + builtin_define ("__csky_elrw__"); + builtin_define ("__CSKY_ELRW__"); + } + if (TARGET_ISTACK) + { + builtin_define ("__csky_istack__"); + builtin_define ("__CSKY_ISTACK__"); + } + if (TARGET_MP) + { + builtin_define ("__csky_mp__"); + builtin_define ("__CSKY_MP__"); + } + if (TARGET_CP) + { + builtin_define ("__csky_cp__"); + builtin_define ("__CSKY_CP__"); + } + if (TARGET_CACHE) + { + builtin_define ("__csky_cache__"); + builtin_define ("__CSKY_CACHE__"); + } + if (TARGET_SECURITY) + { + builtin_define ("__csky_security__"); + builtin_define ("__CSKY_SECURITY__"); + } + if (TARGET_TRUST) + { + builtin_define ("__csky_trust__"); + builtin_define ("__CSKY_TRUST__"); + } + if (TARGET_DSP) + { + builtin_define ("__csky_dsp__"); + builtin_define ("__CSKY_DSP__"); + } + if (TARGET_EDSP) + { + builtin_define ("__csky_edsp__"); + builtin_define ("__CSKY_EDSP__"); + } + if (TARGET_VDSP) + { + builtin_define ("__csky_vdsp__"); + builtin_define ("__CSKY_VDSP__"); + } +} + + +/****************************************************************** + * Storage Layout * + ******************************************************************/ + + +#undef TARGET_PROMOTE_FUNCTION_MODE +#define TARGET_PROMOTE_FUNCTION_MODE \ + default_promote_function_mode_always_promote + +#undef TARGET_CONSTANT_ALIGNMENT +#define TARGET_CONSTANT_ALIGNMENT csky_constant_alignment + + +/****************************************************************** + * Stack Layout and Calling Conventions * + ******************************************************************/ + +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE csky_can_eliminate + +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG csky_function_arg + +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE csky_function_arg_advance + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE csky_function_value + +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE csky_libcall_value + +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P csky_function_value_regno_p + +#undef TARGET_SPLIT_COMPLEX_ARG +#define TARGET_SPLIT_COMPLEX_ARG hook_bool_const_tree_true + +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true + +#undef TARGET_MUST_PASS_IN_STACK +#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size + +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES csky_arg_partial_bytes + +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK csky_output_mi_thunk + +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK \ + hook_bool_const_tree_hwi_hwi_const_tree_true + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE csky_output_function_prologue + +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE csky_output_function_epilogue + +#undef TARGET_WARN_FUNC_RETURN +#define TARGET_WARN_FUNC_RETURN csky_warn_func_return + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY csky_return_in_memory + + +/****************************************************************** + * Implementing the Varargs Macros * + ******************************************************************/ + + +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS csky_setup_incoming_varargs + + +/****************************************************************** + * Implicit Calls to Library Routines * + ******************************************************************/ + + +#undef TARGET_INIT_LIBFUNCS +#define TARGET_INIT_LIBFUNCS csky_init_libfuncs + + +/****************************************************************** + * Dividing the Output into Sections (Texts, Data, . . . ) * + ******************************************************************/ + + +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS TARGET_CSKY_LINUX + + +/****************************************************************** + * Defining target-specific uses of __attribute__ * + ******************************************************************/ + + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE csky_attribute_table + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE csky_option_override + + +/* Implement the BRANCH_COST target macro. */ + +int +csky_default_branch_cost (bool speed_p ATTRIBUTE_UNUSED, + bool predictable_p ATTRIBUTE_UNUSED) +{ + return csky_branch_cost; +} + +bool +csky_default_logical_op_non_short_circuit (void) +{ + return BRANCH_COST (optimize_function_for_speed_p (cfun), false) >= 2; +} + +/****************************************************************** + * Register Usage * + ******************************************************************/ + +#undef TARGET_HARD_REGNO_NREGS +#define TARGET_HARD_REGNO_NREGS csky_hard_regno_nregs + +#undef TARGET_HARD_REGNO_MODE_OK +#define TARGET_HARD_REGNO_MODE_OK csky_hard_regno_mode_ok + +#undef TARGET_MODES_TIEABLE_P +#define TARGET_MODES_TIEABLE_P csky_modes_tieable_p + +#undef TARGET_CAN_CHANGE_MODE_CLASS +#define TARGET_CAN_CHANGE_MODE_CLASS csky_can_change_mode_class + +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE csky_conditional_register_usage + +#undef TARGET_CLASS_LIKELY_SPILLED_P +#define TARGET_CLASS_LIKELY_SPILLED_P csky_class_likely_spilled_p + +#undef TARGET_PREFERRED_RELOAD_CLASS +#define TARGET_PREFERRED_RELOAD_CLASS csky_preferred_reload_class + +#undef TARGET_CLASS_MAX_NREGS +#define TARGET_CLASS_MAX_NREGS csky_class_max_nregs + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD csky_secondary_reload + +#undef TARGET_SPILL_CLASS +#define TARGET_SPILL_CLASS csky_spill_class + + +/****************************************************************** + * Addressing Modes * + ******************************************************************/ + + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM csky_cannot_force_const_mem + +#undef TARGET_LEGITIMATE_CONSTANT_P +#define TARGET_LEGITIMATE_CONSTANT_P csky_legitimate_constant_p + +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS csky_legitimize_address + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P csky_legitimate_address_p + + +/****************************************************************** + * Others * + ******************************************************************/ + + +#undef TARGET_CANNOT_COPY_INSN_P +#define TARGET_CANNOT_COPY_INSN_P csky_cannot_copy_insn_p + + +/****************************************************************** + * Assembler Format * + ******************************************************************/ + + +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND csky_print_operand + +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS csky_print_operand_address + +#undef TARGET_ASM_UNALIGNED_HI_OP +#define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t" + +#undef TARGET_ASM_UNALIGNED_SI_OP +#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t" + +#undef TARGET_DWARF_REGISTER_SPAN +#define TARGET_DWARF_REGISTER_SPAN csky_dwarf_register_span + + +/****************************************************************** + * Miscellaneous Parameters * + ******************************************************************/ + + +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG csky_reorg + +#undef TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS +#define TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS csky_allocate_stack_slots_for_args + +#undef TARGET_HAVE_SPECULATION_SAFE_VALUE +#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed + + +/****************************************************************** + * Trampolines for Nested Functions * + ******************************************************************/ + + +#undef TARGET_ASM_TRAMPOLINE_TEMPLATE +#define TARGET_ASM_TRAMPOLINE_TEMPLATE csky_asm_trampoline_template +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT csky_trampoline_init + +/* The low bit is ignored by jsr and jmp instructions so is safe to use. */ +#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS +#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1 + +/****************************************************************** + * Describing Relative Costs of Operations * + ******************************************************************/ + + +#undef TARGET_REGISTER_MOVE_COST +#define TARGET_REGISTER_MOVE_COST csky_register_move_cost + +#undef TARGET_MEMORY_MOVE_COST +#define TARGET_MEMORY_MOVE_COST csky_memory_move_cost + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS csky_rtx_costs + +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST csky_address_cost + + +/****************************************************************** + * Anchor address * + ******************************************************************/ + + +/* FIXME: the max offset is related to mode size, the following is + defined according to SImode. How to deal with HImode and + QImode, and should the min offset be defined? */ +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET \ + ((TARGET_MINI_REGISTERS && optimize_size) ? 127 : 4095) + + +/****************************************************************** + * Condition Code Status * + ******************************************************************/ + + +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS csky_fixed_condition_code_regs + + +/****************************************************************** + * Adjusting the Instruction Scheduler * + ******************************************************************/ + + +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE csky_sched_issue_rate + +#undef TARGET_SCHED_ADJUST_COST +#define TARGET_SCHED_ADJUST_COST csky_sched_adjust_cost + + +/* The declaration of functions. */ +static void push_csky_minipool_fix (rtx_insn *, HOST_WIDE_INT, rtx *, + machine_mode, rtx); +static void csky_print_operand (FILE *stream, rtx x, int code); + + +/* Define a table to map ISR attribute arguments onto function type + modifiers. */ + +typedef struct +{ + const char *const arg; + const unsigned long return_value; +} isr_attribute_entry; + +static const isr_attribute_entry isr_attribute_map[] = +{ + {"irq", CSKY_FT_ISR }, + {"IRQ", CSKY_FT_ISR }, + {"fiq", CSKY_FT_FIQ }, + {"FIQ", CSKY_FT_FIQ }, + {NULL, CSKY_FT_NORMAL } +}; + + +/* Return the function type of the current function, if it has not been + determined, return CSKY_FT_UNKNOWN. */ + +static unsigned long +get_csky_isr_type (tree argument) +{ + const isr_attribute_entry *ptr; + const char *arg; + + /* if argument is NULL, set default value ISR. */ + if (argument == NULL_TREE) + return CSKY_FT_ISR; + + if (TREE_VALUE (argument) == NULL_TREE + || TREE_CODE (TREE_VALUE (argument)) != STRING_CST) + return CSKY_FT_UNKNOWN; + + arg = TREE_STRING_POINTER (TREE_VALUE (argument)); + + for (ptr = isr_attribute_map; ptr->arg != NULL; ptr++) + if (strcmp (arg, ptr->arg) == 0) + return ptr->return_value; + + return CSKY_FT_UNKNOWN; +} + +/* Classify cfun as a normal function or some sort of interrupt + handler, and set the corresponding bits in cfun->machine->func_type. */ + +static unsigned long +get_csky_current_func_type (void) +{ + if (CSKY_FUNCTION_TYPE (cfun->machine->func_type) == CSKY_FT_UNKNOWN) + { + unsigned long type = CSKY_FT_UNKNOWN; + tree a; + tree attr; + + gcc_assert (TREE_CODE (current_function_decl) == FUNCTION_DECL); + + attr = DECL_ATTRIBUTES (current_function_decl); + a = lookup_attribute ("naked", attr); + if (a != NULL_TREE) + type |= CSKY_FT_NAKED; + a = lookup_attribute ("isr", attr); + if (a == NULL_TREE) + a = lookup_attribute ("interrupt", attr); + if (a == NULL_TREE) + type |= CSKY_FT_NORMAL; + else + type |= get_csky_isr_type (TREE_VALUE (a)); + + cfun->machine->func_type = type; + } + + return cfun->machine->func_type; +} + +/* These typedefs are located at the start of this file, so that + they can be used in the prototypes there. This comment is to + remind readers of that fact so that the following structures + can be understood more easily. + + typedef struct minipool_node Mnode; + typedef struct minipool_fixup Mfix; */ + +struct minipool_node +{ + /* Doubly linked chain of entries. */ + Mnode *next; + Mnode *prev; + /* The maximum offset into the code that this entry can be placed. While + pushing fixes for forward references, all entries are sorted in order + of increasing max_address. */ + HOST_WIDE_INT max_address; + /* Similarly for an entry inserted for a backwards ref. */ + HOST_WIDE_INT min_address; + /* The number of fixes referencing this entry. This can become zero + if we "unpush" an entry. In this case we ignore the entry when we + come to emit the code. */ + int refcount; + /* The offset from the start of the minipool. */ + HOST_WIDE_INT offset; + /* The value in table. */ + rtx value; + /* The mode of value. */ + machine_mode mode; + /* The size of the value. */ + int fix_size; +}; + +struct minipool_fixup +{ + Mfix *next; + rtx_insn *insn; + HOST_WIDE_INT address; + rtx *loc; + machine_mode mode; + int fix_size; + rtx value; + Mnode *minipool; + HOST_WIDE_INT forwards; + HOST_WIDE_INT backwards; +}; + +static Mnode *minipool_vector_head; +static Mnode *minipool_vector_tail; +static rtx minipool_vector_label; +static HOST_WIDE_INT constpool_label_no = 0; + +/* Obstack for minipool constant handling. */ +static struct obstack minipool_obstack; +static char *minipool_startobj; +/* The linked list of all minipool fixes required for this function. */ +Mfix *minipool_fix_head; +Mfix *minipool_fix_tail; +/* The fix entry for the current minipool, once it has been placed. */ +Mfix *minipool_barrier; + +/* Allow GC scanning of the minipool obstack. */ +static void +csky_add_gc_roots (void) +{ + gcc_obstack_init (&minipool_obstack); + minipool_startobj = (char *) obstack_alloc (&minipool_obstack, 0); +} + +/* Implement TARGET_CONSTANT_ALIGNMENT. + Make strings word-aligned so strcpy from constants will be faster. */ +static HOST_WIDE_INT +csky_constant_alignment (const_tree exp, HOST_WIDE_INT align) +{ + if (TREE_CODE (exp) == STRING_CST + && !optimize_size + && align < BITS_PER_WORD) + return BITS_PER_WORD; + return align; +} + +/* Record that there is a natural barrier in the insn stream at + ADDRESS. */ + +static void +push_csky_minipool_barrier (rtx_insn *insn, HOST_WIDE_INT address) +{ + Mfix *fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (*fix)); + + fix->insn = insn; + fix->address = address; + + fix->next = NULL; + if (minipool_fix_head != NULL) + minipool_fix_tail->next = fix; + else + minipool_fix_head = fix; + + minipool_fix_tail = fix; +} + +/* Compute the size of a vector jump table. */ + +static HOST_WIDE_INT +get_csky_jump_table_size (rtx insn) +{ + /* ADDR_VECs only take room if read-only data does into the text + section. */ + if (JUMP_TABLES_IN_TEXT_SECTION || readonly_data_section == text_section) + { + rtx body = PATTERN (insn); + int elt = GET_CODE (body) == ADDR_DIFF_VEC ? 1 : 0; + HOST_WIDE_INT size; + HOST_WIDE_INT modesize; + + modesize = GET_MODE_SIZE (GET_MODE (body)); + size = modesize * XVECLEN (body, elt); + switch (modesize) + { + case 1: + /* Round up size of TBB table to a halfword boundary. */ + size = (size + 1) & ~(HOST_WIDE_INT)1; + break; + case 2: + /* No padding necessary for TBH. */ + break; + case 4: + break; + default: + gcc_unreachable (); + } + return size; + } + + return 0; +} + + +/* Scan INSN and note any of its operands that need fixing. + If DO_PUSHES is false we do not actually push any of the fixups + needed. The function returns TRUE if any fixups were needed/pushed. */ + +static bool +note_csky_invalid_constants (rtx_insn *insn, HOST_WIDE_INT address, + int do_pushes) +{ + bool result = false; + int opno; + + extract_constrain_insn (insn); + + if (recog_data.n_alternatives == 0) + return false; + + /* Fill in recog_op_alt with information about the constraints of + this insn. */ + preprocess_constraints (insn); + + const operand_alternative *op_alt = which_op_alt (); + for (opno = 0; opno < recog_data.n_operands; opno++) + { + /* Things we need to fix can only occur in inputs. */ + if (recog_data.operand_type[opno] != OP_IN) + continue; + + /* If this alternative is a memory reference, then any mention + of constants in this alternative is really to fool reload + into allowing us to accept one there. We need to fix them up + now so that we output the right code. */ + if (op_alt[opno].memory_ok) + { + rtx op = recog_data.operand[opno]; + + if (CONSTANT_P (op)) + { + if (do_pushes) + push_csky_minipool_fix (insn, address, + recog_data.operand_loc[opno], + recog_data.operand_mode[opno], op); + result = true; + } + } + } + + return result; +} + + +/* Add a constant to the minipool for a forward reference. Returns the + node added or NULL if the constant will not fit in this pool. */ + +static Mnode * +add_csky_minipool_forward_ref (Mfix *fix) +{ + /* If set, max_mp is the first pool_entry that has a lower + constraint than the one we are trying to add. */ + Mnode *max_mp = NULL; + HOST_WIDE_INT max_address = fix->address + fix->forwards; + Mnode *mp; + + /* If the minipool starts before the end of FIX->INSN then this FIX + can not be placed into the current pool. Furthermore, adding the + new constant pool entry may cause the pool to start FIX_SIZE bytes + earlier. */ + if (minipool_vector_head + && (fix->address + get_attr_length (fix->insn) + >= minipool_vector_head->max_address - fix->fix_size)) + return NULL; + + /* Scan the pool to see if a constant with the same value has + already been added. While we are doing this, also note the + location where we must insert the constant if it doesn't already + exist. */ + for (mp = minipool_vector_head; mp != NULL; mp = mp->next) + { + if (GET_CODE (fix->value) == GET_CODE (mp->value) + && fix->mode == mp->mode + && (GET_CODE (fix->value) != CODE_LABEL + || (CODE_LABEL_NUMBER (fix->value) + == CODE_LABEL_NUMBER (mp->value))) + && rtx_equal_p (fix->value, mp->value)) + { + /* More than one fix references this entry. */ + mp->refcount++; + return mp; + } + + /* Note the insertion point if necessary. */ + if (max_mp == NULL && mp->max_address > max_address) + max_mp = mp; + } + + /* The value is not currently in the minipool, so we need to create + a new entry for it. If MAX_MP is NULL, the entry will be put on + the end of the list since the placement is less constrained than + any existing entry. Otherwise, we insert the new fix before + MAX_MP and, if necessary, adjust the constraints on the other + entries. */ + mp = XNEW (Mnode); + mp->fix_size = fix->fix_size; + mp->mode = fix->mode; + mp->value = fix->value; + mp->refcount = 1; + /* Not yet required for a backwards ref. */ + mp->min_address = -65536; + + if (max_mp == NULL) + { + mp->max_address = max_address; + mp->next = NULL; + mp->prev = minipool_vector_tail; + + if (mp->prev == NULL) + { + minipool_vector_head = mp; + minipool_vector_label + = gen_csky_constpool_label (gen_rtx_CONST_INT (VOIDmode, + constpool_label_no++)); + } + else + mp->prev->next = mp; + + minipool_vector_tail = mp; + } + else + { + if (max_address > max_mp->max_address - mp->fix_size) + mp->max_address = max_mp->max_address - mp->fix_size; + else + mp->max_address = max_address; + + mp->next = max_mp; + mp->prev = max_mp->prev; + max_mp->prev = mp; + if (mp->prev != NULL) + mp->prev->next = mp; + else + minipool_vector_head = mp; + } + + /* Save the new entry. */ + max_mp = mp; + + /* Scan over the preceding entries and adjust their addresses as + required. */ + while (mp->prev != NULL + && mp->prev->max_address > mp->max_address - mp->prev->fix_size) + { + mp->prev->max_address = mp->max_address - mp->prev->fix_size; + mp = mp->prev; + } + + return max_mp; +} + + +/* Return the cost of forcibly inserting a barrier after INSN. */ + +static int +get_csky_barrier_cost (rtx_insn *insn) +{ + /* Basing the location of the pool on the loop depth is preferable, + but at the moment, the basic block information seems to be + corrupt by this stage of the compilation. */ + int base_cost = 50; + rtx next = next_nonnote_insn (insn); + + if (next != NULL && GET_CODE (next) == CODE_LABEL) + base_cost -= 20; + + switch (GET_CODE (insn)) + { + case CODE_LABEL: + /* It will always be better to place the table before the label, rather + than after it. */ + return 50; + + case INSN: + case CALL_INSN: + return base_cost; + + case JUMP_INSN: + return base_cost - 10; + + default: + return base_cost + 10; + } +} + + +/* Find the best place in the insn stream in the range + (FIX->address,MAX_ADDRESS) to forcibly insert a minipool barrier. + Create the barrier by inserting a jump and add a new fix entry for + it. */ +static Mfix * +create_csky_fix_barrier (Mfix *fix, Mfix *fix_next, + HOST_WIDE_INT max_address) +{ + rtx_barrier *barrier; + rtx_insn *from = (fix ? fix->insn : get_insns ()); + /* The instruction after which we will insert the jump. */ + rtx_insn *selected = NULL; + int selected_cost; + /* The address at which the jump instruction will be placed. */ + HOST_WIDE_INT selected_address = 0; + Mfix *new_fix; + HOST_WIDE_INT count = (fix ? fix->address : 0); + HOST_WIDE_INT max_count = max_address; + rtx_code_label *label = gen_label_rtx (); + + selected_cost = get_csky_barrier_cost (from); + + while (from && count < max_count) + { + int new_cost; + rtx_jump_table_data *table; + + /* Count the length of this insn. */ + count += get_attr_length (from); + + /* If there is a jump table, add its length. */ + if (tablejump_p (from, NULL, &table)) + { + count += get_csky_jump_table_size (table); + + /* Jump tables aren't in a basic block, so base the cost on + the dispatch insn. If we select this location, we will + still put the pool after the table. */ + new_cost = get_csky_barrier_cost (from); + + if (count < max_count + && (!selected || new_cost <= selected_cost)) + { + selected = table; + selected_cost = new_cost; + selected_address = count; + } + + /* Continue after the dispatch table. */ + from = NEXT_INSN (table); + continue; + } + + new_cost = get_csky_barrier_cost (from); + + if (count < max_count + && (!selected || new_cost <= selected_cost)) + { + selected = from; + selected_cost = new_cost; + selected_address = count; + } + + from = NEXT_INSN (from); + } + + /* Make sure that we found a place to insert the jump. */ + gcc_assert (selected); + + /* Create a new JUMP_INSN that branches around a barrier. */ + from = emit_jump_insn_after (gen_jump (label), selected); + JUMP_LABEL (from) = label; + barrier = emit_barrier_after (from); + emit_label_after (label, barrier); + + /* Create a minipool barrier entry for the new barrier. */ + new_fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (* new_fix)); + new_fix->insn = barrier; + new_fix->address = selected_address; + if (fix) + { + new_fix->next = fix->next; + fix->next = new_fix; + } + else + new_fix->next = fix_next; + + return new_fix; +} + + +/* Print a symbolic form of the constant X to the dump file F. + This is used for dump output for -mconstpool in the target-dependent + reorg pass. */ + +static void +print_csky_value (FILE *f, rtx x) +{ + switch (GET_CODE (x)) + { + case CONST_INT: + fprintf (f, HOST_WIDE_INT_PRINT_HEX, INTVAL (x)); + return; + + case CONST_DOUBLE: + fprintf (f, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3)); + return; + + case CONST_VECTOR: + { + int i; + + fprintf (f, "<"); + for (i = 0; i < CONST_VECTOR_NUNITS (x); i++) + { + fprintf (f, HOST_WIDE_INT_PRINT_HEX, + INTVAL (CONST_VECTOR_ELT (x, i))); + if (i < (CONST_VECTOR_NUNITS (x) - 1)) + fputc (',', f); + } + fprintf (f, ">"); + } + return; + + case CONST_STRING: + fprintf (f, "\"%s\"", XSTR (x, 0)); + return; + + case SYMBOL_REF: + fprintf (f, "`%s'", XSTR (x, 0)); + return; + + case LABEL_REF: + fprintf (f, "L%d", INSN_UID (XEXP (x, 0))); + return; + + case CONST: + print_csky_value (f, XEXP (x, 0)); + return; + + case PLUS: + print_csky_value (f, XEXP (x, 0)); + fprintf (f, "+"); + print_csky_value (f, XEXP (x, 1)); + return; + + case PC: + fprintf (f, "pc"); + return; + + default: + fprintf (f, "????"); + return; + } +} + + +/* Record INSN, which will need fixing up to load a value from the + minipool. ADDRESS is the offset of the insn since the start of the + function; LOC is a pointer to the part of the insn which requires + fixing; VALUE is the constant that must be loaded, which is of type + MODE. */ + +static void +push_csky_minipool_fix (rtx_insn *insn, HOST_WIDE_INT address, rtx *loc, + machine_mode mode, rtx value) +{ + #define CSKY_ELRW16_RANGE 1400 + #define CSKY_LRW16_RANGE 700 + #define CSKY_CONSTANT_POOL_RANGE (TARGET_ELRW ? CSKY_ELRW16_RANGE \ + : CSKY_LRW16_RANGE) + + /* Fixes less than a word need padding out to a word boundary. */ + #define CSKY_MINIPOOL_FIX_SIZE(mode) \ + (GET_MODE_SIZE ((mode)) >= 4 ? GET_MODE_SIZE ((mode)) : 4) + + Mfix *fix = (Mfix *) obstack_alloc (&minipool_obstack, sizeof (*fix)); + + fix->insn = insn; + fix->address = address; + fix->loc = loc; + fix->mode = mode; + fix->fix_size = CSKY_MINIPOOL_FIX_SIZE (mode); + fix->value = value; + fix->forwards = CSKY_CONSTANT_POOL_RANGE; + fix->backwards = 0; + fix->minipool = NULL; + + /* If an insn doesn't have a range defined for it, then it isn't + expecting to be reworked by this code. Better to stop now than + to generate duff assembly code. */ + gcc_assert (fix->forwards || fix->backwards); + + if (dump_file) + { + fprintf (dump_file, + ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ", + GET_MODE_NAME (mode), + INSN_UID (insn), (unsigned long) address, + -1 * (long)fix->backwards, (long)fix->forwards); + print_csky_value (dump_file, fix->value); + fprintf (dump_file, "\n"); + } + + /* Add it to the chain of fixes. */ + fix->next = NULL; + + if (minipool_fix_head != NULL) + minipool_fix_tail->next = fix; + else + minipool_fix_head = fix; + + minipool_fix_tail = fix; +} + + +/* Fill in the offsets for minipool entries. */ + +static void +assign_csky_minipool_offsets (Mfix *barrier) +{ + HOST_WIDE_INT offset = 0; + Mnode *mp; + + minipool_barrier = barrier; + + for (mp = minipool_vector_head; mp != NULL; mp = mp->next) + { + mp->offset = offset; + + if (mp->refcount > 0) + offset += mp->fix_size; + } +} + + +/* Output the literal table. */ + +static HOST_WIDE_INT +dump_csky_minipool (rtx_insn *scan) +{ + Mnode *mp; + Mnode *nmp; + HOST_WIDE_INT pool_length = 0; + + if (dump_file) + fprintf (dump_file, + ";; Emitting minipool after insn %u;\ + address %ld; align %d (bytes)\n", + INSN_UID (scan), (unsigned long) minipool_barrier->address, 4); + + scan = emit_insn_after (gen_align_4 (), scan); + scan = emit_insn_after (minipool_vector_label, scan); + + for (mp = minipool_vector_head; mp != NULL; mp = nmp) + { + if (mp->refcount > 0) + { + if (dump_file) + { + fprintf (dump_file, ";; Offset %u, min %ld, max %ld ", + (unsigned) mp->offset, (unsigned long) mp->min_address, + (unsigned long) mp->max_address); + print_csky_value (dump_file, mp->value); + fputc ('\n', dump_file); + } + + switch (mp->fix_size) + { + case 4: + scan = emit_insn_after (gen_consttable_4 (mp->value), scan); + pool_length += 4; + break; + case 8: + scan = emit_insn_after (gen_consttable_8 (mp->value), scan); + pool_length += 8; + break; + default: + gcc_unreachable (); + } + } + + nmp = mp->next; + free (mp); + } + + minipool_vector_head = minipool_vector_tail = NULL; + scan = emit_barrier_after (scan); + + return pool_length; +} + +/* Return true if INSN is a minipool load or instruction that will be + converted to one. It is assumed that INSN has type attribute "load". */ + +bool +csky_minipool_load_p (rtx_insn *insn) +{ + rtx op1, addr; + + extract_insn_cached (insn); + + op1 = recog_data.operand[1]; + + /* This is a constant that has not yet been turned into + a minipool load. */ + if (CONSTANT_P (op1)) + return true; + + /* Constant pool loads are label_refs. */ + if (GET_CODE (op1) == ZERO_EXTEND || GET_CODE (op1) == SIGN_EXTEND) + op1 = XEXP (op1, 0); + if (GET_CODE (op1) != MEM) + return false; + addr = XEXP (op1, 0); + if (GET_CODE (addr) == PLUS && CONST_INT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + return GET_CODE (addr) == LABEL_REF; +} + + +/* Compute the attribute "length" of push or pop insn, according to + the registers it uses. */ + +int +csky_compute_pushpop_length (rtx *operands) +{ + rtx parallel_op = operands[2]; + /* Initialize to elements number of PARALLEL. */ + unsigned indx = XVECLEN (parallel_op, 0) - 1; + unsigned first_indx = 0; + unsigned regno = REGNO (operands[1]); + + if (regno > CSKY_LR_REGNUM) + return 4; + + /* Check each register in the list. */ + for (; indx > first_indx; indx--) + { + regno = REGNO (XEXP (XVECEXP (parallel_op, 0, indx), 0)); + /* If a register number higher than 15 is included, a 32-bit insn + is used. */ + if (regno > CSKY_LR_REGNUM) + return 4; + } + + return 2; +} + +/* Emit constant pools for -mconstpool. */ +static void +csky_emit_constant_pools (void) +{ + rtx_insn *insn; + HOST_WIDE_INT address = 0; + Mfix *fix; + + minipool_fix_head = minipool_fix_tail = NULL; + + /* The first insn must always be a note, or the code below won't + scan it properly. */ + insn = get_insns (); + gcc_assert (NOTE_P (insn)); + + /* Scan the insns and record the operands that need fixing. */ + for (insn = next_nonnote_insn (insn); insn; + insn = next_nonnote_insn (insn)) + { + if (BARRIER_P (insn)) + push_csky_minipool_barrier (insn, address); + else if (INSN_P (insn)) + { + rtx_jump_table_data *table; + + note_csky_invalid_constants (insn, address, true); + address += get_attr_length (insn); + + /* If the insn is a vector jump, add the size of the table + and skip the table. */ + if (tablejump_p (insn, NULL, &table)) + { + address += get_csky_jump_table_size (table); + insn = table; + } + } + } + + fix = minipool_fix_head; + + /* Now scan the fixups and perform the required changes. */ + while (fix) + { + Mfix *ftmp; + Mfix *last_added_fix; + Mfix *last_barrier = NULL; + Mfix *this_fix; + Mnode *mp; + bool has_pending_const = false; + + /* Check if there is any pending constant not processed. */ + for (mp = minipool_vector_head; mp; mp = mp->next) + if (mp->refcount > 0) + { + has_pending_const = true; + break; + } + + /* If no pending constant, skip over barrier insns. */ + if (has_pending_const == false) + { + while (fix && BARRIER_P (fix->insn)) + fix = fix->next; + if (fix == NULL) + break; + } + + last_added_fix = NULL; + + for (ftmp = fix; ftmp; ftmp = ftmp->next) + { + if (BARRIER_P (ftmp->insn)) + { + if (minipool_vector_head + && ftmp->address >= minipool_vector_head->max_address) + break; + + last_barrier = ftmp; + } + else + { + ftmp->minipool = add_csky_minipool_forward_ref (ftmp); + if (ftmp->minipool == NULL) + break; + } + last_added_fix = ftmp; /* Keep track of the last fix added. */ + } + + /* If the last added fix is a barrier, dump minipool after it. */ + if (last_added_fix && BARRIER_P (last_added_fix->insn)) + ftmp = last_barrier; + else + { + /* ftmp is first fix that we can't fit into this pool. + Insert a new barrier in the code somewhere between the previous + fix and this one, and arrange to jump around it. */ + HOST_WIDE_INT max_address; + + /* The last item on the list of fixes must be a barrier, so + we can never run off the end of the list of fixes without + last_barrier being set. */ + gcc_assert (ftmp); + + /* Check that there isn't another fix that is in range that + we couldn't fit into this pool because the pool was + already too large: we need to put the pool before such an + instruction. The pool itself may come just after the + fix because create_csky_fix_barrier also allows space for a + jump instruction. */ + max_address = minipool_vector_head->max_address; + if (ftmp->address < max_address) + max_address = ftmp->address + 1; + last_barrier = create_csky_fix_barrier (last_added_fix, ftmp, + max_address); + } + + assign_csky_minipool_offsets (last_barrier); + + /* Scan over the fixes we have identified for this pool, fixing them + up and adding the constants to the pool itself. */ + for (this_fix = fix; this_fix && ftmp != this_fix; + this_fix = this_fix->next) + { + if (GET_CODE (this_fix->insn) != BARRIER) + { + rtx addr + = plus_constant (Pmode, + gen_rtx_LABEL_REF (VOIDmode, + minipool_vector_label), + this_fix->minipool->offset); + rtx insn_body = PATTERN (this_fix->insn); + rtx src = XEXP (insn_body, 1); + *this_fix->loc = gen_rtx_MEM (this_fix->mode, addr); + if (GET_CODE (this_fix->value) == SYMBOL_REF) + emit_insn_after (gen_rtx_UNSPEC_VOLATILE (VOIDmode, + gen_rtvec (1, src), + VUNSPEC_SYMBOL_REF), + this_fix->insn); + } + } + dump_csky_minipool (last_barrier->insn); + fix = ftmp; + if (fix->next == NULL) + break; + } + + /* Free the minipool memory. */ + obstack_free (&minipool_obstack, minipool_startobj); +} + + +/* Implement TARGET_MACHINE_DEPENDENT_REORG. This handles + -mconstpool output. */ + +static void +csky_reorg (void) +{ + if (TARGET_CONSTANT_POOL) + csky_emit_constant_pools (); +} + + +/* Check to see if the current function contains a branch insn with the + far jump attribute set. Such a function uses the LR register. */ + +static bool +csky_far_jump_used_p (void) +{ + rtx_insn *insn; + if (cfun->machine->far_jump_used) + return true; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == JUMP_INSN + /* Ignore tablejump patterns. */ + && GET_CODE (PATTERN (insn)) != ADDR_VEC + && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC + && get_attr_far_jump (insn) == FAR_JUMP_YES) + { + cfun->machine->far_jump_used = 1; + return true; + } + return false; +} + + +/* Return the mask of registers used by the current function. Set + COUNT to the number of registers used. */ + +static unsigned int +get_csky_live_regs (int *count) +{ + int reg; + unsigned int live_regs_mask = 0; + + *count = 0; + for (reg = 0; reg < CSKY_NGPR_REGS; reg++) + { + bool save = false; + + /* Ignore unsupported registers. */ + if (CSKY_TARGET_ARCH (CK801) && reg > 8 && reg < 13) + continue; + if ((CSKY_TARGET_ARCH (CK801) + || CSKY_TARGET_ARCH (CK802) + || CSKY_TARGET_ARCH (CK803)) + && reg > 15) + break; + + /* Caller-saved registers marked as used. */ + if (df_regs_ever_live_p (reg) && !call_really_used_regs[reg]) + save = true; + + /* Frame pointer marked used. */ + else if (frame_pointer_needed && reg == FRAME_POINTER_REGNUM) + save = true; + + /* This is required for CK801/802 where FP is a fixed reg, otherwise + we end up with no FP value available to the DWARF-2 unwinder. */ + else if (crtl->calls_eh_return && reg == FRAME_POINTER_REGNUM) + save = true; + + /* CK801/802 also need special handling for LR because it's clobbered + by far jumps. */ + else if ((CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802)) + && reg == CSKY_LR_REGNUM + && (!crtl->is_leaf || csky_far_jump_used_p ())) + save = true; + + /* Register is used for EH data return. */ + else if (crtl->calls_eh_return + && reg >= CSKY_FIRST_EH_RETDATA_REGNUM + && reg <= CSKY_LAST_EH_RETDATA_REGNUM) + save = true; + + /* We need a temporary reg to hold the offset for adjusting the SP + for a large stack frame. */ + if (reg == CSKY_STACKADJUST_REGNUM + && cfun->machine->reg_offset > CSKY_MAX_SP_ADJUST * 2) + save = true; + + /* Add reg to the mask. */ + if (save) + { + (*count)++; + live_regs_mask |= (1 << reg); + } + } + return live_regs_mask; +} + +/* Compute the stack frame layout, storing sizes of the various pieces + in cfun->machine. + + Stack frames constructed in the prologue look like: + ... caller's frame ... + incoming SP -> caller's outbound argument overflow + argument spill + optional FP -> register save + local variables + alloca() space + adjusted SP -> outbound argument overflow + + with SP/FP pointing at the base (low address) of the respective area, + and each area aligned to a word boundary. */ + +static void +csky_layout_stack_frame (void) +{ + machine_function *infp = cfun->machine; + int reg_count; + + if (infp->frame_init_p) + return; + + /* Get sizes of local variables & outbound arguments. */ + infp->outbound_size = CSKY_STACK_ALIGN (crtl->outgoing_args_size); + infp->local_offset = infp->outbound_size; + infp->local_size = CSKY_STACK_ALIGN (get_frame_size ()); + infp->reg_offset = infp->local_offset + infp->local_size; + + /* Now compute size of argument spill + saved regs. These do not + need explicit alignment since they are already word-sized. */ + infp->reg_mask = get_csky_live_regs (®_count); + infp->reg_size = reg_count * UNITS_PER_WORD; + infp->arg_offset = infp->reg_offset + infp->reg_size; + infp->arg_size = crtl->args.pretend_args_size; + infp->frame_size = infp->arg_offset + infp->arg_size; + infp->frame_init_p = reload_completed; +} + +/* Implement TARGET_CAN_ELIMINATE. */ +static bool +csky_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) +{ + if (to == STACK_POINTER_REGNUM) + return !frame_pointer_needed; + return true; +} + +/* Worker function for INITIAL_ELIMINATION_OFFSET macro. + Define the offset between two registers, one to be eliminated, and + the other its replacement, at the start of a routine. */ + +HOST_WIDE_INT +csky_initial_elimination_offset (int from, int to) +{ + int offset; + + csky_layout_stack_frame (); + + /* Set OFFSET to the offset to the initial stack pointer. */ + switch (from) + { + case FRAME_POINTER_REGNUM: + offset = cfun->machine->reg_offset; + break; + + case ARG_POINTER_REGNUM: + offset = cfun->machine->arg_offset; + break; + + default: + gcc_unreachable (); + } + + /* If we are asked for the offset to the frame pointer instead, + then subtract the difference between the frame pointer and stack + pointer. */ + if (to == FRAME_POINTER_REGNUM) + offset -= cfun->machine->reg_offset; + return offset; +} + + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ +static rtx +csky_function_arg (cumulative_args_t pcum_v, machine_mode mode, + const_tree type ATTRIBUTE_UNUSED, + bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v); + + if (*pcum < CSKY_NPARM_REGS) + return gen_rtx_REG (mode, CSKY_FIRST_PARM_REGNUM + *pcum); + + return NULL_RTX; +} + + +/* Return the number of registers (words) needed to pass an argument of + MODE and TYPE. */ + +static int +csky_num_arg_regs (machine_mode mode, const_tree type) +{ + int size; + + if (type && mode == BLKmode) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + return CSKY_NUM_WORDS (size); +} + + +/* Implement TARGET_FUNCTION_ARG_ADVANCE. */ + +static void +csky_function_arg_advance (cumulative_args_t pcum_v, machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v); + int param_size = csky_num_arg_regs (mode, type); + + if (*pcum + param_size > CSKY_NPARM_REGS) + *pcum = CSKY_NPARM_REGS; + else + *pcum += param_size; +} + + +/* Implement TARGET_FUNCTION_VALUE. */ +static rtx +csky_function_value (const_tree type, const_tree func, + bool outgoing ATTRIBUTE_UNUSED) +{ + machine_mode mode; + int unsignedp ATTRIBUTE_UNUSED; + int size; + + mode = TYPE_MODE (type); + size = int_size_in_bytes (type); + + /* Since we promote return types, we must promote the mode here too. */ + if (INTEGRAL_TYPE_P (type)) + { + mode = promote_function_mode (type, mode, &unsignedp, func, 1); + return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM); + } + + if (mode == BLKmode && size > UNITS_PER_WORD + && size <= UNITS_PER_WORD * 2) + { + rtx ret_regs[2]; + ret_regs[0] = gen_rtx_EXPR_LIST (SImode, + gen_rtx_REG (SImode, + CSKY_FIRST_RET_REGNUM), + GEN_INT (0 * UNITS_PER_WORD)); + ret_regs[1] = gen_rtx_EXPR_LIST (SImode, + gen_rtx_REG (SImode, + CSKY_FIRST_RET_REGNUM + 1), + GEN_INT (1 * UNITS_PER_WORD)); + + rtvec vec = gen_rtvec (2, ret_regs[0], ret_regs[1]); + + return gen_rtx_PARALLEL (mode, vec); + } + + return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM); +} + + +/* Implement TARGET_LIBCALL_VALUE. */ +static rtx +csky_libcall_value (machine_mode mode, + const_rtx libcall ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, CSKY_FIRST_RET_REGNUM); +} + + +/* Implement TARGET_FUNCTION_VALUE_REGNO_P. + On C-SKY, only r0 can return results. */ + +static bool +csky_function_value_regno_p (const unsigned int regno) +{ + return (regno == CSKY_FIRST_RET_REGNUM); +} + + +/* Return an RTX indicating where the return address to the + calling function can be found. */ +rtx +csky_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) +{ + if (count != 0) + return NULL_RTX; + + return get_hard_reg_initial_val (Pmode, CSKY_LR_REGNUM); +} + + +/* Implement TARGET_ARG_PARTIAL_BYTES. + Return the number of bytes at the beginning of an argument + that must be put in registers. The value must be zero for arguments + that are passed entirely in registers or + that are entirely pushed on the stack. */ +static int +csky_arg_partial_bytes (cumulative_args_t pcum_v, machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v); + int param_size = csky_num_arg_regs (mode, type); + + if (*pcum < CSKY_NPARM_REGS + && *pcum + param_size > CSKY_NPARM_REGS) + return (CSKY_NPARM_REGS - *pcum) * UNITS_PER_WORD; + + return 0; +} + + +/* Implement TARGET_SETUP_INCOMING_VARARGS. + On C-Sky the copy from the argument registers to the stack is emitted + by the prologue hooks, so here we just have to note how much stack space + to save. */ + +static void +csky_setup_incoming_varargs (cumulative_args_t pcum_v, + machine_mode mode, + tree type, + int *pretend_size, + int second_time ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v); + CUMULATIVE_ARGS local_cum; + cumulative_args_t local_cum_v = pack_cumulative_args (&local_cum); + int regs_to_push; + + cfun->machine->uses_anonymous_args = 1; + local_cum = *pcum; + csky_function_arg_advance (local_cum_v, mode, type, true); + regs_to_push = CSKY_NPARM_REGS - local_cum; + if (regs_to_push) + *pretend_size = regs_to_push * UNITS_PER_WORD; +} + + +/* Implement TARGET_ASM_OUTPUT_MI_THUNK. + Output code to add DELTA to the first argument, and then jump + to FUNCTION. Used for C++ multiple inheritance. */ + +static void +csky_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, + HOST_WIDE_INT vcall_offset, + tree function) +{ + const char *thiz = "a0"; + const char *reg0 = "t0"; + const char *reg1 = "t1"; + int maxoff = 4096; /* Constant range for addi/subi. */ + + final_start_function (emit_barrier (), file, 1); + + rtx fnaddr = XEXP (DECL_RTL (function), 0); + + if (CSKY_TARGET_ARCH (CK801)) + { + /* CK801 can't use t registers and has only 16-bit addi/subi. */ + reg0 = "l0"; + reg1 = "l1"; + maxoff = 256; + if (vcall_offset > maxoff || vcall_offset < -maxoff) + fprintf (file, "\tpush\tl0, l1\n"); + else if (delta > maxoff || delta < -maxoff) + fprintf (file, "\tpush\tl0\n"); + } + + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + thiz = "a1"; + + /* Add delta to this_rtx. */ + if (delta != 0) + { + if (delta > maxoff || delta < -maxoff) + { + fprintf (file, "\tlrw\t%s, %ld\n", reg0, (long)delta); + fprintf (file, "\taddu\t%s, %s, %s\n", thiz, thiz, reg0); + } + else + fprintf (file, "\t%s\t%s, %s, %ld\n", + (delta > 0 ? "addi" : "subi"), thiz, thiz, + (long)(delta > 0 ? delta : -delta)); + } + + /* If needed, add *(*this_rtx + vcall_offset) to this_rtx. */ + if (vcall_offset != 0) + { + fprintf (file, "\tld.w\t%s, (%s, 0)\n", reg0, thiz); + + if (vcall_offset > maxoff || vcall_offset < -maxoff) + { + fprintf (file, "\tlrw\t%s, %ld\n", reg1, (long)vcall_offset); + fprintf (file, "\taddu\t%s, %s, %s\n", reg0, reg0, reg1); + } + else + fprintf (file, "\t%s\t%s, %s, %ld\n", + (vcall_offset > 0 ? "addi" : "subi"), reg0, reg0, + (long)(vcall_offset > 0 ? vcall_offset : -vcall_offset)); + + /* Load the offset and add it to this_rtx */ + fprintf (file, "\tld.w\t%s, (%s, 0)\n", reg0, reg0); + fprintf (file, "\taddu\t%s, %s, %s\n", thiz, thiz, reg0); + } + + /* We must pop the scratch regs individually instead of using the + "pop" insn, which also does a return. */ + if (CSKY_TARGET_ARCH (CK801)) + { + if (vcall_offset > maxoff || vcall_offset < -maxoff) + { + fprintf (file, "\tld.w\tl0, (sp, 0)\n"); + fprintf (file, "\tld.w\tl1, (sp, 4)\n"); + fprintf (file, "\taddi\t sp, sp, 8\n"); + } + else if (delta > maxoff || delta < -maxoff) + { + fprintf (file, "\tld.w\tl0, (sp, 0)\n"); + fprintf (file, "\taddi\tsp, sp, 4\n"); + } + } + + fprintf (file, "\tjbr\t"); + output_addr_const (file, fnaddr); + fprintf (file, "\n"); + + final_end_function (); +} + + +/* Implement TARGET_CONDITIONAL_REGISTER_USAGE. + Conditionally modify five variables fixed_regs, call_used_regs, global_regs, + reg_names, and reg_class_contents, to take into account any dependence of + these register sets on target flags. + + CK801 has registers r0-r8 and r13-r15. CK802 and CK803 have registers + r0-r15 (the "low" registers). Other cpus use registers r0-r31 with + -mhigh-registers, otherwise also only r0-r15. + + CK801 only has 16-bit instructions, most of which can only reference + r0-r7 (the "mini" registers). So we mark regs outside that range as + fixed. -msmart can be used on other arch variants to force the same + behavior because it results in smaller code size. + + TODO: investigate whether it's beneficial to use r8-r13 as a spill + class when TARGET_MINI_REGISTERS instead of making them unusable by + the register allocator. */ + +static void +csky_conditional_register_usage (void) +{ + /* Only use mini registers in smart mode or 801. */ + if (TARGET_MINI_REGISTERS) + { + int i; + + for (i = (CSKY_LAST_MINI_REGNUM + 1); i < 32; i++) + { + fixed_regs[i] = 1; + call_used_regs[i] = 1; + call_really_used_regs[i] = 1; + } + } + /* For some targets, the high registers are not supported. + CPUs other than ck801/ck802/ck803 use high registers + depending on -mhigh-registers option. */ + else if (CSKY_TARGET_ARCH (CK802) + || CSKY_TARGET_ARCH (CK803) + || !TARGET_HIGH_REGISTERS) + { + int i; + + for (i = CSKY_FIRST_HIGH_REGNUM; i <= CSKY_LAST_HIGH_REGNUM; i++) + { + fixed_regs[i] = 1; + call_used_regs[i] = 1; + call_really_used_regs[i] = 1; + } + } + + /* On CK801/CK802 we must mark lr as a fixed register because it is + used to implement far jumps. + FIXME: perhaps there should be a command-line option controlling + use of lr for far jumps on ck802 when !TARGET_MINI_REGS, when + you really want lr to be available to the register allocator and + you know there are no far jumps in the code. */ + if (CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802)) + { + fixed_regs[CSKY_LR_REGNUM] = 1; + call_used_regs[CSKY_LR_REGNUM] = 1; + call_really_used_regs[CSKY_LR_REGNUM] = 0; + } + + /* The hi/lo registers are only supported in dsp mode. */ + if (!TARGET_DSP) + { + fixed_regs[CSKY_HI_REGNUM] = 1; + call_used_regs[CSKY_HI_REGNUM] = 1; + call_really_used_regs[CSKY_HI_REGNUM] = 1; + + fixed_regs[CSKY_LO_REGNUM] = 1; + call_used_regs[CSKY_LO_REGNUM] = 1; + call_really_used_regs[CSKY_LO_REGNUM] = 1; + } + + /* The V_REGS are only supported in hard float mode. */ + if (!TARGET_HARD_FLOAT) + { + int regno; + + for (regno = CSKY_FIRST_VFP_REGNUM; + regno <= CSKY_LAST_VFP_REGNUM; regno++) + { + fixed_regs[regno] = 1; + call_used_regs[regno] = 1; + call_really_used_regs[regno] = 1; + } + } + + /* In pic mode, the gb register is not available for register + allocation. Since gb is not clobbered by function + calls, set its call_really_used_regs to 0. */ + if (flag_pic) + { + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + call_really_used_regs[PIC_OFFSET_TABLE_REGNUM] = 0; + } +} + +/* Implement TARGET_HARD_REGNO_NREGS. */ +static unsigned int +csky_hard_regno_nregs (unsigned int regno, machine_mode mode) +{ + if (regno >= CSKY_FIRST_VFP_REGNUM && !CSKY_TARGET_ARCH (CK803)) + return 1; + else + return CSKY_NUM_REGS (mode); +} + +/* Implement TARGET_HARD_REGNO_MODE_OK. Return true if REGNO is a + valid register for holding a quantity of type MODE. */ + +static bool +csky_hard_regno_mode_ok (unsigned int regno, machine_mode mode) +{ + int nregs = CSKY_NUM_REGS (mode); + + /* We can't handle more than doubleword sizes for any register. */ + if (nregs > 2) + return false; + + /* For general registers, return true if mode is one word size. + When the size is larger than one word size, there should + be two successive hard registers to put the data. */ + if (regno < CSKY_NGPR_REGS) + { + if (nregs < 2) + return true; + else if (TARGET_MINI_REGISTERS) + return (regno < CSKY_LAST_MINI_REGNUM); + else if (CSKY_TARGET_ARCH (CK802) + || CSKY_TARGET_ARCH (CK803) + || !TARGET_HIGH_REGISTERS) + /* Without high register, r15 cannot hold doubleword data. */ + return (regno < (CSKY_SP_REGNUM - 1)); + else + return (regno < (CSKY_SP_REGNUM - 1) + || (regno >= CSKY_LR_REGNUM + && regno < CSKY_LAST_HIGH_UNFIXED_REGNUM)); + } + else if (regno == CSKY_CC_REGNUM) + return (mode == CCmode); + else if (regno == CSKY_HI_REGNUM || regno == CSKY_LO_REGNUM) + { + /* Don't allocate hi,lo register for float data even + if in dsp mode, because it will cause high cost + to reload data from hi,lo register. */ + if (!TARGET_DSP || mode == SFmode || mode == DFmode) + return false; + else if (nregs == 2) + return (regno == CSKY_HI_REGNUM); + else + return true; + } + else if (CSKY_VREG_P (regno) && TARGET_HARD_FLOAT) + return true; + + return false; +} + +/* Implement TARGET_MODES_TIEABLE_P. We can't tie DFmode with other modes + when V_REGs might be in use because those registers mess with the stored + bits. */ +static bool +csky_modes_tieable_p (machine_mode mode1, machine_mode mode2) +{ + return !(TARGET_HARD_FLOAT + && mode1 != mode2 + && (mode1 == DFmode || mode2 == DFmode)); +} + +/* Implement TARGET_CAN_CHANGE_MODE_CLASS. + V_REG registers can't do subreg as all values are reformatted to + internal precision. */ +static bool +csky_can_change_mode_class (machine_mode from, + machine_mode to, + reg_class_t rclass) +{ + return (GET_MODE_SIZE (from) == GET_MODE_SIZE (to) + || !reg_classes_intersect_p (V_REGS, rclass)); +} + +/* Implement TARGET_CLASS_LIKELY_SPILLED_P. + We need to define this for MINI_REGS when we only use r0 - r7. + Otherwise we can end up using r0-r4 for function arguments, and don't + have enough left over to do doubleword arithmetic. */ + +static bool +csky_class_likely_spilled_p (reg_class_t rclass) +{ + if ((TARGET_MINI_REGISTERS && rclass == MINI_REGS) + || rclass == C_REGS) + return true; + + return false; +} + + +/* Implement TARGET_PREFERRED_RELOAD_CLASS. + Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS. */ + +static reg_class_t +csky_preferred_reload_class (rtx x, reg_class_t rclass) +{ + if (TARGET_HARD_FLOAT + && CONST_DOUBLE_P (x) + && (GET_MODE (x) == DFmode || GET_MODE (x) == SFmode) + && rclass == NO_REGS) + return GENERAL_REGS; + return rclass; +} + + +/* Implement TARGET_CLASS_MAX_NREGS. + Return the maximum number of consecutive registers of class rclass needed + to hold a value of mode mode. + On the csky, this is the size of MODE in words, + except in the FP regs, where a single reg is always enough. */ + +static unsigned char +csky_class_max_nregs (reg_class_t rclass, machine_mode mode) +{ + if (rclass == V_REGS) + return 1; + else + return CSKY_NUM_REGS (mode); +} + + +/* Implement TARGET_SECONDARY_RELOAD. + If copying a register of RCLASS from/to X requires an intermediate + register, the hook should return the REGISTER_CLASS required for this + intermediate register. + If no intermediate register is required, it should return NO_REGS. + If more than one intermediate register is required, describe the one + that is closest in the copy chain to the reload register. */ + +reg_class_t +csky_secondary_reload (bool in_p ATTRIBUTE_UNUSED, rtx x, + reg_class_t rclass, + machine_mode mode, + secondary_reload_info *sri ATTRIBUTE_UNUSED) +{ + int regno = -1; + + /* Extract the real regno from X. */ + if (GET_CODE (x) == SIGN_EXTEND) + { + int off = 0; + + x = XEXP (x, 0); + + if (reg_renumber) + regno = true_regnum (x); + else + { + while (GET_CODE (x) == SUBREG) + { + off += subreg_regno_offset (REGNO (SUBREG_REG (x)), + GET_MODE (SUBREG_REG (x)), + SUBREG_BYTE (x), GET_MODE (x)); + x = SUBREG_REG (x); + } + + if (GET_CODE (x) == REG) + regno = REGNO (x) + off; + } + } + else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG) + regno = true_regnum (x); + + /* We always require a general register when copying anything to + HI/LO_REGNUM, except when copying an SImode value from HI/LO_REGNUM + to a general register, or when copying from register 0. */ + if ((rclass == HILO_REGS || rclass == LO_REGS || rclass == HI_REGS) + && !CSKY_GENERAL_REGNO_P (regno)) + return GENERAL_REGS; + + if (rclass == V_REGS && !CSKY_GENERAL_REGNO_P (regno)) + { + /* Reload between vector reg and memory does not need an + intermediate register. */ + if (MEM_P (x) && (mode == SFmode || mode == DFmode)) + return NO_REGS; + else + return GENERAL_REGS; + } + + return NO_REGS; +} + +/* Implement TARGET_SPILL_CLASS. + Try spilling to a larger register class before spilling to memory. */ + +static reg_class_t +csky_spill_class (reg_class_t rclass, machine_mode mode ATTRIBUTE_UNUSED) +{ + if ((rclass == MINI_REGS && !TARGET_MINI_REGISTERS) + || (rclass == LOW_REGS && TARGET_HIGH_REGISTERS)) + return GENERAL_REGS; + return NO_REGS; +} + +/* Convert a static initializer array of feature bits to sbitmap + representation. */ +static void +csky_initialize_isa (sbitmap isa, const enum csky_isa_feature *isa_bits) +{ + bitmap_clear (isa); + while (*isa_bits != CSKY_ISA_FEATURE_GET (none)) + bitmap_set_bit (isa, *(isa_bits++)); +} + + +/* Configure a build target TARGET from the user-specified options OPTS and + OPTS_SET. */ +static void +csky_configure_build_target (struct csky_build_target *target, + struct cl_target_option *opts, + struct gcc_options *opts_set) +{ + const struct csky_processors *csky_selected_tune = NULL; + struct csky_processors *csky_selected_cpu = NULL; + struct csky_processors *csky_selected_arch = NULL; + sbitmap all_sbits = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max)); + bitmap_clear (all_sbits); + + bitmap_clear (target->isa); + target->core_name = NULL; + target->arch_name = NULL; + + if (opts_set->x_csky_arch_option) + csky_selected_arch = &all_architectures[opts->x_csky_arch_option]; + + if (opts_set->x_csky_cpu_option) + { + csky_selected_cpu = &all_cores[opts->x_csky_cpu_option]; + csky_selected_tune = &all_cores[opts->x_csky_cpu_option]; + } + + if (csky_selected_cpu) + { + /* TODO: support combination of features + between different cpu & arch, should based on arch. */ + if (csky_selected_arch + && (csky_selected_cpu->base_arch != csky_selected_arch->base_arch)) + warning (0, "cpu %s is not based on arch %s, ignoring the arch", + csky_selected_cpu->name, csky_selected_arch->name); + if (!csky_selected_arch) + csky_selected_arch = &all_architectures[csky_selected_cpu->base_arch]; + csky_initialize_isa (all_sbits, csky_selected_arch->isa_bits); + target->core_name = csky_selected_cpu->name; + } + else if (csky_selected_arch) + { + csky_selected_cpu = csky_selected_arch; + target->arch_name = csky_selected_arch->name; + } + else /* If the user did not specify a processor, choose one for them. */ + { + csky_selected_cpu = &all_cores[TARGET_CPU_DEFAULT]; + csky_selected_arch = &all_architectures[csky_selected_cpu->base_arch]; + csky_initialize_isa (all_sbits, csky_selected_arch->isa_bits); + target->core_name = csky_selected_cpu->name; + } + + /* The selected cpu may be an architecture, so lookup tuning by core ID. */ + if (!csky_selected_tune) + csky_selected_tune = &all_cores[csky_selected_cpu->core]; + gcc_assert (csky_selected_tune); + + gcc_assert (csky_selected_arch); + gcc_assert (csky_selected_cpu); + csky_initialize_isa (target->isa, csky_selected_cpu->isa_bits); + bitmap_ior (target->isa, target->isa, all_sbits); + + /* Finish initializing the target structure. */ + target->arch_pp_name = csky_selected_cpu->arch; + target->base_arch = csky_selected_cpu->base_arch; + target->arch_core = csky_selected_cpu->core; + + sbitmap_free (all_sbits); +} + + +/* Implement TARGET_OPTION_OVERRIDE. */ + +static void +csky_option_override (void) +{ + csky_active_target.isa = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max)); + + /* Create the default target_options structure. We need this early + to configure the overall build target. */ + target_option_default_node = target_option_current_node + = build_target_option_node (&global_options); + + csky_configure_build_target (&csky_active_target, + TREE_TARGET_OPTION (target_option_default_node), + &global_options_set); + +#ifdef SUBTARGET_OVERRIDE_OPTIONS + SUBTARGET_OVERRIDE_OPTIONS; +#endif + + csky_base_arch = csky_active_target.base_arch; + + if (flag_pic && !(CSKY_TARGET_ARCH (CK810) || CSKY_TARGET_ARCH (CK807))) + { + flag_pic = 0; + warning (0, "%qs is not supported by arch %s", + "-fPIC", csky_active_target.arch_pp_name); + } + + /* Check floating-point options for consistency. */ + if (TARGET_HARD_FLOAT) + { + const struct csky_fpu_desc *csky_selected_fpu = NULL; + + if (csky_fpu_index == TARGET_FPU_auto) + { + const char *target_fpu_name; + bool ok; + int fpu_index; + +#ifdef CSKY_FPUTYPE_DEFAULT + target_fpu_name = CSKY_FPUTYPE_DEFAULT; +#else + target_fpu_name = "fpv2"; +#endif + + if (csky_active_target.core_name != NULL + && !strchr (csky_active_target.core_name, 'f')) + target_fpu_name = "auto"; + else if (CSKY_TARGET_ARCH (CK803) || !TARGET_DOUBLE_FLOAT) + target_fpu_name = "fpv2_sf"; + else if (TARGET_DOUBLE_FLOAT && TARGET_FDIVDU) + target_fpu_name = "fpv2_divd"; + + ok = opt_enum_arg_to_value (OPT_mfpu_, target_fpu_name, &fpu_index, + CL_TARGET); + gcc_assert (ok); + csky_fpu_index = (enum csky_fpu_type) fpu_index; + } + + if (CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802)) + error ("%qs is not supported by arch %s", + "-mhard-float", csky_active_target.arch_pp_name); + else if (csky_fpu_index == TARGET_FPU_auto) + error ("%<-mhard-float%> is not supported by the selected CPU"); + else + { + csky_selected_fpu = &all_fpus[csky_fpu_index]; + sbitmap fpu_bits = sbitmap_alloc (CSKY_ISA_FEATURE_GET (max)); + csky_initialize_isa (fpu_bits, csky_selected_fpu->isa_bits); + + bitmap_ior (csky_active_target.isa, csky_active_target.isa, + fpu_bits); + + sbitmap_free (fpu_bits); + } + } + else + { + if (TARGET_DOUBLE_FLOAT > 0) + warning (0, "%<-mdouble-float%> ignored without %<-mhard-float%>"); + TARGET_DOUBLE_FLOAT = 0; + if (TARGET_FDIVDU > 0) + warning (0, "%<-mfdivdu%> ignored without %<-mhard-float%>"); + TARGET_FDIVDU = 0; + } + + /* Extended LRW instructions are enabled by default on CK801, disabled + otherwise. */ + if (TARGET_ELRW == -1) + TARGET_ELRW = CSKY_TARGET_ARCH (CK801); + + /* DSP is enabled either by the processor feature or -mdsp + command-line option. There is no -mno-dsp option as the assembler + doesn't take one. */ + if (!TARGET_DSP) + TARGET_DSP = CSKY_ISA_FEATURE (dsp); + + /* There's both -mdiv and -mno-div. Take default from processor if + neither is specified explicitly. */ + if (TARGET_DIV == -1) + TARGET_DIV = CSKY_ISA_FEATURE (div); + + /* TARGET_CONSTANT_POOL is mandatory for CK801 and CK802 and optional + for other CPUs. + The reason why the compiler has to generate constant pools for CK801/2 + instead of deferring to the assembler is that these cores don't have a + long branch instruction other than jbsr, which clobbers lr. So for + the compiler to correctly save/restore lr it has to know whether there + are long branches, which depends on having accurate branch length + counts, which in turn depends on having control over where constant + pools are placed. */ + if ((CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802)) + && !TARGET_CONSTANT_POOL) + error ("%qs is not supported by arch %s", + "-mno-constpool", csky_active_target.arch_pp_name); + else if (TARGET_CONSTANT_POOL == -1) + TARGET_CONSTANT_POOL = (CSKY_TARGET_ARCH (CK801) + || CSKY_TARGET_ARCH (CK802)); + + /* TARGET_MINI_REGISTERS is mandatory for CK801, the default for CK802, + and optional for other CPUs. TARGET_HIGH_REGISTERS is incompatible + with TARGET_MINI_REGISTERS, is not supported by CK801/802/803, + and is the default for other processors. + See csky_conditional_register_usage. */ + if (TARGET_MINI_REGISTERS > 0 && TARGET_HIGH_REGISTERS > 0) + error ("%<-msmart%> is incompatible with %<-mhigh-registers%>"); + else if (CSKY_TARGET_ARCH (CK801) + || CSKY_TARGET_ARCH (CK802) + || CSKY_TARGET_ARCH (CK803)) + { + if (CSKY_TARGET_ARCH (CK801) + || (CSKY_TARGET_ARCH (CK802) && TARGET_MINI_REGISTERS == -1)) + TARGET_MINI_REGISTERS = 1; + else if (TARGET_MINI_REGISTERS == -1) + TARGET_MINI_REGISTERS = 0; + if (TARGET_HIGH_REGISTERS > 0) + warning (0, "%qs is not supported by arch %s", + "-mhigh-registers", csky_active_target.arch_pp_name); + TARGET_HIGH_REGISTERS = 0; + } + else + { + if (TARGET_MINI_REGISTERS == -1) + TARGET_MINI_REGISTERS = 0; + if (TARGET_HIGH_REGISTERS == -1) + TARGET_HIGH_REGISTERS = !TARGET_MINI_REGISTERS; + } + + /* -mmultiple-stld is the default for everything but CK801, which + doesn't support it. */ + if (CSKY_TARGET_ARCH (CK801)) + { + if (TARGET_MULTIPLE_STLD > 0) + warning (0, "%qs is not supported by arch %s", + "-mmultiple-stld", csky_active_target.arch_pp_name); + TARGET_MULTIPLE_STLD = 0; + } + + /* Initialize boolean versions of the architectural flags, for use + in the .md file. */ + +#undef CSKY_ISA +#define CSKY_ISA(IDENT, DESC) \ + { \ + csky_arch_isa_features[CSKY_ISA_FEATURE_GET (IDENT)] = \ + bitmap_bit_p (csky_active_target.isa, CSKY_ISA_FEATURE_GET (IDENT)); \ + } +#include "csky_isa.def" +#undef CSKY_ISA + + /* TODO */ + + /* Resynchronize the saved target options. */ + cl_target_option_save (TREE_TARGET_OPTION (target_option_default_node), + &global_options); + +#ifdef ENABLE_TPF_DEBUG + /* Don't emit DWARF4 unless specifically selected. The TPF + debuggers do not yet support DWARF 3/4. */ + if (!global_options_set.x_dwarf_strict) + dwarf_strict = 1; + if (!global_options_set.x_dwarf_version) + dwarf_version = 3; +#endif + + /* Don't run the scheduler before reload by default, + since it tends to increase register pressure. */ + if (!global_options_set.x_flag_schedule_insns) + flag_schedule_insns = 0; + + csky_add_gc_roots (); +} + + +/* Return TRUE if X contains any references to TLS symbols. */ + +bool +csky_tls_referenced_p (rtx x) +{ + if (!TARGET_TLS) + return false; + + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, x, ALL) + { + const_rtx x = *iter; + if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0) + return true; + + /* Don't recurse into UNSPEC_TLS looking for TLS symbols; these are + TLS offsets, not real symbol references. */ + if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLS) + iter.skip_subrtxes (); + } + return false; +} + + +/* Implement TARGET_CANNOT_FORCE_CONST_MEM. + Determine if it's legal to put X into the constant pool. This + is not possible for the address of thread-local symbols, which + is checked above. */ + +static bool +csky_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, + rtx x) +{ + return csky_tls_referenced_p (x); +} + + +/* Implement TARGET_LEGITIMATE_CONSTANT_P. Returns nonzero if the + constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ + +static bool +csky_legitimate_constant_p (machine_mode mode, rtx x) +{ + return (!csky_cannot_force_const_mem (mode, x) + && CONSTANT_P (x)); +} + + +/* Return true if X is valid as an CSKY addressing register. */ + +static bool +is_csky_address_register_rtx_p (rtx x, int strict_p) +{ + int regno; + + if (!x) + return false; + if (!REG_P (x)) + return false; + + regno = REGNO (x); + + if (strict_p) + return (CSKY_GENERAL_REGNO_P (regno) + || CSKY_GENERAL_REGNO_P (reg_renumber[regno])); + else + return CSKY_GENERAL_REGNO_P (regno) || regno >= FIRST_PSEUDO_REGISTER; +} + + +/* Return TRUE if X is a thread-local symbol. */ + +static bool +csky_tls_symbol_p (rtx x) +{ + if (!TARGET_TLS) + return false; + + if (GET_CODE (x) != SYMBOL_REF) + return false; + + return SYMBOL_REF_TLS_MODEL (x) != 0; +} + + +/* Handle lazy initialization of __tls_get_addr libfunc. */ +static GTY(()) rtx tls_get_addr_libfunc; + +static rtx +get_tls_get_addr (void) +{ + if (!tls_get_addr_libfunc) + tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr"); + return tls_get_addr_libfunc; +} + + +/* Emit a call to __tls_get_addr. */ + +static rtx_insn * +csky_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc) +{ + rtx label, labelno, unspec, tmp; + rtx_insn *insns; + + start_sequence (); + + labelno = GEN_INT (tls_labelno++); + label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_TLS_LABEL); + unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (3, x, GEN_INT (reloc), label), + UNSPEC_TLS); + tmp = gen_reg_rtx (SImode); + emit_move_insn (reg, unspec); + emit_move_insn (tmp, label); + emit_insn (gen_addsi3 (reg, reg, tmp)); + *valuep = emit_library_call_value (get_tls_get_addr (), + NULL_RTX, LCT_PURE, /* LCT_CONST? */ + Pmode, reg, Pmode); + insns = get_insns (); + end_sequence (); + return insns; +} + +/* Helper function for csky_legitimize_address, to handle the TLS cases. + REG is a scratch register and may be null. */ + +rtx +csky_legitimize_tls_address (rtx x, rtx reg) +{ + rtx dest, tp, label, labelno, unspec, ret, eqv, addend, tmp; + rtx_insn *insns; + unsigned int model = SYMBOL_REF_TLS_MODEL (x); + + if (!reg) + reg = gen_reg_rtx (SImode); + + switch (model) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + insns = csky_call_tls_get_addr (x, reg, &ret, TLS_GD32); + dest = gen_reg_rtx (Pmode); + emit_libcall_block (insns, dest, ret, x); + return dest; + + case TLS_MODEL_LOCAL_DYNAMIC: + insns = csky_call_tls_get_addr (x, reg, &ret, TLS_LDM32); + + /* Attach a unique REG_EQUIV, to allow the RTL optimizers to + share the LDM result with other LD model accesses. */ + eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_TLS); + dest = gen_reg_rtx (Pmode); + emit_libcall_block (insns, dest, ret, eqv); + + /* Load the addend. */ + addend = gen_rtx_UNSPEC (Pmode, + gen_rtvec (2, x, GEN_INT (TLS_LDO32)), + UNSPEC_TLS); + addend = force_reg (SImode, addend); + return gen_rtx_PLUS (Pmode, dest, addend); + + case TLS_MODEL_INITIAL_EXEC: + labelno = GEN_INT (tls_labelno++); + label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_TLS_LABEL); + unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (3, x, GEN_INT (TLS_IE32), label), + UNSPEC_TLS); + tmp = gen_reg_rtx (SImode); + emit_move_insn (reg, unspec); + emit_move_insn (tmp, label); + emit_insn (gen_addsi3 (reg, reg, tmp)); + emit_move_insn (reg, gen_const_mem (Pmode, reg)); + tp = gen_rtx_REG (SImode, CSKY_TLS_REGNUM); + return gen_rtx_PLUS (Pmode, tp, reg); + + case TLS_MODEL_LOCAL_EXEC: + unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (2, x, GEN_INT (TLS_LE32)), + UNSPEC_TLS); + emit_move_insn (reg, unspec); + tp = gen_rtx_REG (SImode, CSKY_TLS_REGNUM); + return gen_rtx_PLUS (Pmode, tp, reg); + + default: + abort (); + } +} + + +/* Implement TARGET_LEGITIMIZE_ADDRESS. */ + +static rtx +csky_legitimize_address (rtx x, rtx orig_x ATTRIBUTE_UNUSED, + machine_mode mode) +{ + if (csky_tls_symbol_p (x)) + return csky_legitimize_tls_address (x, NULL_RTX); + + if (GET_CODE (x) == PLUS) + { + rtx xop0 = XEXP (x, 0); + rtx xop1 = XEXP (x, 1); + + if (is_csky_address_register_rtx_p (xop0, 0) + && CONST_INT_P (xop1)) + { + HOST_WIDE_INT offset = INTVAL (xop1); + + /* Try to replace ld32 rx,(ry, offset), to addi16 rz, oimm8 + and ld16 rx,(rz, new_ld_offset) to avoid emitting a + 32-bit ld, but this addi has a range limitation. */ + if (optimize_size + && offset > CSKY_LD16_MAX_OFFSET (mode) + && offset <= (CSKY_ADDI16_MAX_IMM + + CSKY_LD16_MAX_OFFSET (mode))) + { + HOST_WIDE_INT new_ld_offset + = offset & CSKY_LD16_OFFSET_MASK (mode); + + xop0 = force_operand (plus_constant (Pmode, xop0, + offset - new_ld_offset), + NULL_RTX); + x = plus_constant (Pmode, xop0, new_ld_offset); + } + else if (offset < 0 && offset >= (-CSKY_SUBI16_MAX_IMM)) + x = force_operand (x, NULL_RTX); + else if (offset > CSKY_LD16_MAX_OFFSET (mode) + || offset < 0) + { + /* For the remaining cases, force the constant into a + register. */ + xop1 = force_reg (SImode, xop1); + x = gen_rtx_PLUS (SImode, xop0, xop1); + } + } + + /* If the index is store in register, force the + base to register. */ + if (is_csky_address_register_rtx_p (xop1, 0) + && !is_csky_address_register_rtx_p (xop0, 0)) + { + xop0 = force_operand (xop0, NULL_RTX); + x = gen_rtx_PLUS (SImode, xop0, xop1); + } + } + /* Make sure to take full advantage of the pre-indexed addressing mode + with absolute addresses which often allows for the base register to + be factorized for multiple adjacent memory references, and it might + even allows for the mini pool to be avoided entirely. */ + else if (CONST_INT_P (x) && optimize > 0) + { + HOST_WIDE_INT mask, base, index; + rtx base_reg; + + mask = CSKY_LD16_OFFSET_MASK (mode); + base = INTVAL (x) & ~mask; + index = INTVAL (x) & mask; + base_reg = force_reg (SImode, GEN_INT (base)); + x = plus_constant (Pmode, base_reg, index); + } + + return x; +} + + +/* Return nonzero if INDEX is valid for an address index operand. + ck801 use 16 bits ld + ck802 use 16 and 32 bits ld + others use ld and ldr. */ + +static int +ck801_legitimate_index_p (machine_mode mode, rtx index, + int strict_p ATTRIBUTE_UNUSED) +{ + enum rtx_code code = GET_CODE (index); + + /* When the mode size is larger than 4, we may use two ld instruction + to get data, the index and (index+1) should be valid. */ + if (GET_MODE_SIZE (mode) >= 8) + return (code == CONST_INT + && INTVAL (index) < CSKY_LD16_MAX_OFFSET (SImode) + && INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0); + + if (code == CONST_INT && GET_MODE_SIZE (mode) > 0 + && INTVAL (index) <= CSKY_LD16_MAX_OFFSET (mode) + && INTVAL (index) >= 0) + return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0); + + return 0; +} + + +static int +ck802_legitimate_index_p (machine_mode mode, rtx index, + int strict_p ATTRIBUTE_UNUSED) +{ + enum rtx_code code = GET_CODE (index); + + /* When the mode size is larger than 4, we may use two ld instruction + to get data, the index and (index+1) should be valid. */ + if (GET_MODE_SIZE (mode) >= 8) + return (code == CONST_INT + && INTVAL (index) < CSKY_LD32_MAX_OFFSET (SImode) + && INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0); + + if (code == CONST_INT && GET_MODE_SIZE (mode) > 0 + && INTVAL (index) <= CSKY_LD32_MAX_OFFSET (mode) + && INTVAL (index) >= 0) + return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0); + + return 0; +} + + +/* The instruction ldr rz, (rx, ry << i), i can be 0,1,2,3. + Check that SHIFT is valid, that the code is MULT, and that + the shift is a power of 2. */ + +static bool +is_ldr_shift_p (HOST_WIDE_INT shift, enum rtx_code code) +{ + if (code == ASHIFT) + return (shift >= 0 && shift <= 3); + else if (code == MULT) + return (shift == 1 + || shift == 2 + || shift == 4 + || shift == 8); + else + return false; +} + + +static int +ck810_legitimate_index_p (machine_mode mode, rtx index, int strict_p) +{ + enum rtx_code code = GET_CODE (index); + + if (TARGET_HARD_FLOAT + && (mode == SFmode || mode == DFmode)) + return (code == CONST_INT && INTVAL (index) < 1024 + && INTVAL (index) >= 0 + && (INTVAL (index) & 3) == 0); + + if (code == CONST_INT) + { + /* When the mode size is larger than 4, we may use two ld instruction + to get data, the index and (index+1) should be valid. */ + if (GET_MODE_SIZE (mode) >= 8) + return (INTVAL (index) < CSKY_LD32_MAX_OFFSET (SImode) + && INTVAL (index) >= 0 && (INTVAL (index) & 3) == 0); + + if (GET_MODE_SIZE (mode) > 0 + && INTVAL (index) <= CSKY_LD32_MAX_OFFSET (mode) + && INTVAL (index) >= 0) + return ((INTVAL (index) % GET_MODE_SIZE (mode)) == 0); + } + /* Allow ld.w rx, (gb, sym@got) when -fpic specially. */ + else if (code == UNSPEC) + return (flag_pic == 1 + && (XINT (index, 1) == UNSPEC_PIC_SYMBOL_PLT + || XINT (index, 1) == UNSPEC_PIC_SYMBOL_GOT)); + /* The follow index is for ldr instruction, the ldr cannot + load dword data, so the mode size should not be larger than + 4. */ + else if (GET_MODE_SIZE (mode) <= 4) + { + if (is_csky_address_register_rtx_p (index, strict_p)) + return 1; + else if (code == MULT || code == ASHIFT) + { + rtx xiop0 = XEXP (index, 0); + rtx xiop1 = XEXP (index, 1); + + /* FIXME can the xiop1 be the reg and xiop0 be the int when mult? */ + return (is_csky_address_register_rtx_p (xiop0, strict_p) + && CONST_INT_P (xiop1) + && is_ldr_shift_p (INTVAL (xiop1), code)); + } + } + + return 0; +} + + +static int +csky_legitimate_index_p (machine_mode mode, rtx index, int strict_p) +{ + if (CSKY_TARGET_ARCH (CK801)) + return ck801_legitimate_index_p (mode, index, strict_p); + else if (CSKY_TARGET_ARCH (CK802)) + return ck802_legitimate_index_p (mode, index, strict_p); + else + return ck810_legitimate_index_p (mode, index, strict_p); +} + + +/* Implement TARGET_LEGITIMATE_ADDRESS_P. + Recognizes RTL expressions that are valid memory addresses for an + instruction. The MODE argument is the machine mode for the MEM + expression that wants to use this address. + + It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should + convert common non-canonical forms to canonical form so that they will + be recognized. */ + +static bool +csky_legitimate_address_p (machine_mode mode, rtx addr, bool strict_p) +{ + enum rtx_code code = GET_CODE (addr); + + /* Match the RTX form emitted for constant pool references. + After reload constants split into minipools will have addresses + from a LABEL_REF. */ + if (reload_completed + && ((code == LABEL_REF) + || (code == CONST + && GET_CODE (XEXP (addr, 0)) == PLUS + && GET_CODE (XEXP (XEXP (addr, 0), 0)) == LABEL_REF + && CONST_INT_P (XEXP (XEXP (addr, 0), 1))))) + return 1; + + if (is_csky_address_register_rtx_p (addr, strict_p)) + return 1; + /* It is a pc-relative load, may be generated for constpool. */ + else if (GET_CODE (addr) == LABEL_REF) + return 1; + + if (code == PLUS) + { + rtx xop0 = XEXP (addr, 0); + rtx xop1 = XEXP (addr, 1); + + return ((is_csky_address_register_rtx_p (xop0, strict_p) + && csky_legitimate_index_p (mode, xop1, strict_p)) + || (is_csky_address_register_rtx_p (xop1, strict_p) + && csky_legitimate_index_p (mode, xop0, strict_p))); + } + + return 0; +} + + +/* Functions to save and restore machine-specific function data. */ + +static struct machine_function * +csky_init_machine_status (void) +{ + struct machine_function *machine; + + machine = ggc_cleared_alloc (); + +#if CSKY_FT_UNKNOWN != 0 + machine->func_type = CSKY_FT_UNKNOWN; +#endif + return machine; +} + + +/* Implement INIT_EXPANDERS. */ + +void +csky_init_expanders (void) +{ + /* Arrange to initialize and mark the machine per-function status. */ + init_machine_status = csky_init_machine_status; +} + + +/* Implement TARGET_CANNOT_COPY_INSN_P. + We must not copy any rtx that uses a pc-relative address. */ + +static bool +csky_cannot_copy_insn_p (rtx_insn *insn) +{ + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, PATTERN (insn), ALL) + { + const_rtx x = *iter; + if (GET_CODE (x) == UNSPEC + && (XINT (x, 1) == UNSPEC_TLS_LABEL + || XINT (x, 1) == UNSPEC_PIC_SYMBOL_GOTPC_GRS)) + return true; + } + return false; +} + + +/* Extract the parts of an RTL expression that is a valid memory address + for an instruction. Return FALSE if it is a invalid memory address. */ + +struct csky_address +{ + rtx base, index, symbol, label, disp; + HOST_WIDE_INT scale; +}; + +static bool +decompose_csky_address (rtx addr, struct csky_address *out) +{ + rtx base = NULL_RTX, index = NULL_RTX, disp = NULL_RTX; + HOST_WIDE_INT scale = 1; + rtx scale_rtx = NULL_RTX; + int i; + + out->base = out->index = out->symbol = out->label = out->disp = NULL_RTX; + out->scale = 0; + + if (REG_P (addr)) + { + out->base = addr; + return true; + } + + if (GET_CODE (addr) == LABEL_REF) + { + out->label = addr; + return true; + } + + if (GET_CODE (addr) == CONST) + addr = XEXP (addr, 0); + + if (GET_CODE (addr) == PLUS) + { + rtx addends[2], op; + + addends[0] = XEXP (addr, 0); + addends[1] = XEXP (addr, 1); + + if (GET_CODE (addends[0]) == LABEL_REF && CONST_INT_P (addends[1])) + { + out->label = addends[0]; + out->disp = addends[1]; + return true; + } + + if (!REG_P (addends[0])) + std::swap (addends[0], addends[1]); + + for (i = 0; i < 2; ++i) + { + op = addends[i]; + switch (GET_CODE (op)) + { + case REG: + if (!base) + base = op; + else if (!index) + index = op; + else + return false; + break; + case CONST_INT: + case UNSPEC: + if (disp) + return false; + disp = op; + break; + case MULT: + if (index) + return false; + index = XEXP (op, 0); + scale_rtx = XEXP (op, 1); + if (!CONST_INT_P (index) && !CONST_INT_P (scale_rtx)) + return false; + else if (CONST_INT_P (index)) + std::swap (index, scale_rtx); + scale = INTVAL (scale_rtx); + break; + case ASHIFT: + if (index) + return false; + index = XEXP (op, 0); + scale_rtx = XEXP (op, 1); + if (!CONST_INT_P (scale_rtx)) + return false; + scale = scale << INTVAL (scale_rtx); + break; + default: + return false; + } + } + } + + if (!base) + return false; + + out->base = base; + out->index = index; + out->disp = disp; + out->scale = scale; + + return true; +} + +/* Helper function for the csky_simple_mem_operand predicate. Returns + true if OP is an address of the form reg + displacement. */ + +bool +csky_simple_addr_operand_p (rtx op) +{ + struct csky_address addr; + + if (!decompose_csky_address (op, &addr)) + return false; + + /* FIXME The PIC related code. + Check if load the symbol address from got table. */ + if (addr.disp && GET_CODE (addr.disp) == UNSPEC) + return false; + if (!addr.index && !addr.symbol) + return true; + return false; +} + + +/* Print the UNSPEC operand in X to the STREAM. */ + +static void +csky_output_pic_addr_const (FILE *stream, rtx x, int code) +{ + + if (GET_CODE (x) != UNSPEC) + return; + + if (UNSPEC_TLS == XINT (x, 1)) + { + /* FIXME It is not reached */ + return; + } + + csky_print_operand (stream, XVECEXP (x, 0, 0), code); + + switch (XINT (x, 1)) + { + case UNSPEC_PIC_SYMBOL_GOTOFF: + fputs ("@GOTOFF", stream); + break; + case UNSPEC_PIC_SYMBOL_PLT: + fputs ("@PLT", stream); + break; + case UNSPEC_PIC_SYMBOL_GOT: + fputs ("@GOT", stream); + break; + case UNSPEC_PIC_SYMBOL_GOTPC: + fputs ("@GOTPC", stream); + break; + case UNSPEC_PIC_SYMBOL_BSR: + break; + default: + break; + } +} + + +/* Output the constpool label according to the rtx expression X. */ + +static void +csky_output_constpool_label (FILE *stream, rtx x) +{ + char buf[15]; + + gcc_assert (GET_CODE (x) == LABEL_REF); + x = XEXP (x, 0); + + if (GET_CODE (x) == UNSPEC_VOLATILE && XINT (x, 1) == VUNSPEC_POOL_LABEL) + { + ASM_GENERATE_INTERNAL_LABEL (buf, CSKY_CONSTPOOL_LABEL_PREFIX, + INTVAL (XVECEXP (x, 0, 0))); + assemble_name (stream, buf); + } +} + + +/* Implement TARGET_PRINT_OPERAND_ADDRESS. */ + +static void +csky_print_operand_address (FILE *stream, + machine_mode mode ATTRIBUTE_UNUSED, + rtx x) +{ + + struct csky_address addr; + + decompose_csky_address (x, &addr); + + if (addr.label && addr.disp && GET_CODE (addr.disp) == CONST_INT) + { + fprintf (stream, "["); + csky_output_constpool_label (stream, addr.label); + fprintf (stream, "+%d]", (int) INTVAL (addr.disp)); + } + else if (addr.label) + { + fprintf (stream, "["); + csky_output_constpool_label (stream, addr.label); + fprintf (stream, "]"); + } + else if (addr.symbol && addr.disp && GET_CODE (addr.disp) == CONST_INT) + { + fprintf (stream, "["); + output_addr_const (stream, addr.symbol); + fprintf (stream, "+%d]", (int) INTVAL (addr.disp)); + } + else if (addr.symbol) + { + fprintf (stream, "["); + output_addr_const (stream, addr.symbol); + fprintf (stream, "]"); + } + else if (addr.disp && GET_CODE (addr.disp) == CONST_INT) + fprintf (stream, "(%s, %d)", + reg_names[REGNO (addr.base)], (int) INTVAL (addr.disp)); + else if (addr.disp && GET_CODE (addr.disp) == UNSPEC) + { + if (REGNO (addr.base) != CSKY_GB_REGNUM) + fprintf (stream, "(%s, ", reg_names[REGNO (addr.base)]); + else + fprintf (stream, "["); + csky_output_pic_addr_const (stream, addr.disp, 0); + fprintf (stream, "%s", (REGNO (addr.base) != CSKY_GB_REGNUM) + ? ")" : "]"); + } + else if (addr.index) + fprintf (stream, "(%s, %s << %d)", + reg_names[REGNO (addr.base)], reg_names[REGNO (addr.index)], + exact_log2 ((int) (addr.scale))); + else + fprintf (stream, "(%s, 0)", reg_names[REGNO (addr.base)]); +} + + +/* Implement TARGET_PRINT_OPERAND. + Print operand X (an rtx) in assembler syntax to file STREAM + according to modifier CODE. + + 'N' print the log2(X+1), mainly used for bmaski + 'P' print the log2(X) + 'Q' print the log2(~X) + 'O' print a decimal number + 'M' print a decimal number as its negative + 'R' print the next register or memory location along, i.e. the lsw in + a double word value + 'H' print the high 16 bits of a constant. */ + +static void +csky_print_operand (FILE *stream, rtx x, int code) +{ + switch (code) + { + case 'N': + if ((INTVAL (x) & 0xffffffff) == 0xffffffff) + fprintf (stream, "0"); + else + fprintf (stream, "%d", + (int) exact_log2 ((INTVAL (x) & 0xffffffff) + 1) % 32); + break; + case 'P': + fprintf (stream, "%d", + (int) exact_log2 (INTVAL (x) & 0xffffffff)); + break; + case 'Q': + fprintf (stream, "%d", + (int) exact_log2 (~INTVAL (x) & 0xffffffff)); + break; + case 'O': + fprintf (stream, "%d", (int) INTVAL (x)); + break; + case 'M': + fprintf (stream, "%d", (int) (-INTVAL (x))); + break; + case 'R': + /* Next location along in memory or register. */ + switch (GET_CODE (x)) + { + case REG: + fputs (reg_names[REGNO (x) + 1], stream); + break; + case MEM: + csky_print_operand_address + (stream, GET_MODE (x), XEXP (adjust_address (x, SImode, 4), 0)); + break; + default: + gcc_unreachable (); + } + break; + case 'H': + fprintf (stream, "%ld", (long)((INTVAL (x) & 0xFFFF0000) >> 16)); + break; + default: + switch (GET_CODE (x)) + { + case REG: + fputs (reg_names[REGNO (x)], stream); + break; + case MEM: + output_address (GET_MODE (x), XEXP (x, 0)); + break; + case UNSPEC: + csky_output_pic_addr_const (stream, x, code); + break; + default: + output_addr_const (stream, x); + break; + } + break; + } +} + + + +/* Implement TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS. */ + +static bool +csky_allocate_stack_slots_for_args (void) +{ + /* Naked functions should not allocate stack slots for arguments. */ + return !CSKY_FUNCTION_IS_NAKED (get_csky_current_func_type ()); +} + + +/* Can we generate a constant with a single instruction, without using + lrw? */ + +static int +const_ok_for_cskyv2 (HOST_WIDE_INT value) +{ + /* Try exact power of two. It can be generated by bgeni. */ + if (CSKY_CONST_OK_FOR_Ub (value)) + return 1; + + /* Try exact power of two - 1. It can be generated by bmaski. */ + if (CSKY_CONST_OK_FOR_Uc (value) && value != -1) + return 1; + + /* Try if it can be generated by movi. */ + if (CSKY_CONST_OK_FOR_I (value)) + return 1; + + /* The constant can be generated by movih. + Notice that movih is a 32-bit instruction. */ + if (CSKY_CONST_OK_FOR_MOVIH (value)) + return 1; + + return 0; +} + + +/* Tricks for synthesizing constants from values that can be directly + manipulated by machine instructions. */ + +enum csky_inline_const_type +{ + IC_UNINLINABLE = 0, /* Not inlineable */ + IC_SINGLE, /* Single instruction */ + IC_APPEND_NOT, /* Single instruction followed by a not */ + IC_APPEND_ADDI, /* Single insn followed by an addi */ + IC_APPEND_SUBI, /* Single insn followed by a subi */ + IC_BGENI_ADDI, /* Single insn(bgeni) followed by an addi */ + IC_BGENI_SUBI, /* Single insn(bgeni) followed by a subi */ + IC_APPEND_BSETI, /* Single insn followed by bseti */ + IC_APPEND_MOVI, /* Single insn followed by movi */ + IC_APPEND_BCLRI, /* Single insn followed by bclri */ + IC_APPEND_ROTLI, /* Single insn followed by rotli */ + IC_APPEND_LSLI, /* Single insn followed by lsli */ + IC_APPEND_IXH, /* Single insn followed by ixh */ + IC_APPEND_IXW /* Single insn followed by ixw */ +}; + + +/* Try tricks to load a constant inline and return the trick number if + success, or IC_UNINLINABLE. */ + +static enum csky_inline_const_type +try_csky_constant_tricks (HOST_WIDE_INT value, HOST_WIDE_INT *x, + HOST_WIDE_INT *y) +{ + HOST_WIDE_INT i, value_invert; + unsigned HOST_WIDE_INT bit, shf, rot, lobits, hibits; + + value &= 0xffffffff; + value_invert = ~value & 0xffffffff; + + if (const_ok_for_cskyv2 (value)) + { + *x = value; + return IC_SINGLE; + } + + /* Since movih is 32 bits, do not use it here, better code may + be generated later. */ + if (const_ok_for_cskyv2 (value_invert) + && !CSKY_CONST_OK_FOR_MOVIH (value_invert)) + { + *x = value_invert; + return IC_APPEND_NOT; + } + + /* One immediate generate instruction, and one 16-bit subi or addi. */ + for (i = 1; i <= 32; i++) + { + if (const_ok_for_cskyv2 (value - i) + && !CSKY_CONST_OK_FOR_MOVIH (value - i)) + { + *x = value - i; + *y = i; + return IC_APPEND_ADDI; + } + + if (const_ok_for_cskyv2 (value + i) + && !CSKY_CONST_OK_FOR_MOVIH (value - i)) + { + *x = value + i; + *y = i; + return IC_APPEND_SUBI; + } + } + + /* Generate bgeni + addi. */ + if (CSKY_CONST_OK_FOR_Ub (value & 0xfffff000)) + { + *x = (value & 0xfffff000); + *y = (value & 0xfff); + return IC_BGENI_ADDI; + } + + /* Generate bgeni + subi. */ + lobits = value & 0xfff; + hibits = (unsigned HOST_WIDE_INT)(value & 0xfffff000) + (1 << 12); + if (exact_log2 (hibits) >= 1 + && exact_log2 (hibits) <= 30 + && lobits != 0) + { + *x = hibits; + *y = (0x1000 - lobits); + return IC_BGENI_SUBI; + } + + /* One immediate generate instruction, and one bseti or bclri. */ + bit = 0x80000000ULL; + for (i = 0; i <= 31; i++) + { + if (const_ok_for_cskyv2 (value & ~bit) + && !CSKY_CONST_OK_FOR_MOVIH (value & ~bit)) + { + *y = bit; + *x = (value & ~bit); + return IC_APPEND_BSETI; + } + + if (const_ok_for_cskyv2 (value | bit) + && !CSKY_CONST_OK_FOR_MOVIH (value | bit)) + { + *y = ~bit & 0xffffffff; + *x = value | bit; + return IC_APPEND_BCLRI; + } + + bit >>= 1; + } + + /* One immediate generate instruction, and one rotli or lsli. */ + shf = value; + rot = value; + for (i = 1; i < 31; i++) + { + int c; + + /* Rotate left. */ + c = rot << 31; + rot >>= 1; + rot &= 0x7FFFFFFF; + rot |= c; + + if (const_ok_for_cskyv2 (rot) && !CSKY_CONST_OK_FOR_MOVIH (rot)) + { + *y = i; + *x = rot; + return IC_APPEND_ROTLI; + } + + /* Can't use logical shift when low order bit is one. */ + if (shf & 1) + shf = 0; + else + shf >>= 1; + + if (shf != 0 && const_ok_for_cskyv2 (shf) + && !CSKY_CONST_OK_FOR_MOVIH (shf)) + { + *y = i; + *x = shf; + return IC_APPEND_LSLI; + } + } + + /* One immediate generate instruction, and one ixh. */ + if (CSKY_ISA_FEATURE (E2) + && (value % 3) == 0 + && const_ok_for_cskyv2 (value / 3) + && !CSKY_CONST_OK_FOR_MOVIH (value / 3)) + { + *x = value / 3; + return IC_APPEND_IXH; + } + + /* One immediate generate instruction, and one ixw. */ + if (CSKY_ISA_FEATURE (E2) + && (value % 5) == 0 + && const_ok_for_cskyv2 (value / 5) + && !CSKY_CONST_OK_FOR_MOVIH (value / 5)) + { + *x = value / 5; + return IC_APPEND_IXW; + } + + /* Generate movih + bseti. */ + if (CSKY_CONST_OK_FOR_Ub (value & 0xffff)) + { + *x = value & 0xffff0000; + *y = value & 0xffff; + return IC_APPEND_BSETI; + } + + /* Generate movih + not. */ + if (CSKY_CONST_OK_FOR_MOVIH (value_invert)) + { + *x = value_invert; + return IC_APPEND_NOT; + } + + /* One movih, and one 16bits addi or subi. */ + for (i = 1; i <= 32; i++) + { + if (CSKY_CONST_OK_FOR_MOVIH (value - i)) + { + *x = value - i; + *y = i; + return IC_APPEND_ADDI; + } + + if (CSKY_CONST_OK_FOR_MOVIH (value + i)) + { + *x = value + i; + *y = i; + return IC_APPEND_SUBI; + } + } + + /* One movih, and one bseti or bclri. */ + bit = 0x80000000ULL; + for (i = 0; i <= 31; i++) + { + if (CSKY_CONST_OK_FOR_MOVIH (value & ~bit)) + { + *y = bit; + *x = value & ~bit; + return IC_APPEND_BSETI; + } + + if (CSKY_CONST_OK_FOR_MOVIH (value | bit)) + { + *y = ~bit & 0xffffffff; + *x = value | bit; + return IC_APPEND_BCLRI; + } + + bit >>= 1; + } + + /* One movih, and one rotli or lsli. */ + shf = value; + rot = value; + for (i = 1; i < 31; i++) + { + int c; + + /* Rotate left. */ + c = rot << 31; + rot >>= 1; + rot &= 0x7FFFFFFF; + rot |= c; + + if (CSKY_CONST_OK_FOR_MOVIH (rot)) + { + *y = i; + *x = rot; + return IC_APPEND_ROTLI; + } + + /* Can't use logical shift when low order bit is one. */ + if (shf & 1) + shf = 0; + else + shf >>= 1; + + if (shf != 0 && CSKY_CONST_OK_FOR_MOVIH (shf)) + { + *y = i; + *x = shf; + return IC_APPEND_LSLI; + } + } + + return IC_UNINLINABLE; +} + + +/* Actually output a constant using a trick. + FIXME: I think this would be better handled by a splitter than at the + asm output level. */ + +static const char * +csky_output_inline_const (machine_mode mode, rtx operands[]) +{ + HOST_WIDE_INT x = 0, y = 0; + enum csky_inline_const_type trick_type; + rtx out_operands[3]; + char buf[256]; + char load_op[128]; + const char *dst_fmt; + HOST_WIDE_INT value = INTVAL (operands[1]); + int ivalue = (int) value; + unsigned int uvalue = (unsigned int) value; + + trick_type = try_csky_constant_tricks (value, &x, &y); + /* lrw's are handled separately: Large inlinable constants never get + turned into lrw's. Our caller uses try_csky_constant_tricks to back + off to an lrw rather than calling this routine. */ + gcc_assert (trick_type != IC_UNINLINABLE); + + /* Operands: 0 = dst, 1 = load immedate., 2 = adjust immedate. */ + out_operands[0] = operands[0]; + out_operands[1] = GEN_INT (x); + if (trick_type != IC_SINGLE && trick_type != IC_APPEND_NOT) + out_operands[2] = GEN_INT (y); + + /* Select dst format based on mode. */ + if (mode == DImode && TARGET_BIG_ENDIAN) + dst_fmt = "%R0"; + else + dst_fmt = "%0"; + + /* Try movi16: 0~31,movi32: 0~65535. */ + if (CSKY_CONST_OK_FOR_I (x)) + sprintf (load_op, "movi\t%s, %%1", dst_fmt); + /* Try exact power of two - 1. */ + else if (CSKY_CONST_OK_FOR_Uc (x)) + sprintf (load_op, "bmaski\t%s, %%N1", dst_fmt); + /* Try movih. */ + else if (CSKY_CONST_OK_FOR_MOVIH (x)) + sprintf (load_op, "movih\t%s, %%H1", dst_fmt); + else + { + sprintf (load_op, "BADMOVI-inline_const %s, %%1", dst_fmt); + gcc_unreachable (); + } + + switch (trick_type) + { + case IC_SINGLE: + strcpy (buf, load_op); + break; + /* Add instruction 'not'. */ + case IC_APPEND_NOT: + sprintf (buf, "%s\n\tnot\t%s, %s\t// %d 0x%x", load_op, dst_fmt, + dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'addi'. */ + case IC_APPEND_ADDI: + sprintf (buf, "%s\n\taddi\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'subi'. */ + case IC_APPEND_SUBI: + sprintf (buf, "%s\n\tsubi\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'addi', the last instruction is bgeni. */ + case IC_BGENI_ADDI: + sprintf (buf, "%s\n\taddi\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'subi', the last instruction is bgeni. */ + case IC_BGENI_SUBI: + sprintf (buf, "%s\n\tsubi\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'bseti'. */ + case IC_APPEND_BSETI: + sprintf (buf, "%s\n\tbseti\t%s, %s, %%P2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'movi'. */ + case IC_APPEND_MOVI: + sprintf (buf, "%s\n\tmovi\t%s, %%2\t// %d 0x%x", load_op, dst_fmt, + ivalue, uvalue); + break; + /* Add instruction 'bclri'. */ + case IC_APPEND_BCLRI: + sprintf (buf, "%s\n\tbclri\t%s, %s, %%Q2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'rotli'. */ + case IC_APPEND_ROTLI: + sprintf (buf, "%s\n\trotli\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'lsli'. */ + case IC_APPEND_LSLI: + sprintf (buf, "%s\n\tlsli\t%s, %s, %%2\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'ixh'. */ + case IC_APPEND_IXH: + sprintf (buf, "%s\n\tixh\t%s, %s, %s\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, dst_fmt, ivalue, uvalue); + break; + /* Add instruction 'ixw'. */ + case IC_APPEND_IXW: + sprintf (buf, "%s\n\tixw\t%s, %s, %s\t// %d 0x%x", load_op, + dst_fmt, dst_fmt, dst_fmt, ivalue, uvalue); + break; + default: + return ""; + } + + output_asm_insn (buf, out_operands); + + return ""; +} + +/* This is a helper function for the Uo constraint for movsi patterns. */ + +bool +csky_inlinable_constant (HOST_WIDE_INT value) +{ + HOST_WIDE_INT x, y; + return (!(CSKY_TARGET_ARCH (CK802) || CSKY_TARGET_ARCH (CK801)) + && try_csky_constant_tricks (value, &x, &y)); +} + + +/* Return true if the constant VAL can be expressed by an 8-bit constant + with a shift value, filling in *BASE and *SHIFT. */ + +bool +csky_shifted_imm8_constant (unsigned HOST_WIDE_INT val, + unsigned int *base, unsigned int *shift) +{ + unsigned HOST_WIDE_INT mask = 0xff; + int i; + val = val & (unsigned HOST_WIDE_INT) 0xffffffffu; + if (val == 0) + return 0; + + for (i = 0; i < 25; i++) + if ((val & (mask << i)) == val) + { + if (base) + *base = (unsigned int) (val >> i); + if (shift) + *shift = (unsigned int) i; + return true; + } + + return false; +} + + +/* Output a move of a word or less value. */ + +const char * +csky_output_move (rtx insn ATTRIBUTE_UNUSED, rtx operands[], + machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx dst = operands[0]; + rtx src = operands[1]; + struct csky_address op0, op1; + + if (REG_P (dst)) + { + /* The situation mov reg to reg. */ + if (REG_P (src)) + { + int dstreg = REGNO (dst); + int srcreg = REGNO (src); + + /* hilo registers exchange their places, + and their order of Dimode as same as other + general registers in LITTLE_ENDIAN mode. */ + if (TARGET_BIG_ENDIAN) + { + if (dstreg == CSKY_HI_REGNUM) + return "mthi\t%1"; + else if (dstreg == CSKY_LO_REGNUM) + return "mtlo\t%1"; + else if (srcreg == CSKY_HI_REGNUM) + return "mfhi\t%0"; + else if (srcreg == CSKY_LO_REGNUM) + return "mflo\t%0"; + } + else + { + if (dstreg == CSKY_HI_REGNUM) + return "mtlo\t%1"; + else if (dstreg == CSKY_LO_REGNUM) + return "mthi\t%1"; + else if (srcreg == CSKY_HI_REGNUM) + return "mflo\t%0"; + else if (srcreg == CSKY_LO_REGNUM) + return "mfhi\t%0"; + } + + if (CSKY_VREG_P (dstreg) && CSKY_VREG_P (srcreg)) + return "fmovs\t%0, %1"; + if (CSKY_VREG_P (dstreg)) + return "fmtvrl\t%0, %1"; + if (CSKY_VREG_P (srcreg)) + return "fmfvrl\t%0, %1"; + + if (REGNO (src) == CSKY_CC_REGNUM) + return "mvc\t%0"; + else + return "mov\t%0, %1"; + } + /* The situation mov memory to reg. */ + else if (GET_CODE (src) == MEM) + { + decompose_csky_address (XEXP (src, 0), &op1); + + if (op1.index) + switch (GET_MODE (src)) + { + case E_HImode: + return "ldr.h\t%0, %1"; + case E_QImode: + return "ldr.b\t%0, %1"; + case E_SImode: + case E_SFmode: + if (CSKY_VREG_P (REGNO (dst))) + return "fldrs\t%0, %1"; + else + return "ldr.w\t%0, %1"; + default: + gcc_unreachable (); + } + /* Generate lrw rx, [LABEL]. This happens when the compiler + generates constant pool references and uses lrw to get the + constant into memory. */ + else if (op1.label) + return "lrw\t%0, %1"; + /* Generate lrs.w rx, [symbol@GOT/PLT]. */ + else if (flag_pic == 1 && op1.disp && GET_CODE (op1.disp) == UNSPEC) + return "lrs.w\t%0, %1"; + else + switch (GET_MODE (src)) + { + case E_HImode: + return "ld.h\t%0, %1"; + case E_QImode: + return "ld.b\t%0, %1"; + case E_SFmode: + case E_SImode: + if (CSKY_VREG_P (REGNO (dst))) + return "flds\t%0, %1"; + else + return "ld.w\t%0, %1"; + default: + gcc_unreachable (); + } + } + /* The situation mov integer to reg. */ + else if (GET_CODE (src) == CONST_INT || + (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode)) + { + HOST_WIDE_INT x, y; + const REAL_VALUE_TYPE *d; + long l; + + if (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode) + { + d = CONST_DOUBLE_REAL_VALUE (src); + REAL_VALUE_TO_TARGET_SINGLE (*d, l); + operands[1] = GEN_INT (l); + src = operands[1]; + } + + if (try_csky_constant_tricks (INTVAL (src), &x, &y)) + return csky_output_inline_const (SImode, operands); + /* Return '#' to split it. */ + else if (CSKY_CONST_OK_FOR_T (INTVAL (src))) + return "#"; + else + return "lrw\t%0, %x1\t"; + } + else if (TARGET_ANCHOR && GET_CODE (src) == SYMBOL_REF) + { + if (SYMBOL_REF_FUNCTION_P (src)) + return "lrw\t%0, %1@BTEXT"; + else + return "lrw\t%0, %1@BDATA"; + } + else if (GET_CODE (src) == UNSPEC + && XINT (src, 1) == UNSPEC_PIC_SYMBOL_GRS) + return "grs\t%0, %1"; + else + return "lrw\t%0, %1"; + } + else if (GET_CODE (dst) == MEM) + { + decompose_csky_address (XEXP (dst, 0), &op0); + + if (op0.index) + switch (GET_MODE (src)) + { + case E_HImode: + return "str.h\t%1, %0"; + case E_QImode: + return "str.b\t%1, %0"; + case E_SFmode: + case E_SImode: + if (CSKY_VREG_P (REGNO (src))) + return "fstrs\t%1, %0"; + else + return "str.w\t%1, %0"; + default: + gcc_unreachable (); + } + else + switch (GET_MODE (dst)) + { + case E_HImode: + return "st.h\t%1, %0"; + case E_QImode: + return "st.b\t%1, %0"; + case E_SImode: + case E_SFmode: + if (CSKY_VREG_P (REGNO (src))) + return "fsts\t%1, %0"; + else + return "st.w\t%1, %0"; + default: + gcc_unreachable (); + } + } + + gcc_unreachable (); +} + + +/* Output a move of a word or less value. Specific for ck801. */ + +const char * +csky_output_ck801_move (rtx insn ATTRIBUTE_UNUSED, rtx operands[], + machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx dst = operands[0]; + rtx src = operands[1]; + struct csky_address op1; + + if (REG_P (dst)) + { + if (REG_P (src)) + return "mov\t%0, %1"; + else if (GET_CODE (src) == MEM) + { + decompose_csky_address (XEXP (src, 0), &op1); + + /* Generate lrw rx, [LABEL]. This happens when the compiler + generates constant pool references and uses lrw to get the + constant in memory. */ + if (op1.label) + return "lrw\t%0, %1"; + else + switch (GET_MODE (src)) + { + case E_HImode: + return "ld.h\t%0, %1"; + case E_QImode: + return "ld.b\t%0, %1"; + case E_SFmode: + case E_SImode: + return "ld.w\t%0, %1"; + default: + gcc_unreachable (); + } + } + else if (GET_CODE (src) == CONST_INT) + { + if (REGNO (dst) > 7) + return "lrw\t%0, %x1\t"; + else if (CSKY_CONST_OK_FOR_N (INTVAL (src) + 1)) + return "movi\t%0, %1"; + /* Return '#' to split it. */ + else if (CSKY_CONST_OK_FOR_T (INTVAL (src))) + return "#"; + else if (csky_shifted_imm8_constant (INTVAL (src), NULL, NULL)) + return "#"; + else + return "lrw\t%0, %x1\t"; + } + else if (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode) + { + const REAL_VALUE_TYPE *d; + long l; + + d = CONST_DOUBLE_REAL_VALUE (src); + REAL_VALUE_TO_TARGET_SINGLE (*d, l); + operands[1] = GEN_INT (l); + src = operands[1]; + + if (CSKY_CONST_OK_FOR_N (INTVAL (src) + 1)) + return "movi\t%0, %1"; + else + return "lrw\t%0, %x1\t"; + } + else if (TARGET_ANCHOR && GET_CODE (src) == SYMBOL_REF) + { + if (SYMBOL_REF_FUNCTION_P (src)) + return "lrw\t%0, %1@BTEXT"; + else + return "lrw\t%0, %1@BDATA"; + } + else + return "lrw\t%0, %1"; + } + else if (GET_CODE (dst) == MEM) + switch (GET_MODE (dst)) + { + case E_HImode: + return "st.h\t%1, %0"; + case E_QImode: + return "st.b\t%1, %0"; + case E_SImode: + case E_SFmode: + return "st.w\t%1, %0"; + default: + gcc_unreachable (); + } + + gcc_unreachable (); +} + + +/* Return a sequence of instructions to perform DI or DF move. + Since the CSKY cannot move a DI or DF in one instruction, we have + to take care when we see overlapping source and dest registers. */ + +const char * +csky_output_movedouble (rtx operands[], + machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx dst = operands[0]; + rtx src = operands[1]; + + if (REG_P (dst)) + { + if (REG_P (src)) + { + int dstreg = REGNO (dst); + int srcreg = REGNO (src); + + if (CSKY_HILO_REG_P (srcreg)) + { + if (TARGET_BIG_ENDIAN) + return "mfhi\t%0\n\tmflo\t%R0"; + else + return "mfhi\t%R0\n\tmflo\t%0"; + } + else if (CSKY_HILO_REG_P (dstreg)) + { + if (TARGET_BIG_ENDIAN) + return "mthi\t%1\n\tmtlo\t%R1"; + else + return "mthi\t%R1\n\tmtlo\t%1"; + } + else if (CSKY_VREG_P (srcreg) && CSKY_VREG_P (dstreg)) + return "fmovd\t%0, %1"; + else if (CSKY_VREG_P (srcreg)) + { + /* Since the vector registers in fpuv2_soft processors + like ck803f are 32 bits wide, just one insn is needed + to complete the move operation. */ + if (TARGET_SOFT_FPU) + return "fmfvrl\t%0, %1"; + else if (TARGET_BIG_ENDIAN) + return "fmfvrh\t%0, %1\n\tfmfvrl\t%R0, %1"; + else + return "fmfvrh\t%R0, %1\n\tfmfvrl\t%0, %1"; + } + else if (CSKY_VREG_P (dstreg)) + { + if (TARGET_SOFT_FPU) + return "fmtvrl\t%0, %1"; + else if (TARGET_BIG_ENDIAN) + return "fmtvrh\t%0, %1\n\tfmtvrl\t%0, %R1"; + else + return "fmtvrh\t%0, %R1\n\tfmtvrl\t%0, %1"; + } + + /* Ensure the second source not overwritten. */ + if (srcreg + 1 == dstreg) + return "mov\t%R0, %R1\n\tmov\t%0, %1"; + else + return "mov\t%0, %1\n\tmov\t%R0, %R1"; + } + else if (GET_CODE (src) == MEM) + { + rtx memexp = XEXP (src, 0); + int dstreg = REGNO (dst); + int basereg = -1; + struct csky_address op0; + + decompose_csky_address (XEXP (src, 0), &op0); + + if (GET_CODE (memexp) == LABEL_REF + || (GET_CODE (memexp) == CONST + && GET_CODE (XEXP (memexp, 0)) == PLUS + && GET_CODE (XEXP (XEXP (memexp, 0), 0)) == LABEL_REF)) + return "lrw\t%0, [%1]\n\tlrw\t%R0, [%R1]"; + else if (GET_CODE (memexp) == REG) + basereg = REGNO (memexp); + else if (GET_CODE (memexp) == PLUS) + { + if (GET_CODE (XEXP (memexp, 0)) == REG) + basereg = REGNO (XEXP (memexp, 0)); + else if (GET_CODE (XEXP (memexp, 1)) == REG) + basereg = REGNO (XEXP (memexp, 1)); + else + gcc_unreachable (); + } + else + gcc_unreachable (); + + + /* When FPUV2. */ + if (CSKY_VREG_P (dstreg)) + { + if (op0.index) + return "fldrd\t%0, %1"; + else + return "fldd\t%0, %1"; + } + /* FIXME length attribute is wrong here. */ + if (dstreg == basereg) + /* Just load them in reverse order. */ + return "ld.w\t%R0, %R1\n\tld.w\t%0, %1"; + else + return "ld.w\t%0, %1\n\tld.w\t%R0, %R1"; + } + else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE) + { + split_double (src, operands + 2, operands + 3); + + if (CSKY_CONST_OK_FOR_I (INTVAL (operands[2]))) + output_asm_insn ("movi\t%0, %2", operands); + else if (CSKY_CONST_OK_FOR_Uc (INTVAL (operands[2]))) + output_asm_insn ("bmaski\t%0, %N2", operands); + else if (CSKY_CONST_OK_FOR_Ub (INTVAL (operands[2]))) + output_asm_insn ("bgeni\t%0, %P2", operands); + else + output_asm_insn ("lrw\t%0, %2", operands); + + if (CSKY_CONST_OK_FOR_I (INTVAL (operands[3]))) + output_asm_insn ("movi\t%R0, %3", operands); + else if (CSKY_CONST_OK_FOR_Uc (INTVAL (operands[3]))) + output_asm_insn ("bmaski\t%R0, %N3", operands); + + else if (CSKY_CONST_OK_FOR_Ub (INTVAL (operands[3]))) + output_asm_insn ("bgeni\t%R0, %P3", operands); + else + output_asm_insn ("lrw\t%R0, %3", operands); + + return ""; + } + else + gcc_unreachable (); + } + else if (GET_CODE (dst) == MEM && GET_CODE (src) == REG) + { + rtx memexp = XEXP (dst, 0); + int srcreg = REGNO (src); + int basereg = -1; + struct csky_address op0; + + decompose_csky_address (XEXP (dst, 0), &op0); + + if (GET_CODE (memexp) == REG) + basereg = REGNO (memexp); + else if (GET_CODE (memexp) == PLUS) + { + if (GET_CODE (XEXP (memexp, 0)) == REG) + basereg = REGNO (XEXP (memexp, 0)); + else if (GET_CODE (XEXP (memexp, 1)) == REG) + basereg = REGNO (XEXP (memexp, 1)); + else + gcc_unreachable (); + } + else + gcc_unreachable (); + + /* When FPUV2. */ + if (CSKY_VREG_P (srcreg)) + { + if (op0.index) + return "fstrd\t%1, %0"; + else + return "fstd\t%1, %0"; + } + /* FIXME length attribute is wrong here. */ + if (srcreg == basereg) + /* Just load them in reverse order. */ + return "st.w\t%R1, %R0\n\tst.w\t%1, %0"; + else + return "st.w\t%1, %0\n\tst.w\t%R1, %R0"; + } + else + gcc_unreachable (); +} + + +const char * +csky_output_ck801_movedouble (rtx operands[], + machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx dst = operands[0]; + rtx src = operands[1]; + + if (REG_P (dst)) + { + if (REG_P (src)) + { + int dstreg = REGNO (dst); + int srcreg = REGNO (src); + + /* Ensure the second source not overwritten. */ + if (srcreg + 1 == dstreg) + return "mov\t%R0, %R1\n\tmov\t%0, %1"; + else + return "mov\t%0, %1\n\tmov\t%R0, %R1"; + } + else if (GET_CODE (src) == MEM) + { + rtx memexp = XEXP (src, 0); + int dstreg = REGNO (dst); + int basereg = -1; + struct csky_address op0; + + decompose_csky_address (XEXP (src, 0), &op0); + + if (GET_CODE (memexp) == LABEL_REF + || (GET_CODE (memexp) == CONST + && GET_CODE (XEXP (memexp, 0)) == PLUS + && GET_CODE (XEXP (XEXP (memexp, 0), 0)) == LABEL_REF)) + return "lrw\t%0, [%1]\n\tlrw\t%R0, [%R1]"; + else if (GET_CODE (memexp) == REG) + basereg = REGNO (memexp); + else if (GET_CODE (memexp) == PLUS) + { + if (GET_CODE (XEXP (memexp, 0)) == REG) + basereg = REGNO (XEXP (memexp, 0)); + else if (GET_CODE (XEXP (memexp, 1)) == REG) + basereg = REGNO (XEXP (memexp, 1)); + else + gcc_unreachable (); + } + else + gcc_unreachable (); + + /* FIXME length attribute is wrong here. */ + if (dstreg == basereg) + /* Just load them in reverse order. */ + return "ld.w\t%R0, %R1\n\tld.w\t%0, %1"; + else + return "ld.w\t%0, %1\n\tld.w\t%R0, %R1"; + } + else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE) + { + split_double (src, operands + 2, operands + 3); + + if (REGNO (dst) <= 7 + && CSKY_CONST_OK_FOR_N (INTVAL (operands[2]) + 1)) + output_asm_insn ("movi\t%0, %2", operands); + else + output_asm_insn ("lrw\t%0, %2", operands); + + + if (REGNO (dst) <= 6 + && CSKY_CONST_OK_FOR_N (INTVAL (operands[3]) + 1)) + output_asm_insn ("movi\t%R0, %3", operands); + else + output_asm_insn ("lrw\t%R0, %3", operands); + + return ""; + + + } + else + gcc_unreachable (); + } + else if (GET_CODE (dst) == MEM && GET_CODE (src) == REG) + { + rtx memexp = XEXP (dst, 0); + int srcreg = REGNO (src); + int basereg = -1; + struct csky_address op0; + + decompose_csky_address (XEXP (dst, 0), &op0); + + if (GET_CODE (memexp) == REG) + basereg = REGNO (memexp); + else if (GET_CODE (memexp) == PLUS) + { + if (GET_CODE (XEXP (memexp, 0)) == REG) + basereg = REGNO (XEXP (memexp, 0)); + else if (GET_CODE (XEXP (memexp, 1)) == REG) + basereg = REGNO (XEXP (memexp, 1)); + else + gcc_unreachable (); + } + else + gcc_unreachable (); + + /* FIXME length attribute is wrong here. */ + if (srcreg == basereg) + /* Just load them in reverse order. */ + return "st.w\t%R1, %R0\n\tst.w\t%1, %0"; + else + return "st.w\t%1, %0\n\tst.w\t%R1, %R0"; + } + else + gcc_unreachable (); +} + +/* Split operands for an AND expression when OPERANDS[2] is a constant. + Note operands[0] is marked earlyclobber in this case and can be + overwritten. Return true if "DONE", false otherwise. */ +bool +csky_split_and (rtx *operands) +{ + HOST_WIDE_INT mask = INTVAL (operands[2]); + rtx not_value = GEN_INT (~mask); + int i; + + /* All zeros or all ones can be handled by a move instruction. */ + if (mask == 0) + { + emit_move_insn (operands[0], const0_rtx); + return true; + } + if (mask == -1) + { + emit_move_insn (operands[0], operands[1]); + return true; + } + + /* Check for constants that can be handled directly by the 32-bit andi + instruction. */ + if (CSKY_ISA_FEATURE (E2) && csky_arith_O_operand (operands[2], SImode)) + return false; + + /* Try to transform to andni instruction. */ + if (CSKY_ISA_FEATURE (E2) && csky_arith_O_operand (not_value, SImode)) + { + emit_insn (gen_cskyv2_andnsi3 (operands[0], not_value, operands[1])); + return true; + } + + /* If there are only one or two 0 bits in the constant, we can + replace the operation with bclri instructions on those bits. + Note CK801 has only the 16-bit bclri that operates on a single + register, so we must count a move if we are post-reload. */ + if (popcount_hwi (~mask & 0xffffffff) + <= (reload_completed && !CSKY_ISA_FEATURE (E2) ? 1 : 2)) + { + rtx input = operands[1]; + + if (!CSKY_ISA_FEATURE (E2)) + { + emit_move_insn (operands[0], input); + input = operands[0]; + } + + for (i = 0; i < 32; i++) + if ((mask & (1 << i)) == 0x0) + { + emit_insn (gen_bclri (operands[0], input, GEN_INT (i))); + input = operands[0]; + } + return true; + } + + /* If the constant mask is outside the [0, 4095] range for + constraint O, or if constraint O is not allowed (ck801), + maybe the constant is a contiguous bit range that we can + handle by bit extract (low bits) or shifts (high bits). */ + for (i = (CSKY_ISA_FEATURE (E2) ? 13 : 1); i < 32; i++) + { + if ((((HOST_WIDE_INT) 1) << i) - 1 == mask) + { + if (CSKY_ISA_FEATURE (2E3)) + emit_insn (gen_cskyv2_extzv (operands[0], operands[1], + GEN_INT (i), const0_rtx)); + else + { + rtx shift = GEN_INT (32 - i); + rtx reg = (reload_completed + ? operands[0] : gen_reg_rtx (SImode)); + + emit_insn (gen_ashlsi3 (reg, operands[1], shift)); + emit_insn (gen_lshrsi3 (operands[0], reg, shift)); + } + return true; + } + else if ((((HOST_WIDE_INT) 1) << i) - 1 == ~mask) + { + rtx shift = GEN_INT (i); + rtx reg = (reload_completed + ? operands[0] : gen_reg_rtx (SImode)); + + emit_insn (gen_lshrsi3 (reg, operands[1], shift)); + emit_insn (gen_ashlsi3 (operands[0], reg, shift)); + return true; + } + } + + /* If the constant is a negative number, it seems better to use + andn and copy the NOT_VALUE to a register instead of the + original value, since the NOT_VALUE is always smaller and thus + more likely to be representable as a small constant. + This transformation can only be done before reload because + it requires a temporary. Hopefully register allocation can get + rid of the extra move required for CK801. */ + if (!reload_completed && INTVAL (operands[2]) < 0) + { + rtx reg = copy_to_mode_reg (SImode, not_value); + + if (CSKY_ISA_FEATURE (E2)) + emit_insn (gen_cskyv2_andnsi3 (operands[0], reg, operands[1])); + else + { + emit_move_insn (operands[0], operands[1]); + emit_insn (gen_ck801_andnsi3 (operands[0], reg, operands[0])); + } + return true; + } + + /* If the above ways are all not working, move the constant + to a register. We can clobber operands[0] as it is + marked earlyclobber in the insn constraints, but then we have to + swap operands 1 and 2 to match the constraints on the 2-operand + 16-bit and instruction. */ + if (reload_completed) + { + emit_move_insn (operands[0], operands[2]); + operands[2] = operands[1]; + operands[1] = operands[0]; + } + else + operands[2] = copy_to_mode_reg (SImode, operands[2]); + return false; +} + +/* Split operands for an IOR expression when OPERANDS[2] is a constant. + Note operands[0] is marked earlyclobber in this case and can be + overwritten. Return true if "DONE", false otherwise. */ +bool +csky_split_ior (rtx *operands) +{ + HOST_WIDE_INT mask = INTVAL (operands[2]); + int i; + + /* All zeros or all ones can be handled by a move instruction. */ + if (mask == 0) + { + emit_move_insn (operands[0], operands[1]); + return true; + } + if (mask == -1) + { + emit_move_insn (operands[0], gen_int_mode (-1, SImode)); + return true; + } + + /* Check for constants that can be handled directly by the 32-bit ori + instruction. */ + if (CSKY_ISA_FEATURE (E2) && csky_literal_I_operand (operands[2], SImode)) + return false; + + /* If there are only one or two 1 bits in the value, we can replace + the operation with bseti instructions to set those bits. + Note CK801 has only the 16-bit bclri that operates on a single + register, so we must count a move if we are post-reload. */ + if (popcount_hwi (mask & 0xffffffff) + <= (reload_completed && !CSKY_ISA_FEATURE (E2) ? 1 : 2)) + { + rtx input = operands[1]; + + if (!CSKY_ISA_FEATURE (E2)) + { + emit_move_insn (operands[0], input); + input = operands[0]; + } + + for (i = 0; i < 32; i++) + if (mask & (1 << i)) + { + emit_insn (gen_bseti (operands[0], input, GEN_INT (i))); + input = operands[0]; + } + return true; + } + + /* If the above ways are all not working, move the constant + to a register. We can clobber operands[0] as it is + marked earlyclobber in the insn constraints, but then we have to + swap operands 1 and 2 to match the constraints on the 2-operand + 16-bit ior instruction. */ + if (reload_completed) + { + emit_move_insn (operands[0], operands[2]); + operands[2] = operands[1]; + operands[1] = operands[0]; + } + else + operands[2] = copy_to_mode_reg (SImode, operands[2]); + return false; +} + + +/* Split operands for an XOR expression when OPERANDS[2] is a constant. + Note operands[0] is marked earlyclobber in this case and can be + overwritten. Return true if "DONE", false otherwise. */ +bool +csky_split_xor (rtx *operands) +{ + HOST_WIDE_INT mask = INTVAL (operands[2]); + + /* All zeros can be turned into move instruction. */ + if (mask == 0) + { + emit_move_insn (operands[0], operands[1]); + return true; + } + + /* All ones can be turned into a bitwise not. */ + if (mask == -1) + { + if (CSKY_ISA_FEATURE (E2)) + emit_insn (gen_cskyv2_one_cmplsi2 (operands[0], operands[1])); + else + { + emit_move_insn (operands[0], operands[1]); + emit_insn (gen_ck801_one_cmplsi2 (operands[0], operands[0])); + } + return true; + } + + /* Check for constants that can be handled directly by the 32-bit xori + instruction. */ + if (CSKY_ISA_FEATURE (E2) && csky_arith_O_operand (operands[2], SImode)) + return false; + + /* If the above ways are all not working, move the constant + to a register. We can clobber operands[0] as it is + marked earlyclobber in the insn constraints, but then we have to + swap operands 1 and 2 to match the constraints on the 2-operand + 16-bit ior instruction. */ + if (reload_completed) + { + emit_move_insn (operands[0], operands[2]); + operands[2] = operands[1]; + operands[1] = operands[0]; + } + else + operands[2] = copy_to_mode_reg (SImode, operands[2]); + return false; +} + + +/* Return true if X is an address form involving a symbol or label ref. */ +bool +csky_symbolic_address_p (rtx x) +{ + switch (GET_CODE (x)) + { + case SYMBOL_REF: + case LABEL_REF: + return 1; + case CONST: + x = XEXP (x, 0); + return ((GET_CODE (XEXP (x, 0)) == SYMBOL_REF + || GET_CODE (XEXP (x, 0)) == LABEL_REF) + && GET_CODE (XEXP (x, 1)) == CONST_INT); + default: + return 0; + } +} + + +/* Emit a comparison instruction. + Return true if an inverted comparison is generated. */ + +bool +csky_emit_compare (enum rtx_code code, rtx op0, rtx op1) +{ + bool invert; + rtx cc_reg = gen_rtx_REG (CCmode, CSKY_CC_REGNUM); + + if (GET_CODE (op1) == CONST_INT) + { + HOST_WIDE_INT val = INTVAL (op1); + + switch (code) + { + case GTU: + /* Unsigned (GTU 0) is the same as (NE 0); everything else is + converted below to LEU (reversed cmphs). */ + if (val == 0) + code = NE; + /* Check whether (GTU A imm) can become (GEU A imm + 1). */ + else if (TARGET_MINI_REGISTERS + ? CSKY_CONST_OK_FOR_J (val + 1) + : CSKY_CONST_OK_FOR_Uk (val + 1)) + { + op1 = GEN_INT (val + 1); + code = GEU; + } + break; + /* Check whether (LE A imm) can become (LT A imm + 1), + or (GT A imm) can become (GE A imm + 1). */ + case GT: + case LE: + if (TARGET_MINI_REGISTERS + ? CSKY_CONST_OK_FOR_J (val + 1) + : CSKY_CONST_OK_FOR_Uk (val + 1)) + { + op1 = GEN_INT (val + 1); + code = code == LE ? LT : GE; + } + break; + + default: + break; + } + } + + if (CONSTANT_P (op1) && GET_CODE (op1) != CONST_INT) + op1 = force_reg (GET_MODE (op1), op1); + + /* cmpnei: 0-31 (K immediate) + ti: 1-32 (J immediate, 0 using btsti x,31). */ + invert = false; + switch (code) + { + /* Use inverted condition, cmpne. */ + case EQ: + code = NE; + invert = true; + /* Fall through. */ + /* Use normal condition, cmpne. */ + case NE: + if (GET_CODE (op1) == CONST_INT + && (TARGET_MINI_REGISTERS + ? !csky_literal_K_operand (op1, SImode) + : !csky_literal_I_operand (op1, SImode))) + op1 = force_reg (SImode, op1); + break; + + /* Use inverted condition, reversed cmplt. */ + case LE: + code = GT; + invert = true; + /* Fall through. */ + /* Use normal condition, reversed cmplt. */ + case GT: + if (GET_CODE (op1) == CONST_INT) + op1 = force_reg (SImode, op1); + break; + + /* Use inverted condition, cmplt. */ + case GE: + code = LT; + invert = true; + /* Fall through. */ + /* Use normal condition, cmplt. */ + case LT: + /* covered by btsti x,31. */ + if (GET_CODE (op1) == CONST_INT && INTVAL (op1) != 0 + && (TARGET_MINI_REGISTERS + ? !csky_literal_J_operand (op1, SImode) + : !csky_literal_Uk_operand (op1, SImode))) + op1 = force_reg (SImode, op1); + break; + + /* Use inverted condition, cmple. */ + case GTU: + /* We coped with unsigned > 0 above. */ + gcc_assert (GET_CODE (op1) != CONST_INT || INTVAL (op1) != 0); + code = LEU; + invert = true; + /* Fall through. */ + /* Use normal condition, reversed cmphs. */ + case LEU: + if (GET_CODE (op1) == CONST_INT && INTVAL (op1) != 0) + op1 = force_reg (SImode, op1); + break; + + /* Use inverted condition, cmphs. */ + case LTU: + code = GEU; + invert = true; + /* Fall through. */ + /* Use normal condition, cmphs. */ + case GEU: + if (GET_CODE (op1) == CONST_INT && INTVAL (op1) != 0 + && (TARGET_MINI_REGISTERS + ? !csky_literal_J_operand (op1, SImode) + : !csky_literal_Uk_operand (op1, SImode))) + op1 = force_reg (SImode, op1); + break; + + default: + break; + } + + emit_insn (gen_rtx_SET (cc_reg, + gen_rtx_fmt_ee (code, CCmode, op0, op1))); + return invert; +} + +/* Return true if push/pop can be used to save/restore all the registers + indicated by MASK. We currently don't attempt to handle situations where + some of the registers could be handled by push/pop and others saved and + restored individually. */ + +static bool +csky_can_use_pushpop (unsigned int mask) +{ + int i; + int end_reg; + + if (!TARGET_PUSHPOP) + return false; + + if (mask == 0) + return false; + + /* Regs 0-3, 12-14, 18-27, 29-31 cannot be in the mask. */ + if (mask & 0xeffc700f) + return false; + + /* Regs in the range r4-r11 must be contiguous. */ + for (end_reg = 0, i = 11; i >= 4; i--) + { + if (!end_reg && (mask & (1 << i))) + end_reg = i; + if (end_reg && !(mask & (1 << i))) + return false; + } + + /* Likewise for regs in the range r16-r17. */ + for (end_reg = 0, i = 17; i >= 16; i--) + { + if (!end_reg && (mask & (1 << i))) + end_reg = i; + if (end_reg && !(mask & (1 << i))) + return false; + } + + return true; +} + + +/* Return true if store/load multiple instructions can be used to + save/restore at least some of the registers indicated by MASK. + Unlike the push/pop case, this does handle partial ranges. + Set *BR and *ER to the beginning and end (respectively) of the + register range that can be handled. */ + +static bool +csky_can_use_ldstm (int mask, int *br, int *er) +{ + int regno; + int begin_reg = 0, end_reg = 0; + int count = 0; + + if (!TARGET_MULTIPLE_STLD) + return false; + + /* We'll only handle registers in the range 4-11, the contiguous range + of caller-saved registers. Higher-numbered registers are handled + individually in addition to this, but we'll give up on doing ldstm + entirely if we need to save/restore the low-numbered EH registers. */ + if (mask & 0xf) + return false; + + for (regno = 4; regno <= 11; regno++) + { + if (mask & 1 << regno) + { + if (!begin_reg) + begin_reg = regno; + end_reg = regno; + count++; + } + else if (begin_reg) + break; + } + + if (count >= CSKY_MIN_MULTIPLE_STLD && count <= CSKY_MAX_MULTIPLE_STLD) + { + if (br) + *br = begin_reg; + if (er) + *er = end_reg; + return true; + } + return false; +} + + +const char * +csky_output_return_instruction (void) +{ + unsigned long func_type = get_csky_current_func_type (); + + if (CSKY_FUNCTION_IS_NAKED (func_type)) + return ""; + if (CSKY_FUNCTION_IS_INTERRUPT (func_type)) + return "ipop\n\tnir\n"; + else + return "rts\n"; +} + + +/* Adjust the stack pointer by OFFSET bytes. OFFSET is negative if this + is in the prologue, positive if in the epilogue. This may require + multiple instructions and/or use of CSKY_STACKADJUST_REGNUM as + a scratch register. Emit CFA notes as appropriate. */ +static void +expand_csky_stack_adjust (int offset) +{ + rtx set; + rtx_insn *insn; + int size = (offset > 0 ? offset : -offset); + + if (offset == 0) + return; + + /* If OFFSET is too large for addi/subi, load it into + CSKY_STACKADJUST_REGNUM and use a register add/sub instead. + This case is not mentioned in the ABI documentation, but it is + supported by GDB prologue analysis provided that the instruction(s) + to initialize CSKY_STACKADJUST_REGNUM appear directly before + the sub. Depending on the value of OFFSET, this might be a + lrw instruction or the "tricks" used by csky_output_inline_const to + encode special-case integer constants. */ + if (size > CSKY_MAX_SP_ADJUST * 2) + { + rtx tmp, dwarf; + + /* We should have reserved the scratch register already in + csky_layout_stack_frame. */ + gcc_assert (cfun->machine->reg_size != 0 + && (cfun->machine->reg_mask + & (1 << CSKY_STACKADJUST_REGNUM))); + + /* Prevent the optimizer from reordering these instructions to + keep GDB happy. */ + if (!flag_sched_prolog) + emit_insn (gen_blockage ()); + + tmp = gen_rtx_REG (SImode, CSKY_STACKADJUST_REGNUM); + emit_move_insn (tmp, GEN_INT (size)); + + if (offset > 0) + set = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp); + else + set = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp); + insn = emit_insn (set); + RTX_FRAME_RELATED_P (insn) = 1; + dwarf = gen_rtx_SET (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, offset)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); + + /* More make GDB happy. */ + if (!flag_sched_prolog) + emit_insn (gen_blockage ()); + } + + /* Use one or two addi or subi insns to adjust stack. */ + else + while (size) + { + int delta = (size > CSKY_MAX_SP_ADJUST + ? CSKY_MAX_SP_ADJUST : size); + + if (offset > 0) + set = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (delta)); + else + set = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (delta)); + insn = emit_insn (set); + RTX_FRAME_RELATED_P (insn) = 1; + size -= delta; + } +} + + +/* Generate and emit an insn that we will recognize as a push_multi. + Unfortunately, since this insn does not reflect very well the actual + semantics of the operation, we need to annotate the insn for the benefit + of DWARF2 frame unwind information. DWARF_REGS_MASK is a subset of + MASK for registers that should be annotated for DWARF2 frame unwind + information. */ + +static rtx +emit_csky_regs_push (unsigned long mask) +{ + int num_regs = 0; + int i, j; + rtx par; + rtx dwarf; + rtx tmp; + int dwarf_par_index; + + for (i = 0; i < CSKY_NGPR_REGS; i++) + { + if (mask & (1 << i)) + num_regs++; + } + + /* The reg range for push is:r4-r11,r15-r17,r28. */ + gcc_assert (num_regs && num_regs <= 12); + + /* For the body of the insn we are going to generate an UNSPEC in + parallel with several USEs. This allows the insn to be recognized + by the push_multi pattern in the csky.md file. + + The body of the insn looks something like this: + + (parallel [ + (set (mem:BLK (pre_modify:SI (reg:SI sp) + (const_int:SI ))) + (unspec:BLK [(reg:SI r4)] UNSPEC_PUSHPOP_MULT)) + (use (reg:SI XX)) + (use (reg:SI YY)) + ... + ]) + + For the frame note however, we try to be more explicit and actually + show each register being stored into the stack frame, plus a (single) + decrement of the stack pointer. We do it this way in order to be + friendly to the stack unwinding code, which only wants to see a single + stack decrement per instruction. The RTL we generate for the note looks + something like this: + + (sequence [ + (set (reg:SI sp) (plus:SI (reg:SI sp) (const_int -20))) + (set (mem:SI (reg:SI sp)) (reg:SI r4)) + (set (mem:SI (plus:SI (reg:SI sp) (const_int 4))) (reg:SI XX)) + (set (mem:SI (plus:SI (reg:SI sp) (const_int 8))) (reg:SI YY)) + ... + ]) + + FIXME:: In an ideal world the PRE_MODIFY would not exist and + instead we'd have a parallel expression detailing all + the stores to the various memory addresses so that debug + information is more up-to-date. Remember however while writing + this to take care of the constraints with the push instruction. + + Note also that this has to be taken care of for the VFP registers. + + For more see PR43399. */ + + par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs)); + dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_regs + 1)); + dwarf_par_index = 1; + + for (i = 0; i < CSKY_NGPR_REGS; i++) + if (mask & (1 << i)) + { + rtx reg = gen_rtx_REG (SImode, i); + rtx addr = plus_constant (Pmode, stack_pointer_rtx, -4 * num_regs); + tmp = gen_frame_mem (BLKmode, + gen_rtx_PRE_MODIFY (Pmode, + stack_pointer_rtx, addr)); + XVECEXP (par, 0, 0) + = gen_rtx_SET (tmp, + gen_rtx_UNSPEC (BLKmode, + gen_rtvec (1, reg), + UNSPEC_PUSHPOP_MULT)); + tmp = gen_rtx_SET (gen_frame_mem (SImode, stack_pointer_rtx), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, dwarf_par_index++) = tmp; + + break; + } + + for (j = 1, i++; j < num_regs; i++) + if (mask & (1 << i)) + { + rtx reg = gen_rtx_REG (SImode, i); + rtx addr = plus_constant (Pmode, stack_pointer_rtx, 4 * j); + tmp = gen_rtx_SET (gen_frame_mem (SImode, addr), reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg); + XVECEXP (dwarf, 0, dwarf_par_index++) = tmp; + j++; + } + + par = emit_insn (par); + + tmp = gen_rtx_SET (stack_pointer_rtx, + plus_constant (Pmode, stack_pointer_rtx, -4 * num_regs)); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, 0) = tmp; + + add_reg_note (par, REG_FRAME_RELATED_EXPR, dwarf); + RTX_FRAME_RELATED_P (par) = 1; + + return par; +} + + +/* Generate and emit an insn pattern that we will recognize as a pop_multi. + SAVED_REGS_MASK shows which registers need to be restored. + + Unfortunately, since this insn does not reflect very well the actual + semantics of the operation, we need to annotate the insn for the benefit + of DWARF2 frame unwind information. */ + +static void +emit_csky_regs_pop (unsigned long mask) +{ + int num_regs = 0; + int i, j; + rtx par; + + for (i = 0; i < CSKY_NGPR_REGS; i++) + if (mask & (1 << i)) + num_regs++; + + /* The reg range for push is:r4-r11,r15-r17,r28. */ + gcc_assert (num_regs && num_regs <= 12); + + /* The first element is (return), + the second element is + (set (reg:SI 'first reg number') + (unspec:SI [(mem)] UNSPEC_PUSHPOP_MULT), + the rest elements is (use (reg:SI 'rest reg number')), + so the length should be number of register to be poped + plus one. */ + par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num_regs + 1)); + + XVECEXP (par, 0, 0) = ret_rtx; + + for (i = 0; i < CSKY_NGPR_REGS; i++) + if (mask & (1 << i)) + { + rtx reg = gen_rtx_REG (SImode, i); + rtx addr = plus_constant (Pmode, stack_pointer_rtx, 4 * num_regs); + rtx tmp = gen_frame_mem (SImode, + gen_rtx_POST_MODIFY (Pmode, + stack_pointer_rtx, addr)); + XVECEXP (par, 0, 1) + = gen_rtx_SET (reg, + gen_rtx_UNSPEC (SImode, + gen_rtvec (1, tmp), + UNSPEC_PUSHPOP_MULT)); + break; + } + + for (j = 2, i++; j < (num_regs + 1); i++) + if (mask & (1 << i)) + { + rtx reg = gen_rtx_REG (SImode, i); + XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg); + j++; + } + + par = emit_jump_insn (par); +} + + +/* Generate the function prologue. */ + +void +csky_expand_prologue (void) +{ + rtx_insn *insn; + unsigned long func_type = get_csky_current_func_type (); + unsigned int reg_mask; + int reg_size; + + if (CSKY_FUNCTION_IS_NAKED (func_type)) + { + if (flag_stack_usage_info) + current_function_static_stack_size = 0; + return; + } + + csky_layout_stack_frame (); + reg_mask = cfun->machine->reg_mask; + reg_size = cfun->machine->reg_size; + + /* Adjust stack pointer past argument overflow area. */ + if (cfun->machine->arg_size != 0) + { + int offset = cfun->machine->arg_size; + expand_csky_stack_adjust (- offset); + + /* If we have a parameter passed partially in regs and partially + in memory, the registers will have been stored to memory already + in function.c. So we only need to copy varargs from registers + to stack. */ + if (cfun->machine->uses_anonymous_args) + { + int rn = CSKY_FIRST_PARM_REGNUM + CSKY_NPARM_REGS - 1; + for (offset -= 4; offset >= 0; offset -= 4, rn--) + { + rtx dst = gen_frame_mem (SImode, + plus_constant (Pmode, + stack_pointer_rtx, + offset)); + insn = emit_move_insn (dst, gen_rtx_REG (SImode, rn)); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + } + + /* Push caller-saved registers to stack. */ + if (csky_can_use_pushpop (reg_mask)) + emit_csky_regs_push (reg_mask); + else if (reg_size) + { + int sreg = -1, ereg = -1; + bool stm_p = csky_can_use_ldstm (reg_mask, &sreg, &ereg); + int stm_regs = stm_p ? ereg - sreg + 1 : 0; + int stm_size = stm_regs * 4; + + /* First adjust the SP to the low end of the register save area. */ + expand_csky_stack_adjust (- reg_size); + + /* Emit individual register saves. Even if we are going to emit an + stm, we may need to save individual registers above that too. */ + if (reg_size > stm_size) + { + int offset = reg_size - 4; + int regno = 31; + for ( ; regno > ereg; regno--) + if (reg_mask & (1 << regno)) + { + rtx dst = gen_rtx_MEM (SImode, + plus_constant (Pmode, + stack_pointer_rtx, + offset)); + rtx insn = emit_insn (gen_movsi (dst, + gen_rtx_REG (SImode, regno))); + RTX_FRAME_RELATED_P (insn) = 1; + if (offset == stm_size) + break; + offset -= 4; + } + } + + /* If possible, emit a stm to do a bulk store of sequential + registers to the stack. Note that it is an error in the ABI + documentation that it doesn't list stm as a valid prologue + instruction. */ + if (stm_p) + { + rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (stm_regs)); + int regno, slot; + for (regno = sreg, slot = 0; regno <= ereg; regno++, slot++) + { + rtx reg = gen_rtx_REG (SImode, regno); + rtx addr = plus_constant (Pmode, stack_pointer_rtx, slot * 4); + rtx set = gen_rtx_SET (gen_frame_mem (SImode, addr), reg); + RTX_FRAME_RELATED_P (set) = 1; + XVECEXP (par, 0, slot) = set; + } + insn = emit_insn (par); + RTX_FRAME_RELATED_P (insn) = 1; + } + } + + /* Initialize hard frame pointer, if necessary. It points at the base + of the register save area. */ + if (frame_pointer_needed) + { + insn = emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Reserve stack space for locals and outgoing args. */ + expand_csky_stack_adjust (- cfun->machine->reg_offset); + + /* Put the GOT address in reg_gb for PIC, using R13 as a scratch. + See section 4.7.1 in the ABI documentation, + "Function Prologue for PIC". */ + if (flag_pic && (reg_mask & (1 << PIC_OFFSET_TABLE_REGNUM))) + { + rtx l1 = gen_label_rtx (); + rtx grs_label = gen_rtx_LABEL_REF (SImode, l1); + rtx reg_gb = gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM); + rtx reg_temp = gen_rtx_REG (SImode, 13); + + rtx tmp0_unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, grs_label), + UNSPEC_PIC_SYMBOL_GOTPC_GRS); + rtx tmp1_unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, grs_label), + UNSPEC_PIC_SYMBOL_GOTPC); + + emit_insn (gen_prologue_get_pc (tmp0_unspec)); + emit_move_insn (reg_temp, tmp1_unspec); + emit_insn (gen_addsi3 (reg_gb, reg_gb, reg_temp)); + } + + if (flag_stack_usage_info) + current_function_static_stack_size = cfun->machine->frame_size; + + if (!flag_sched_prolog) + emit_insn (gen_blockage ()); +} + +void +csky_expand_epilogue (void) +{ + unsigned long func_type = get_csky_current_func_type (); + unsigned int reg_mask; + int reg_size; + int adjust; + rtx_insn *insn; + + if (!flag_sched_prolog) + emit_insn (gen_blockage ()); + + if (CSKY_FUNCTION_IS_NAKED (func_type)) + { + emit_jump_insn (gen_simple_return ()); + return; + } + + /* Get the frame information. */ + csky_layout_stack_frame (); + reg_mask = cfun->machine->reg_mask; + reg_size = cfun->machine->reg_size; + adjust = reg_size + cfun->machine->arg_size; + + /* Restore the SP to the base of the register save area. */ + if (frame_pointer_needed) + { + insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + else + expand_csky_stack_adjust (cfun->machine->reg_offset); + + /* Restore the callee-saved registers. */ + if (csky_can_use_pushpop (reg_mask) + && cfun->machine->arg_size == 0 + && !CSKY_FUNCTION_IS_INTERRUPT (func_type) + && !crtl->calls_eh_return) + { + /* Pop includes an implicit return, so we are done. */ + emit_csky_regs_pop (reg_mask); + return; + } + else if (reg_size) + { + int sreg = -1, ereg = -1; + bool ldm_p = csky_can_use_ldstm (reg_mask, &sreg, &ereg); + int ldm_regs = ldm_p ? ereg - sreg + 1 : 0; + int ldm_size = ldm_regs * 4; + + /* Emit individual register loads. Even if we are going to emit an + ldm, we may need to load individual registers above that too. */ + if (reg_size > ldm_size) + { + int offset = reg_size - 4; + int regno = 31; + for ( ; regno > ereg; regno--) + if (reg_mask & (1 << regno)) + { + rtx src = gen_frame_mem (SImode, + plus_constant (Pmode, + stack_pointer_rtx, + offset)); + rtx reg = gen_rtx_REG (SImode, regno); + insn = emit_move_insn (reg, src); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_CFA_RESTORE, reg); + if (offset == ldm_size) + break; + offset -= 4; + } + } + + /* If possible, emit a ldm to do a bulk load of sequential + registers from the stack. */ + if (ldm_p) + { + rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (ldm_regs)); + int regno, slot; + for (regno = sreg, slot = 0; regno <= ereg; regno++, slot++) + { + rtx reg = gen_rtx_REG (SImode, regno); + rtx addr = plus_constant (Pmode, stack_pointer_rtx, slot * 4); + rtx set = gen_rtx_SET (reg, gen_frame_mem (SImode, addr)); + XVECEXP (par, 0, slot) = set; + } + insn = emit_insn (par); + RTX_FRAME_RELATED_P (insn) = 1; + for (regno = sreg; regno <= ereg; regno++) + { + rtx reg = gen_rtx_REG (SImode, regno); + add_reg_note (insn, REG_CFA_RESTORE, reg); + } + } + } + + /* Emit the final stack pointer adjustment to deallocate the saved + registers and incoming argument area. */ + expand_csky_stack_adjust (adjust); + + /* Extra stack adjustment for exception handler return. */ + if (crtl->calls_eh_return) + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + + /* Now we can return. */ + emit_jump_insn (gen_simple_return ()); +} + + +static void +csky_output_function_prologue (FILE *f) +{ + unsigned long func_type = get_csky_current_func_type (); + + switch ((int) CSKY_FUNCTION_TYPE (func_type)) + { + default: + case CSKY_FT_NORMAL: + break; + case CSKY_FT_INTERRUPT: + { + asm_fprintf (f, "\t# Interrupt Service Routine.\n"); + asm_fprintf (f, "\tnie\n\tipush\n"); + break; + } + case CSKY_FT_FIQ: + asm_fprintf (f, "\t# Fast Interrupt Service Routine.\n"); + break; + case CSKY_FT_EXCEPTION: + asm_fprintf (f, "\t# CSKY Exception Handler.\n"); + break; + case CSKY_FT_NAKED: + asm_fprintf (f, "\t# Naked Function: prologue and epilogue \ + provided by programmer.\n"); + return; + } + + csky_layout_stack_frame (); + + /* Generate .stack_size function-name, size for callgraph; + the default stack size is 0. */ + if (TARGET_STACK_SIZE && cfun->machine->frame_size > 0) + { + gcc_assert (current_function_decl != NULL); + const char *func_name = + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)); + if (func_name[0] == '*') + asm_fprintf (f, "\t.stack_size %s, %d\n", + &func_name[1], cfun->machine->frame_size); + else + asm_fprintf (f, "\t.stack_size %s, %d\n", + func_name, cfun->machine->frame_size); + } +} + + +static void +csky_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED) +{ + +} + + +/* Helper for csky_eh_return splitter: store the call frame exception + handler address in lr. */ +void +csky_set_eh_return_address (rtx source, rtx scratch) +{ + HOST_WIDE_INT delta = 0; + rtx basereg, addr; + unsigned int reg_mask; + + csky_layout_stack_frame (); + reg_mask = cfun->machine->reg_mask; + + if (reg_mask & (1 << CSKY_LR_REGNUM)) + { + /* Find LR in the stack frame. */ + int i = 0; + + if (frame_pointer_needed) + { + basereg = frame_pointer_rtx; + delta = 0; + } + else + { + basereg = stack_pointer_rtx; + delta = cfun->machine->reg_offset; + } + + /* At this point, (basereg + delta) points at the low end of + the reg save area. Regs are saved sequentially from low + to high from this address. */ + for (i = 0; i < CSKY_LR_REGNUM; i++) + if (reg_mask & (1 << i)) + delta += 4; + + if ((CSKY_TARGET_ARCH (CK801) && delta >= CSKY_LD16_MAX_OFFSET (Pmode)) + || delta >= CSKY_LD32_MAX_OFFSET (Pmode)) + { + emit_insn (gen_movsi (scratch, GEN_INT (delta))); + emit_insn (gen_addsi3 (scratch, scratch, basereg)); + addr = scratch; + } + else + addr = plus_constant (Pmode, basereg, delta); + emit_move_insn (gen_frame_mem (Pmode, addr), source); + } + else + emit_move_insn (gen_rtx_REG (Pmode, CSKY_LR_REGNUM), source); +} + +/* Return TRUE if X references a SYMBOL_REF. */ + +bool +csky_symbol_mentioned_p (rtx x) +{ + const char *fmt; + int i; + + if (GET_CODE (x) == SYMBOL_REF) + return true; + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (csky_symbol_mentioned_p (XVECEXP (x, i, j))) + return true; + } + else if (fmt[i] == 'e' && csky_symbol_mentioned_p (XEXP (x, i))) + return true; + } + return false; +} + + +/* Return TRUE if X references a LABEL_REF. */ + +bool +csky_label_mentioned_p (rtx x) +{ + const char *fmt; + int i; + + if (GET_CODE (x) == LABEL_REF) + return true; + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (x, i) - 1; j >= 0; j--) + if (csky_label_mentioned_p (XVECEXP (x, i, j))) + return true; + } + else if (fmt[i] == 'e' && csky_label_mentioned_p (XEXP (x, i))) + return true; + } + + return false; +} + + +static bool +tls_unspec_mentioned_p (rtx x) +{ + switch (GET_CODE (x)) + { + case CONST: + return tls_unspec_mentioned_p (XEXP (x, 0)); + + case UNSPEC: + if (XINT (x, 1) == UNSPEC_TLS) + return true; + + /* Fall through. */ + default: + return false; + } +} + + +/* Implement LEGITIMATE_PIC_OPERAND_P. */ +bool +csky_legitimate_pic_operand_p (rtx x) +{ + if (tls_unspec_mentioned_p (x)) + return true; + if (csky_symbol_mentioned_p (x) || csky_label_mentioned_p (x)) + return false; + return true; +} + +rtx +csky_legitimize_pic_address (rtx orig, rtx reg, bool gotrel_p) +{ + rtx pic_reg = gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM); + bool optimize_p = false; + + if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) + { + rtx pic_ref, address, rtx_tmp; + rtx insn; + rtx pic_reg = gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM); + int subregs = 0; + + if (reg == 0) + { + gcc_assert (can_create_pseudo_p ()); + reg = gen_reg_rtx (Pmode); + subregs = 1; + } + + if (subregs) + address = gen_reg_rtx (Pmode); + else + address = reg; + + if (GET_CODE (orig) == SYMBOL_REF && !SYMBOL_REF_LOCAL_P (orig)) + { + /* When gotrel_p generate sym@GOT, otherwise generate sym@PLT. */ + rtx_tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig), + (gotrel_p + ? UNSPEC_PIC_SYMBOL_GOT + : UNSPEC_PIC_SYMBOL_PLT)); + optimize_p = gotrel_p; + if (flag_pic != 1) + { + emit_move_insn (address, rtx_tmp); + rtx_tmp = gen_rtx_MULT (Pmode, address, GEN_INT (1)); + } + pic_ref = gen_const_mem (Pmode, + gen_rtx_PLUS (Pmode, pic_reg, rtx_tmp)); + } + else + { + /* bsr symbol */ + if (flag_pic == 1 && !gotrel_p) + { + pic_ref = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, orig), + UNSPEC_PIC_SYMBOL_BSR); + return pic_ref; + } + /* grs rx, symbol */ + else if (flag_pic == 1 && (GET_CODE (orig) == SYMBOL_REF) + && SYMBOL_REF_FUNCTION_P (orig)) + { + pic_ref = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, orig), + UNSPEC_PIC_SYMBOL_GRS); + return pic_ref; + } + /* lrw rx, symbol@GOTOFF; add rx, rx, gb */ + else + { + rtx_tmp = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, orig), + UNSPEC_PIC_SYMBOL_GOTOFF); + emit_move_insn (address, rtx_tmp); + pic_ref = gen_rtx_PLUS (Pmode, address, pic_reg); + optimize_p = true; + } + } + + insn = emit_move_insn (reg, pic_ref); + /* Put a REG_EQUAL note on this insn, + so that it can be optimized by loop. */ + if (optimize_p) + set_unique_reg_note (insn, REG_EQUAL, orig); + + return reg; + } + else if (GET_CODE (orig) == CONST) + { + rtx base, offset; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 1) == pic_reg) + return orig; + + if (reg == 0) + { + gcc_assert (can_create_pseudo_p ()); + reg = gen_reg_rtx (Pmode); + } + + gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS); + + base = csky_legitimize_pic_address (XEXP (XEXP (orig, 0), 0), + reg, gotrel_p); + offset = csky_legitimize_pic_address (XEXP (XEXP (orig, 0), 1), + base == reg ? 0 : reg, gotrel_p); + + if (GET_CODE (offset) == CONST_INT) + return plus_constant (Pmode, base, INTVAL (offset)); + + return gen_rtx_PLUS (Pmode, base, offset); + } + + return orig; +} + + +/* Functions to output assembly code for a function call. */ + +char * +csky_output_call (rtx *operands, int index) +{ + static char buffer[20]; + rtx addr = operands[index]; + + if (REG_P (addr)) + sprintf (buffer, "jsr\t%%%d", index); + else if (flag_pic && (GET_CODE (addr) == UNSPEC)) + sprintf (buffer, "bsr\t%%%d", index); + else + sprintf (buffer, "jbsr\t%%%d", index); + + return buffer; +} + + +/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE. + Output assembler code for a block containing the constant parts + of a trampoline, leaving space for the variable parts. + Note that STATIC_CHAIN_REGNUM is t1 (aka r12) on ck801 and + t1 (r13) otherwise. */ + +static void +csky_asm_trampoline_template (FILE *f) +{ + if (CSKY_ISA_FEATURE (2E3)) + { + fprintf (f, "\tlrw\t%s, [.Lstatic_chain]\n", + reg_names[STATIC_CHAIN_REGNUM]); + fprintf (f, "\tjmpi\t[.Lfunc_address]\n"); + /* 2 32-bit insns = 8 bytes. */ + } + else if (CSKY_TARGET_ARCH (CK801)) + { + /* It's hard to provide general support for trampolines on this + core. We need a register other than the one holding the + static chain (r13) to hold the function pointer for the + indirect jump to it. But ck801 has such a limited register set + there is no other call-clobbered scratch register available -- in + particular, this core does not have r12, which we use for the + ck802 case below. If we use a callee-saved register like r4, + saving the old value on the stack screws up the stack frame + if there are overflow arguments pushed on the stack + by the caller. In theory we could test for that and handle + limited cases with parameters that all fit in r0-r3 with no + stack overflow, but punt for now. */ + sorry ("Nested function trampolines not supported on CK801."); + } + else + { + fprintf (f, "\tlrw\t%s, [.Lfunc_address]\n", + reg_names[CSKY_T1_REGNUM]); + fprintf (f, "\tlrw\t%s, [.Lstatic_chain]\n", + reg_names[STATIC_CHAIN_REGNUM]); + fprintf (f, "\tjmp\t%s\n", + reg_names[CSKY_T1_REGNUM]); + /* To align constant pool on a word boundary. */ + fprintf (f, "\t.align 2\n"); + /* 2 32-bit lrw insns + 16-bit jump + 16-bit pad = 12 bytes. */ + } + + fprintf (f, ".Lstatic_chain:\n"); + fprintf (f, "\t.long 0\n"); + fprintf (f, ".Lfunc_address:\n"); + fprintf (f, "\t.long 0\n"); + /* 2 words of constant pool = 8 bytes. */ +} + +/* Worker function for TARGET_TRAMPOLINE_INIT. */ + +static void +csky_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) +{ + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + rtx mem, a_tramp; + int pool = TRAMPOLINE_SIZE - 8; + + emit_block_move (m_tramp, assemble_trampoline_template (), + GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); + + mem = adjust_address (m_tramp, SImode, pool); + emit_move_insn (mem, chain_value); + mem = adjust_address (m_tramp, SImode, pool + 4); + emit_move_insn (mem, fnaddr); + + a_tramp = XEXP (m_tramp, 0); + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__clear_cache"), + LCT_NORMAL, VOIDmode, a_tramp, Pmode, + plus_constant (Pmode, a_tramp, TRAMPOLINE_SIZE), Pmode); +} + + +/* Emit a comparison insn for float values. + Return true if the comparison is inverted. */ + +bool +csky_emit_compare_float (enum rtx_code code, rtx op0, rtx op1) +{ + rtx cc_reg = gen_rtx_REG (CCmode, CSKY_CC_REGNUM); + bool invert; + machine_mode mode = GET_MODE (op1); + + if (op1 != CONST0_RTX (mode)) + op1 = force_reg (mode, op1); + + invert = false; + switch (code) + { + case EQ: + code = NE; + invert = true; + break; + + case NE: + break; + case LE: + if (op1 == CONST0_RTX (mode)) + op1 = force_reg (mode, op1); + break; + case GT: + if (op1 == CONST0_RTX (mode)) + op1 = force_reg (mode, op1); + break; + case GE: + break; + case LT: + if (op1 == CONST0_RTX (mode)) + { + code = GE; + invert = true; + } + break; + case UNORDERED: + break; + case ORDERED: + code = UNORDERED; + invert = true; + break; + + default: + break; + } + + emit_insn (gen_rtx_SET (cc_reg, gen_rtx_fmt_ee (code, CCmode, op0, op1))); + + return invert; +} + +/* Support for the Q memory constraint. Returns true if OP is a MEM RTX + with an address consisting of base + index or base + displacement. */ +bool +csky_valid_fpuv2_mem_operand (rtx op) +{ + struct csky_address addr; + + if (GET_CODE (op) != MEM) + return false; + + if (!decompose_csky_address (XEXP (op, 0), &addr)) + return false; + + /* Verify base register. */ + if (!is_csky_address_register_rtx_p (addr.base, 0)) + return false; + + /* Verify index operand. */ + if (addr.index) + { + if (!is_csky_address_register_rtx_p (addr.index, 0)) + return false; + + if (addr.scale == 1 || addr.scale == 2 || addr.scale == 4 + || addr.scale == 8) + return true; + + return false; + } + /* Verify disp operand. */ + else if (addr.disp) + { + rtx disp = addr.disp; + + if (!CONST_INT_P (disp)) + return false; + + if (((unsigned) INTVAL (disp) % 4) == 0 + && (unsigned) INTVAL (disp) <= (unsigned) 1020) + return true; + + return false; + } + return true; +} + + +/* Returns the (interrupt) function type of the current + function, or CSKY_FT_UNKNOWN if the type cannot be determined. */ + +static unsigned long +csky_isr_value (tree argument) +{ + const isr_attribute_entry *ptr; + const char *arg; + + /* No argument - default to IRQ. */ + if (argument == NULL_TREE) + return CSKY_FT_ISR; + + /* Get the value of the argument. */ + if (TREE_VALUE (argument) == NULL_TREE + || TREE_CODE (TREE_VALUE (argument)) != STRING_CST) + return CSKY_FT_UNKNOWN; + + arg = TREE_STRING_POINTER (TREE_VALUE (argument)); + + /* Check it against the list of known arguments. */ + for (ptr = isr_attribute_map; ptr->arg != NULL; ptr++) + if (strcmp (arg, ptr->arg) == 0) + return ptr->return_value; + + /* An unrecognized interrupt type. */ + return CSKY_FT_UNKNOWN; +} + +/* Handle an attribute requiring a FUNCTION_DECL; + arguments as in struct attribute_spec.handler. */ + +static tree +csky_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute only applies to functions", + name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle an "interrupt" or "isr" attribute; + arguments as in struct attribute_spec.handler. */ + +static tree +csky_handle_isr_attribute (tree *node, tree name, tree args, int flags, + bool *no_add_attrs) +{ + + if (!TARGET_ISTACK) + { + warning (OPT_Wattributes, "%qE attribute ignored without -mistack", + name); + *no_add_attrs = true; + return NULL_TREE; + } + + if (DECL_P (*node)) + { + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute only applies to functions", + name); + *no_add_attrs = true; + } + } + else + { + if (TREE_CODE (*node) == FUNCTION_TYPE + || TREE_CODE (*node) == METHOD_TYPE) + { + if (csky_isr_value (args) == CSKY_FT_UNKNOWN) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + } + else if (TREE_CODE (*node) == POINTER_TYPE + && (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE + || TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE) + && csky_isr_value (args) != CSKY_FT_UNKNOWN) + { + *node = build_variant_type_copy (*node); + TREE_TYPE (*node) = build_type_attribute_variant (TREE_TYPE (*node), + tree_cons (name, args, TYPE_ATTRIBUTES (TREE_TYPE (*node)))); + *no_add_attrs = true; + } + else if (flags & ((int)ATTR_FLAG_DECL_NEXT + | (int)ATTR_FLAG_FUNCTION_NEXT + | (int)ATTR_FLAG_ARRAY_NEXT)) + { + *no_add_attrs = true; + return tree_cons (name, args, NULL_TREE); + } + else + warning (OPT_Wattributes, "%qE attribute ignored", name); + } + return NULL_TREE; +} + + +/* Implement TARGET_REGISTER_MOVE_COST: compute extra cost of moving data + between one register class and another. */ + +int +csky_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t from, reg_class_t to) +{ +#define GR_REG_CLASS_P(CLASS) \ + ((CLASS) == GENERAL_REGS || (CLASS) == MINI_REGS || (CLASS) == SP_REGS \ + || (CLASS) == LOW_REGS) + +#define HILO_REG_CLASS_P(CLASS) \ + ((CLASS) == HI_REGS || (CLASS) == LO_REGS || (CLASS) == HILO_REGS) + +#define V_REG_CLASS_P(CLASS) \ + ((CLASS) == V_REGS) + + if (V_REG_CLASS_P (from) && V_REG_CLASS_P (to)) + return 2; + + if ((V_REG_CLASS_P (from) && GR_REG_CLASS_P (to)) + || (GR_REG_CLASS_P (from) && V_REG_CLASS_P (to))) + return 6; + + if ((HILO_REG_CLASS_P (from) && GR_REG_CLASS_P (to)) + || (GR_REG_CLASS_P (from) && HILO_REG_CLASS_P (to))) + return 16; + + if (HILO_REG_CLASS_P (from) && HILO_REG_CLASS_P (to)) + return 32; + + if ((HILO_REG_CLASS_P (from) && V_REG_CLASS_P (to)) + || (V_REG_CLASS_P (from) && HILO_REG_CLASS_P (to))) + return 64; + + return 2; +} + + +/* Implement TARGET_MEMORY_MOVE_COST: compute the cost of moving data + between registers and memory. */ + +int +csky_memory_move_cost (machine_mode mode, reg_class_t rclass, + bool in) +{ + return (4 + memory_move_secondary_cost (mode, rclass, in)); +} + + +/* TARGET_RTX_COSTS helper for ck801/ck802. */ + +static bool +ck802_ck801_rtx_costs (rtx x, int code, int outer_code, int *total, + bool speed) +{ + machine_mode mode = GET_MODE (x); + switch (code) + { + /* Accessing memory costs quite a lot for first word; */ + case MEM: + *total = COSTS_N_INSNS (1 + CSKY_NUM_REGS (mode)); + return false; + case DIV: + case UDIV: + case MOD: + case UMOD: + *total = 100; + return true; + + case ROTATE: + case ROTATERT: + case ASHIFT: + case LSHIFTRT: + case ASHIFTRT: + if (speed) + *total = 2; + else + *total = COSTS_N_INSNS (1); + return false; + + case MINUS: + case PLUS: + *total = COSTS_N_INSNS (CSKY_NUM_REGS (mode)); + return false; + + case AND: + { + enum rtx_code subcode = GET_CODE (XEXP (x, 1)); + + /* If subcode is "not", we'll try to combine it into e.g. "andn" + instruction, so give AND itself zero cost. */ + if (subcode == NOT) + { + *total = 0; + return false; + } + } + /* Fall through. */ + case XOR: + case IOR: + *total = COSTS_N_INSNS (CSKY_NUM_REGS (mode)); + return false; + + case MULT: + /* FIXME: is ixw supported on ck801/ck802? */ + /* We can use "ix.h/w" insn to replace multiply by 2 or 4. + "ix.h/w" is a 32-bit insn, so let its cost be a little less than + "mult" insn. */ + if (REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1))) + { + unsigned HOST_WIDE_INT m + = (unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1))); + if ((m == 2 || m == 4) && outer_code == PLUS) + { + *total = 2; + return true; + } + else + { + /* Because mult is relatively slower than other operations, + we try to use other insns when optimizing for speed. + When optimizing for size, give it lower cost. */ + if (speed) + { + *total = COSTS_N_INSNS (10 * CSKY_NUM_REGS (mode)); + return true; + } + int cycle = 0; + while (m) + { + m >>= 2; + cycle++; + } + *total = COSTS_N_INSNS (1) + cycle; + return false; + } + } + if (!speed) + *total = COSTS_N_INSNS (1); + return false; + + case NEG: + /* Usually, we use subtract from 0 to substitute for neg, and + it costs 1 extra insn to move 0 to a register. */ + *total = COSTS_N_INSNS (2 * CSKY_NUM_REGS (mode)); + return false; + + case NOT: + *total = COSTS_N_INSNS (CSKY_NUM_REGS (mode)); + return false; + + case COMPARE: + *total = COSTS_N_INSNS (1); + return false; + + case SIGN_EXTEND: + case ZERO_EXTEND: + *total = COSTS_N_INSNS (CSKY_NUM_REGS (mode)); + return false; + + case SIGN_EXTRACT: + case ZERO_EXTRACT: + if (REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1)) + && CONST_INT_P (XEXP (x, 2)) + && INTVAL (XEXP (x, 1)) == 8 + && INTVAL (XEXP (x, 2)) % 8 == 0) + { + *total = COSTS_N_INSNS (1); + return true; + } + *total = COSTS_N_INSNS (CSKY_NUM_REGS (mode)); + return false; + + case CONST_INT: + { + unsigned HOST_WIDE_INT t = (unsigned HOST_WIDE_INT) (INTVAL (x)); + + if (outer_code == COMPARE) + { + if (t < 0x10000) + *total = 0; + else + *total = COSTS_N_INSNS (2); + } + else if (outer_code == AND || outer_code == IOR || outer_code == XOR) + { + /* "andi,xori,ori" are 32-bit insns, so let it cost a + little more. */ + if (t < 0x1000) + { + /* Try replacing "andi" by "sextb/h", so let it cost more. */ + if (outer_code == AND && (t == 0xff || t == 0xffff)) + { + *total = 8; + return true; + } + *total = 2; + } + else if (t < 0x10000) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + } + else if (outer_code == PLUS || outer_code == MINUS) + { + /* "addi/subi rx,ry,imm", if imm<9, it is more often a + 16-bit insn. If imm>=9, use "movi" insn; it's probably + less than "addi/subi". */ + if (t < 9) + *total = 0; + else if (t < 0x1000) + *total = 2; + else if (t < 0x10000) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + } + else if (outer_code == ROTATE || outer_code == ROTATERT + || outer_code == LSHIFTRT || outer_code == ASHIFTRT + || outer_code == ASHIFT) + { + if (t < 32) + *total = 0; + else + *total = COSTS_N_INSNS (2); + } + else + { + if (t < 0x10000) + if (outer_code == SET && t < 256) + *total = 0; + else + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + } + } + return true; + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = COSTS_N_INSNS (3); + return true; + default: + return false; + } +} + + +/* TARGET_RTX_COSTS helper for ck803. */ + +static bool +ck803_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, + int *total, bool speed ATTRIBUTE_UNUSED) +{ + switch (code) + { + case SET: + if (MEM_P (XEXP (x, 1))) + { + struct csky_address op1; + bool address_valid + = decompose_csky_address (XEXP (XEXP (x, 1), 0), &op1); + if (op1.index) + { + *total = COSTS_N_INSNS (3); + return true; + } + else if (address_valid) + { + *total = COSTS_N_INSNS (1); + return true; + } + } + if (REG_P (XEXP (x, 0)) && (GET_CODE (XEXP (x, 1)) == PLUS)) + { + rtx sub_exp = XEXP (x, 1); + if (REG_P (XEXP (sub_exp, 0)) && REG_P (XEXP (sub_exp, 1))) + { + *total = COSTS_N_INSNS (1); + return true; + } + } + return false; + case MULT: + if (REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1))) + { + HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); + if (val % 2 == 0 && val < 0xffffffff && val > 0) + { + *total = COSTS_N_INSNS (1); + return true; + } + } + return false; + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = COSTS_N_INSNS (3); + return true; + default: + return false; + } +} + +/* TARGET_RTX_COSTS helper for ck807+ arches. */ + +static bool +ck807_ck810_rtx_costs (rtx x, int code, + int outer_code ATTRIBUTE_UNUSED, + int *total, bool speed ATTRIBUTE_UNUSED) +{ + switch (code) + { + case MULT: + if (REG_P (XEXP (x, 0)) && CONST_INT_P (XEXP (x, 1))) + { + HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); + if (val % 2 == 0 && val < 0xffffffff && val > 0) + { + *total = COSTS_N_INSNS (1); + return true; + } + } + return false; + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = COSTS_N_INSNS (3); + return true; + default: + return false; + } +} + + +/* Implement TARGET_RTX_COSTS, to compute a (partial) cost for rtx X. + Return true if the complete cost has been computed, and false if + subexpressions should be scanned. In either case, *TOTAL contains + the cost result. */ + +static bool +csky_rtx_costs (rtx x, machine_mode mode ATTRIBUTE_UNUSED, int outer_code, + int opno ATTRIBUTE_UNUSED, int *total, bool speed) +{ + int code = GET_CODE (x); + + if (CSKY_TARGET_ARCH (CK802) || CSKY_TARGET_ARCH (CK801)) + return ck802_ck801_rtx_costs (x, code, outer_code, total, speed); + else if (CSKY_TARGET_ARCH (CK803)) + return ck803_rtx_costs (x, code, outer_code, total, speed); + else if (CSKY_TARGET_ARCH (CK807) || CSKY_TARGET_ARCH (CK810)) + return ck807_ck810_rtx_costs (x, code, outer_code, total, speed); + else + gcc_unreachable (); +} + +/* Emit assembly code for CASESI. This is only used on CK801 and CK802 + when optimizing for size, and uses helper functions in libgcc instead + of doing the control transfer inline. */ + +const char * +csky_output_casesi (rtx *operands) +{ + rtx diff_vec = PATTERN (NEXT_INSN (as_a (operands[0]))); + + gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC); + + switch (GET_MODE (diff_vec)) + { + case E_QImode: + return (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned + ? "jbsr\t___gnu_csky_case_uqi" + : "jbsr\t___gnu_csky_case_sqi"); + case E_HImode: + return (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned + ? "jbsr\t___gnu_csky_case_uhi" + : "jbsr\t___gnu_csky_case_shi"); + case E_SImode: + return "jbsr\t___gnu_csky_case_si"; + default: + gcc_unreachable (); + } +} + +/* Implement TARGET_SCHED_ISSUE_RATE. Lookup the issue rate in the + per-core tuning structs. */ +static int +csky_sched_issue_rate (void) +{ + if (CSKY_TARGET_ARCH (CK810)) + return 2; + else + return 1; +} + + +/* This function implements the target macro TARGET_SCHED_ADJUST_COST. + It corrects the value of COST based on the relationship between + INSN and DEP through the dependence DEP_TYPE. It returns the new + value. */ + +static int +csky_sched_adjust_cost (rtx_insn *insn, + int dep_type, + rtx_insn *dep, + int cost, + unsigned int dw ATTRIBUTE_UNUSED) +{ + if (dep_type == REG_DEP_ANTI || dep_type == REG_DEP_OUTPUT) + return 0; + /* The REG_DEP_TRUE situation. */ + else if (recog_memoized (insn) >= 0 && recog_memoized (dep) >= 0) + { + enum attr_type insn_type = get_attr_type (insn); + if (CSKY_TARGET_ARCH (CK803)) + { + /* The ld or st's base reg depends on the pre insn, + it will delay 1 cycle. */ + if (insn_type == TYPE_LOAD || insn_type == TYPE_STORE) + { + rtx pattern = PATTERN (insn); + + gcc_assert (GET_CODE (pattern) == SET); + rtx addr = (insn_type == TYPE_LOAD + ? SET_SRC (pattern) : SET_DEST (pattern)); + + enum rtx_code code = GET_CODE (addr); + if (code == ZERO_EXTEND || code == SIGN_EXTEND) + addr = XEXP (addr, 0); + gcc_assert (GET_CODE (addr) == MEM); + + rtx base = XEXP (addr, 0); + rtx reg = NULL_RTX; + if (REG_P (base)) + reg = base; + if (GET_CODE (base) == PLUS + && GET_CODE (XEXP (base, 0)) == REG) + reg = XEXP (base, 0); + if ((reg != NULL_RTX) && reg_set_p (reg, PATTERN (dep))) + return 2; + } + } + else if (CSKY_TARGET_ARCH (CK802)) + { + if ((insn_type == TYPE_CALL_JSR || insn_type == TYPE_BRANCH_JMP) + && get_attr_type (dep) != TYPE_LOAD) + return 1; + + if (insn_type == TYPE_LOAD || insn_type == TYPE_STORE) + { + rtx pattern = PATTERN (insn); + + gcc_assert (GET_CODE (pattern) == SET); + + rtx addr = (insn_type == TYPE_LOAD + ? SET_SRC (pattern) : SET_DEST (pattern)); + + enum rtx_code code = GET_CODE (addr); + if (code == ZERO_EXTEND || code == SIGN_EXTEND) + addr = XEXP (addr, 0); + gcc_assert (GET_CODE (addr) == MEM); + + rtx base = XEXP (addr, 0); + rtx reg = NULL_RTX; + if (REG_P (base)) + reg = base; + if (GET_CODE (base) == PLUS + && GET_CODE (XEXP (base, 0)) == REG) + reg = XEXP (base, 0); + if ((reg != NULL_RTX) && reg_set_p (reg, PATTERN (dep)) + && get_attr_type (dep) != TYPE_LOAD) + return 1; + + if (insn_type == TYPE_STORE + && reg_referenced_p (SET_SRC (pattern), PATTERN (dep))) + return 1; + } + } + } + return cost; +} + +static bool +csky_warn_func_return (tree decl) +{ + /* Naked functions are implemented entirely in assembly, including the + return sequence, so suppress warnings about this. */ + return lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) == NULL_TREE; +} + + +/* Implement TARGET_RETURN_IN_MEMORY to decide whether TYPE should be + returned in memory (true) or in a register (false). + FNTYPE is the type of the function making the call. */ +static bool +csky_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 > 2 * UNITS_PER_WORD); +} + + +/* Implement TARGET_DWARF_REGISTER_SPAN. + Dwarf models VFP registers as 64-bit or 128-bit registers default. + GCC models tham as 32-bit registers, so we need to describe this to + the DWARF generation code. Other registers can use the default. */ +static rtx +csky_dwarf_register_span (rtx rtl) +{ + machine_mode mode; + unsigned regno; + rtx parts[16]; + int nregs; + int i; + + regno = REGNO (rtl); + if (!CSKY_VREG_P (regno)) + return NULL_RTX; + + mode = GET_MODE (rtl); + if (GET_MODE_SIZE (mode) < 8) + return NULL_RTX; + + if (TARGET_SOFT_FPU) + { + nregs = GET_MODE_SIZE (mode) / 4; + for (i = 0; i < nregs; i += 2) + if (TARGET_BIG_ENDIAN) + { + parts[i] = gen_rtx_REG (SImode, regno + i + 1); + parts[i + 1] = gen_rtx_REG (SImode, regno + i); + } + else + { + parts[i] = gen_rtx_REG (SImode, regno + i); + parts[i + 1] = gen_rtx_REG (SImode, regno + i + 1); + } + } + else + { + /* FIXME: dwarf2 considers all general registers to be the same + as the CPU bit width. Transform the 64-bit FPU registers to + 32 bits here, and we will modify the unwind processing to + fit CSKY architecture later. */ + nregs = GET_MODE_SIZE (mode) / 8; + for (i = 0; i < nregs; i++) + parts[i] = gen_rtx_REG (SImode, regno + i); + } + + return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nregs , parts)); +} + +/* Implement TARGET_INIT_LIBFUNCS. */ + +static void +csky_init_libfuncs (void) +{ + if (TARGET_CSKY_LINUX) + init_sync_libfuncs (UNITS_PER_WORD); + if (!TARGET_LIBCCRT) + return; + + #define CSKY_GCC_SYM(sym) "__csky_ccrt_" # sym + + /* int */ + + /* Arithmetic functions */ + set_optab_libfunc (ashl_optab, DImode, CSKY_GCC_SYM (ashldi3)); + set_optab_libfunc (ashr_optab, DImode, CSKY_GCC_SYM (ashrdi3)); + set_optab_libfunc (sdiv_optab, SImode, CSKY_GCC_SYM (divsi3)); + set_optab_libfunc (sdiv_optab, DImode, CSKY_GCC_SYM (divdi3)); + set_optab_libfunc (lshr_optab, DImode, CSKY_GCC_SYM (lshrdi3)); + set_optab_libfunc (smod_optab, SImode, CSKY_GCC_SYM (modsi3)); + set_optab_libfunc (smod_optab, DImode, CSKY_GCC_SYM (moddi3)); + set_optab_libfunc (smul_optab, DImode, CSKY_GCC_SYM (muldi3)); + set_optab_libfunc (neg_optab, DImode, CSKY_GCC_SYM (negdi2)); + set_optab_libfunc (udiv_optab, SImode, CSKY_GCC_SYM (udivsi3)); + set_optab_libfunc (udiv_optab, DImode, CSKY_GCC_SYM (udivdi3)); + set_optab_libfunc (udivmod_optab, DImode, CSKY_GCC_SYM (udivmoddi4)); + set_optab_libfunc (umod_optab, SImode, CSKY_GCC_SYM (umodsi3)); + set_optab_libfunc (umod_optab, DImode, CSKY_GCC_SYM (umoddi3)); + + /* Comparison functions */ + set_optab_libfunc (cmp_optab, DImode, CSKY_GCC_SYM (cmpdi2)); + set_optab_libfunc (ucmp_optab, DImode, CSKY_GCC_SYM (ucmpdi2)); + + /* Trapping arithmetic functions */ + set_optab_libfunc (absv_optab, SImode, CSKY_GCC_SYM (absvsi2)); + set_optab_libfunc (absv_optab, DImode, CSKY_GCC_SYM (absvdi2)); + set_optab_libfunc (addv_optab, SImode, CSKY_GCC_SYM (addvsi3)); + set_optab_libfunc (addv_optab, DImode, CSKY_GCC_SYM (addvdi3)); + set_optab_libfunc (smulv_optab, SImode, CSKY_GCC_SYM (mulvsi3)); + set_optab_libfunc (smulv_optab, DImode, CSKY_GCC_SYM (mulvdi3)); + set_optab_libfunc (negv_optab, SImode, CSKY_GCC_SYM (negvsi2)); + set_optab_libfunc (negv_optab, DImode, CSKY_GCC_SYM (negvdi2)); + set_optab_libfunc (subv_optab, SImode, CSKY_GCC_SYM (subvsi3)); + set_optab_libfunc (subv_optab, DImode, CSKY_GCC_SYM (subvdi3)); + + /* Bit operations */ + set_optab_libfunc (clz_optab, SImode, CSKY_GCC_SYM (clzsi2)); + set_optab_libfunc (clz_optab, DImode, CSKY_GCC_SYM (clzdi2)); + set_optab_libfunc (ctz_optab, SImode, CSKY_GCC_SYM (ctzsi2)); + set_optab_libfunc (ctz_optab, DImode, CSKY_GCC_SYM (ctzdi2)); + set_optab_libfunc (ffs_optab, DImode, CSKY_GCC_SYM (ffsdi2)); + set_optab_libfunc (parity_optab, SImode, CSKY_GCC_SYM (paritysi2)); + set_optab_libfunc (parity_optab, DImode, CSKY_GCC_SYM (paritydi2)); + set_optab_libfunc (popcount_optab,SImode, CSKY_GCC_SYM (popcountsi2)); + set_optab_libfunc (popcount_optab,DImode, CSKY_GCC_SYM (popcountdi2)); + set_optab_libfunc (bswap_optab, SImode, CSKY_GCC_SYM (bswapsi2)); + set_optab_libfunc (bswap_optab, DImode, CSKY_GCC_SYM (bswapdi2)); + + /* float */ + + /* Arithmetic functions */ + set_optab_libfunc (add_optab, SFmode, CSKY_GCC_SYM (addsf3)); + set_optab_libfunc (add_optab, DFmode, CSKY_GCC_SYM (adddf3)); + set_optab_libfunc (sub_optab, SFmode, CSKY_GCC_SYM (subsf3)); + set_optab_libfunc (sub_optab, DFmode, CSKY_GCC_SYM (subdf3)); + set_optab_libfunc (smul_optab, SFmode, CSKY_GCC_SYM (mulsf3)); + set_optab_libfunc (smul_optab, DFmode, CSKY_GCC_SYM (muldf3)); + set_optab_libfunc (sdiv_optab, SFmode, CSKY_GCC_SYM (divsf3)); + set_optab_libfunc (sdiv_optab, DFmode, CSKY_GCC_SYM (divdf3)); + set_optab_libfunc (neg_optab, SFmode, CSKY_GCC_SYM (negsf2)); + set_optab_libfunc (neg_optab, DFmode, CSKY_GCC_SYM (negdf2)); + + /* Conversion functions */ + set_conv_libfunc (sext_optab, DFmode, SFmode, CSKY_GCC_SYM (extendsfdf2)); + set_conv_libfunc (trunc_optab, SFmode, DFmode, CSKY_GCC_SYM (truncdfsf2)); + set_conv_libfunc (sfix_optab, SImode, SFmode, CSKY_GCC_SYM (fixsfsi)); + set_conv_libfunc (sfix_optab, SImode, DFmode, CSKY_GCC_SYM (fixdfsi)); + set_conv_libfunc (sfix_optab, DImode, SFmode, CSKY_GCC_SYM (fixsfdi)); + set_conv_libfunc (sfix_optab, DImode, DFmode, CSKY_GCC_SYM (fixdfdi)); + set_conv_libfunc (ufix_optab, SImode, SFmode, CSKY_GCC_SYM (fixunssfsi)); + set_conv_libfunc (ufix_optab, SImode, DFmode, CSKY_GCC_SYM (fixunsdfsi)); + set_conv_libfunc (ufix_optab, DImode, SFmode, CSKY_GCC_SYM (fixunssfdi)); + set_conv_libfunc (ufix_optab, DImode, DFmode, CSKY_GCC_SYM (fixunsdfdi)); + set_conv_libfunc (sfloat_optab, SFmode, SImode, CSKY_GCC_SYM (floatsisf)); + set_conv_libfunc (sfloat_optab, DFmode, SImode, CSKY_GCC_SYM (floatsidf)); + set_conv_libfunc (sfloat_optab, SFmode, DImode, CSKY_GCC_SYM (floatdisf)); + set_conv_libfunc (sfloat_optab, DFmode, DImode, CSKY_GCC_SYM (floatdidf)); + set_conv_libfunc (ufloat_optab, SFmode, SImode, CSKY_GCC_SYM (floatunsisf)); + set_conv_libfunc (ufloat_optab, DFmode, SImode, CSKY_GCC_SYM (floatunsidf)); + set_conv_libfunc (ufloat_optab, SFmode, DImode, CSKY_GCC_SYM (floatundisf)); + set_conv_libfunc (ufloat_optab, DFmode, DImode, CSKY_GCC_SYM (floatundidf)); + + /* Comparison functions */ + set_optab_libfunc (cmp_optab, SFmode, CSKY_GCC_SYM (cmpsf2)); + set_optab_libfunc (cmp_optab, DFmode, CSKY_GCC_SYM (cmpdf2)); + set_optab_libfunc (unord_optab, SFmode, CSKY_GCC_SYM (unordsf2)); + set_optab_libfunc (unord_optab, DFmode, CSKY_GCC_SYM (unorddf2)); + set_optab_libfunc (eq_optab, SFmode, CSKY_GCC_SYM (eqsf2)); + set_optab_libfunc (eq_optab, DFmode, CSKY_GCC_SYM (eqdf2)); + set_optab_libfunc (ne_optab, SFmode, CSKY_GCC_SYM (nesf2)); + set_optab_libfunc (ne_optab, DFmode, CSKY_GCC_SYM (nedf2)); + set_optab_libfunc (ge_optab, SFmode, CSKY_GCC_SYM (gesf2)); + set_optab_libfunc (ge_optab, DFmode, CSKY_GCC_SYM (gedf2)); + set_optab_libfunc (lt_optab, SFmode, CSKY_GCC_SYM (ltsf2)); + set_optab_libfunc (lt_optab, DFmode, CSKY_GCC_SYM (ltdf2)); + set_optab_libfunc (le_optab, SFmode, CSKY_GCC_SYM (lesf2)); + set_optab_libfunc (le_optab, DFmode, CSKY_GCC_SYM (ledf2)); + set_optab_libfunc (gt_optab, SFmode, CSKY_GCC_SYM (gtsf2)); + set_optab_libfunc (gt_optab, DFmode, CSKY_GCC_SYM (gtdf2)); +} + + +/* Implement TARGET_ADDRESS_COST to estimate cost of the memory address X. + For C-SKY, (register) and (register + offset) have the same cost. + Other situations cost more. */ + +static int +csky_address_cost (rtx x, machine_mode mode ATTRIBUTE_UNUSED, + addr_space_t as ATTRIBUTE_UNUSED, + bool speed ATTRIBUTE_UNUSED) +{ + enum rtx_code code = GET_CODE (x); + + if (code == REG) + return COSTS_N_INSNS (1); + if (code == PLUS + && REG_P (XEXP (x, 0)) + && CONST_INT_P (XEXP (x, 1))) + return COSTS_N_INSNS (1); + + return COSTS_N_INSNS (3); +} + + +/* Implement TARGET_FIXED_CONDITION_CODE_REGS. */ + +static bool +csky_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = CSKY_CC_REGNUM; + *p2 = INVALID_REGNUM; + return true; +} + + +struct gcc_target targetm = TARGET_INITIALIZER; + +#include "gt-csky.h" diff --git a/gcc/config/csky/csky.h b/gcc/config/csky/csky.h new file mode 100644 index 00000000000..39aac6bb4b8 --- /dev/null +++ b/gcc/config/csky/csky.h @@ -0,0 +1,1054 @@ +/* Declarations for the C-SKY back end. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + + +#ifndef GCC_CSKY_H +#define GCC_CSKY_H + +/* In some places e.g. csky_secondary_reload, we use -1 to indicate an + invalid register. In other places where N is unsigned the comparison + to zero would give an error, so explicitly cast to int here. */ +#define CSKY_GENERAL_REGNO_P(N) \ + ((N) < CSKY_NGPR_REGS && (int)(N) >= 0) + +#define CSKY_VREG_P(N) \ + ((N) >= CSKY_FIRST_VFP_REGNUM && (N) <= CSKY_LAST_VFP_REGNUM) + +#define CSKY_HILO_REG_P(N) \ + ((N) == CSKY_HI_REGNUM || (N) == CSKY_LO_REGNUM) + +/* Helper macros for constant constraints and predicates. */ +#define CSKY_VALUE_BETWEEN(VALUE, LOW, HIGH) \ + ((VALUE) >= (LOW) && (VALUE) <= (HIGH)) + +#define CSKY_CONST_OK_FOR_I(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 0, 65535) + +#define CSKY_CONST_OK_FOR_J(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 1, 32) + +#define CSKY_CONST_OK_FOR_K(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 0, 31) + +#define CSKY_CONST_OK_FOR_L(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 1, 8) + +#define CSKY_CONST_OK_FOR_M(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 1, 4096) + +#define CSKY_CONST_OK_FOR_N(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 1, 256) + +#define CSKY_CONST_OK_FOR_O(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 0, 4095) + +#define CSKY_CONST_OK_FOR_P(VALUE) \ + (((VALUE) & 0x3) == 0 && CSKY_VALUE_BETWEEN (VALUE, 4, 508)) + +#define CSKY_CONST_OK_FOR_T(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, -256, -1) + +#define CSKY_CONST_OK_FOR_Ub(VALUE) \ + (exact_log2 (VALUE & 0xFFFFFFFF) >= 0) + +#define CSKY_CONST_OK_FOR_Uc(VALUE) \ + ((VALUE) == (HOST_WIDE_INT) -1 \ + || (exact_log2 ((VALUE) + 1) >= 0 \ + && exact_log2 ((VALUE) + 1) <= 31)) + +#define CSKY_CONST_OK_FOR_Ud(VALUE) \ + ((CSKY_CONST_OK_FOR_I ((VALUE) & 0xffffffff) \ + || CSKY_CONST_OK_FOR_Ub ((VALUE)) \ + || CSKY_CONST_OK_FOR_Uc (((VALUE) << 32) >> 32)) \ + && (CSKY_CONST_OK_FOR_I ((VALUE) >> 32) \ + || CSKY_CONST_OK_FOR_Ub ((VALUE) >> 32) \ + || CSKY_CONST_OK_FOR_Uc ((VALUE) >> 32))) \ + +#define CSKY_CONST_OK_FOR_Ug(VALUE) \ + (((VALUE) & 0x3) == 0 && CSKY_VALUE_BETWEEN (VALUE, -508, -4)) + +#define CSKY_CONST_OK_FOR_Uh(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, -31, 0) + +#define CSKY_CONST_OK_FOR_Uj(VALUE) \ + (((VALUE) & 0x3) == 0 && CSKY_VALUE_BETWEEN (VALUE, 1, 1024)) + +#define CSKY_CONST_OK_FOR_Uk(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, 1, 65536) + +#define CSKY_CONST_OK_FOR_Ul(VALUE) \ + (((VALUE) & 0x3) == 0 && CSKY_VALUE_BETWEEN (VALUE, -1024, -4)) + +#define CSKY_CONST_OK_FOR_Um(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, -4096, -1) + +#define CSKY_CONST_OK_FOR_US(VALUE) \ + CSKY_VALUE_BETWEEN (VALUE, -8, -1) + +#define CSKY_CONST_OK_FOR_MOVIH(VALUE) \ + (((VALUE) & 0xFFFF) == 0) + +#ifndef TARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT CSKY_TARGET_CORE_GET(ck810f) +#endif + +/* Options that are enabled by default are specified as such in the + .opt file. */ +#define TARGET_DEFAULT 0 + +/* The highest CSKY architecture version supported by the target. */ +#define CSKY_TARGET_ARCH(arch) \ + (csky_base_arch == CSKY_TARGET_ARCH_GET (arch)) + +/* Define some macros for target code generation options. */ +#define TARGET_SOFT_FPU \ + (csky_fpu_index == TARGET_FPU_fpv2_sf) +#define TARGET_CASESI \ + (optimize_size && TARGET_CONSTANT_POOL \ + && (CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802))) +#define TARGET_TLS \ + (CSKY_TARGET_ARCH (CK807) || CSKY_TARGET_ARCH (CK810)) + +/* Number of loads/stores handled by ldm/stm. */ +#define CSKY_MIN_MULTIPLE_STLD 3 +#define CSKY_MAX_MULTIPLE_STLD 12 + +/* Pull in enums and defines for processor/arch variants. This makes + it possible to use CSKY_TARGET_ARCH in macros defined in this file. */ +#include "csky_opts.h" +extern enum csky_base_architecture csky_base_arch; + +/* Pull in enums and defines for ISA features. Likewise required to + support use of CSKY_ISA_FEATURE in this file. + Note that the CSKY_ISA_FEATURE macro tests properties of the + particular processor we're compiling for, not code generation + options that may have dependencies on those features. The latter + are handled by TARGET_xxxx macros/variables instead. See csky.opt. */ +#include "csky_isa.h" +extern int csky_arch_isa_features[]; +#define CSKY_ISA_FEATURE(IDENT) \ + csky_arch_isa_features[CSKY_ISA_FEATURE_GET (IDENT)] + +/****************************************************************** + * Storage Layout * + ******************************************************************/ + + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. */ +#define BITS_BIG_ENDIAN 0 + +/* If the most significant byte of a word is the lowest numbered. */ +#define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0) + +/* If the most significant word of a multiword number is the lowest. */ +#define WORDS_BIG_ENDIAN (BYTES_BIG_ENDIAN) + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. */ +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ + (MODE) = SImode; + + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY 32 + +/* Boundary (in *bits*) on which stack pointer should be aligned. + Per C-SKY, the published V2 ABI document is incorrect and the proper + alignment is on a 4-byte boundary rather than 8 bytes. */ +#define STACK_BOUNDARY 32 + +/* Align definitions of arrays, unions and structures so that + initializations and copies can be made more efficient. This is not + ABI-changing, so it only affects places where we can see the + definition. Increasing the alignment tends to introduce padding, + so don't do this when optimizing for size/conserving stack space. */ +#define CSKY_EXPAND_ALIGNMENT(COND, EXP, ALIGN) \ + (((COND) && ((ALIGN) < BITS_PER_WORD) \ + && (TREE_CODE (EXP) == ARRAY_TYPE \ + || TREE_CODE (EXP) == UNION_TYPE \ + || TREE_CODE (EXP) == RECORD_TYPE)) \ + ? BITS_PER_WORD : (ALIGN)) + +/* Align global data. */ +#define DATA_ALIGNMENT(EXP, ALIGN) \ + CSKY_EXPAND_ALIGNMENT (!optimize_size, EXP, ALIGN) + +/* Similarly, make sure that objects on the stack are sensibly aligned. */ +#define LOCAL_ALIGNMENT(EXP, ALIGN) \ + CSKY_EXPAND_ALIGNMENT (!flag_conserve_stack, EXP, ALIGN) + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 32 + +/* Every structures size must be a multiple of 8 bits. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* Look at the fundamental type that is used for a bit-field and use + that to impose alignment on the enclosing structure. + struct s {int a:8}; should have same alignment as "int", not "char". */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* Largest integer machine mode for structures. If undefined, the default + is GET_MODE_SIZE(DImode). */ +#define MAX_FIXED_MODE_SIZE 64 + +/* Allocation boundary (in *bits*) for the code of a function. + Optimize ck801 and ck802 a little harder for size. */ +#define FUNCTION_BOUNDARY \ + (((CSKY_TARGET_ARCH (CK801) || CSKY_TARGET_ARCH (CK802)) \ + && optimize_size) \ + ? 16 : 32) + +/* C-SKY does not support unaligned access. */ +#define STRICT_ALIGNMENT 1 + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef UINT_LEAST32_TYPE +#define UINT_LEAST32_TYPE "unsigned int" + +#undef INT_LEAST32_TYPE +#define INT_LEAST32_TYPE "int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +/****************************************************************** + * Layout of Source Language Data Types * + ******************************************************************/ + + +/* 'char' is unsigned by default for backward compatibility. */ +#define DEFAULT_SIGNED_CHAR 0 + + +/****************************************************************** + * Stack Layout and Calling Conventions * + ******************************************************************/ + + +/* Basic Stack Layout */ + + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_DOWNWARD 1 + +/* Define this to nonzero if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +#define FRAME_GROWS_DOWNWARD 1 + +/* Offset of first parameter from the argument pointer register value. */ +#define FIRST_PARM_OFFSET(FNDECL) 0 + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame. */ +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + csky_return_addr (COUNT, FRAME) + +/* Pick up the return address upon entry to a procedure. Used for + dwarf2 unwind information. This also enables the table driven + mechanism. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, CSKY_LR_REGNUM) + + +/* Exception Handling Support */ + +/* The register that holds the return address in exception handlers. */ +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (SImode, CSKY_EH_STACKADJ_REGNUM) + + +/* Registers That Address the Stack Frame */ + + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM CSKY_SP_REGNUM + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 8 + +/* Base register for access to arguments of the function. This is a fake + register that is always eliminated. */ +#define ARG_POINTER_REGNUM 32 + +/* Static chain register. + Register use is more restricted on CK801. */ +#define STATIC_CHAIN_REGNUM (CSKY_TARGET_ARCH (CK801) ? 13 : 12) + + +/* Eliminating Frame Pointer and Arg Pointer */ + + +/* Definitions for register eliminations. + + This is an array of structures. Each structure initializes one pair + of eliminable registers. The "from" register number is given first, + followed by "to". Eliminations of the same "from" register are listed + in order of preference. + + We have two registers that can be eliminated on the CSKY. First, the + arg pointer register can often be eliminated in favor of the stack + pointer register. Secondly, the pseudo frame pointer register can always + be eliminated; it is replaced with the stack pointer. */ +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM },\ + { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM },\ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }} + +/* Define the offset between two registers, one to be eliminated, and the + other its replacement, at the start of a routine. */ +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = csky_initial_elimination_offset (FROM, TO) + + +/* Passing Function Arguments on the Stack */ + + +/* Define this if the maximum size of all the outgoing args is to be + accumulated and pushed during the prologue. The amount can be + found in the variable crtl->outgoing_args_size. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + + +/* Passing Arguments in Registers */ + + +/* A C type for declaring a variable that is used as the first argument of + TARGET_ FUNCTION_ARG and other related values. */ +#define CUMULATIVE_ARGS int + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + + On CSKY, the offset always starts at 0: the first parm reg is always + the same reg. */ +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ + ((CUM) = 0) + +/* True if N is a possible register number for function argument passing. + On the CSKY, r0-r3 are used to pass args. + The int cast is to prevent a complaint about unsigned comparison to + zero, since CSKY_FIRST_PARM_REGNUM is zero. */ +#define FUNCTION_ARG_REGNO_P(REGNO) \ + (((int)(REGNO) >= CSKY_FIRST_PARM_REGNUM) && \ + ((REGNO) < (CSKY_NPARM_REGS + CSKY_FIRST_PARM_REGNUM))) + +/* How Large Values Are Returned */ + + +/* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return + values must be in memory. On the CSKY, small + structures (eight bytes or fewer) are returned in + the register pair r0/r1. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. + + On the CSKY, the function epilogue recovers the stack pointer from the + frame. */ +#define EXIT_IGNORE_STACK 1 + + +/****************************************************************** + * Register Usage & Register Classes * + ******************************************************************/ + + +#define FIRST_PSEUDO_REGISTER 71 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + On C-SKY, r14 is SP, r26 is used by linker, + r27 is used by assembler, r28 is data base address, + r29 is GOT base address, r30 is handler base address, + r31 is TLS register. */ +#define FIXED_REGISTERS \ + /* r0 r1 r2 r3 r4 r5 r6 r7 */ \ +{ 0, 0, 0, 0, 0, 0, 0, 0, \ + /* r8 r9 r10 r11 r12 r13 r14 r15 */ \ + 0, 0, 0, 0, 0, 0, 1, 0, \ + /* r16 r17 r18 r19 r20 r21 r22 r23 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + /* r24 r25 r26 r27 r28 r29 r30 tls */ \ + 0, 0, 1, 1, 1, 1, 1, 1, \ + /* reserved c hi lo */ \ + 1, 1, 0, 0, \ + /* reserved */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* vr0 vr1 vr2 vr3 vr4 vr5 vr6 vr7 */ \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + /* vr8 vr9 vr10 vr11 vr12 vr13 vr14 vr15 */ \ + 0, 0, 0, 0, 0, 0, 0, 0 , \ + /* reserved */ \ + 1, 1, \ + /* epc */ \ + 1 \ +} + +/* 1 for registers that is clobbered (in general) by function calls. + If a register has 0, the compiler automatically saves it on + function entry and restores it on function exit, if the register + is used within the function. */ +#define CALL_USED_REGISTERS \ + /* r0 r1 r2 r3 r4 r5 r6 r7 */ \ +{ 1, 1, 1, 1, 0, 0, 0, 0, \ + /* r8 r9 r10 r11 r12 r13 r14 r15 */ \ + 0, 0, 0, 0, 1, 1, 1, 0, \ + /* r16 r17 r18 r19 r20 r21 r22 r23 */ \ + 0, 0, 1, 1, 1, 1, 1, 1, \ + /* r24 r25 r26 r27 r28 r29 r30 r31 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* reserved c hi lo */ \ + 1, 1, 1, 1, \ + /* reserved */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* vr0 vr1 vr2 vr3 vr4 vr5 vr6 vr7 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* vr8 vr9 vr10 vr11 vr12 vr13 vr14 vr15 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* reserved */ \ + 1, 1, \ + /* epc */ \ + 1 \ +} + +/* Like `CALL_USED_REGISTERS' but used to overcome a historical + problem which makes CALL_USED_REGISTERS *always* include + all the FIXED_REGISTERS. Until this problem has been + resolved this macro can be used to overcome this situation. + In particular, block_propagate() requires this list + be accurate, or we can remove registers which should be live. + This macro is used in get_csky_live_regs(). */ +#define CALL_REALLY_USED_REGISTERS \ + /* r0 r1 r2 r3 r4 r5 r6 r7 */ \ +{ 1, 1, 1, 1, 0, 0, 0, 0, \ + /* r8 r9 r10 r11 r12 r13 r14 r15 */ \ + 0, 0, 0, 0, 1, 1, 1, 0, \ + /* r16 r17 r18 r19 r20 r21 r22 r23 */ \ + 0, 0, 1, 1, 1, 1, 1, 1, \ + /* r24 r25 r26 r27 r28 r29 r30 r31 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* reserved c hi lo */ \ + 1, 1, 1, 1, \ + /* reserved */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* vr0 vr1 vr2 vr3 vr4 vr5 vr6 vr7 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* vr8 vr9 vr10 vr11 vr12 vr13 vr14 vr15 */ \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + /* reserved */ \ + 1, 1, \ + /* epc */ \ + 1 \ +} + +#define REGISTER_NAMES \ +{ \ + "a0", "a1", "a2", "a3", "l0", "l1", "l2", "l3", \ + "l4", "l5", "l6", "l7", "t0", "t1", "sp", "lr", \ + "l8", "l9", "t2", "t3", "t4", "t5", "t6", "t7", \ + "t8", "t9", "r26", "r27", "gb", "r29", "svbr", "r31", \ + /* reserved */ \ + "reserved", \ + /* CC register: 33 */ \ + "c", \ + /* DSP instruction register: 34, 35 */ \ + "hi", "lo", \ + "reserved", "reserved", "reserved", "reserved", "reserved", \ + "reserved", "reserved", "reserved", "reserved", "reserved", \ + "reserved", "reserved", "reserved", "reserved", "reserved", \ + "reserved", \ + /* V registers: 52~67 */ \ + "vr0", "vr1", "vr2", "vr3", "vr4", "vr5", "vr6", "vr7", \ + "vr8", "vr9", "vr10", "vr11", "vr12", "vr13", "vr14", "vr15", \ + "reserved", "reserved", \ + "epc" \ +} + +/* Table of additional register names to use in user input. */ +#define ADDITIONAL_REGISTER_NAMES \ +{ \ + {"r0", 0}, \ + {"r1", 1}, \ + {"r2", 2}, \ + {"r3", 3}, \ + {"r4", 4}, \ + {"r5", 5}, \ + {"r6", 6}, \ + {"r7", 7}, \ + {"r8", 8}, \ + {"r9", 9}, \ + {"r10", 10}, \ + {"r11", 11}, \ + {"r12", 12}, \ + {"r13", 13}, \ + {"r14", 14}, \ + {"r15", 15}, \ + {"r16", 16}, \ + {"r17", 17}, \ + {"r18", 18}, \ + {"r19", 19}, \ + {"r20", 20}, \ + {"r21", 21}, \ + {"r22", 22}, \ + {"r23", 23}, \ + {"r24", 24}, \ + {"r25", 25}, \ + {"r26", 26}, \ + {"r27", 27}, \ + {"r28", 28}, \ + {"r29", 29}, \ + {"r30", 30}, \ + {"r31", 31}, \ +} + +/* The order in which registers should be allocated. + It is better to use the registers the caller need not save. + Allocate r0 through r3 in reverse order since r3 is least likely + to contain a function parameter; in addition results are returned + in r0. It is quite good to use lr since other calls may clobber + it anyway. */ +#define REG_ALLOC_ORDER \ +/* r3 r2 r1 r0 r12 r13 r18 r19 */ \ + { 3, 2, 1, 0, 12, 13, 18, 19, \ +/* r20 r21 r22 r23 r24 r25 */ \ + 20, 21, 22, 23, 24, 25, \ +/* r15 r4 r5 r6 r7 r8 r9 r10 r11 */ \ + 15, 4, 5, 6, 7, 8, 9, 10, 11, \ +/* r16 r17 r26 r27 r28 r29 r30 hi lo */ \ + 16, 17, 26, 27, 28, 29, 30, 34, 35, \ +/* vr0 vr1 vr2 vr3 vr4 vr5 vr6 vr7 */ \ + 52, 53, 54, 55, 56, 57, 58, 59, \ +/* vr8 vr9 vr10 vr11 vr12 vr13 vr14 vr15 */ \ + 60, 61, 62, 63, 64, 65, 66, 67, \ +/* reserved */ \ + 36, 37, 38, 39, 40, 41, 42, 43, \ + 44, 45, 46, 47, 48, 49, 50, 51, \ +/* sp tls reserved c reserved epc */ \ + 14, 31, 32, 33, 68, 69, 70 } + +/* Register classes. */ +enum reg_class +{ + NO_REGS, + MINI_REGS, + SP_REGS, + LOW_REGS, + GENERAL_REGS, + C_REGS, + HI_REGS, + LO_REGS, + HILO_REGS, + V_REGS, + OTHER_REGS, + RESERVE_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "MINI_REGS", \ + "SP_REGS", \ + "LOW_REGS", \ + "GENERAL_REGS", \ + "C_REGS", \ + "HI_REGS", \ + "LO_REGS", \ + "HILO_REGS", \ + "V_REGS", \ + "OTHER_REGS", \ + "RESERVE_REGS", \ + "ALL_REGS", \ +} + +/* Define which registers fit in which classes. This is an initializer + for a vector of HARD_REG_SET of length N_REG_CLASSES. */ +#define REG_CLASS_CONTENTS \ +{ \ + {0x00000000, 0x00000000, 0x00000000 }, /* NO_REGS */ \ + {0x000000FF, 0x00000000, 0x00000000 }, /* MINI_REGS */ \ + {0x00004000, 0x00000000, 0x00000000 }, /* SP_REGS */ \ + {0x0000FFFF, 0x00000000, 0x00000000 }, /* LOW_REGS */ \ + {0xFFFFFFFF, 0x00000000, 0x00000000 }, /* GENERAL_REGS */ \ + {0x00000000, 0x00000002, 0x00000000 }, /* C_REGS */ \ + {0x00000000, 0x00000004, 0x00000000 }, /* HI_REG */ \ + {0x00000000, 0x00000008, 0x00000000 }, /* LO_REG */ \ + {0x00000000, 0x0000000c, 0x00000000 }, /* HILO_REGS */ \ + {0x00000000, 0xFFF00000, 0x0000000F }, /* V_REGS */ \ + {0x00000000, 0x00000000, 0x00000040 }, /* OTHER_REGS */ \ + {0x00000000, 0x0FF00001, 0x00000030 }, /* RESERVE_REGS */ \ + {0xFFFFFFFF, 0xFFFFFFFF, 0x0000007F }, /* ALL_REGS */ \ +} + +/* Return register class from regno. */ +extern enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER]; +#define REGNO_REG_CLASS(REGNO) regno_reg_class[REGNO] + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS (CSKY_ISA_FEATURE (2E3) ? GENERAL_REGS : NO_REGS) +#define BASE_REG_CLASS GENERAL_REGS + +/* TODO is it necessary to set it to MINI_REGS to emit more 16-bit + instructions? */ +#define MODE_BASE_REG_CLASS(MODE) GENERAL_REGS + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. + + The reg_renumber is used to map pseudo regs into hardware + regs, it is set up as a result of register allocation. */ +#ifdef REG_OK_STRICT +#define REGNO_OK_FOR_BASE_P(REGNO) \ + (CSKY_GENERAL_REGNO_P (REGNO) \ + || CSKY_GENERAL_REGNO_P (reg_renumber[(REGNO)]) ) +#else +#define REGNO_OK_FOR_BASE_P(REGNO) \ + (CSKY_GENERAL_REGNO_P (REGNO) \ + || (REGNO) >= FIRST_PSEUDO_REGISTER) +#endif + + +#ifdef REG_OK_STRICT +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + (CSKY_GENERAL_REGNO_P (REGNO) \ + || CSKY_GENERAL_REGNO_P (reg_renumber[(REGNO)]) ) +#else +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + (CSKY_GENERAL_REGNO_P (REGNO) \ + || (REGNO) >= FIRST_PSEUDO_REGISTER) +#endif + + +/****************************************************************** + * Addressing Modes * + ******************************************************************/ + + +/* Recognize any constant value that is a valid address. */ +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF) + +/* Maximum number of registers that can appear in a valid memory address. + Shifts in addresses can't be by a register. */ +#define MAX_REGS_PER_ADDRESS 2 + + +/****************************************************************** + * Run-time Target * + ******************************************************************/ + + +#define TARGET_CPU_CPP_BUILTINS() \ + csky_cpu_cpp_builtins (pfile) + +/****************************************************************** + * Per-function Data * + ******************************************************************/ + + +/* Initialize data used by insn expanders. This is called from insn_emit, + once for every function before code is generated. */ +#define INIT_EXPANDERS csky_init_expanders () + + +/****************************************************************** + * Dividing the Output into Sections (Texts, Data, . . . ) * + ******************************************************************/ + + +/* Switch to the text or data segment. */ +#define TEXT_SECTION_ASM_OP "\t.text" +#define DATA_SECTION_ASM_OP "\t.data" + +/* The subroutine calls in the .init and .fini sections create literal + pools which must be jumped around... */ +#define FORCE_CODE_SECTION_ALIGN \ + asm ("br 1f ; .literals ; .align 2 ; 1:"); + +/* Define this macro to be an expression with a nonzero value if + jump tables (for tablejump insns) should be output in the text section, + along with the assembler instructions. */ +#define JUMP_TABLES_IN_TEXT_SECTION TARGET_CASESI + + +/****************************************************************** + * Assembler Format * + ******************************************************************/ + + +/* A C string constant for text to be output before(after) each asm + statement or group of consecutive ones. */ +#undef ASM_APP_ON +#define ASM_APP_ON "// inline asm begin\n" +#undef ASM_APP_OFF +#define ASM_APP_OFF "// inline asm end\n" + +/* A C string constant describing how to begin a comment in the target + assembler language. */ +#define ASM_COMMENT_START "\t//" + +/* This says how to output an assembler line + to define a global common symbol, with alignment information. */ +#undef ASM_OUTPUT_ALIGNED_COMMON +#define ASM_OUTPUT_ALIGNED_COMMON(STREAM, NAME, SIZE, ALIGN) \ + do \ + { \ + fputs ("\t.comm\t", STREAM); \ + assemble_name (STREAM, NAME); \ + fprintf (STREAM, ",%lu, %u\n", (unsigned long)(SIZE), \ + (ALIGN) / BITS_PER_UNIT); \ + } \ +while (0) + +/* Define a local common symbol whose alignment we wish to specify. + ALIGN comes in as bits, we have to turn it into bytes. */ +#undef ASM_OUTPUT_ALIGNED_LOCAL +#define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \ + do \ +{ \ + fputs ("\t.bss\t", (STREAM)); \ + assemble_name ((STREAM), (NAME)); \ + fprintf ((STREAM), ",%d, %d\n", (int)(SIZE), \ + (ALIGN) / BITS_PER_UNIT); \ +} \ +while (0) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP "\t.global\t" + +/* Output a reference to a label. */ +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ + fprintf (STREAM, "%s%s", user_label_prefix, \ + (* targetm.strip_name_encoding) (NAME)) + +/* Make an internal label into a string. */ +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(STRING, PREFIX, NUM) \ + sprintf (STRING, "*.%s%ld", PREFIX, (long) NUM) + +/* This is how to output an insn to push a register on the stack. + It need not be very fast code. */ +#define ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \ + fprintf (STREAM, "\tsubi\t %s,%d\n\tst.w\t %s,(%s)\n", \ + reg_names[STACK_POINTER_REGNUM], \ + (STACK_BOUNDARY / BITS_PER_UNIT), \ + reg_names[REGNO], \ + reg_names[STACK_POINTER_REGNUM]) + +/* This is how to output an insn to pop a register from the stack. */ +#define ASM_OUTPUT_REG_POP(STREAM,REGNO) \ + fprintf (STREAM, "\tld.w\t %s,(%s)\n\taddi\t %s,%d\n", \ + reg_names[REGNO], \ + reg_names[STACK_POINTER_REGNUM], \ + reg_names[STACK_POINTER_REGNUM], \ + (STACK_BOUNDARY / BITS_PER_UNIT)) + +/* Output an element of a dispatch table. */ +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \ + fprintf (STREAM, "\t.long\t.L%d\n", VALUE) + +/* This is how to output an assembler line + that says to advance the location counter by SIZE bytes. */ +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(STREAM,SIZE) \ + fprintf (STREAM, "\t.fill %d, 1\n", (int)(SIZE)) + +/* Align output to a power of two. Note ".align 0" is redundant, + and also GAS will treat it as ".align 2" which we do not want. */ +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + do \ + { \ + if ((POWER) > 0) \ + fprintf (STREAM, "\t.align\t%d\n", POWER); \ + } \ + while (0) + + +/****************************************************************** + * Controlling the Compilation Driver * + ******************************************************************/ + + +/* Define this macro as a C expression for the initializer of an + array of string to tell the driver program which options are + defaults for this target and thus do not need to be handled + specially when using MULTILIB_OPTIONS. */ +#undef MULTILIB_DEFAULTS +#define MULTILIB_DEFAULTS \ + {"mlittle-endian", "mcpu=ck810f", "msoft-float"} + +/* Support for a compile-time default CPU, et cetera. The rules are: + --with-arch is ignored if -march or -mcpu are specified. + --with-cpu is ignored if -march or -mcpu are specified, and is overridden + by --with-arch. */ +#define OPTION_DEFAULT_SPECS \ + {"arch", "%{!march=*:%{!mcpu=*:-march=%(VALUE)}}" }, \ + {"cpu", "%{!march=*:%{!mcpu=*:-mcpu=%(VALUE)}}" }, \ + {"endian", "%{!mbig-endian:%{!mlittle-endian:-m%(VALUE)-endian}}" }, \ + {"float", "%{!msoft-float:%{!mhard-float:-m%(VALUE)-float}}" }, + + +/****************************************************************** + * Position Independent Code * + ******************************************************************/ + +/* Define the global table register. */ +#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? CSKY_GB_REGNUM : INVALID_REGNUM) + +/* Nonzero if x is a legitimate immediate operand on the target machine + when generating position-independent code. */ +#define LEGITIMATE_PIC_OPERAND_P(X) \ + csky_legitimate_pic_operand_p (X) + + +/****************************************************************** + * Controlling Debugging Information Format * + ******************************************************************/ + + +/* Define this macro if GCC should produce dwarf version 2 format debugging + output in response to the `-g' option. */ +#define DWARF2_DEBUGGING_INFO 1 + +/* Define this macro to 0 if your target supports DWARF 2 frame unwind + information, but it does not yet work with exception handling. */ +#define DWARF2_UNWIND_INFO 1 + +/* Define this if you have arranged for GCC to support + more than one format of debugging output. + The value of this macro only affects the default debugging output. */ +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Define this macro if the target’s representation + for dwarf registers used in .eh_frame or .debug_frame + is different from that used in other debug info sections. + Given a GCC hard register number, + this macro should return the .eh_frame register number.*/ +#define DWARF_FRAME_REGNUM(REG) DBX_REGISTER_NUMBER (REG) + +/* If INCOMING_RETURN_ADDR_RTX is defined & the RTL is REG, + define DWARF_FRAME_RETURN_COLUMN to DWARF_FRAME_REGNUM. */ +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (CSKY_LR_REGNUM) + +/* Use r0 and r1 to pass exception handling information. */ +#define EH_RETURN_DATA_REGNO(N) ((N) < 2 ? N : INVALID_REGNUM) + +/* How to renumber registers for dbx and gdb. */ +extern const int csky_dbx_regno[]; +#define DBX_REGISTER_NUMBER(REGNO) ((unsigned int) csky_dbx_regno[REGNO]) + + +/****************************************************************** + * Miscellaneous Parameters * + ******************************************************************/ + + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE SImode + +/* Define if operations between registers always perform the operation + on the full register even if a narrower mode is specified. */ +#define WORD_REGISTER_OPERATIONS 1 + +/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD + will either zero-extend or sign-extend. The value of this macro should + be the code that says which one of the two operations is implicitly + done, UNKNOWN if none. */ +#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 4 + +/* Shift counts are truncated to 6-bits (0 to 63) instead of the expected + 5-bits, so we can not define SHIFT_COUNT_TRUNCATED to true for this + target. */ +#define SHIFT_COUNT_TRUNCATED 0 + +#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 32, 1) + +/* The machine modes of pointers and functions. */ +#define Pmode SImode +#define FUNCTION_MODE Pmode + +/* Define this macro to be a C expression to indicate when jump-tables + should contain relative addresses. */ +#define CASE_VECTOR_PC_RELATIVE \ + (optimize_size && TARGET_CONSTANT_POOL \ + && (CSKY_TARGET_ARCH (CK802) || CSKY_TARGET_ARCH (CK801))) + +/* Return the preferred mode for an addr_diff_vec when the minimum + and maximum offset are known. */ +#define CASE_VECTOR_SHORTEN_MODE(min, max, body) \ + (min >= 0 && max < 512 \ + ? (ADDR_DIFF_VEC_FLAGS (body).offset_unsigned = 1, QImode) \ + : min >= -256 && max < 256 \ + ? (ADDR_DIFF_VEC_FLAGS (body).offset_unsigned = 0, QImode) \ + : min >= 0 && max < 8192 \ + ? (ADDR_DIFF_VEC_FLAGS (body).offset_unsigned = 1, HImode) \ + : min >= -4096 && max < 4096 \ + ? (ADDR_DIFF_VEC_FLAGS (body).offset_unsigned = 0, HImode) \ + : SImode) + +/* This is how to output an element of a case-vector that is relative. */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ + do \ + { \ + if (optimize_size && TARGET_CONSTANT_POOL \ + && (CSKY_TARGET_ARCH (CK802) || CSKY_TARGET_ARCH (CK801))) \ + { \ + switch (GET_MODE (BODY)) \ + { \ + case E_QImode: \ + asm_fprintf (STREAM, "\t.byte\t(.L%d-.L%d)/2\n", \ + VALUE, REL); \ + break; \ + case E_HImode: /* TBH */ \ + asm_fprintf (STREAM, "\t.short\t(.L%d-.L%d)/2\n", \ + VALUE, REL); \ + break; \ + case E_SImode: \ + asm_fprintf (STREAM, "\t.long\t.L%d-.L%d\n", \ + VALUE, REL); \ + break; \ + default: \ + gcc_unreachable (); \ + } \ + } \ + else \ + asm_fprintf (STREAM, "\t.long\t.L%d@GOTOFF\n", VALUE); \ + } while (0) + +/* This macro is not documented yet. + But we do need it to make jump table vector aligned. */ +#define ADDR_VEC_ALIGN(JUMPTABLE) 0 + +/* We have to undef this first to override the version from elfos.h. */ +#undef ASM_OUTPUT_CASE_LABEL +#define ASM_OUTPUT_CASE_LABEL(stream, prefix, num, table) \ + do \ + { \ + if (GET_MODE (PATTERN (table)) == SImode) \ + ASM_OUTPUT_ALIGN (stream, 2); \ + (*targetm.asm_out.internal_label) (stream, prefix, num); \ + } while (0) + +/* Make sure subsequent insns are aligned after a byte-sized jump offset + table. */ +#define ASM_OUTPUT_CASE_END(stream, num, table) \ + do \ + { \ + if (GET_MODE (PATTERN (table)) == QImode) \ + ASM_OUTPUT_ALIGN (stream, 1); \ + } while (0) + + + + +/****************************************************************** + * Trampolines for Nested Functions * + ******************************************************************/ + + +/* Length in units of the trampoline for entering a nested function. */ +#define TRAMPOLINE_SIZE (CSKY_ISA_FEATURE (2E3) ? 16 : 20) + +/* Alignment required for a trampoline in bits. */ +#define TRAMPOLINE_ALIGNMENT 32 + + +/****************************************************************** + * Describing Relative Costs of Operations * + ******************************************************************/ + + +/* Nonzero if access to memory by bytes is slow and undesirable. + For RISC chips, it means that access to memory by bytes is no + better than access by words when possible, so grab a whole word + and maybe make use of that. */ +#define SLOW_BYTE_ACCESS 0 + +/* On C-SKY, function CSE would allow use of 16-bit jsr instructions + instead of normal 32-bit calls. But it also needs a separate constant + pool entry for the function address and an instruction to load it, and + may cause additional spills due to increased register pressure, etc. + It doesn't seem like a good idea overall. */ +#define NO_FUNCTION_CSE 1 + +/* Try to generate sequences that don't involve branches, we can then use + conditional instructions. */ +#define BRANCH_COST(speed_p, predictable_p) \ + csky_default_branch_cost (speed_p, predictable_p) + +/* False if short circuit operation is preferred. */ +#define LOGICAL_OP_NON_SHORT_CIRCUIT \ + (csky_default_logical_op_non_short_circuit ()) + + +/****************************************************************** + * Generating Code for Profiling * + ******************************************************************/ + + +#define FUNCTION_PROFILER(FILE, LABELNO) + +#endif /* GCC_CSKY_H */ diff --git a/gcc/config/csky/csky.md b/gcc/config/csky/csky.md new file mode 100644 index 00000000000..4f6329d143a --- /dev/null +++ b/gcc/config/csky/csky.md @@ -0,0 +1,3798 @@ +;; Machine description for C-SKY processors. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + + +;; ------------------------------------------------------------------------ +;; Constant +;; ------------------------------------------------------------------------ + +;; Register numbering. + +(define_constants + [(CSKY_NGPR_REGS 32) + (CSKY_NPARM_REGS 4) + (CSKY_FIRST_PARM_REGNUM 0) + (CSKY_FIRST_RET_REGNUM 0) + (CSKY_FIRST_VFP_REGNUM 52) + (CSKY_LAST_VFP_REGNUM 67) + (CSKY_FIRST_HIGH_REGNUM 16) + (CSKY_LAST_HIGH_REGNUM 31) + (CSKY_FIRST_MINI_REGNUM 0) + (CSKY_LAST_MINI_REGNUM 7) + (CSKY_T0_REGNUM 12) + (CSKY_T1_REGNUM 13) + (CSKY_SP_REGNUM 14) + (CSKY_CC_REGNUM 33) + (CSKY_HI_REGNUM 34) + (CSKY_LO_REGNUM 35) + (CSKY_LR_REGNUM 15) + (CSKY_LAST_HIGH_UNFIXED_REGNUM 25) + (CSKY_GB_REGNUM 28) + (CSKY_TLS_REGNUM 31) + (CSKY_FIRST_EH_RETDATA_REGNUM 0) + (CSKY_LAST_EH_RETDATA_REGNUM 1) + (CSKY_EH_STACKADJ_REGNUM 2) + (CSKY_STACKADJUST_REGNUM 4) +]) + +;; Supported TLS relocations. + +(define_constants + [(TLS_GD32 0) + (TLS_LDM32 1) + (TLS_LDO32 2) + (TLS_IE32 3) + (TLS_LE32 4) +]) + +;; Unspec constants. + +(define_c_enum "unspec" + [ + ; Push or pop multiple operation: operand 0 is the first register, + ; subsequent registers are in parallel (use ...) expressions. + UNSPEC_PUSHPOP_MULT + + ; Represent TLS base, TLS offset, and TLS base + offset, respectively. + UNSPEC_TLS_BASE + UNSPEC_TLS_LABEL + UNSPEC_TLS + + ; PIC symbol relocations. + UNSPEC_PIC_SYMBOL_GOTPC + UNSPEC_PIC_SYMBOL_GOTPC_GRS + UNSPEC_PIC_SYMBOL_GOTOFF + UNSPEC_PIC_SYMBOL_GOT + UNSPEC_PIC_SYMBOL_PLT + UNSPEC_PIC_SYMBOL_BSR + UNSPEC_PIC_SYMBOL_GRS + + ; casesi dispatch table. + UNSPEC_CSKY_CASESI + ]) + + +(define_c_enum "unspecv" + [ + ; Used for constant pools. + VUNSPEC_ALIGN + VUNSPEC_POOL_LABEL + VUNSPEC_POOL_4 + VUNSPEC_POOL_8 + VUNSPEC_SYMBOL_REF + + ; Support for the eh_return pattern. + VUNSPEC_EH_RETURN + ]) + + +;; ------------------------------------------------------------------------ +;; Attributes +;; ------------------------------------------------------------------------ + +;; LENGTH of an instruction (in bytes). + +(define_attr "length" "" + (if_then_else (match_test "CSKY_TARGET_ARCH (CK801)") + (const_int 2) + (const_int 4))) + +;; Used for ck801 to represent whether we need to use bsr for long +;; distance jumps. If set to yes, the function will save lr in the +;; prologue. + +(define_attr "far_jump" "yes,no" (const_string "no")) + +;; Used for insn schedule. + +(define_attr "type" + "alu,load,store,cmp,branch,cbranch,addsub,alu_ix,branch_jmp,call_jsr,call" + (const_string "alu")) + + +;; ------------------------------------------------------------------------ +;; Include files +;; ------------------------------------------------------------------------ + +(include "constraints.md") +(include "predicates.md") +(include "csky_insn_fpu.md") +(include "csky_insn_dsp.md") +(include "csky_pipeline_ck801.md") +(include "csky_pipeline_ck802.md") +(include "csky_pipeline_ck803.md") +(include "csky_pipeline_ck810.md") + +;; ------------------------------------------------------------------------ +;; Mov insns +;; ------------------------------------------------------------------------ + +(define_mode_iterator QHI [QI HI]) + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " + { + rtx scratch = !can_create_pseudo_p () ? operands[0] : 0; + if (can_create_pseudo_p () && MEM_P (operands[0])) + { + operands[1] = force_reg (SImode, operands[1]); + emit_insn (gen_rtx_SET (operands[0], operands[1])); + DONE; + } + + /* Recognize the case where operand[1] is a reference to thread-local + data and load its address to a register. */ + if (csky_tls_referenced_p (operands[1])) + { + rtx tmp = operands[1]; + rtx addend = NULL; + + if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS) + { + addend = XEXP (XEXP (tmp, 0), 1); + tmp = XEXP (XEXP (tmp, 0), 0); + } + + gcc_assert (GET_CODE (tmp) == SYMBOL_REF); + gcc_assert (SYMBOL_REF_TLS_MODEL (tmp) != 0); + + tmp = csky_legitimize_tls_address (tmp, scratch); + if (addend) + { + tmp = gen_rtx_PLUS (SImode, tmp, addend); + tmp = force_operand (tmp, operands[0]); + } + operands[1] = tmp; + } + else if (flag_pic + && (CONSTANT_P (operands[1]) + || csky_symbol_mentioned_p (operands[1]) + || csky_label_mentioned_p (operands[1]))) + operands[1] = csky_legitimize_pic_address (operands[1], scratch, true); + }" +) + +;; Note that we conservatively estimate all load and store insns as having +;; a size of 4 bytes throughout even though some variants can be encoded +;; as 2-byte machine instructions. Getting more accurate instruction counts +;; would be better handled by calling into a C function than encoding it +;; as an RTL conditional here. +;; Also note that we don't count the extra space required for constant +;; pool entries here; that's handled by the constant pool entries themselves. +;; In -mno-constpool cases where we're relying on the assembler to create +;; the constant pool, we'll undercount branch lengths, but in that case the +;; assembler also handles branch relaxation as needed. It's only ck801 that +;; requires compiler cooperation when long branches are needed. + +(define_insn "*cskyv2_movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "=b,r,r,r, r, r, r,r, m,r,*y,*r,*v,*r,*v") + (match_operand:SI 1 "general_operand" " b,r,I,Un,Uc,Uo,m,miF,r,c,*r,*y,*r,*v,*v"))] + "CSKY_ISA_FEATURE (E2)" + "* return csky_output_move (insn, operands, SImode);" + [(set_attr "length" "2,4,4,4,4,8,4,4,4,4,4,4,4,4,4") + (set_attr "type" "alu,alu,alu,alu,alu,alu,load,load,store,alu,alu,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,a, a,r,r, m,r") + (match_operand:SI 1 "general_operand" "r, Up,T,m,miF,r,c"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_move (insn, operands, SImode);" + [(set_attr "length" "2,2,2,4,4,4,2") + (set_attr "type" "alu,alu,alu,load,load,store,alu")] +) + +;; Convert negative assignments to zero minus positive numbers. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "satisfies_constraint_T (operands[1])" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))] + "operands[2] = const0_rtx;" +) + +;; Convert const assignments to small number of assignments and left shift. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))] + " + { + unsigned int base, shift; + + if (!csky_shifted_imm8_constant (INTVAL (operands[1]), &base, &shift)) + FAIL; + if (shift == 0) + FAIL; + operands[1] = GEN_INT (base); + operands[2] = GEN_INT (shift); + }" +) + + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " + { + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (HImode, operands[1]); + else if (CONSTANT_P (operands[1]) + && (GET_CODE (operands[1]) != CONST_INT + || (! CSKY_CONST_OK_FOR_I (INTVAL (operands[1])) + && ! CSKY_CONST_OK_FOR_Ub (INTVAL (operands[1])) + && ! CSKY_CONST_OK_FOR_Uc (INTVAL (operands[1])))) + && ! reload_completed && ! reload_in_progress) + { + rtx reg = gen_reg_rtx (SImode); + emit_insn (gen_movsi (reg, operands[1])); + operands[1] = gen_lowpart (HImode, reg); + } + }" +) + +(define_insn "*cskyv2_movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=b,r,r,r, r, r, r,r, m,r,*y,*r,*v,*r,*v") + (match_operand:HI 1 "general_operand" " b,r,I,Un,Uc,Uo,m,miF,r,c,*r,*y,*r,*v,*v"))] + "CSKY_ISA_FEATURE (E2)" + "* return csky_output_move (insn, operands, HImode);" + [(set_attr "length" "2,4,4,4,4,8,4,4,4,4,4,4,4,4,4") + (set_attr "type" "alu,alu,alu,alu,alu,alu,load,load,store,alu,alu,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,a, a,r,r, m,r") + (match_operand:HI 1 "general_operand" "r, Up,T,m,miF,r,c"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_move (insn, operands, HImode);" + [(set_attr "length" "2,2,2,4,4,4,2") + (set_attr "type" "alu,alu,alu,load,load,store,alu")] +) + + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " + { + if (can_create_pseudo_p () && GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (QImode, operands[1]); + else if (CONSTANT_P (operands[1]) + && (GET_CODE (operands[1]) != CONST_INT + || (! CSKY_CONST_OK_FOR_I (INTVAL (operands[1])) + && ! CSKY_CONST_OK_FOR_Ub (INTVAL (operands[1])) + && ! CSKY_CONST_OK_FOR_Uc (INTVAL (operands[1])))) + && ! reload_completed && ! reload_in_progress) + { + rtx reg = gen_reg_rtx (SImode); + emit_insn (gen_movsi (reg, operands[1])); + operands[1] = gen_lowpart (QImode, reg); + } + }" +) + +(define_insn "*cskyv2_movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=b,r,r,r, r, r, r,r, m,r,*y,*r,*v,*r,*v") + (match_operand:QI 1 "general_operand" " b,r,I,Un,Uc,Uo,m,miF,r,c,*r,*y,*r,*v,*v"))] + "CSKY_ISA_FEATURE (E2)" + "* return csky_output_move (insn, operands, QImode);" + [(set_attr "length" "2,4,4,4,4,8,4,4,4,4,4,4,4,4,4") + (set_attr "type" "alu,alu,alu,alu,alu,alu,load,load,store,alu,alu,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r,a, a,r,r, m,r") + (match_operand:QI 1 "general_operand" "r, Up,T,m,miF,r,c"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_move (insn, operands, QImode);" + [(set_attr "length" "2,2,2,4,4,4,2") + (set_attr "type" "alu,alu,alu,load,load,store,alu")] +) + + +(define_expand "movdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + "if (can_create_pseudo_p () && GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (DImode, operands[1]);" +) + +;; Convert negative assignments to zero minus positive numbers. +(define_split + [(set (match_operand:QHI 0 "register_operand" "") + (match_operand:QHI 1 "const_int_operand" ""))] + "satisfies_constraint_T (operands[1])" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 4) (match_dup 3)) + (set (match_dup 0) (match_dup 5))] + " + { + int low; + + if (TARGET_BIG_ENDIAN) + low = 4 - mode_size[GET_MODE (operands[0])]; + else + low = 0; + operands[2] = const0_rtx; + if (can_create_pseudo_p ()) + operands[4] = gen_reg_rtx (SImode); + else + operands[4] = gen_rtx_REG (SImode, REGNO (operands[0])); + operands[3] = gen_rtx_PLUS (SImode, operands[4], operands[1]); + operands[5] = gen_rtx_SUBREG (GET_MODE (operands[0]), operands[4], low); + }" +) + +;; Convert const assignments to small number of assignments and left shift. +(define_split + [(set (match_operand:QHI 0 "register_operand" "") + (match_operand:QHI 1 "const_int_operand" ""))] + "" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 3) (ashift:SI (match_dup 3) (match_dup 2))) + (set (match_dup 0) (match_dup 4))] + " + { + unsigned int base, shift; + int low; + + if (!csky_shifted_imm8_constant (INTVAL (operands[1]), &base, &shift)) + FAIL; + if (shift == 0) + FAIL; + + if (TARGET_BIG_ENDIAN) + low = 4 - mode_size[GET_MODE (operands[0])]; + else + low = 0; + + operands[1] = GEN_INT (base); + operands[2] = GEN_INT (shift); + if (can_create_pseudo_p ()) + operands[3] = gen_reg_rtx (SImode); + else + operands[3] = gen_rtx_REG (SImode, REGNO (operands[0])); + operands[4] = gen_rtx_SUBREG (GET_MODE (operands[0]), operands[3], low); + }" +) + + +(define_insn "*csky_movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "=b,r,r, r,r, m,*r,*y,*v,*r,*v") + (match_operand:DI 1 "general_operand" " b,r,Ud,m,miF,r,*y,*r,*r,*v,*v"))] + "CSKY_ISA_FEATURE (E2)" + "* return csky_output_movedouble (operands, DImode);" + [(set_attr "length" "4,8,8,8,8,8,16,16,16,16,16") + (set_attr "type" "alu,alu,alu,load,load,store,alu,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,a, a,r,r, m") + (match_operand:DI 1 "general_operand" "r, Up,T,m,miF,r"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_movedouble (operands, DImode);" + [(set_attr "length" "4,4,4,8,8,8") + (set_attr "type" "alu,alu,alu,load,load,store")] +) + +;; Float mov instructions. + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " + if (GET_CODE (operands[0]) == MEM && can_create_pseudo_p ()) + operands[1] = force_reg (SFmode, operands[1]); + " +) + +;; FIXME: maybe the vreg load/stores should have their own type attr. +(define_insn "*csky_movsf_fpv2" + [(set (match_operand:SF 0 "nonimmediate_operand" "=b,r,v,r,r,r, m,Q,v,v,v") + (match_operand:SF 1 "general_operand" " b,r,r,v,m,mF,r,v,Q,v,m"))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "* return csky_output_move (insn, operands, SFmode);" + [(set_attr "length" "2,4,4,4,4,4,4,4,4,4,4") + (set_attr "type" "alu,alu,alu,alu,load,load,store,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r, m") + (match_operand:SF 1 "general_operand" " r,m,mF,r"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_move (insn, operands, SFmode);" + [(set_attr "length" "2,4,4,4") + (set_attr "type" "alu,load,load,store")] +) + +(define_insn "*csky_movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=b,r,r,r, m") + (match_operand:SF 1 "general_operand" " b,r,m,mF,r"))] + "CSKY_ISA_FEATURE (E2) && !CSKY_ISA_FEATURE (fpv2_sf)" + "* return csky_output_move (insn, operands, SFmode);" + [(set_attr "length" "2,4,4,4,4") + (set_attr "type" "alu,alu,load,load,store")] +) + + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " + if (GET_CODE (operands[0]) == MEM && can_create_pseudo_p ()) + operands[1] = force_reg (DFmode, operands[1]); + " +) + +;; FIXME: maybe the vreg load/stores should have their own type attr. +(define_insn "*csky_movdf_fpv2" + [(set (match_operand:DF 0 "nonimmediate_operand" "=b,r,v,r,r,r, m,Q,v,v,v") + (match_operand:DF 1 "general_operand" "b,r,r,v,m,mF,r,v,Q,v,m"))] + "CSKY_ISA_FEATURE (fpv2_df)" + "* return csky_output_movedouble (operands, DFmode);" + [(set_attr "length" "4,8,8,8,8,8,8,8,8,8,8") + (set_attr "type" "alu,alu,alu,alu,load,load,store,alu,alu,alu,alu")] +) + +(define_insn "*ck801_movdf" + [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,r, m") + (match_operand:DF 1 "general_operand" " r,m,mF,r"))] + "CSKY_ISA_FEATURE (E1)" + "* return csky_output_ck801_movedouble (operands, DFmode);" + [(set_attr "length" "4,8,8,8") + (set_attr "type" "alu,load,load,store")] +) + +(define_insn "*csky_movdf" + [(set (match_operand:DF 0 "nonimmediate_operand" "=b,r,r,r, m") + (match_operand:DF 1 "general_operand" " b,r,m,mF,r"))] + "CSKY_ISA_FEATURE (E2) && !CSKY_ISA_FEATURE (fpv2_df)" + "* return csky_output_movedouble (operands, DFmode);" + [(set_attr "length" "4,8,8,8,8") + (set_attr "type" "alu,alu,load,load,store")] +) + +;; The only CCmode move supported is a nop. Without this pattern, +;; CSE is unable to eliminate redundant comparisons in conditional +;; execution expressions. + +(define_insn "*movcc_nop" + [(set (reg:CC CSKY_CC_REGNUM) (reg:CC CSKY_CC_REGNUM))] + "" + "" + [(set_attr "length" "0")] +) + +;; ------------------------------------------------------------------------ +;; Conditional mov insns +;; ------------------------------------------------------------------------ + +;; Only handle integer comparisons because float and double require +;; library calls. + +(define_expand "movsicc" + [(set (match_operand 0 "register_operand" "") + (if_then_else:SI (match_operand 1 "ordered_comparison_operator" "") + (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "register_operand" "")))] + "CSKY_ISA_FEATURE (E2)" + " + { + bool invert = csky_emit_compare (GET_CODE (operands[1]), + XEXP (operands[1], 0), + XEXP (operands[1], 1)); + + if (invert) + emit_insn (gen_movf (operands[0], operands[2], operands[3])); + else + emit_insn (gen_movt (operands[0], operands[2], operands[3])); + DONE; + }") + +(define_insn "movt" + [(set (match_operand:SI 0 "register_operand" "=r, r") + (if_then_else:SI (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (match_operand:SI 1 "register_operand" "r, 0") + (match_operand:SI 2 "register_operand" "0, r")))] + "CSKY_ISA_FEATURE (E2)" + "@ + movt\t%0, %1 + movf\t%0, %2" + [(set_attr "length" "4,4")] +) + +(define_insn "movf" + [(set (match_operand:SI 0 "register_operand" "=r, r") + (if_then_else:SI (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (match_operand:SI 1 "register_operand" "r, 0") + (match_operand:SI 2 "register_operand" "0, r")))] + "CSKY_ISA_FEATURE (E2)" + "@ + movf\t%0, %1 + movt\t%0, %2" + [(set_attr "length" "4,4")] +) + +(define_expand "cstoresi4" + [(set (match_operand:SI 0 "register_operand" "") + (match_operator 1 "ordered_comparison_operator" + [(match_operand:SI 2 "csky_compare_operand" "") + (match_operand:SI 3 "nonmemory_operand" "")]))] + "" + " + { + bool invert = csky_emit_compare (GET_CODE (operands[1]), + operands[2], operands[3]); + + if (invert) + emit_insn (gen_mvcv (operands[0])); + else if (CSKY_ISA_FEATURE (E1)) + { + emit_insn (gen_movsi (operands[0], const0_rtx)); + emit_insn (gen_ck801_addc (operands[0], operands[0], operands[0])); + } + else + emit_insn (gen_mvc (operands[0])); + DONE; + }" +) + +(define_insn "mvc" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (reg:CC CSKY_CC_REGNUM) (const_int 0)))] + "CSKY_ISA_FEATURE (E2)" + "mvc\t%0" +) + +(define_insn "mvcv" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (reg:CC CSKY_CC_REGNUM) (const_int 0)))] + "" + "mvcv\t%0" +) + +;; ------------------------------------------------------------------------ +;; Arithmetic insns +;; ------------------------------------------------------------------------ + +(define_insn "abssi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (abs:SI (match_operand:SI 1 "register_operand" "r")))] + "CSKY_ISA_FEATURE (2E3)" + "abs\t%0, %1" + [(set_attr "type" "alu")] +) + +(define_insn "extvsi" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "") + (match_operand:SI 3 "const_int_operand" "")))] + "CSKY_ISA_FEATURE (2E3)" + { + operands[2] = GEN_INT (INTVAL (operands[3]) + INTVAL (operands[2]) - 1); + return \"sext\t%0, %1, %2, %3\"; + } +) + +(define_insn "insvsi" + [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "const_int_operand" "i") + (match_operand:SI 2 "const_int_operand" "i")) + (match_operand:SI 3 "register_operand" "r"))] + "CSKY_ISA_FEATURE (2E3)" + { + operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1]) - 1); + return \"ins\t%0, %3, %1, %2\"; + } +) + +(define_expand "bseti" + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operand:SI 1 "register_operand" "") + (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" ""))))] + "" + "") + +(define_insn "smart_bseti" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_operand:SI 1 "register_operand" "0") + (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" "K"))))] + "TARGET_MINI_REGISTERS" + "bseti\t%0, %2" + [(set_attr "length" "2")]) + +(define_insn "fast_bseti" + [(set (match_operand:SI 0 "register_operand" "=a,r") + (ior:SI (match_operand:SI 1 "register_operand" "0,r") + (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" "K,K"))))] + "!TARGET_MINI_REGISTERS" + "bseti\t%0, %1, %2" + [(set_attr "length" "2,4")]) + +(define_expand "bclri" + [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (not:SI (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" "")))))] + "" + "") + +(define_insn "smart_bclri" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (match_operand:SI 1 "register_operand" "0") + (not:SI (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" "K")))))] + "TARGET_MINI_REGISTERS" + "bclri\t%0, %2" + [(set_attr "length" "2")]) + +(define_insn "fast_bclri" + [(set (match_operand:SI 0 "register_operand" "=a,r") + (and:SI (match_operand:SI 1 "register_operand" "0,r") + (not:SI (ashift:SI (const_int 1) + (match_operand:SI 2 "csky_literal_K_operand" "K,K")))))] + "!TARGET_MINI_REGISTERS" + "bclri\t%0, %1, %2" + [(set_attr "length" "2,4")]) + + +;; Shift instructions. + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashift:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_K_operand" "")))] + "" + "" +) + +(define_insn "*cskyv2_ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,a,r") + (ashift:SI (match_operand:SI 1 "register_operand" "0,r,a,r") + (match_operand:SI 2 "csky_arith_K_operand" "b,r,K,K")))] + "CSKY_ISA_FEATURE (E2)" + "@ + lsl %0, %1, %2 + lsl %0, %1, %2 + lsli %0, %1, %2 + lsli %0, %1, %2" + [(set_attr "length" "2,4,2,4")] +) + +(define_insn "ck801_ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=a,r") + (ashift:SI (match_operand:SI 1 "register_operand" "a,0") + (match_operand:SI 2 "csky_arith_K_operand" "K,r")))] + "CSKY_ISA_FEATURE (E1)" + "@ + lsli %0, %1, %2 + lsl %0, %1, %2" +) + + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_K_operand" "")))] + "" + "" +) + +(define_insn "*cskyv2_ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,a,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,r,a,r") + (match_operand:SI 2 "csky_arith_K_operand" "b,r,K,K")))] + "CSKY_ISA_FEATURE (E2)" + "@ + asr %0, %1, %2 + asr %0, %1, %2 + asri %0, %1, %2 + asri %0, %1, %2" + [(set_attr "length" "2,4,2,4")] +) + +(define_insn "*ck801_ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=a,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "a,0") + (match_operand:SI 2 "csky_arith_K_operand" "K,r")))] + "CSKY_ISA_FEATURE (E1)" + "@ + asri %0, %1, %2 + asr %0, %1, %2" +) + + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_K_operand" "")))] + "" + "" +) + +(define_insn "*cskyv2_lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,a,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,r,a,r") + (match_operand:SI 2 "csky_arith_K_operand" "b,r,K,K")))] + "CSKY_ISA_FEATURE (E2)" + "@ + lsr %0, %1, %2 + lsr %0, %1, %2 + lsri %0, %1, %2 + lsri %0, %1, %2" + [(set_attr "length" "2,4,2,4")] +) + +(define_insn "ck801_lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=a,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "a,0") + (match_operand:SI 2 "csky_arith_K_operand" "K,r")))] + "CSKY_ISA_FEATURE (E1)" + "@ + lsri %0, %1, %2 + lsr %0, %1, %2" +) + + +(define_expand "rotlsi3" + [(set (match_operand:SI 0 "register_operand" "") + (rotate:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_K_operand" "")))] + "" + "" +) + +(define_insn "*cskyv2_rotlsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,r") + (rotate:SI (match_operand:SI 1 "register_operand" "0,r,r") + (match_operand:SI 2 "csky_arith_K_operand" "b,r,K")))] + "CSKY_ISA_FEATURE (E2)" + "@ + rotl %0, %1, %2 + rotl %0, %1, %2 + rotli %0, %1, %2" + [(set_attr "length" "2,4,4")] +) + +(define_insn "*ck801_rotlsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (rotate:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "csky_arith_K_operand" "r")))] + "CSKY_ISA_FEATURE (E1)" + "rotl %0, %1, %2" +) + + +;; Add instructions. +;; C-SKY addi and subi machine instructions only accept positive immediate +;; values, so we have to special case immediates <= 0 in these patterns. + +(define_expand "addsi3" + [(set (match_operand:SI 0 "register_operand" "") + (plus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")))] + "" + "" +) + +(define_insn "smart_addsi3" + [(set (match_operand:SI 0 "register_operand" "=a,r,a,a,a,a, r,r") + (plus:SI (match_operand:SI 1 "register_operand" "%a,0,0,a,0,a, r,r") + (match_operand:SI 2 "nonmemory_operand" "a, r,N,L,T,Us,M,Um")))] + "TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2) + && operands[0] != stack_pointer_rtx + && operands[1] != stack_pointer_rtx" + "@ + addu\t%0, %1, %2 + addu\t%0, %1, %2 + addi\t%0, %1, %2 + addi\t%0, %1, %2 + subi\t%0, %1, %M2 + subi\t%0, %1, %M2 + addi\t%0, %1, %2 + subi\t%0, %1, %M2" + [(set_attr "length" "2,2,2,2,2,2,4,4") + (set_attr "type" "addsub")] +) + +(define_insn_and_split "*smart_addsi3_sp" + [(set (match_operand:SI 0 "register_operand" "=z,z, z,a,&a,z,a,r") + (plus:SI (match_operand:SI 1 "register_operand" "0, 0, 0,z, z,a,z,r") + (match_operand:SI 2 "nonmemory_operand" "P, Ug,r,Uq,i,a,a,M")))] + "TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2) + && (operands[0] == stack_pointer_rtx || operands[1] == stack_pointer_rtx)" + "@ + addi\t%0, %1, %2 + subi\t%0, %1, %M2 + addu\t%0, %1, %2 + addi\t%0, %1, %2 + # + addu\t%0, %1, %2 + addu\t%0, %1, %2 + addi\t%0, %1, %2" + "(operands[0] != stack_pointer_rtx + && operands[1] == stack_pointer_rtx + && !satisfies_constraint_Uq (operands[2]))" + [(set (match_dup 0) + (plus:SI (match_dup 1) (match_dup 0)))] + "emit_move_insn (operands[0], operands[2]);" + [(set_attr "type" "addsub")] +) + +(define_insn "*ck801_addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,a,a,a,a,a, !z,!z,!z,a") + (plus:SI (match_operand:SI 1 "register_operand" "%0,a,0,a,0,a, 0, 0, 0, !z") + (match_operand:SI 2 "nonmemory_operand" "r, a,N,L,T,Us,P, Ug,r, Uq")))] + "CSKY_ISA_FEATURE (E1)" + "@ + addu\t%0, %1, %2 + addu\t%0, %1, %2 + addi\t%0, %1, %2 + addi\t%0, %1, %2 + subi\t%0, %1, %M2 + subi\t%0, %1, %M2 + addi\t%0, %1, %2 + subi\t%0, %1, %M2 + addu\t%0, %1, %2 + addi\t%0, %1, %2" + [(set_attr "type" "addsub")] +) + +(define_insn "fast_addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r, r") + (plus:SI (match_operand:SI 1 "register_operand" "%r,r, r") + (match_operand:SI 2 "nonmemory_operand" "M, Um,r")))] + "!TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2)" + "@ + addi\t%0, %1, %2 + subi\t%0, %1, %M2 + addu\t%0, %1, %2" + [(set_attr "type" "addsub")] +) + +(define_expand "adddi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "csky_arith_int1_operand" ""))) + (clobber (reg:CC CSKY_CC_REGNUM))])] + "" + " + if (CSKY_ISA_FEATURE (E1) && (GET_CODE (operands[2]) != REG)) + operands[2] = force_reg (DImode, operands[2]); + " +) + +/* Note that the csky addc instruction both reads and writes the carry bit. + The purpose of the initial cmplt instruction in the expansion is to + clear the carry bit before adding the lo words. */ + +(define_insn_and_split "*cskyv2_adddi3" + [(set (match_operand:DI 0 "register_operand" "=b,&r") + (plus:DI (match_operand:DI 1 "register_operand" "%0,r") + (match_operand:DI 2 "register_operand" "b, r"))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cmpltsi_r (copy_rtx (l1), copy_rtx (l1))); + emit_insn (gen_cskyv2_addc (l0, l1, l2)); + emit_insn (gen_cskyv2_addc (h0, h1, h2)); + DONE; + } + [(set_attr "length" "6,12")] +) + +(define_insn_and_split "*ck801_adddi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r"))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E1)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cmpltsi_r (copy_rtx (l1), copy_rtx (l1))); + emit_insn (gen_ck801_addc (l0, l1, l2)); + emit_insn (gen_ck801_addc (h0, h1, h2)); + DONE; + } + [(set_attr "length" "6")] +) + +;; Special case for "longlong += 1". + +(define_insn_and_split "*cskyv2_adddi1_1" + [(set (match_operand:DI 0 "register_operand" "=&r") + (plus:DI (match_operand:DI 1 "register_operand" "0") + (const_int 1))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + + if (TARGET_MINI_REGISTERS) + { + emit_insn (gen_smart_addsi3 (l0, copy_rtx (l0), + gen_int_mode (1, SImode))); + emit_insn (gen_smart_cmpnesi_i (copy_rtx (l0), + gen_int_mode (0, SImode))); + emit_insn (gen_cskyv2_addcc_invert (h0, copy_rtx (h0), + gen_int_mode (1, SImode))); + } + else + { + emit_insn (gen_fast_addsi3 (l0, copy_rtx (l0), + gen_int_mode (1, SImode))); + emit_insn (gen_fast_cmpnesi_i (copy_rtx (l0), + gen_int_mode (0, SImode))); + emit_insn (gen_cskyv2_addcc_invert (h0, copy_rtx (h0), + gen_int_mode (1, SImode))); + } + DONE; + } + [(set (attr "length") + (if_then_else (match_test "TARGET_MINI_REGISTERS") + (const_int 8) + (const_int 12)))] +) + +;; sub instructions. + +(define_expand "subsi3" + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")))] + "" + "" +) + +(define_insn "smart_subsi3" + [(set (match_operand:SI 0 "register_operand" "=a,a,a,a,a,a") + (minus:SI (match_operand:SI 1 "register_operand" "a, 0,0,a,0,a") + (match_operand:SI 2 "nonmemory_operand" "a, a,N,L,T,Us")))] + "TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2) + && operands[0] != stack_pointer_rtx + && operands[1] != stack_pointer_rtx" + "@ + subu\t%0, %1, %2 + subu\t%0, %1, %2 + subi\t%0, %1, %2 + subi\t%0, %1, %2 + addi\t%0, %1, %M2 + addi\t%0, %1, %M2" + [(set_attr "length" "2,2,2,2,2,2") + (set_attr "type" "addsub")] +) + +(define_insn "*smart_subsi3_sp" + [(set (match_operand:SI 0 "register_operand" "=z,z, z,a, a,r") + (minus:SI (match_operand:SI 1 "register_operand" "0, 0, 0,z, a,r") + (match_operand:SI 2 "nonmemory_operand" "P, Ug,a,Ur,a,M")))] + "TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2) + && (operands[0] == stack_pointer_rtx || operands[1] == stack_pointer_rtx)" + "@ + subi\t%0, %1, %2 + addi\t%0, %1, %M2 + subu\t%0, %1, %2 + addi\t%0, %1, %M2 + subu\t%0, %1, %2 + subi\t%0, %1, %2" + [(set_attr "length" "2,2,2,2,2,4") + (set_attr "type" "addsub")] +) + +(define_insn "*ck801_subsi3" + [(set (match_operand:SI 0 "register_operand" "=a,a,a,a,a,a") + (minus:SI (match_operand:SI 1 "register_operand" "0, a,0,a,0,a") + (match_operand:SI 2 "nonmemory_operand" "a, a,N,L,T,Us")))] + "CSKY_ISA_FEATURE (E1) + && operands[0] != stack_pointer_rtx + && operands[1] != stack_pointer_rtx" + "@ + subu\t%0, %1, %2 + subu\t%0, %1, %2 + subi\t%0, %1, %2 + subi\t%0, %1, %2 + addi\t%0, %1, %M2 + addi\t%0, %1, %M2" + [(set_attr "type" "addsub")] +) + +(define_insn "*ck801_subsi3_sp" + [(set (match_operand:SI 0 "register_operand" "=a,z,z, z") + (minus:SI (match_operand:SI 1 "register_operand" "z, 0,0, 0") + (match_operand:SI 2 "nonmemory_operand" "Ur,P,Ug,r")))] + "CSKY_ISA_FEATURE (E1) + && (operands[0] == stack_pointer_rtx || operands[1] == stack_pointer_rtx)" + "@ + addi\t%0, %1, %M2 + subi\t%0, %1, %2 + addi\t%0, %1, %M2 + subu\t%0, %1, %2" + [(set_attr "type" "addsub")] +) + +(define_insn "fast_subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (minus:SI (match_operand:SI 1 "register_operand" "r, r,r") + (match_operand:SI 2 "nonmemory_operand" "r, M,Um")))] + "!TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2)" + "@ + subu\t%0, %1, %2 + subi\t%0, %1, %2 + addi\t%0, %1, %M2" + [(set_attr "type" "addsub")] +) + +(define_expand "subdi3" + [(parallel [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (reg:CC CSKY_CC_REGNUM))])] + "" + "" +) + +/* Note that the csky subc instruction both reads and writes the C bit. + The purpose of the initial cmphs instruction in the expansion is to + set the C bit before subtracting the lo words. */ + +(define_insn_and_split "*cskyv2_subdi3" + [(set (match_operand:DI 0 "register_operand" "=b,&r") + (minus:DI (match_operand:DI 1 "register_operand" "0, r") + (match_operand:DI 2 "register_operand" "b, r"))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cmpgeusi_r (copy_rtx (l1), copy_rtx (l1))); + emit_insn (gen_cskyv2_subc (l0, l1, l2)); + emit_insn (gen_cskyv2_subc (h0, h1, h2)); + DONE; + } + [(set_attr "length" "6,12")] +) + +(define_insn_and_split "*ck801_subdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "register_operand" "r"))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E1)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cmpgeusi_r (copy_rtx (l1), copy_rtx (l1))); + emit_insn (gen_ck801_subc (l0, l1, l2)); + emit_insn (gen_ck801_subc (h0, h1, h2)); + DONE; + } + [(set_attr "length" "6")] +) + +;; Special case for "longlong -= 1". + +(define_insn_and_split "*cskyv2_subdi1_1" + [(set (match_operand:DI 0 "register_operand" "=&r") + (plus:DI (match_operand:DI 1 "register_operand" "0") + (const_int -1))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + + if (TARGET_MINI_REGISTERS) + { + emit_insn (gen_smart_cmpnesi_i (copy_rtx (l0), + gen_int_mode (0, SImode))); + emit_insn (gen_cskyv2_addcc_invert (h0, copy_rtx (h0), + gen_int_mode (-1, SImode))); + emit_insn (gen_smart_subsi3 (l0, copy_rtx (l0), + gen_int_mode (1, SImode))); + } + else + { + emit_insn (gen_fast_cmpnesi_i (copy_rtx (l0), + gen_int_mode (0, SImode))); + emit_insn (gen_cskyv2_addcc_invert (h0, copy_rtx (h0), + gen_int_mode (-1, SImode))); + emit_insn (gen_fast_subsi3 (l0, copy_rtx (l0), + gen_int_mode (1, SImode))); + } + DONE; + } + [(set (attr "length") + (if_then_else (match_test "TARGET_MINI_REGISTERS") + (const_int 8) + (const_int 12)))] +) + +;; Add with carry. + +(define_insn "cskyv2_addc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (ne:SI (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "register_operand" "r,r")))) + (set (reg:CC CSKY_CC_REGNUM) + (compare:CC + (plus:SI (match_dup 1) (match_dup 2)) + (match_dup 1)))] + "CSKY_ISA_FEATURE (E2)" + "addc\t%0, %1, %2" + [(set_attr "length" "2,4") + (set_attr "type" "addsub")] +) + +(define_insn "ck801_addc" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (ne:SI (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "r")))) + (set (reg:CC CSKY_CC_REGNUM) + (compare:CC + (plus:SI (match_dup 1) (match_dup 2)) + (match_dup 1)))] + "CSKY_ISA_FEATURE (E1)" + "addc\t%0, %1, %2" + [(set_attr "length" "2") + (set_attr "type" "addsub")] +) + +;; Subtract with borrow. +;; Note that in these insns, the sense of C bit is reversed; they subtract 1 +;; if the C bit is not set, and on output the bit is set to 0 for borrow +;; and 1 for no borrow. + +(define_insn "cskyv2_subc" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "register_operand" "0, r") + (plus:SI (match_operand:SI 2 "register_operand" "r, r") + (eq:SI (reg:CC CSKY_CC_REGNUM) (const_int 0))))) + (set (reg:CC CSKY_CC_REGNUM) + (not (compare:CC (match_dup 1) (match_dup 2))))] + "CSKY_ISA_FEATURE (E2)" + "subc\t%0, %1, %2" + [(set_attr "length" "2,4") + (set_attr "type" "addsub")] +) + +(define_insn "ck801_subc" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (plus:SI (match_operand:SI 2 "register_operand" "r") + (eq:SI (reg:CC CSKY_CC_REGNUM) (const_int 0))))) + (set (reg:CC CSKY_CC_REGNUM) + (not (compare:CC (match_dup 1) (match_dup 2))))] + "CSKY_ISA_FEATURE (E1)" + "subc\t%0, %1, %2" + [(set_attr "length" "2") + (set_attr "type" "addsub")] +) + +;; ------------------------------------------------------------------------ +;; Multiplication insns +;; ------------------------------------------------------------------------ + +(define_expand "mulsi3" + [(set (match_operand:SI 0 "register_operand" "") + (mult:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" "")))] + "" + "" +) + +(define_insn "*cskyv2_mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E2)" + "mult\t%0, %1, %2" +) + +(define_insn "*ck801_mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E1)" + "mult\t%0, %1, %2" +) + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%r")) + (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "CSKY_ISA_FEATURE (2E3)" + "mulsh\t%0, %1, %2" +) + + +;; ------------------------------------------------------------------------ +;; Conditional add insns +;; ------------------------------------------------------------------------ + +(define_expand "addsicc" + [(match_operand:SI 0 "register_operand" "") + (match_operand 1 "ordered_comparison_operator" "") + (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "csky_literal_K_Uh_operand" "")] + "CSKY_ISA_FEATURE (E2)" + " + { + bool invert = csky_emit_compare (GET_CODE (operands[1]), + XEXP (operands[1], 0), + XEXP (operands[1], 1)); + if (invert) + emit_insn (gen_cskyv2_addcc_invert (operands[0], operands[2], + operands[3])); + else + emit_insn (gen_cskyv2_addcc (operands[0], operands[2], operands[3])); + + DONE; + }" +) + +(define_insn_and_split "cskyv2_addcc" + [(set (match_operand:SI 0 "register_operand" "=r,r,&r,&r") + (if_then_else:SI + (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_operand:SI 1 "register_operand" "0,0,r,r") + (match_operand:SI 2 "csky_literal_K_Uh_operand" "K,Uh,K,Uh")) + (match_dup 1)))] + "CSKY_ISA_FEATURE (E2)" + "@ + inct\t%0, %1, %2 + dect\t%0, %1, %M2 + # + #" + "reload_completed && !rtx_equal_p (operands[0], operands[1])" + [(set (match_dup 0) + (if_then_else:SI (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_dup 0) (match_dup 2))))] + { + emit_insn (gen_movf (copy_rtx (operands[0]), + copy_rtx (operands[1]), + copy_rtx (operands[0]))); + } + [(set_attr "length" "4,4,8,8") + (set_attr "type" "addsub")] +) + +(define_insn_and_split "cskyv2_addcc_invert" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (if_then_else:SI + (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_operand:SI 1 "register_operand" "0,0,r,r") + (match_operand:SI 2 "csky_literal_K_Uh_operand" "K,Uh,K,Uh")) + (match_dup 1)))] + "CSKY_ISA_FEATURE (E2)" + "@ + incf\t%0, %1, %2 + decf\t%0, %1, %M2 + # + #" + "reload_completed && !rtx_equal_p (operands[0], operands[1])" + [(set (match_dup 0) + (if_then_else:SI (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (plus:SI (match_dup 0) (match_dup 2))))] + { + emit_insn (gen_movt (copy_rtx (operands[0]), + copy_rtx (operands[1]), + copy_rtx (operands[0]))); + } + [(set_attr "length" "4,4,8,8") + (set_attr "type" "addsub")] +) + + +;; ------------------------------------------------------------------------ +;; Extzv insns +;; ------------------------------------------------------------------------ + +(define_expand "extzvsi" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extract:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "") + (match_operand:SI 3 "const_int_operand" "")))] + "" + "{ + /* ck802 has xtrb but not zext, so we'll use xtrb if we can. */ + if (CSKY_ISA_FEATURE (E2) && !CSKY_ISA_FEATURE (2E3) + && (INTVAL (operands[2]) == 8) + && (INTVAL (operands[3]) % 8 == 0)) + { + rtx xtrb = gen_rtx_SET (operands[0], + gen_rtx_ZERO_EXTRACT (SImode, + operands[1], + operands[2], + operands[3])); + emit (gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, xtrb, + gen_hard_reg_clobber (CCmode, 33)))); + DONE; + } + else if (!CSKY_ISA_FEATURE (2E3)) + { + /* Use lsri and lsli to do extzv on targets without zext. */ + rtx lshft = GEN_INT (32 - (INTVAL (operands[2]) + + INTVAL (operands[3]))); + rtx rshft = GEN_INT (32 - INTVAL (operands[2])); + rtx tmp1 = gen_reg_rtx (SImode); + rtx tmp2 = gen_reg_rtx (SImode); + + emit_insn (gen_rtx_SET (tmp1, operands[1])); + emit_insn (gen_rtx_SET (tmp2, gen_rtx_ASHIFT (SImode, tmp1, lshft))); + emit_insn (gen_rtx_SET (operands[0], + gen_rtx_LSHIFTRT (SImode, tmp2, rshft))); + DONE; + } + else + { + emit_insn (gen_cskyv2_extzv (operands[0], operands[1], + operands[2], operands[3])); + DONE; + } +}") + +(define_insn "cskyv2_extzv" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "csky_literal_K_operand" "K") + (match_operand:SI 3 "csky_literal_K_operand" "K")))] + "CSKY_ISA_FEATURE (2E3)" + { + operands[2] = GEN_INT (INTVAL (operands[3]) + INTVAL (operands[2]) - 1); + return \"zext\t%0, %1, %2, %3\"; + } +) + +(define_insn "*cskyv2_xtrb0" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "0,r") + (const_int 8) + (const_int 24))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "@ + lsri\t%0, %0, 24 + xtrb0\t%0, %1" +) + +(define_insn "*cskyv2_xtrb1" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (const_int 8) + (const_int 16))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "xtrb1\t%0, %1" +) + +(define_insn "*cskyv2_xtrb2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (const_int 8) + (const_int 8))) + (clobber (reg:CC CSKY_CC_REGNUM))] + "CSKY_ISA_FEATURE (E2)" + "xtrb2\t%0, %1" +) + + +;; ------------------------------------------------------------------------- +;; Zero extension instructions +;; ------------------------------------------------------------------------- + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "register_operand" "r")))] + "" + "zexth\t%0, %1" +) + +(define_insn "*cskyv2_zextend_ldh" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "csky_simple_mem_operand" "m")))] + "" + "ld.h\t%0, %1" + [(set_attr "length" "4") + (set_attr "type" "load")] +) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:QI 1 "register_operand" "r")))] + "" + "zextb\t%0, %1" +) + +(define_insn "*cskyv2_zextend_ldb" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:QI 1 "csky_simple_mem_operand" "m")))] + "" + "ld.b\t%0, %1" + [(set_attr "length" "4") + (set_attr "type" "load")] +) + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (match_operand:QI 1 "register_operand" "r")))] + "" + "zextb\t%0, %1" +) + +(define_insn "*cskyv2_zextend_ldbhi" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (match_operand:QI 1 "csky_simple_mem_operand" "m")))] + "" + "ld.b\t%0, %1" + [(set_attr "length" "4") + (set_attr "type" "load")] +) + +;; ------------------------------------------------------------------------- +;; clzm2 instructions +;; ------------------------------------------------------------------------- + +(define_insn "clzsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (clz:SI (match_operand:SI 1 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E2)" + "ff1 %0,%1" +) + +;; ------------------------------------------------------------------------- +;; one_cmplm2 instructions +;; ------------------------------------------------------------------------- + +(define_expand "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "") + (not:SI (match_operand:SI 1 "register_operand" "")))] + "" + "" +) + +(define_insn "cskyv2_one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=b,r") + (not:SI (match_operand:SI 1 "register_operand" "0,r")))] + "CSKY_ISA_FEATURE (E2)" + "not %0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "alu,alu")] +) + +(define_insn "ck801_one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "0")))] + "CSKY_ISA_FEATURE (E1)" + "not %0, %1" + [(set_attr "length" "2") + (set_attr "type" "alu")] +) + +;; ------------------------------------------------------------------------- +;; Sign extension instructions +;; ------------------------------------------------------------------------- + +;; One test shows that the following code helps to +;; reduce one 'load' and two 'mov'. +(define_expand "extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r"))] + "" + "{ + int low, high; + + if (TARGET_BIG_ENDIAN) + low = 4, high = 0; + else + low = 0, high = 4; + + emit_insn (gen_rtx_SET (gen_rtx_SUBREG (SImode, operands[0], low), + operands[1])); + + emit_insn (gen_rtx_SET (gen_rtx_SUBREG (SImode, operands[0], high), + gen_rtx_ASHIFTRT (SImode, + gen_rtx_SUBREG (SImode, + operands[0], + low), + GEN_INT (31)))); + DONE; + }" +) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "register_operand" "r")))] + "" + "sexth %0, %1" +) + +(define_insn "*cskyv2_sextend_ldhs" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "csky_simple_mem_operand" "m")))] + "CSKY_ISA_FEATURE (E2)" + "ld.hs\t%0, %1" + [(set_attr "length" "4") + (set_attr "type" "load")] +) + +;; qi -> si +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "register_operand" "r")))] + "" + "sextb %0, %1" +) + +;; qi -> hi +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI (match_operand:QI 1 "register_operand" "r")))] + "" + "sextb %0, %1" +) + +;; ------------------------------------------------------------------------- +;; And instructions +;; ------------------------------------------------------------------------- + +(define_expand "andsi3" + [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_any_imm_operand" "")))] + "" + "") + +(define_insn_and_split "cskyv2_andsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,r,&r") + (and:SI (match_operand:SI 1 "register_operand" "%0,r,r,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "b,r,O,i")))] + "CSKY_ISA_FEATURE (E2)" + "@ + and\t%0, %1, %2 + and\t%0, %1, %2 + andi\t%0, %1, %2 + #" + "(CONST_INT_P (operands[2]) + && (operands[2] == const0_rtx + || !csky_arith_O_operand (operands[2], SImode)))" + [(set (match_dup 0) (and:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_and (operands)) + DONE; + } + [(set_attr "length" "2,4,4,8") + (set_attr "type" "alu,alu,alu,alu")] +) + +(define_insn_and_split "ck801_andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,&r") + (and:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "r,i")))] + "CSKY_ISA_FEATURE (E1)" + "@ + and\t%0, %1, %2 + #" + "CONST_INT_P (operands[2])" + [(set (match_dup 0) (and:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_and (operands)) + DONE; + } + [(set_attr "length" "2,4") + (set_attr "type" "alu,alu")] +) + +;; Note that the operands for the andnsi3 patterns are reversed compared +;; to the actual machine insn: operand 1 is the inverted operand. + +(define_insn "cskyv2_andnsi3" + [(use (and:SI (not:SI (match_operand:SI 1 "csky_arith_O_operand" "b,r,O")) + (match_operand:SI 2 "register_operand" "0,r,r"))) + (set (match_operand:SI 0 "register_operand" "=b,r,r") + (and:SI (not:SI (match_dup 1)) + (match_dup 2)))] + "CSKY_ISA_FEATURE (E2)" + "@ + andn\t%0, %2, %1 + andn\t%0, %2, %1 + andni\t%0, %2, %1" + [(set_attr "length" "2,4,4") + (set_attr "type" "alu,alu,alu")] +) + +(define_insn "ck801_andnsi3" + [(use (and:SI (not:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "0"))) + (set (match_operand:SI 0 "register_operand" "=r") + (and:SI (not:SI (match_dup 1)) + (match_dup 2)))] + "CSKY_ISA_FEATURE (E1)" + "andn\t%0, %2, %1" +) + +(define_expand "anddi3" + [(set (match_operand:DI 0 "register_operand" "") + (and:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "csky_arith_any_imm_operand" "")))] + "" + " + { + if (CONST_INT_P (operands[2])) + { + HOST_WIDE_INT ival = INTVAL (operands[2]); + if (ival == (HOST_WIDE_INT) 0xffffffff) + { + emit_move_insn (gen_lowpart (SImode, operands[0]), + gen_lowpart (SImode, operands[1])); + emit_move_insn (gen_highpart (SImode, operands[0]), const0_rtx); + DONE; + } + else if (ival == (HOST_WIDE_INT) ((unsigned HOST_WIDE_INT) -1 << 32)) + { + emit_move_insn (gen_lowpart (SImode, operands[0]), const0_rtx); + emit_move_insn (gen_highpart (SImode, operands[0]), + gen_highpart (SImode, operands[1])); + DONE; + } + else + FAIL; + } + }") + + + +(define_insn_and_split "*cskyv2_anddi3" + [(set (match_operand:DI 0 "register_operand" "=&b,&r") + (and:DI (match_operand:DI 1 "register_operand" "%0,r") + (match_operand:DI 2 "register_operand" "b,r")))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cskyv2_andsi3 (l0, l1, l2)); + emit_insn (gen_cskyv2_andsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4,8")] +) + +(define_insn_and_split "*ck801_anddi3" + [(set (match_operand:DI 0 "register_operand" "=&r") + (and:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E1)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_ck801_andsi3 (l0, l1, l2)); + emit_insn (gen_ck801_andsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4")] +) + + +;; ------------------------------------------------------------------------- +;; Ior instructions +;; ------------------------------------------------------------------------- + +(define_expand "iorsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_any_imm_operand" "")))] + "" + "") + +(define_insn_and_split "cskyv2_iorsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,r,&r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,r,r,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "b, r,I,i")))] + "CSKY_ISA_FEATURE (E2)" + "@ + or\t%0, %1, %2 + or\t%0, %1, %2 + ori\t%0, %1, %2 + #" + "(CONST_INT_P (operands[2]) + && (operands[2] == const0_rtx + || !csky_literal_I_operand (operands[2], SImode)))" + [(set (match_dup 0) (ior:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_ior (operands)) + DONE; + } + [(set_attr "length" "2,4,4,8") + (set_attr "type" "alu,alu,alu,alu")] +) + +(define_insn_and_split "ck801_iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,&r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "r,i")))] + "CSKY_ISA_FEATURE (E1)" + "@ + or\t%0, %1, %2 + #" + "CONST_INT_P (operands[2])" + [(set (match_dup 0) (ior:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_ior (operands)) + DONE; + } + [(set_attr "length" "2,4") + (set_attr "type" "alu,alu")] +) + +(define_expand "iordi3" + [(set (match_operand:DI 0 "register_operand" "") + (ior:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" "")))] + "" + "" +) + +(define_insn_and_split "*cskyv2_iordi3" + [(set (match_operand:DI 0 "register_operand" "=&b,&r") + (ior:DI (match_operand:DI 1 "register_operand" "%0, r") + (match_operand:DI 2 "register_operand" "b, r")))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cskyv2_iorsi3 (l0, l1, l2)); + emit_insn (gen_cskyv2_iorsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4,8")] +) + +(define_insn_and_split "*ck801_iordi3" + [(set (match_operand:DI 0 "register_operand" "=&r") + (ior:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E1)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_ck801_iorsi3 (l0, l1, l2)); + emit_insn (gen_ck801_iorsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4")] +) + + +;; ------------------------------------------------------------------------- +;; Xor instructions +;; ------------------------------------------------------------------------- + +(define_expand "xorsi3" + [(set (match_operand:SI 0 "register_operand" "") + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "csky_arith_any_imm_operand" "")))] + "" + "") + +(define_insn_and_split "cskyv2_xorsi3" + [(set (match_operand:SI 0 "register_operand" "=b,r,r,&r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,r,r,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "b, r,O,i")))] + "CSKY_ISA_FEATURE (E2)" + "@ + xor\t%0, %1, %2 + xor\t%0, %1, %2 + xori\t%0, %1, %2 + #" + "(CONST_INT_P (operands[2]) + && (operands[2] == const0_rtx + || !csky_arith_O_operand (operands[2], SImode)))" + [(set (match_dup 0) (xor:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_xor (operands)) + DONE; + } + [(set_attr "length" "2,4,4,8") + (set_attr "type" "alu,alu,alu,alu")] +) + +(define_insn_and_split "ck801_xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,&r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "csky_arith_any_imm_operand" "r,i")))] + "CSKY_ISA_FEATURE (E1)" + "@ + xor\t%0, %1, %2 + #" + "CONST_INT_P (operands[2])" + [(set (match_dup 0) (xor:SI (match_dup 1) (match_dup 2)))] + { + if (csky_split_xor (operands)) + DONE; + } + [(set_attr "length" "2,4") + (set_attr "type" "alu,alu")] +) + +(define_expand "xordi3" + [(set (match_operand:DI 0 "register_operand" "") + (xor:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" "")))] + "" + "" +) + +(define_insn_and_split "*cskyv2_xordi3" + [(set (match_operand:DI 0 "register_operand" "=&b,&r") + (xor:DI (match_operand:DI 1 "register_operand" "%0, r") + (match_operand:DI 2 "register_operand" "b, r")))] + "CSKY_ISA_FEATURE (E2)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_cskyv2_xorsi3 (l0, l1, l2)); + emit_insn (gen_cskyv2_xorsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4,8")] +) + +(define_insn_and_split "*ck801_xordi3" + [(set (match_operand:DI 0 "register_operand" "=&r") + (xor:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E1)" + "#" + "reload_completed" + [(const_int 0)] + { + int hi = TARGET_BIG_ENDIAN ? 0 : UNITS_PER_WORD; + int lo = TARGET_BIG_ENDIAN ? UNITS_PER_WORD : 0; + rtx l0 = simplify_gen_subreg (SImode, operands[0], DImode, lo); + rtx h0 = simplify_gen_subreg (SImode, operands[0], DImode, hi); + rtx l1 = simplify_gen_subreg (SImode, operands[1], DImode, lo); + rtx h1 = simplify_gen_subreg (SImode, operands[1], DImode, hi); + rtx l2 = simplify_gen_subreg (SImode, operands[2], DImode, lo); + rtx h2 = simplify_gen_subreg (SImode, operands[2], DImode, hi); + + emit_insn (gen_ck801_xorsi3 (l0, l1, l2)); + emit_insn (gen_ck801_xorsi3 (h0, h1, h2)); + DONE; + } + [(set_attr "length" "4")] +) + +;; ------------------------------------------------------------------------- +;; Div instructions +;; ------------------------------------------------------------------------- + +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (div:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")))] + "TARGET_DIV" + "divs\t%0, %1, %2" +) + +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (udiv:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")))] + "TARGET_DIV" + "divu\t%0, %1, %2" +) + + +;; ----------------------------------------------------------------- +;; Multiple load and store insns +;; ----------------------------------------------------------------- + +(define_expand "load_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "" "") + (match_operand:SI 1 "" "")) + (use (match_operand:SI 2 "" ""))])] + "TARGET_MULTIPLE_STLD" + "{ + int regno, count, i; + rtx base,src; + + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) < 2 + || INTVAL (operands[2]) > CSKY_MAX_MULTIPLE_STLD + || GET_CODE (operands[1]) != MEM + || !REG_P (XEXP (operands[1], 0)) + || XEXP (operands[1], 0) != stack_pointer_rtx + || GET_CODE (operands[0]) != REG + || (REGNO (XEXP (operands[1], 0)) > REGNO (operands[0]) + && (REGNO (XEXP (operands[1], 0)) + < REGNO (operands[0]) + INTVAL (operands[2])))) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[0]); + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + + base = force_reg (SImode, XEXP (operands[1], 0)); + src = replace_equiv_address (operands[1], base); + + for (i = 0; i < count; i++) + XVECEXP (operands[3], 0, i) + = gen_rtx_SET (gen_rtx_REG (SImode, regno + i), + adjust_address_nv (src, SImode, i * 4)); + }" +) + +(define_expand "store_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "") + (match_operand:SI 1 "")) + (use (match_operand:SI 2 ""))])] + "TARGET_MULTIPLE_STLD" + "{ + int regno, count, i; + rtx base, dest; + + /* Support only storing a constant number of registers to memory and + only if at least two registers. */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) < 2 + || INTVAL (operands[2]) > CSKY_MAX_MULTIPLE_STLD + || GET_CODE (operands[0]) != MEM + || !REG_P (XEXP (operands[0], 0)) + || XEXP (operands[0], 0) != stack_pointer_rtx + || GET_CODE (operands[1]) != REG + || (REGNO (XEXP (operands[0], 0)) >= REGNO (operands[1]) + && (REGNO (XEXP (operands[0], 0)) + < REGNO (operands[1]) + INTVAL (operands[2])))) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[1]); + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + + base = force_reg (SImode, XEXP (operands[0], 0)); + dest = replace_equiv_address (operands[0], base); + + for (i = 0; i < count; i++) + XVECEXP (operands[3], 0, i) + = gen_rtx_SET (adjust_address_nv (dest, SImode, i * 4), + gen_rtx_REG (SImode, regno + i)); + }" +) + + +(define_insn "*csky_ldmsi12" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + (set (match_operand:SI 9 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 28)))) + (set (match_operand:SI 10 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 32)))) + (set (match_operand:SI 11 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 36)))) + (set (match_operand:SI 12 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 40)))) + (set (match_operand:SI 13 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 44)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 12" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi11" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + (set (match_operand:SI 9 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 28)))) + (set (match_operand:SI 10 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 32)))) + (set (match_operand:SI 11 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 36)))) + (set (match_operand:SI 12 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 40)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 11" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi10" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + (set (match_operand:SI 9 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 28)))) + (set (match_operand:SI 10 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 32)))) + (set (match_operand:SI 11 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 36)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 10" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi9" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + (set (match_operand:SI 9 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 28)))) + (set (match_operand:SI 10 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 32)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 9" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi8" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + (set (match_operand:SI 9 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 28)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 8" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi7" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + (set (match_operand:SI 8 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 24)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 7" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_ldmsi6" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + (set (match_operand:SI 7 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 20)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 6" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_ldmsi5" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + (set (match_operand:SI 6 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 16)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 5" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_ldmsi4" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + (set (match_operand:SI 5 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 12)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 4" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_ldmsi3" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + (set (match_operand:SI 4 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 8)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 3" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_ldmsi2" + [(match_parallel 0 "csky_load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r") + (mem:SI (match_operand:SI 2 "register_operand" "r"))) + (set (match_operand:SI 3 "register_operand" "=r") + (mem:SI (plus:SI (match_dup 2) (const_int 4)))) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 2" + { + static char load_op[256] = {0}; + int count = REGNO (operands[1]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[count]; + sprintf (load_op, \"ldm\t%%1 - %s, (%%2)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_stmsi12" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 9 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 32))) + (match_operand:SI 10 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 36))) + (match_operand:SI 11 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 40))) + (match_operand:SI 12 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 44))) + (match_operand:SI 13 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 12" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi11" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 9 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 32))) + (match_operand:SI 10 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 36))) + (match_operand:SI 11 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 40))) + (match_operand:SI 12 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 11" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi10" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 9 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 32))) + (match_operand:SI 10 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 36))) + (match_operand:SI 11 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 10" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi9" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 9 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 32))) + (match_operand:SI 10 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 9" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi8" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 9 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 8" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi7" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 8 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 7" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi6" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 7 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 6" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + +(define_insn "*csky_stmsi5" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 6 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 5" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi4" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 5 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 4" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi3" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 4 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 3" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +(define_insn "*csky_stmsi2" + [(match_parallel 0 "csky_store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand:SI 2 "register_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 3 "register_operand" "r")) + ])] + "TARGET_MULTIPLE_STLD && XVECLEN (operands[0], 0) == 2" + { + static char load_op[256] = {0}; + int end = REGNO (operands[2]) + XVECLEN (operands[0], 0) - 1; + const char *reg_rz = reg_names[end]; + sprintf (load_op, \"stm\t%%2 - %s, (%%1)\", reg_rz); + return load_op; + } +) + + +;; ------------------------------------------------------------------------ +;; Jump and linkage insns +;; ------------------------------------------------------------------------ + +(define_expand "tablejump" + [(parallel [(set (pc) (match_operand:SI 0 "register_operand" "r")) + (use (label_ref (match_operand 1 "" "")))])] + "" + " + if (flag_pic) + operands[0] = expand_simple_binop (Pmode, PLUS, operands[0], + pic_offset_table_rtx, NULL_RTX, + 1, OPTAB_DIRECT); + " +) + +(define_insn "*tablejump" + [(set (pc) (match_operand:SI 0 "register_operand" "r")) + (use (label_ref (match_operand 1 "" "")))] + "" + "jmp %0" + [(set_attr "type" "branch_jmp")] +) + +(define_expand "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "" +) + +(define_insn "*csky_jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "CSKY_ISA_FEATURE (E2)" + "jbr %l0" + [(set_attr "type" "branch")] +) + +;; The length of bsr is not really 5; it's used to distinguish from br32. +;; Since the length attribute is treated specially it doesn't seem possible +;; to compute the far_jump attribute directly and use that. + +(define_insn "*ck801_ck802_jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "CSKY_ISA_FEATURE (E1) || CSKY_ISA_FEATURE (E2)" + "*{ + if (get_attr_length (insn) != 5) + return \"jbr\\t%l0\"; + else + return \"bsr\\t%l0\\t//far jump\"; + }" + [(set_attr "type" "branch") + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "5") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -1024)) + (le (minus (match_dup 0) (pc)) (const_int 1022))) + (const_int 2) + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -65536)) + (le (minus (match_dup 0) (pc)) (const_int 65534))) + (const_int 4) + (const_int 5))))] +) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "b,r"))] + "" + "@ + jmp\t%0 + jmp\t%0" + [(set_attr "length" "2,4") + (set_attr "type" "branch_jmp")] +) + + +;; ------------------------------------------------------------------------ +;; Conditional jump insns +;; ------------------------------------------------------------------------ + +(define_expand "cbranchsi4" + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(match_operand:SI 1 "csky_compare_operand") + (match_operand:SI 2 "nonmemory_operand")]) + (label_ref (match_operand 3 "")) + (pc)))] + "" + "{ + enum rtx_code code = GET_CODE (operands[0]); + + if (CSKY_ISA_FEATURE (2E3) + && (code == LE || code == LT || code == GT + || code == GE || code == EQ || code == NE) + && operands[2] == const0_rtx) + { + /* These cases match the jbez, jbnez, etc insns below. + TODO: Handling this in the expander is suboptimal since it + fails to detect cases where the constant 0 would fall out + from subsequent forward propagation or loop optimizers; maybe + it would be better to have a splitter here, but when to split? */ + } + else + { + bool invert = csky_emit_compare (code, operands[1], operands[2]); + + if (invert) + emit_jump_insn (gen_csky_jbf (operands[3])); + else + emit_jump_insn (gen_csky_jbt (operands[3])); + DONE; + } + }" +) + +(define_insn "csky_jbt" + [(set (pc) + (if_then_else (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (2E3)" + "jbt\t%l0" + [(set_attr "type" "cbranch")] +) + +(define_insn "csky_jbf" + [(set (pc) + (if_then_else (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (2E3)" + "jbf\t%l0" + [(set_attr "type" "cbranch")] +) + + +;;; CK802 has 32-bit jbt/jbf instructions, but no insn other +;;; than bsr for far jumps. + +(define_insn "ck802_jbt" + [(set (pc) (if_then_else (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (E2)" + { + if (get_attr_length (insn) == 6) + return \"jbf\\t.LCB%=\;bsr\\t%l0\\t//far jump\\n.LCB%=:\"; + else + return \"jbt\\t%l0\"; + } + [(set_attr "type" "cbranch") + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "6") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -1024)) + (le (minus (match_dup 0) (pc)) (const_int 1022))) + (const_int 2) + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -65534)) + (le (minus (match_dup 0) (pc)) (const_int 65534))) + (const_int 4) + (const_int 6))))] +) + +(define_insn "ck802_jbf" + [(set (pc) (if_then_else (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (E2)" + { + if (get_attr_length (insn) == 6) + return \"jbt\\t.LCB%=\;bsr\\t%l0\\t//far jump\\n.LCB%=:\"; + else + return \"jbf\\t%l0\"; + } + [(set_attr "type" "cbranch") + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "6") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -1024)) + (le (minus (match_dup 0) (pc)) (const_int 1022))) + (const_int 2) + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -65534)) + (le (minus (match_dup 0) (pc)) (const_int 65534))) + (const_int 4) + (const_int 6))))] +) + +;; The length of the bsr case is not really 7; it's used to distinguish +;; from br32. +;; Note that we have to adjust the backward range of the jbr case to +;; account for the jbf in front of it. +(define_insn "ck801_jbt" + [(set (pc) (if_then_else (ne (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (E1)" + { + if (get_attr_length (insn) == 6) + return \"jbf\\t.LCB%=\;jbr\\t%l0\\n.LCB%=:\"; + else if (get_attr_length (insn) == 7) + return \"jbf\\t.LCB%=\;bsr\\t%l0\\t//far jump\\n.LCB%=:\"; + else + return \"jbt\\t%l0\"; + } + [(set_attr "type" "cbranch") + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "7") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -1024)) + (le (minus (match_dup 0) (pc)) (const_int 1022))) + (const_int 2) + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -65534)) + (le (minus (match_dup 0) (pc)) (const_int 65534))) + (const_int 6) + (const_int 7))))] +) + +(define_insn "ck801_jbf" + [(set (pc) + (if_then_else (eq (reg:CC CSKY_CC_REGNUM) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (E1)" + { + if (get_attr_length (insn) == 6) + return \"jbt\\t.LCB%=\;jbr\\t%l0\\n.LCB%=:\"; + else if (get_attr_length (insn) == 7) + return \"jbt\\t.LCB%=\;bsr\\t%l0\\t//far jump\\n.LCB%=:\"; + else + return \"jbf\\t%l0\"; + } + [(set_attr "type" "cbranch") + (set (attr "far_jump") + (if_then_else + (eq_attr "length" "7") + (const_string "yes") + (const_string "no"))) + (set (attr "length") + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -1024)) + (le (minus (match_dup 0) (pc)) (const_int 1022))) + (const_int 2) + (if_then_else + (and (ge (minus (match_dup 0) (pc)) (const_int -65534)) + (le (minus (match_dup 0) (pc)) (const_int 65534))) + (const_int 6) + (const_int 7))))] +) + +(define_code_iterator zero_cond [lt le gt ge eq ne]) + +(define_code_attr inst [(lt "jblz") (le "jblsz") (gt "jbhz") (ge "jbhsz") (eq "jbez") (ne "jbnez")]) + +(define_insn "*" + [(set (pc) + (if_then_else (zero_cond (match_operand:SI 0 "register_operand" "r") + (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "CSKY_ISA_FEATURE (2E3)" + "\t%0, %l1" + [(set_attr "type" "cbranch")] +) + +;; ------------------------------------------------------------------------ +;; return insns +;; ------------------------------------------------------------------------ + +(define_insn "simple_return" + [(simple_return)] + "reload_completed" + "* + return csky_output_return_instruction (); + " +) + +(define_expand "eh_return" + [(use (match_operand 0 "general_operand" ""))] + "" + "{ + emit_insn (gen_csky_eh_return (operands[0])); + DONE; + }" +) + +;; We can't expand this before we know where the link register is stored. +(define_insn_and_split "csky_eh_return" + [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] + VUNSPEC_EH_RETURN) + (clobber (match_scratch:SI 1 "=&r"))] + "" + "#" + "reload_completed" + [(const_int 0)] + "{ + csky_set_eh_return_address (operands[0], operands[1]); + DONE; + }" +) + +;; ------------------------------------------------------------------------- +;; SImode signed integer comparisons +;; ------------------------------------------------------------------------- + +(define_insn "*cmpnesi_r" + [(set (reg:CC CSKY_CC_REGNUM) + (ne:CC (match_operand:SI 0 "register_operand" "b,r") + (match_operand:SI 1 "register_operand" "b,r")))] + "" + "@ + cmpne\t%0, %1 + cmpne\t%0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +;; cmpnei range is 0-31 for Smart mode. +(define_insn "smart_cmpnesi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (ne:CC (match_operand:SI 0 "register_operand" "a") + (match_operand:SI 1 "csky_literal_K_operand" "K")))] + "TARGET_MINI_REGISTERS" + "cmpnei\t%0, %1" + [(set_attr "type" "cmp")] +) + +;; cmpnei range is 0 - 65536 for Fast mode. +(define_insn "fast_cmpnesi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (ne:CC (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "csky_literal_I_operand" "I")))] + "!TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2)" + "cmpnei\t%0, %1" + [(set_attr "type" "cmp")] +) + +(define_insn "*cmpgtsi" + [(set (reg:CC CSKY_CC_REGNUM) + (gt:CC (match_operand:SI 0 "register_operand" "b,r") + (match_operand:SI 1 "register_operand" "b,r")))] + "" + "cmplt\t%1, %0" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +(define_insn "cmpltsi_r" + [(set (reg:CC CSKY_CC_REGNUM) + (lt:CC (match_operand:SI 0 "register_operand" "b,r") + (match_operand:SI 1 "register_operand" "b,r")))] + "" + "cmplt\t%0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +;; cmplti range is 1-32 for Smart mode. +(define_insn "*smart_cmpltsi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (lt:CC (match_operand:SI 0 "register_operand" "a") + (match_operand:SI 1 "csky_literal_J_operand" "J")))] + "TARGET_MINI_REGISTERS" + "cmplti\t%0, %1" + [(set_attr "length" "2") + (set_attr "type" "cmp")] +) + + +;; cmplti range is 1-65536 for Fast mode. +(define_insn "*fast_cmpltsi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (lt:CC (match_operand:SI 0 "register_operand" "a,r") + (match_operand:SI 1 "csky_literal_Uk_operand" "J,Uk")))] + "!TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2)" + "cmplti\t%0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +; Covers cmplti x,0. +(define_insn "*cskyv2_cmpltsi_0" + [(set (reg:CC CSKY_CC_REGNUM) + (lt:CC (match_operand:SI 0 "register_operand" "a,r") + (const_int 0)))] + "CSKY_ISA_FEATURE (E2)" + "btsti\t%0, 31" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +(define_insn "*ck801_cmpltsi_0" + [(set (reg:CC CSKY_CC_REGNUM) + (lt:CC (match_operand:SI 0 "register_operand" "a") + (const_int 0)))] + "CSKY_ISA_FEATURE (E1)" + "btsti\t%0, 31" + [(set_attr "type" "cmp")] +) + +;; Decrement and test instructions. +;; In theory decne could be used in conjunction with jbt to implement +;; doloop_end, but that seems to encourage the loop optimizer to introduce +;; an additional induction variable and doesn't actually result in tighter +;; loop code for that reason. + +(define_insn "*cskyv2_declt" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "Uh"))) + (set (reg:CC CSKY_CC_REGNUM) + (lt:CC (plus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "CSKY_ISA_FEATURE (2E3)" + "declt\t%0, %1, %M2" +) + +(define_insn "*cskyv2_decgt" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "Uh"))) + (set (reg:CC CSKY_CC_REGNUM) + (gt:CC (plus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "CSKY_ISA_FEATURE (2E3)" + "decgt\t%0, %1, %M2" +) + +(define_insn "*cskyv2_decne" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "Uh"))) + (set (reg:CC CSKY_CC_REGNUM) + (ne:CC (plus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "CSKY_ISA_FEATURE (2E3)" + "decne\t%0, %1, %M2" +) + +;; ------------------------------------------------------------------------- +;; SImode unsigned integer comparisons +;; ------------------------------------------------------------------------- + +(define_insn "cmpgeusi_r" + [(set (reg:CC CSKY_CC_REGNUM) + (geu:CC (match_operand:SI 0 "register_operand" "b,r") + (match_operand:SI 1 "register_operand" "b,r")))] + "" + "cmphs\t%0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +(define_insn "*smart_cmpgeusi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (geu:CC (match_operand:SI 0 "register_operand" "a") + (match_operand:SI 1 "csky_literal_J_operand" "J")))] + "TARGET_MINI_REGISTERS" + "cmphsi\t%0, %1" + [(set_attr "length" "2") + (set_attr "type" "cmp")] +) + +(define_insn "*fast_cmpgeusi_i" + [(set (reg:CC CSKY_CC_REGNUM) + (geu:CC (match_operand:SI 0 "register_operand" "a,r") + (match_operand:SI 1 "csky_literal_Uk_operand" "J,Uk")))] + "!TARGET_MINI_REGISTERS && CSKY_ISA_FEATURE (E2)" + "cmphsi\t%0, %1" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +(define_insn "*cmpleusi" + [(set (reg:CC CSKY_CC_REGNUM) + (leu:CC (match_operand:SI 0 "register_operand" "b,r") + (match_operand:SI 1 "register_operand" "b,r")))] + "" + "cmphs\t%1, %0" + [(set_attr "length" "2,4") + (set_attr "type" "cmp")] +) + +;; ------------------------------------------------------------------------- +;; Function call insns +;; ------------------------------------------------------------------------- + +(define_expand "call" + [(parallel [(call (match_operand:SI 0 "" "") (match_operand 1 "" "")) + (clobber (reg:SI CSKY_LR_REGNUM))])] + "" + " + { + rtx pic_ref; + rtx addr_ref = XEXP (operands[0], 0); + + if (flag_pic + && (CONSTANT_P (addr_ref) + || csky_symbol_mentioned_p (addr_ref) + || csky_label_mentioned_p (addr_ref))) + { + pic_ref = csky_legitimize_pic_address (addr_ref, 0, false); + operands[0] = gen_rtx_MEM (GET_MODE (pic_ref), pic_ref); + } + + if (GET_CODE (operands[0]) == MEM + && ! register_operand (XEXP (operands[0], 0), SImode) + && ! csky_symbolic_address_p (XEXP (operands[0], 0)) + && ! (flag_pic + && csky_unspec_operand (XEXP (operands[0], 0), SImode))) + operands[0] = gen_rtx_MEM (GET_MODE (operands[0]), + force_reg (Pmode, XEXP (operands[0], 0))); + }" +) + + +(define_insn "*call_internal" + [(call (mem:SI (match_operand:SI 0 "csky_call_address_operand" "b,r,S")) + (match_operand 1 "" "")) + (clobber (reg:SI CSKY_LR_REGNUM))] + "" + "@ + jsr\t%0 + jsr\t%0 + jbsr\t%0" + [(set_attr "length" "2,4,4") + (set_attr "type" "call_jsr,call_jsr,call")] +) + +(define_insn "*call_internal_pic" + [(call (mem:SI (match_operand:SI 0 "csky_unspec_operand" "X")) + (match_operand 1 "" "")) + (clobber (reg:SI CSKY_LR_REGNUM))] + "flag_pic" + "* return csky_output_call (operands, 0);" + [(set_attr "length" "4")] +) + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "register_operand" "") + (call (match_operand:SI 1 "" "") (match_operand 2 "" ""))) + (clobber (reg:SI CSKY_LR_REGNUM))])] + "" + "{ + rtx pic_ref; + rtx addr_ref = XEXP (operands[1], 0); + + if (flag_pic + && (CONSTANT_P (addr_ref) + || csky_symbol_mentioned_p (addr_ref) + || csky_label_mentioned_p (addr_ref))) + { + pic_ref = csky_legitimize_pic_address (addr_ref, 0, false); + operands[1] = gen_rtx_MEM (GET_MODE (pic_ref), pic_ref); + } + + if (GET_CODE (operands[1]) == MEM + && ! register_operand (XEXP (operands[1], 0), SImode) + && ! csky_symbolic_address_p (XEXP (operands[1], 0)) + && ! (flag_pic + && csky_unspec_operand (XEXP (operands[1], 0), SImode))) + operands[1] = gen_rtx_MEM (GET_MODE (operands[1]), + force_reg (Pmode, XEXP (operands[1], 0))); + }") + + +(define_insn "*call_value_internal" + [(set (match_operand 0 "register_operand" "=r,r,r") + (call (mem:SI (match_operand:SI 1 "csky_call_address_operand" "b, r,S")) + (match_operand 2 "" ""))) + (clobber (reg:SI CSKY_LR_REGNUM))] + "" + "@ + jsr\t%1 + jsr\t%1 + jbsr\t%1" + [(set_attr "length" "2,4,4") + (set_attr "type" "call_jsr,call_jsr,call")] +) + +(define_insn "*call_value_internal_pic" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:SI (match_operand:SI 1 "csky_unspec_operand" "X")) + (match_operand 2 "" ""))) + (clobber (reg:SI CSKY_LR_REGNUM))] + "flag_pic" + "* return csky_output_call (operands, 1);" +) + +(define_insn "*call_value_struct" + [(set (match_parallel 0 "" + [(expr_list (match_operand 3 "register_operand" "") + (match_operand 4 "immediate_operand" "")) + (expr_list (match_operand 5 "register_operand" "") + (match_operand 6 "immediate_operand" ""))]) + (call (mem:SI (match_operand:SI 1 "csky_call_address_operand" "b,r,S")) + (match_operand 2 "" ""))) + (clobber (reg:SI CSKY_LR_REGNUM))] + "" + "@ + jsr\t%1 + jsr\t%1 + jbsr\t%1" + [(set_attr "length" "2,4,4") + (set_attr "type" "call_jsr,call_jsr,call")] +) + +(define_insn "*call_value_struct_pic" + [(set (match_parallel 0 "" + [(expr_list (match_operand 3 "register_operand" "") + (match_operand 4 "immediate_operand" "")) + (expr_list (match_operand 5 "register_operand" "") + (match_operand 6 "immediate_operand" ""))]) + (call (mem:SI (match_operand:SI 1 "csky_unspec_operand" "X")) + (match_operand 2 "" ""))) + (clobber (reg:SI CSKY_LR_REGNUM))] + "flag_pic" + "* return csky_output_call (operands, 1);" +) + + +;; ------------------------------------------------------------- +;; prologue & epilogue +;; ------------------------------------------------------------- + +(define_expand "prologue" + [(clobber (const_int 0))] + "" + " + { + csky_expand_prologue (); + DONE; + }" +) + +(define_expand "epilogue" + [(clobber (const_int 0))] + "" + " + { + csky_expand_epilogue (); + DONE; + }" +) + +/* TODO: pushpop */ +;; Push multiple registers to the stack. Registers are in parallel (use ...) +;; expressions. For simplicity, the first register is also in the unspec +;; part. +(define_insn "*push_multi" + [(match_parallel 2 "registers_push" + [(set (match_operand:BLK 0 "push_memory_operand" "") + (unspec:BLK [(match_operand:SI 1 "register_operand" "")] + UNSPEC_PUSHPOP_MULT))])] + "" + { + int num_saves = XVECLEN (operands[2], 0); + int i; + char pattern[100]; + + strcpy (pattern, \"push\\t%1\"); + + for (i = 1; i < num_saves; i++) + { + strcat (pattern, \", \"); + strcat (pattern, + reg_names[REGNO (XEXP (XVECEXP (operands[2], 0, i), 0))]); + } + + output_asm_insn (pattern, operands); + + return \"\"; + } + [(set (attr "length") + (symbol_ref "csky_compute_pushpop_length (operands)"))] +) + +;; Pop (as used in epilogue RTL) +;; +(define_insn "*pop_multi" + [(match_parallel 2 "registers_pop" + [(return) + (set (match_operand:SI 1 "register_operand" "") + (unspec:SI [(match_operand:SI 0 "pop_memory_operand" "")] + UNSPEC_PUSHPOP_MULT))])] + "" + { + int num_saves = XVECLEN (operands[2], 0); + int i; + char pattern[100]; + + strcpy (pattern, \"pop\\t%1\"); + + for (i = 2; i < num_saves; i++) + { + strcat (pattern, \", \"); + strcat (pattern, + reg_names[REGNO (XEXP (XVECEXP (operands[2], 0, i), 0))]); + } + + output_asm_insn (pattern, operands); + + return \"\"; + } + [(set (attr "length") + (symbol_ref "csky_compute_pushpop_length (operands)"))] +) + + +;; ------------------------------------------------------------------------- +;; PIC related insns +;; ------------------------------------------------------------------------- + +(define_insn "prologue_get_pc" + [(set (reg:SI 28) + (match_operand:SI 0 "" "X"))] + "(GET_CODE (operands[0]) == UNSPEC) + && (XINT (operands[0], 1) == UNSPEC_PIC_SYMBOL_GOTPC_GRS)" + { + operands[0] = XVECEXP (operands[0], 0, 0); + output_asm_insn (\"grs\tgb, %0\", operands); + default_internal_label (asm_out_file, \"L\", + CODE_LABEL_NUMBER (XEXP (operands[0], 0))); + return \"\"; + } +) + +(define_insn "*pic_got_pc" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC_SYMBOL_GOTPC))] + "flag_pic" + "lrw\t%0, %1@GOTPC" +) + +(define_insn "*pic_symbol_gotoff" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC_SYMBOL_GOTOFF))] + "flag_pic" + "lrw\t%0, %1@GOTOFF" +) + +(define_insn "*pic_symbol_got" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC_SYMBOL_GOT))] + "flag_pic" + "lrw\t%0, %1@GOT" +) + +(define_insn "*pic_symbol_plt" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC_SYMBOL_PLT))] + "flag_pic" + "lrw\t%0, %1@PLT" +) + +(define_insn "*pic_symbol_grs" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC_SYMBOL_GRS))] + "flag_pic" + "grs\t%0, %1" +) + +(define_expand "builtin_setjmp_receiver" + [(label_ref (match_operand 0 "" ""))] + "flag_pic" + "{ + rtx l1 = gen_label_rtx(); + rtx grs_label = gen_rtx_LABEL_REF (SImode, l1); + rtx reg_gb = gen_rtx_REG (SImode, PIC_OFFSET_TABLE_REGNUM); + rtx reg_temp = gen_rtx_REG (SImode, 12); + + rtx tmp0_unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, grs_label), + UNSPEC_PIC_SYMBOL_GOTPC_GRS); + rtx tmp1_unspec = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, grs_label), + UNSPEC_PIC_SYMBOL_GOTPC); + + emit_insn (gen_prologue_get_pc (tmp0_unspec)); + emit_move_insn (reg_temp, tmp1_unspec); + emit_insn (gen_addsi3 (reg_gb, reg_gb, reg_temp)); + emit_use (reg_gb); + + DONE; + }" +) + +;; ------------------------------------------------------------------------- +;; TLS related insns +;; ------------------------------------------------------------------------- + + +;; UNSPEC_TLS can take either 2 or 3 operands. Operand 0 is the symbol_ref, +;; operand 1 is a CONST_INT identifying the TLS model, and the optional +;; operand 3 is an UNSPEC_TLS_LABEL. +;; The 3-operand case is for TLS_GD32, TLS_LDM32, and TLS_IE32. +;; The 2-operand case is for TLS_LE32 and TLS_LDO32. + +;; Move PC-relative TLS label to reg. This is used for the TLS_GD32 +;; and TLS_GD32 models (when setting up a call to tls_get_addr) and +;; also TLS_IE32. + +(define_insn "*tls_pcrel_label" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "const_int_operand" "")] + UNSPEC_TLS_LABEL))] + "TARGET_TLS" + "grs\t%0, .LTLS%1" + [(set_attr "length" "4")] +) + +;; This pattern is used to load the TLS base for the same models as above. +;; The embedded UNSPEC_TLS_LABEL only identifies the label to emit and +;; doesn't generate a reference to it; that's handled by the *tls_pcrel_label +;; pattern above. The label offset needs to be added to the result stored +;; in operand 0 by this insn. + +(define_insn "*tls_get_symbol_1" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "") + (match_operand 2 "" "") + (unspec:SI [(match_operand 3 "" "")] UNSPEC_TLS_LABEL)] + UNSPEC_TLS))] + "TARGET_TLS" + { + default_internal_label (asm_out_file, \"LTLS\", INTVAL (operands[3])); + switch (INTVAL (operands[2])) + { + case TLS_GD32: + return \"lrw\t%0, %1@TLSGD32\"; + case TLS_LDM32: + return \"lrw\t%0, %1@TLSLDM32\"; + case TLS_IE32: + return \"lrw\t%0, %1@GOTTPOFF\"; + default: + return \"\"; + } + } +) + +;; This pattern matches the two-operand form of UNSPEC_TLS. + +(define_insn "*tls_get_symbol_2" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "") + (match_operand 2 "" "")] + UNSPEC_TLS))] + "TARGET_TLS" + { + switch (INTVAL (operands[2])) + { + case TLS_LE32: + return \"lrw\t%0, %1@TPOFF\"; + case TLS_LDO32: + return \"lrw\t%0, %1@TLSLDO32\"; + default: + return \"\"; + } + } +) + + +;; ------------------------------------------------------------- +;; Misc insns +;; ------------------------------------------------------------- + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "length" "2")] +) + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 0))] + "" + "bkpt" + [(set (attr "length") (const_int 2)) + (set_attr "type" "alu")] +) + + +;; ------------------------------------------------------------- +;; Special patterns for dealing with the constant pool +;; ------------------------------------------------------------- + +(define_insn "align_4" + [(unspec_volatile [(const_int 0)] VUNSPEC_ALIGN)] + "" + { + assemble_align(32); + return \"\"; + } + [(set_attr "length" "0")] +) + +(define_insn "csky_constpool_label" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_LABEL)] + "" + { + char tmp_label[15]; + ASM_GENERATE_INTERNAL_LABEL (tmp_label, \"LCP\", INTVAL (operands[0])); + assemble_label (asm_out_file, tmp_label); + return \"\"; + } + [(set_attr "length" "0")] +) + +(define_insn "consttable_4" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_4)] + "" + { + if (CONST_DOUBLE_P (operands[0])) + assemble_real (*CONST_DOUBLE_REAL_VALUE (operands[0]), + SFmode, BITS_PER_WORD); + else + { + assemble_integer (operands[0], 4, BITS_PER_WORD, 1); + mark_symbol_refs_as_used (operands[0]); + } + return \"\"; + } + [(set_attr "length" "4")] +) + +(define_insn "consttable_8" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_POOL_8)] + "" + { + if (CONST_DOUBLE_P (operands[0])) + assemble_real (*CONST_DOUBLE_REAL_VALUE (operands[0]), + DFmode, BITS_PER_WORD); + else + assemble_integer (operands[0], 8, BITS_PER_WORD, 1); + return \"\"; + } + [(set_attr "length" "8")] +) + +;;FIXME record the deferred symbol_ref information with use insn +(define_insn "*cskyv2_use_symbol_ref" + [(unspec_volatile [(match_operand 0 "" "")] VUNSPEC_SYMBOL_REF)] + "" + "" + [(set_attr "length" "0")] +) + + +;; ------------------------------------------------------------ +;; switch case optimize +;; ------------------------------------------------------------ + +(define_expand "casesi" + [(match_operand:SI 0 "register_operand" "") ; index to jump on + (match_operand:SI 1 "const_int_operand" "") ; lower bound + (match_operand:SI 2 "const_int_operand" "") ; total range (max - min) + (match_operand:SI 3 "" "") ; table label + (match_operand:SI 4 "" "")] ; Out of range label (default:) + "TARGET_CASESI" + " + { + enum insn_code code; + if (operands[1] != const0_rtx) + { + rtx reg = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (reg, + operands[0], + GEN_INT (INTVAL (operands[1])))); + operands[0] = reg; + } + + code = CODE_FOR_csky_casesi_internal; + + if (!insn_data[(int) code].operand[1].predicate(operands[2], SImode)) + operands[2] = force_reg (SImode,operands[2]); + + emit_jump_insn (GEN_FCN ((int) code) (operands[0],operands[2], + operands[3],operands[4])); + DONE; + }" +) + +(define_expand "csky_casesi_internal" + [(match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "csky_literal_Uk_operand" "") + (match_operand 2 "" "") + (match_operand 3 "" "")] + "" + { + rtx reg0; + rtx test = gen_rtx_GTU (VOIDmode, operands[0], operands[1]); + emit_jump_insn (gen_cbranchsi4 (test, operands[0], operands[1], + operands[3])); + reg0 = gen_rtx_REG (SImode, 0); + emit_move_insn (reg0, operands[0]); + emit_jump_insn (gen_csky_casesi_dispatch (operands[2])); + DONE; + } +) + +(define_insn "csky_casesi_dispatch" + [(parallel [(set (pc) (unspec [(reg:SI 0) + (label_ref (match_operand 0 "" ""))] + UNSPEC_CSKY_CASESI)) + (clobber (reg:SI CSKY_LR_REGNUM))])] + "" + "*return csky_output_casesi (operands);" + [(set_attr "length" "4")] +) + +;; ------------------------------------------------------------------------ +;; index insns +;; ------------------------------------------------------------------------ + +(define_insn "*cskyv2_indexsi_t" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r") + (const_int 4)) + (match_operand:SI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E2)" + "ixw\t%0, %2, %1" +) + +(define_insn "*cskyv2_indexhi_t" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r") + (const_int 2)) + (match_operand:SI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E2)" + "ixh\t%0, %2, %1" +) + +(define_insn "*cskyv2_indexdi_t" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r") + (const_int 8)) + (match_operand:SI 2 "register_operand" "r")))] + "CSKY_ISA_FEATURE (2E3)" + "ixd\t%0, %2, %1" +) + +;; ------------------------------------------------------------------------ +;; swap insns +;; ------------------------------------------------------------------------ + +(define_insn "bswapsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (bswap:SI (match_operand:SI 1 "register_operand" "r")))] + "CSKY_ISA_FEATURE (E2)" + "revb\t%0, %1" +) diff --git a/gcc/config/csky/csky.opt b/gcc/config/csky/csky.opt new file mode 100644 index 00000000000..55d2659e2f7 --- /dev/null +++ b/gcc/config/csky/csky.opt @@ -0,0 +1,173 @@ +;; Command-line options for the C-SKY back end. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . + + +HeaderInclude +config/csky/csky_opts.h + +;; Architecture/CPU options. +;; Normal CPU and arch enums are loaded from csky_tables.opt. + +; For backward compatibility only. +march=ck803s +Target Report Var(flag_arch_ck803s) Undocumented + +march= +Target RejectNegative ToLower Joined Enum(csky_arch) Var(csky_arch_option) Save +Specify the target architecture. + +mcpu= +Target RejectNegative ToLower Joined Enum(csky_processor_type) Var(csky_cpu_option) Init(TARGET_CPU_csky_none) Save +Specify the target processor. + +;; Endianness options. + +mbig-endian +Target RejectNegative Report Mask(BIG_ENDIAN) +Generate big-endian code. + +EB +Target RejectNegative Report Alias(mbig-endian) Undocumented + +mlittle-endian +Target RejectNegative Report InverseMask(BIG_ENDIAN) +Generate little-endian code. + +EL +Target RejectNegative Report Alias(mlittle-endian) Undocumented + +;; Floating point options. These affect code generation but not +;; assembly. + +mhard-float +Target Report RejectNegative Mask(HARD_FLOAT) +Enable hardware floating-point instructions. + +msoft-float +Target Report RejectNegative InverseMask(HARD_FLOAT) +Use library calls to perform floating-point operations (default). + +mfpu= +Target RejectNegative Joined Enum(csky_fpu) Var(csky_fpu_index) Init(TARGET_FPU_auto) Save +Specify the target floating-point hardware/format. + +mdouble-float +Target Report Var(TARGET_DOUBLE_FLOAT) Init(-1) +Generate C-SKY FPU double float instructions (default for hard float). + +mfdivdu +Target Report Var(TARGET_FDIVDU) Init(-1) +Generate frecipd/fsqrtd/fdivd instructions (default for hard float). + +;; Instruction set extensions. Most of these don't affect code +;; generation, and are passed through to the assembler. +;; There are builtin preprocessor defines for each of these. + +melrw +Target Report Var(TARGET_ELRW) Init(-1) +Enable the extended LRW instruction (default for CK801). + +mistack +Target Report Mask(ISTACK) +Enable interrupt stack instructions. + +mmp +Target Report RejectNegative Mask(MP) +Enable multiprocessor instructions. + +mcp +Target Report RejectNegative Mask(CP) +Enable coprocessor instructions. + +mcache +Target Report RejectNegative Mask(CACHE) +Enable cache prefetch instructions. + +msecurity +Target Report RejectNegative Mask(SECURITY) +Enable C-SKY SECURE instructions. + +mmac +Target Report RejectNegative Alias(msecurity) Undocumented + +mtrust +Target Report RejectNegative Mask(TRUST) +Enable C-SKY TRUST instructions. + +mdsp +Target Report RejectNegative Var(TARGET_DSP) +Enable C-SKY DSP instructions. + +medsp +Target Report RejectNegative Mask(EDSP) +Enable C-SKY Enhanced DSP instructions. + +mvdsp +Target Report RejectNegative Mask(VDSP) +Enable C-SKY Vector DSP instructions. + +;; Code generation options not passed to the assembler. + +mdiv +Target Report Var(TARGET_DIV) Init(-1) +Generate divide instructions. + +msmart +Target Report Var(TARGET_MINI_REGISTERS) Init(-1) +Generate code for Smart Mode. + +mhigh-registers +Target Report Var(TARGET_HIGH_REGISTERS) Init(-1) +Enable use of R16-R31 (default). + +manchor +Target Report Var(TARGET_ANCHOR) +Generate code using global anchor symbol addresses. + +mpushpop +Target Report Var(TARGET_PUSHPOP) Init(1) +Generate push/pop instructions (default). + +mmultiple-stld +Target Report Var(TARGET_MULTIPLE_STLD) Init(-1) +Generate stm/ldm instructions (default). + +mstm +Target Report Alias(mmultiple-stld) Undocumented + +mconstpool +Target Report Var(TARGET_CONSTANT_POOL) Init(-1) +Generate constant pools in the compiler instead of assembler. + +mstack-size +Target Report Var(TARGET_STACK_SIZE) Init(0) +Emit .stack_size directives. + +mccrt +Target Report Var(TARGET_LIBCCRT) Init(0) +Generate code for C-SKY compiler runtime instead of libgcc. + +mbranch-cost= +Target Report Joined RejectNegative UInteger Var(csky_branch_cost) Init(1) +Set the branch costs to roughly the specified number of instructions. + +msched-prolog +Target Report Var(flag_sched_prolog) Init(0) +Permit scheduling of function prologue and epilogue sequences. diff --git a/gcc/config/csky/csky_cores.def b/gcc/config/csky/csky_cores.def new file mode 100644 index 00000000000..5159a073360 --- /dev/null +++ b/gcc/config/csky/csky_cores.def @@ -0,0 +1,199 @@ +/* Architecture and core descriptions for the C-SKY back end. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +/* Before using #include to read this file, define a macro: + + CSKY_ARCH(NAME, CORE, ARCH, ISA) + + The NAME is the name of the architecture, represented as a string + constant. The CORE is the identifier for a core representative of + this architecture. ARCH is the architecture revision. ISA is the + detailed architectural capabilities of the core. */ + +#ifdef CSKY_ARCH +CSKY_ARCH ("ck801", ck801, CK801, + CSKY_ISA_FEAT (CSKY_ISA_CK801)) +CSKY_ARCH ("ck802", ck802, CK802, + CSKY_ISA_FEAT (CSKY_ISA_CK802)) +CSKY_ARCH ("ck803", ck803, CK803, + CSKY_ISA_FEAT (CSKY_ISA_CK803)) +CSKY_ARCH ("ck807", ck807, CK807, + CSKY_ISA_FEAT (CSKY_ISA_CK807) CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_ARCH ("ck810", ck810, CK810, + CSKY_ISA_FEAT (CSKY_ISA_CK810) CSKY_ISA_FEAT (CSKY_ISA_DSP)) +#endif + + +/* Before using #include to read this file, define a macro: + + CSKY_CORE(CORE_NAME, INTERNAL_IDENT, TUNE_IDENT, ARCH, ISA) + + The isa features of core will inherit the ARCH. + + The CORE_NAME is the name of the core, represented as a string constant. + The INTERNAL_IDENT is the name of the core represented as an identifier. + This must be unique for each entry in this table. + The TUNE_IDENT is the name of the core for which scheduling decisions + should be made, represented as an identifier. + The ARCH is the architecture revision implemented by the chip. + The ISA is the detailed architectural capabilities of the core. */ + +#ifdef CSKY_CORE +/* ck801 Architecture Processors */ +CSKY_CORE ("ck801", ck801, ck801, CK801, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck801t", ck801t, ck801t, CK801, + CSKY_ISA_FEAT_NONE) + +/* ck802 Architecture Processors */ +CSKY_CORE ("ck802", ck802, ck802, CK802, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck802t", ck802t, ck802t, CK802, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck802j", ck802j, ck802j, CK802, + CSKY_ISA_FEAT (isa_bit_java)) + +/* ck803 Architecture Processors */ +CSKY_CORE ("ck803", ck803, ck803, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803h", ck803h, ck803h, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803t", ck803t, ck803t, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803ht", ck803ht, ck803ht, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803f", ck803f, ck803f, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803fh", ck803fh, ck803fh, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803e", ck803e, ck803e, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803eh", ck803eh, ck803eh, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803et", ck803et, ck803et, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803eht", ck803eht, ck803eht, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803ef", ck803ef, ck803ef, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803efh", ck803efh, ck803efh, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803ft", ck803ft, ck803ft, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803eft", ck803eft, ck803eft, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803efht", ck803efht, ck803efht, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803r1", ck803r1, ck803r1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803hr1", ck803hr1, ck803hr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803tr1", ck803tr1, ck803tr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803htr1", ck803htr1, ck803htr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803fr1", ck803fr1, ck803fr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803fhr1", ck803fhr1, ck803fhr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803er1", ck803er1, ck803er1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803ehr1", ck803ehr1, ck803ehr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803etr1", ck803etr1, ck803etr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803ehtr1", ck803ehtr1, ck803ehtr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803efr1", ck803efr1, ck803efr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803efhr1", ck803efhr1, ck803efhr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803ftr1", ck803ftr1, ck803ftr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803eftr1", ck803eftr1, ck803eftr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) +CSKY_CORE ("ck803efhtr1", ck803efhtr1, ck803efhtr1, CK803, + CSKY_ISA_FEAT (isa_bit_3E3r1)) + +/* ck803s Architecture Processors */ +CSKY_CORE ("ck803s", ck803s, ck803s, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803st", ck803st, ck803st, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803se", ck803se, ck803se, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803sf", ck803sf, ck803sf, CK803, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck803sef", ck803sef, ck803sef, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) +CSKY_CORE ("ck803seft", ck803seft, ck803seft, CK803, + CSKY_ISA_FEAT (CSKY_ISA_DSP)) + +/* ck807 Architecture Processors */ +CSKY_CORE ("ck807e", ck807e, ck807e, CK807, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck807ef", ck807ef, ck807ef, CK807, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck807", ck807, ck807, CK807, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck807f", ck807f, ck807f, CK807, + CSKY_ISA_FEAT_NONE) + +/* ck810 Architecture Processors */ +CSKY_CORE ("ck810e", ck810e, ck810e, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810et", ck810et, ck810et, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810ef", ck810ef, ck810ef, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810eft", ck810eft, ck810eft, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810", ck810, ck810, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810v", ck810v, ck810v, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810f", ck810f, ck810f, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810t", ck810t, ck810t, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810fv", ck810fv, ck810fv, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810tv", ck810tv, ck810tv, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810ft", ck810ff, ck810ft, CK810, + CSKY_ISA_FEAT_NONE) +CSKY_CORE ("ck810ftv", ck810ftv, ck810ftv, CK810, + CSKY_ISA_FEAT_NONE) +#endif + + +/* Before using #include to read this file, define a macro: + + CSKY_FPU(NAME, CNAME, ISA) + + NAME is the publicly visible option name. + CNAME is a C-compatible variable name substring. + ISA is the list of feature bits that this FPU provides. */ + +#ifdef CSKY_FPU +CSKY_FPU ("fpv2_sf", fpv2_sf, CSKY_ISA_FEAT (CSKY_ISA_FPv2_SF)) +CSKY_FPU ("fpv2", fpv2, CSKY_ISA_FEAT (CSKY_ISA_FPv2)) +CSKY_FPU ("fpv2_divd", fpv2_divd, CSKY_ISA_FEAT (CSKY_ISA_FPv2_DIVD)) +#endif diff --git a/gcc/config/csky/csky_genopt.sh b/gcc/config/csky/csky_genopt.sh new file mode 100644 index 00000000000..bf145a44727 --- /dev/null +++ b/gcc/config/csky/csky_genopt.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# Generate csky_tables.opt from the lists in *.def. +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. +# +# 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 +# . + +cat <. + +Enum +Name(csky_processor_type) Type(enum csky_processor_type) +Known CSKY CPUs (for use with the -mcpu= options): + +EOF + +awk -F'[(, ]+' '/^CSKY_CORE/ { + name = $2 + enum = $3 + gsub("\"", "", name) + print "EnumValue" + print "Enum(csky_processor_type) String(" name ") Value( TARGET_CPU_" enum ")" + print "" +}' $1/csky_cores.def + +cat <. */ + +;; ------------------------------------------------------------ +;; DSP insns +;; ------------------------------------------------------------ + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=y") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_DSP" + "muls\t%1, %2" +) + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=y") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_DSP" + "mulu\t%1, %2" +) + +(define_insn "maddsidi4" + [(set (match_operand:DI 0 "register_operand" "=y") + (plus:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))) + (match_operand:DI 3 "register_operand" "0")))] + "TARGET_DSP" + "mulsa\t%1, %2" +) + +(define_insn "umaddsidi4" + [(set (match_operand:DI 0 "register_operand" "=y") + (plus:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))) + (match_operand:DI 3 "register_operand" "0")))] + "TARGET_DSP" + "mulua\t%1, %2" +) + +(define_insn "msubsidi4" + [(set (match_operand:DI 0 "register_operand" "=y") + (minus:DI (match_operand:DI 3 "register_operand" "0") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r")))))] + "TARGET_DSP" + "mulss\t%1, %2" +) + +(define_insn "umsubsidi4" + [(set (match_operand:DI 0 "register_operand" "=y") + (minus:DI (match_operand:DI 3 "register_operand" "0") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r")))))] + "TARGET_DSP" + "mulus\t%1, %2" +) + +(define_insn "*mulall_s16_0" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 3 "register_operand" " 0") + (mult:SI (match_operand:SI 1 "register_operand" " r") + (match_operand:SI 2 "register_operand" " r"))))] + "CSKY_ISA_FEATURE (3E3r1)" + "mula.32.l\t%0, %1, %2" + [(set_attr "type" "alu") + (set_attr "length" "4")]) + +(define_insn "*mulall_s16_1" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" " r") + (match_operand:SI 2 "register_operand" " r")) + (match_operand:SI 3 "register_operand" " 0")))] + "CSKY_ISA_FEATURE (3E3r1)" + "mula.32.l\t%0, %1, %2" + [(set_attr "type" "alu") + (set_attr "length" "4")]) diff --git a/gcc/config/csky/csky_insn_fpu.md b/gcc/config/csky/csky_insn_fpu.md new file mode 100644 index 00000000000..d188ef24bc7 --- /dev/null +++ b/gcc/config/csky/csky_insn_fpu.md @@ -0,0 +1,567 @@ +;; C-SKY FPU instruction descriptions. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +;; ------------------------------------------------------------------------- +;; Float Abs instructions +;; ------------------------------------------------------------------------- + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=v,r") + (abs:SF (match_operand:SF 1 "register_operand" "v, r")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "@ + fabss\t%0, %1 + bclri\t%0, %1, 31") + +(define_insn "absdf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (abs:DF (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fabsd\t%0, %1") + + +;; ------------------------------------------------------------------------- +;; Float Neg instructions +;; ------------------------------------------------------------------------- + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=v") + (neg:SF (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnegs\t%0, %1") + +(define_insn "negdf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (neg:DF (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnegd\t%0, %1") + + +;; ------------------------------------------------------------------------- +;; Float Sqrt instructions +;; ------------------------------------------------------------------------- + +(define_insn "sqrtsf2" + [(set (match_operand:SF 0 "register_operand" "=v") + (sqrt:SF (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fsqrts\t%0, %1") + +(define_insn "sqrtdf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (sqrt:DF (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_divd)" + "fsqrtd\t%0, %1") + + +;; ------------------------------------------------------------------------- +;; Float Add instructions +;; ------------------------------------------------------------------------- + +(define_insn "addsf3" + [(set (match_operand:SF 0 "register_operand" "=v") + (plus:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fadds\t%0, %1, %2") + +(define_insn "adddf3" + [(set (match_operand:DF 0 "register_operand" "=v") + (plus:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "faddd\t%0, %1, %2") + + +;; ------------------------------------------------------------------------- +;; Float Sub instructions +;; ------------------------------------------------------------------------- + +(define_insn "subsf3" + [(set (match_operand:SF 0 "register_operand" "=v") + (minus:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fsubs\t%0, %1, %2") + +(define_insn "subdf3" + [(set (match_operand:DF 0 "register_operand" "=v") + (minus:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fsubd\t%0, %1, %2") + + +;; ------------------------------------------------------------------------- +;; Float Mul instructions +;; ------------------------------------------------------------------------- + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "register_operand" "=v") + (mult:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fmuls\t%0, %1, %2") + +(define_insn "muldf3" + [(set (match_operand:DF 0 "register_operand" "=v") + (mult:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fmuld\t%0, %1, %2") + +(define_insn "*fpuv2_nmulsf3_1" + [(set (match_operand:SF 0 "register_operand" "=v") + (mult:SF (neg:SF (match_operand:SF 1 "register_operand" "%v")) + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnmuls\t%0, %1, %2") + +(define_insn "*fpuv2_nmulsf3_2" + [(set (match_operand:SF 0 "register_operand" "=v") + (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v"))))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnmuls\t%0, %1, %2") + +(define_insn "*fpuv2_nmuldf3_1" + [(set (match_operand:DF 0 "register_operand" "=v") + (mult:DF (neg:DF (match_operand:DF 1 "register_operand" "%v")) + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnmuld\t%0, %1, %2") + +(define_insn "*fpuv2_nmuldf3_2" + [(set (match_operand:DF 0 "register_operand" "=v") + (neg:DF (mult:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v"))))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnmuld\t%0, %1, %2") + + +;; ------------------------------------------------------------------------- +;; Float Div instructions +;; ------------------------------------------------------------------------- + +(define_expand "divsf3" + [(set (match_operand:SF 0 "register_operand" "") + (div:SF (match_operand:SF 1 "csky_arith_float1_operand" "") + (match_operand:SF 2 "register_operand" "")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "") + +(define_insn "*fpuv2_divsf3" + [(set (match_operand:SF 0 "register_operand" "=v") + (div:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fdivs\t%0, %1, %2") + +(define_insn "*fpuv2_1_divsf3" + [(set (match_operand:SF 0 "register_operand" "=v") + (div:SF (match_operand:SF 1 "csky_const_float1_operand" "i") + (match_operand:SF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "frecips\t%0, %2") + + +(define_expand "divdf3" + [(set (match_operand:DF 0 "register_operand" "") + (div:DF (match_operand:DF 1 "csky_arith_float1_operand" "") + (match_operand:DF 2 "register_operand" "")))] + "CSKY_ISA_FEATURE (fpv2_divd)" + "") + +(define_insn "*fpuv2_divdf3" + [(set (match_operand:DF 0 "register_operand" "=v") + (div:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_divd)" + "fdivd\t%0, %1, %2") + +(define_insn "*fpuv2_1_divdf3" + [(set (match_operand:DF 0 "register_operand" "=v") + (div:DF (match_operand:DF 1 "csky_const_float1_operand" "i") + (match_operand:DF 2 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_divd)" + "frecipd\t%0, %2") + + +;; ------------------------------------------------------------------------- +;; Float add(sub) with mult instructions +;; ------------------------------------------------------------------------- + +;; vrz <= vrz + vrx * vry +(define_insn "*fpuv2_fmacs" + [(set (match_operand:SF 0 "register_operand" "=v") + (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")) + (match_operand:SF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fmacs\t%0, %1, %2") + +(define_insn "*fpuv2_fmacd" + [(set (match_operand:DF 0 "register_operand" "=v") + (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")) + (match_operand:DF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fmacd\t%0, %1, %2") + +;; vrz <= vrz - vrx * vry +(define_insn "*fpuv2_fnmacs" + [(set (match_operand:SF 0 "register_operand" "=v") + (minus:SF (match_operand:SF 1 "register_operand" "0") + (mult:SF (match_operand:SF 2 "register_operand" "v") + (match_operand:SF 3 "register_operand" "v"))))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnmacs\t%0, %2, %3") + +(define_insn "*fpuv2_fnmacd" + [(set (match_operand:DF 0 "register_operand" "=v") + (minus:DF (match_operand:DF 1 "register_operand" "0") + (mult:DF (match_operand:DF 2 "register_operand" "v") + (match_operand:DF 3 "register_operand" "v"))))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnmacd\t%0, %2, %3") + +;; vrz <= vrx * vry - vrz +(define_insn "*fpuv2_fmscs" + [(set (match_operand:SF 0 "register_operand" "=v") + (minus:SF (mult:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")) + (match_operand:SF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fmscs\t%0, %1, %2") + +(define_insn "*fpuv2_fmscd" + [(set (match_operand:DF 0 "register_operand" "=v") + (minus:DF (mult:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")) + (match_operand:DF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fmscd\t%0, %1, %2") + +;; vrz = - (vrz + vrx * vry) +(define_insn "*fpuv2_fnmscs_1" + [(set (match_operand:SF 0 "register_operand" "=v") + (minus:SF (mult:SF (neg:SF (match_operand:SF 1 "register_operand" "%v")) + (match_operand:SF 2 "register_operand" "v")) + (match_operand:SF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnmscs\t%0, %1, %2") + +(define_insn "*fpuv2_fnmscs_2" + [(set (match_operand:SF 0 "register_operand" "=v") + (neg:SF (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "v") + (match_operand:SF 2 "register_operand" "v")) + (match_operand:SF 3 "register_operand" "0"))))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fnmscs\t%0, %1, %2") + +(define_insn "*fpuv2_fnmscd_1" + [(set (match_operand:DF 0 "register_operand" "=v") + (minus:DF (mult:DF (neg:DF (match_operand:DF 1 "register_operand" "%v")) + (match_operand:DF 2 "register_operand" "v")) + (match_operand:DF 3 "register_operand" "0")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnmscd\t%0, %1, %2") + +(define_insn "*fpuv2_fnmscd_2" + [(set (match_operand:DF 0 "register_operand" "=v") + (neg:DF (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "v") + (match_operand:DF 2 "register_operand" "v")) + (match_operand:DF 3 "register_operand" "0"))))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fnmscd\t%0, %1, %2") + + +;; ------------------------------------------------------------------------- +;; Float compare instructions +;; ------------------------------------------------------------------------- + +(define_expand "cbranchsf4" + [(set (pc) (if_then_else (match_operator 0 "csky_float_comparison_operator" + [(match_operand:SF 1 "register_operand") + (match_operand:SF 2 "csky_compare_operand_float")]) + (label_ref (match_operand 3 "")) + (pc)))] + "CSKY_ISA_FEATURE (fpv2_sf)" + " + { + enum rtx_code code = GET_CODE (operands[0]); + bool invert = csky_emit_compare_float (code, operands[1], operands[2]); + + if (invert) + emit_jump_insn (gen_csky_jbf (operands[3])); + else + emit_jump_insn (gen_csky_jbt (operands[3])); + + DONE; + }") + +(define_insn "*fpuv2_unordered" + [(set (reg:CC 33) (unordered:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmpuos\t%0, %1") + +(define_insn "*fpuv2_unordered_zero" + [(set (reg:CC 33) (unordered:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmpuos\t%0, %0") + +(define_insn "*fpuv2_ne" + [(set (reg:CC 33) (ne:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmpnes\t%0, %1") + +(define_insn "*fpuv2_gt" + [(set (reg:CC 33) (gt:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmplts\t%1, %0") + +(define_insn "*fpuv2_ge" + [(set (reg:CC 33) (ge:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmphss\t%0, %1") + +(define_insn "*fpuv2_lt" + [(set (reg:CC 33) (lt:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmplts\t%0, %1") + +(define_insn "*fpuv2_le" + [(set (reg:CC 33) (le:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmphss\t%1, %0") + +(define_insn "*fpuv2_gez" + [(set (reg:CC 33) (ge:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmpzhss\t%0") + +(define_insn "*fpuv2_nez" + [(set (reg:CC 33) (ne:CC (match_operand:SF 0 "register_operand" "v") + (match_operand:SF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fcmpznes\t%0") + + +(define_expand "cbranchdf4" + [(set (pc) (if_then_else (match_operator 0 "csky_float_comparison_operator" + [(match_operand:DF 1 "register_operand") + (match_operand:DF 2 "csky_compare_operand_float")]) + (label_ref (match_operand 3 "")) + (pc)))] + "CSKY_ISA_FEATURE (fpv2_df)" + " + { + enum rtx_code code = GET_CODE (operands[0]); + bool invert = csky_emit_compare_float (code, operands[1], operands[2]); + + if (invert) + emit_jump_insn (gen_csky_jbf (operands[3])); + else + emit_jump_insn (gen_csky_jbt (operands[3])); + + DONE; +}") + +(define_insn "*fpuv2_dunordered" + [(set (reg:CC 33) (unordered:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpuod\t%0, %1") + +(define_insn "*fpuv2_dunordered_zero" + [(set (reg:CC 33) (unordered:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpuod\t%0, %0") + +(define_insn "*fpuv2_dne" + [(set (reg:CC 33) (ne:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpned\t%0, %1") + +(define_insn "*fpuv2_dgt" + [(set (reg:CC 33) (gt:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpltd\t%1, %0") + +(define_insn "*fpuv2_dge" + [(set (reg:CC 33) (ge:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmphsd\t%0, %1") + +(define_insn "*fpuv2_dlt" + [(set (reg:CC 33) (lt:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpltd\t%0, %1") + +(define_insn "*fpuv2_dle" + [(set (reg:CC 33) (le:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmphsd\t%1, %0") + +(define_insn "*fpuv2_dgez" + [(set (reg:CC 33) (ge:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpzhsd\t%0") + +(define_insn "*fpuv2_dnez" + [(set (reg:CC 33) (ne:CC (match_operand:DF 0 "register_operand" "v") + (match_operand:DF 1 "csky_const_float0_operand" "i")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fcmpzned\t%0") + + +;; ------------------------------------------------------------------------- +;; Float convert instructions +;; ------------------------------------------------------------------------- + +;; DF <- SF +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (float_extend:DF (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fstod\t%0, %1") + +;; SF <- DF +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=v") + (float_truncate:SF (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fdtos\t%0, %1") + +;; SF <- SI +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "register_operand" "=v") + (float:SF (match_operand:SI 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fsitos\t%0, %1") + +;; DF <- SI +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (float:DF (match_operand:SI 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fsitod\t%0, %1") + +;; SF <- unsigned SI +(define_insn "floatunssisf2" + [(set (match_operand:SF 0 "register_operand" "=v") + (unsigned_float:SF (match_operand:SI 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fuitos\t%0, %1") + +;; DF <- unsigned SI +(define_insn "floatunssidf2" + [(set (match_operand:DF 0 "register_operand" "=v") + (unsigned_float:DF (match_operand:SI 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fuitod\t%0, %1") + +;; SI <- SF +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "register_operand" "=v") + (fix:SI (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fstosi.rz\t%0, %1") + +;; SI <- DF +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "=v") + (fix:SI (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fdtosi.rz\t%0, %1") + +;; unsigned SI <- SF +(define_insn "fixuns_truncsfsi2" + [(set (match_operand:SI 0 "register_operand" "=v") + (unsigned_fix:SI (match_operand:SF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_sf)" + "fstoui.rz\t%0, %1") + +;; unsigned SI <- DF +(define_insn "fixuns_truncdfsi2" + [(set (match_operand:SI 0 "register_operand" "=v") + (unsigned_fix:SI (match_operand:DF 1 "register_operand" "v")))] + "CSKY_ISA_FEATURE (fpv2_df)" + "fdtoui.rz\t%0, %1") + + +;; ------------------------------------------------------------------------- +;; Float mov instructions +;; ------------------------------------------------------------------------- + +;; Note: movsf and movdf patterns are in csky.md. + +;; cstore SF +(define_expand "cstoresf4" + [(set (match_operand:SI 0 "register_operand" "") + (match_operator 1 "ordered_comparison_operator" + [(match_operand:SF 2 "register_operand" "") + (match_operand:SF 3 "csky_compare_operand_float" "")]))] + "CSKY_ISA_FEATURE (fpv2_sf)" + " + { + bool invert = csky_emit_compare_float (GET_CODE (operands[1]), + operands[2], operands[3]); + if (invert) + emit_insn (gen_mvcv (operands[0])); + else + emit_insn (gen_mvc (operands[0])); + DONE; + }" +) + +;; cstore DF +(define_expand "cstoredf4" + [(set (match_operand:SI 0 "register_operand" "") + (match_operator 1 "ordered_comparison_operator" + [(match_operand:DF 2 "register_operand" "") + (match_operand:DF 3 "csky_compare_operand_float" "")]))] + "CSKY_ISA_FEATURE (fpv2_df)" + " + { + bool invert = csky_emit_compare_float (GET_CODE (operands[1]), + operands[2], operands[3]); + if (invert) + emit_insn (gen_mvcv (operands[0])); + else + emit_insn (gen_mvc (operands[0])); + DONE; + }" +) diff --git a/gcc/config/csky/csky_isa.def b/gcc/config/csky/csky_isa.def new file mode 100644 index 00000000000..d99940e8a65 --- /dev/null +++ b/gcc/config/csky/csky_isa.def @@ -0,0 +1,59 @@ +/* ISA feature descriptions for the C-SKY back end. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +/* Before using #include to read this file, define a macro: + CSKY_ISA(CNAME, DESCRIPTION) + */ + +/* Common insns */ +CSKY_ISA (E1, "Extended insns for arch ck801 from base") +CSKY_ISA (E2, "Extended insns for arch ck802 from base") +CSKY_ISA (2E3, "Extended insns for arch ck803 from ck802") +CSKY_ISA (3E3r1, "Extended insns for cpu ck803n from ck803") +CSKY_ISA (3E7, "Extended insns for arch ck807 from ck803") +CSKY_ISA (7E10, "Extended insns for arch ck810 from ck807") + +/* Special insns */ +CSKY_ISA (div, "divide insns") + +/* Extended insns */ +CSKY_ISA (dsp, "Extended insns for DSP") +CSKY_ISA (java, "Extended insns for Java") + +CSKY_ISA (fpv2_sf, "Single precision operations supported") +CSKY_ISA (fpv2_df, "Double precision operations supported") +CSKY_ISA (fpv2_divd, "Double precision div operations supported") + +/* Specific insns mode */ +#ifdef CSKY_ISA_MACRO +#define CSKY_ISA_CK801 CSKY_ISA_FEATURE_GET (E1) +#define CSKY_ISA_CK802 CSKY_ISA_FEATURE_GET (E2) +#define CSKY_ISA_CK803 CSKY_ISA_CK802, CSKY_ISA_FEATURE_GET (2E3), \ + CSKY_ISA_FEATURE_GET (div) +#define CSKY_ISA_CK803R1 CSKY_ISA_CK803, CSKY_ISA_FEATURE_GET (3E3r1) +#define CSKY_ISA_CK807 CSKY_ISA_CK803, CSKY_ISA_FEATURE_GET (3E7) +#define CSKY_ISA_CK810 CSKY_ISA_CK807, CSKY_ISA_FEATURE_GET (7E10) + +#define CSKY_ISA_DSP CSKY_ISA_FEATURE_GET (dsp) + +#define CSKY_ISA_FPv2_SF CSKY_ISA_FEATURE_GET (fpv2_sf) +#define CSKY_ISA_FPv2 CSKY_ISA_FPv2_SF, CSKY_ISA_FEATURE_GET (fpv2_df) +#define CSKY_ISA_FPv2_DIVD CSKY_ISA_FPv2, CSKY_ISA_FEATURE_GET (fpv2_divd) +#endif diff --git a/gcc/config/csky/csky_isa.h b/gcc/config/csky/csky_isa.h new file mode 100644 index 00000000000..24578d70d48 --- /dev/null +++ b/gcc/config/csky/csky_isa.h @@ -0,0 +1,47 @@ +/* ISA feature enumerations for C-SKY targets. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + +#ifndef GCC_CSKY_ISA_FEATURE_H +#define GCC_CSKY_ISA_FEATURE_H + + +#ifndef CSKY_ISA_MACRO +#define CSKY_ISA_MACRO +#endif + +#define CSKY_ISA_FEATURE_DEFINE(x) isa_bit_ ## x +#define CSKY_ISA_FEATURE_GET(x) CSKY_ISA_FEATURE_DEFINE (x) + +enum csky_isa_feature + { + CSKY_ISA_FEATURE_DEFINE (none), +#undef CSKY_ISA +#define CSKY_ISA(IDENT, DESC) \ + CSKY_ISA_FEATURE_DEFINE (IDENT), +#include "csky_isa.def" +#undef CSKY_ISA + CSKY_ISA_FEATURE_DEFINE (max) + }; + +#define CSKY_ISA_FEAT(x) x, +#define CSKY_ISA_FEAT_NONE CSKY_ISA_FEAT (isa_bit_none) + + +#endif /* GCC_CSKY_ISA_FEATURE_H */ diff --git a/gcc/config/csky/csky_opts.h b/gcc/config/csky/csky_opts.h new file mode 100644 index 00000000000..f206537d842 --- /dev/null +++ b/gcc/config/csky/csky_opts.h @@ -0,0 +1,63 @@ +/* Processor and arch enumerations for C-SKY targets. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by C-SKY Microsystems and Mentor Graphics. + + 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 + . */ + + +#ifndef CSKY_OPTS_H +#define CSKY_OPTS_H + + +/* The various CSKY cores. */ +enum csky_processor_type +{ +#undef CSKY_CORE +#define CSKY_CORE(NAME, INTERNAL_IDENT, IDENT, ARCH, ISA) \ + TARGET_CPU_##INTERNAL_IDENT, +#include "csky_cores.def" +#undef CSKY_CORE + /* Used to indicate that no processor has been specified. */ + TARGET_CPU_csky_none +}; +#define CSKY_TARGET_CORE_GET(name) TARGET_CPU_ ## name + +/* The various CSKY architectures. */ +enum csky_base_architecture +{ +#undef CSKY_ARCH +#define CSKY_ARCH(NAME, CORE_IDENT, ARCH, ISA) \ + CSKY_BASE_ARCH_##ARCH, +#include "csky_cores.def" +#undef CSKY_ARCH + CSKY_BASE_ARCH_NONE +}; +#define CSKY_TARGET_ARCH_GET(name) CSKY_BASE_ARCH_ ## name + +/* The various CSKY FPUs. */ +enum csky_fpu_type +{ +#undef CSKY_FPU +#define CSKY_FPU(NAME, CNAME, ISA) TARGET_FPU_##CNAME, +#include "csky_cores.def" + TARGET_FPU_auto +#undef CSKY_FPU +}; +#define CSKY_TARGET_FPU_GET(name) TARGET_FPU_ ## name + + +#endif /* CSKY_OPTS_H */ diff --git a/gcc/config/csky/csky_pipeline_ck801.md b/gcc/config/csky/csky_pipeline_ck801.md new file mode 100644 index 00000000000..00fc465240c --- /dev/null +++ b/gcc/config/csky/csky_pipeline_ck801.md @@ -0,0 +1,54 @@ +;; Scheduler information for C-SKY CK801 processors. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +;; This is just a placeholder for a more accurate pipeline +;; description for CK801. + +(define_automaton "ck801") + +(define_cpu_unit "ck801_ex1" "ck801") +(define_cpu_unit "ck801_exit" "ck801") + +(define_insn_reservation "ck801_generic" 1 + (and (match_test "CSKY_TARGET_ARCH (CK801)") + (eq_attr "type" "alu,cmp,branch,cbranch,addsub,alu_ix,branch_jmp,call_jsr,call")) + "ck801_ex1+ck801_exit") + +(define_insn_reservation "ck801_load" 1 + (and (match_test "CSKY_TARGET_ARCH (CK801)") + (and (eq_attr "type" "load") + (match_test "!csky_minipool_load_p (insn)"))) + "ck801_ex1+ck801_exit") + +(define_insn_reservation "ck801_pool" 1 + (and (match_test "CSKY_TARGET_ARCH (CK801)") + (and (eq_attr "type" "load") + (match_test "csky_minipool_load_p (insn)"))) + "ck801_ex1+ck801_exit") + +(define_insn_reservation "ck801_store" 1 + (and (match_test "CSKY_TARGET_ARCH (CK801)") + (eq_attr "type" "store")) + "ck801_ex1+ck801_exit") + +;; Switching between constant pool loads and loads/stores in the data section +;; carries an extra penalty. +(define_bypass 2 "ck801_load,ck801_store" "ck801_pool") +(define_bypass 2 "ck801_pool" "ck801_load,ck801_store") diff --git a/gcc/config/csky/csky_pipeline_ck802.md b/gcc/config/csky/csky_pipeline_ck802.md new file mode 100644 index 00000000000..f185d8c7757 --- /dev/null +++ b/gcc/config/csky/csky_pipeline_ck802.md @@ -0,0 +1,77 @@ +;; Instruction scheduling information for C-SKY CK802 processors. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +(define_automaton "csky_ck802") + +(define_cpu_unit "csky_ck802_ex" "csky_ck802") +(define_cpu_unit "csky_ck802_wb" "csky_ck802") + +(define_insn_reservation "ck802_alu" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "alu")) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_branch" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "branch, branch_jmp")) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_cmp" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "cmp")) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_cbranch" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "cbranch")) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_call" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "call, call_jsr")) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_load" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (and (eq_attr "type" "load") + (match_test "!csky_minipool_load_p (insn)"))) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_pool" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (and (eq_attr "type" "load") + (match_test "csky_minipool_load_p (insn)"))) + "csky_ck802_ex, csky_ck802_wb") + +(define_insn_reservation "ck802_store" 2 + (and (match_test "CSKY_TARGET_ARCH (CK802)") + (eq_attr "type" "store")) + "csky_ck802_ex, csky_ck802_wb") + +;; Switching between constant pool loads and loads/stores in the data section +;; carries an extra penalty. +(define_bypass 3 "ck802_load,ck802_store" "ck802_pool") +(define_bypass 3 "ck802_pool" "ck802_load,ck802_store") + +(define_bypass 1 "*" "ck802_alu") + +(define_bypass 1 "*" "ck802_branch") + +(define_bypass 2 "ck802_cmp" "ck802_cbranch") diff --git a/gcc/config/csky/csky_pipeline_ck803.md b/gcc/config/csky/csky_pipeline_ck803.md new file mode 100644 index 00000000000..f140cf6d54d --- /dev/null +++ b/gcc/config/csky/csky_pipeline_ck803.md @@ -0,0 +1,64 @@ +;; Scheduler information for C-SKY CK803 processors. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +(define_automaton "ck803") + +(define_cpu_unit "ck803_ex1" "ck803") +(define_cpu_unit "ck803_exit" "ck803") + +(define_insn_reservation "ck803_3cycle" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (eq_attr "type" "alu,cmp,branch,branch_jmp,call_jsr,call")) + "ck803_ex1+ck803_exit") + +(define_insn_reservation "ck803_alu1" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (eq_attr "type" "addsub,alu_ix")) + "ck803_ex1+ck803_exit") + +(define_insn_reservation "ck803_cbranch" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (eq_attr "type" "cbranch")) + "ck803_ex1+ck803_exit") + +(define_insn_reservation "ck803_load" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (and (eq_attr "type" "load") + (match_test "!csky_minipool_load_p (insn)"))) + "ck803_ex1+ck803_exit") + +(define_insn_reservation "ck803_pool" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (and (eq_attr "type" "load") + (match_test "csky_minipool_load_p (insn)"))) + "ck803_ex1+ck803_exit") + +(define_insn_reservation "ck803_store" 1 + (and (match_test "CSKY_TARGET_ARCH (CK803)") + (eq_attr "type" "store")) + "ck803_ex1+ck803_exit") + +;; Switching between constant pool loads and loads/stores in the data section +;; carries an extra penalty. +(define_bypass 2 "ck803_load,ck803_store" "ck803_pool") +(define_bypass 2 "ck803_pool" "ck803_load,ck803_store") + +(define_bypass 2 "ck803_3cycle,ck803_cbranch,ck803_load,ck803_store,ck803_pool" + "ck803_cbranch") diff --git a/gcc/config/csky/csky_pipeline_ck810.md b/gcc/config/csky/csky_pipeline_ck810.md new file mode 100644 index 00000000000..aaba00b868f --- /dev/null +++ b/gcc/config/csky/csky_pipeline_ck810.md @@ -0,0 +1,34 @@ +;; Instruction scheduling information for C-SKY CK810 processors. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + + +;;------------------------------------------------------------- +;; Pipeline descriptions for ck810 +;;------------------------------------------------------------- + +(define_attr "cycle" "1,2,not_used_yet" + (const_string "1")) +(define_automaton "cskyv2_ck810") +(define_cpu_unit "pipeline_alu0" "cskyv2_ck810") +(define_insn_reservation "alu_one_cycle" 1 + (and (eq_attr "cycle" "1") + (not (ior (match_test "CSKY_TARGET_ARCH (CK803)") + (match_test "CSKY_TARGET_ARCH (CK802)")))) + "pipeline_alu0") diff --git a/gcc/config/csky/csky_tables.opt b/gcc/config/csky/csky_tables.opt new file mode 100644 index 00000000000..ae3438d4473 --- /dev/null +++ b/gcc/config/csky/csky_tables.opt @@ -0,0 +1,230 @@ +; -*- buffer-read-only: t -*- +; Generated automatically by csky_genopt.sh from csky_cores.def. + +; Copyright (C) 2018 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 +; . + +Enum +Name(csky_processor_type) Type(enum csky_processor_type) +Known CSKY CPUs (for use with the -mcpu= options): + +EnumValue +Enum(csky_processor_type) String(ck801) Value( TARGET_CPU_ck801) + +EnumValue +Enum(csky_processor_type) String(ck801t) Value( TARGET_CPU_ck801t) + +EnumValue +Enum(csky_processor_type) String(ck802) Value( TARGET_CPU_ck802) + +EnumValue +Enum(csky_processor_type) String(ck802t) Value( TARGET_CPU_ck802t) + +EnumValue +Enum(csky_processor_type) String(ck802j) Value( TARGET_CPU_ck802j) + +EnumValue +Enum(csky_processor_type) String(ck803) Value( TARGET_CPU_ck803) + +EnumValue +Enum(csky_processor_type) String(ck803h) Value( TARGET_CPU_ck803h) + +EnumValue +Enum(csky_processor_type) String(ck803t) Value( TARGET_CPU_ck803t) + +EnumValue +Enum(csky_processor_type) String(ck803ht) Value( TARGET_CPU_ck803ht) + +EnumValue +Enum(csky_processor_type) String(ck803f) Value( TARGET_CPU_ck803f) + +EnumValue +Enum(csky_processor_type) String(ck803fh) Value( TARGET_CPU_ck803fh) + +EnumValue +Enum(csky_processor_type) String(ck803e) Value( TARGET_CPU_ck803e) + +EnumValue +Enum(csky_processor_type) String(ck803eh) Value( TARGET_CPU_ck803eh) + +EnumValue +Enum(csky_processor_type) String(ck803et) Value( TARGET_CPU_ck803et) + +EnumValue +Enum(csky_processor_type) String(ck803eht) Value( TARGET_CPU_ck803eht) + +EnumValue +Enum(csky_processor_type) String(ck803ef) Value( TARGET_CPU_ck803ef) + +EnumValue +Enum(csky_processor_type) String(ck803efh) Value( TARGET_CPU_ck803efh) + +EnumValue +Enum(csky_processor_type) String(ck803ft) Value( TARGET_CPU_ck803ft) + +EnumValue +Enum(csky_processor_type) String(ck803eft) Value( TARGET_CPU_ck803eft) + +EnumValue +Enum(csky_processor_type) String(ck803efht) Value( TARGET_CPU_ck803efht) + +EnumValue +Enum(csky_processor_type) String(ck803r1) Value( TARGET_CPU_ck803r1) + +EnumValue +Enum(csky_processor_type) String(ck803hr1) Value( TARGET_CPU_ck803hr1) + +EnumValue +Enum(csky_processor_type) String(ck803tr1) Value( TARGET_CPU_ck803tr1) + +EnumValue +Enum(csky_processor_type) String(ck803htr1) Value( TARGET_CPU_ck803htr1) + +EnumValue +Enum(csky_processor_type) String(ck803fr1) Value( TARGET_CPU_ck803fr1) + +EnumValue +Enum(csky_processor_type) String(ck803fhr1) Value( TARGET_CPU_ck803fhr1) + +EnumValue +Enum(csky_processor_type) String(ck803er1) Value( TARGET_CPU_ck803er1) + +EnumValue +Enum(csky_processor_type) String(ck803ehr1) Value( TARGET_CPU_ck803ehr1) + +EnumValue +Enum(csky_processor_type) String(ck803etr1) Value( TARGET_CPU_ck803etr1) + +EnumValue +Enum(csky_processor_type) String(ck803ehtr1) Value( TARGET_CPU_ck803ehtr1) + +EnumValue +Enum(csky_processor_type) String(ck803efr1) Value( TARGET_CPU_ck803efr1) + +EnumValue +Enum(csky_processor_type) String(ck803efhr1) Value( TARGET_CPU_ck803efhr1) + +EnumValue +Enum(csky_processor_type) String(ck803ftr1) Value( TARGET_CPU_ck803ftr1) + +EnumValue +Enum(csky_processor_type) String(ck803eftr1) Value( TARGET_CPU_ck803eftr1) + +EnumValue +Enum(csky_processor_type) String(ck803efhtr1) Value( TARGET_CPU_ck803efhtr1) + +EnumValue +Enum(csky_processor_type) String(ck803s) Value( TARGET_CPU_ck803s) + +EnumValue +Enum(csky_processor_type) String(ck803st) Value( TARGET_CPU_ck803st) + +EnumValue +Enum(csky_processor_type) String(ck803se) Value( TARGET_CPU_ck803se) + +EnumValue +Enum(csky_processor_type) String(ck803sf) Value( TARGET_CPU_ck803sf) + +EnumValue +Enum(csky_processor_type) String(ck803sef) Value( TARGET_CPU_ck803sef) + +EnumValue +Enum(csky_processor_type) String(ck803seft) Value( TARGET_CPU_ck803seft) + +EnumValue +Enum(csky_processor_type) String(ck807e) Value( TARGET_CPU_ck807e) + +EnumValue +Enum(csky_processor_type) String(ck807ef) Value( TARGET_CPU_ck807ef) + +EnumValue +Enum(csky_processor_type) String(ck807) Value( TARGET_CPU_ck807) + +EnumValue +Enum(csky_processor_type) String(ck807f) Value( TARGET_CPU_ck807f) + +EnumValue +Enum(csky_processor_type) String(ck810e) Value( TARGET_CPU_ck810e) + +EnumValue +Enum(csky_processor_type) String(ck810et) Value( TARGET_CPU_ck810et) + +EnumValue +Enum(csky_processor_type) String(ck810ef) Value( TARGET_CPU_ck810ef) + +EnumValue +Enum(csky_processor_type) String(ck810eft) Value( TARGET_CPU_ck810eft) + +EnumValue +Enum(csky_processor_type) String(ck810) Value( TARGET_CPU_ck810) + +EnumValue +Enum(csky_processor_type) String(ck810v) Value( TARGET_CPU_ck810v) + +EnumValue +Enum(csky_processor_type) String(ck810f) Value( TARGET_CPU_ck810f) + +EnumValue +Enum(csky_processor_type) String(ck810t) Value( TARGET_CPU_ck810t) + +EnumValue +Enum(csky_processor_type) String(ck810fv) Value( TARGET_CPU_ck810fv) + +EnumValue +Enum(csky_processor_type) String(ck810tv) Value( TARGET_CPU_ck810tv) + +EnumValue +Enum(csky_processor_type) String(ck810ft) Value( TARGET_CPU_ck810ff) + +EnumValue +Enum(csky_processor_type) String(ck810ftv) Value( TARGET_CPU_ck810ftv) + +Enum +Name(csky_arch) Type(int) +Known CSKY architectures (for use with the -march= option): + +EnumValue +Enum(csky_arch) String(ck801) Value(0) + +EnumValue +Enum(csky_arch) String(ck802) Value(1) + +EnumValue +Enum(csky_arch) String(ck803) Value(2) + +EnumValue +Enum(csky_arch) String(ck807) Value(3) + +EnumValue +Enum(csky_arch) String(ck810) Value(4) + +Enum +Name(csky_fpu) Type(enum csky_fpu_type) +Known CSKY FPUs (for use with the -mfpu= option): + +EnumValue +Enum(csky_fpu) String(fpv2_sf) Value(TARGET_FPU_fpv2_sf) + +EnumValue +Enum(csky_fpu) String(fpv2) Value(TARGET_FPU_fpv2) + +EnumValue +Enum(csky_fpu) String(fpv2_divd) Value(TARGET_FPU_fpv2_divd) + +EnumValue +Enum(csky_fpu) String(auto) Value(TARGET_FPU_auto) diff --git a/gcc/config/csky/predicates.md b/gcc/config/csky/predicates.md new file mode 100644 index 00000000000..2899f0bd0b0 --- /dev/null +++ b/gcc/config/csky/predicates.md @@ -0,0 +1,298 @@ +;; Predicates for C-SKY. +;; Copyright (C) 2018 Free Software Foundation, Inc. +;; Contributed by C-SKY Microsystems and Mentor Graphics. +;; +;; 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 +;; . */ + +;; Return 1 if OP is a load multiple operation. + +(define_predicate "csky_load_multiple_operation" + (match_code "parallel") +{ + int count = XVECLEN (op, 0); + int dest_regno; + rtx src_addr; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM + || GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0)) != REG + || XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0) != stack_pointer_rtx) + return 0; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); + src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_DEST (elt)) != REG + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != (unsigned) (dest_regno + i) + || GET_CODE (SET_SRC (elt)) != MEM + || GET_MODE (SET_SRC (elt)) != SImode + || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) + || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4) + return 0; + } + return 1; +}) + +;; Similar, for store multiple. + +(define_predicate "csky_store_multiple_operation" + (match_code "parallel") +{ + int count = XVECLEN (op, 0); + int src_regno; + rtx dest_addr; + int i; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM + || GET_CODE (XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0)) != REG + || XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0) != stack_pointer_rtx + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) + return 0; + + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); + dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned) (src_regno + i) + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != SImode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) + || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4) + return 0; + } + return 1; +}) + + +(define_predicate "csky_arith_K_operand" + (match_code "reg,subreg,const_int") + { + if (register_operand (op, mode)) + return 1; + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_K (INTVAL (op))) + return 1; + return 0; + }) + +(define_predicate "csky_literal_K_operand" + (match_code "const_int") + { + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_K (INTVAL (op))) + return 1; + return 0; + }) + +(define_predicate "csky_literal_I_operand" + (match_code "const_int") + { + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_I (INTVAL (op))) + return 1; + return 0; + }) + +(define_predicate "csky_literal_J_operand" + (match_code "const_int") + { + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_J (INTVAL (op))) + return 1; + return 0; + }) + +(define_predicate "csky_literal_Uk_operand" + (match_code "const_int") + { + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_Uk (INTVAL (op))) + return 1; + return 0; + }) + +;; Nonzero if OP is a register or constant value of 1 + +(define_predicate "csky_arith_int1_operand" + (match_code "reg,subreg,const_int") + { + if (register_operand (op, mode)) + return 1; + if (op == const1_rtx) + return 1; + return 0; + }) + + +;; Nonzero if OP is legal address for function call + +(define_predicate "csky_call_address_operand" + (match_code "reg,subreg,symbol_ref") + { + if (!flag_pic && (GET_CODE (op) == SYMBOL_REF)) + return 1; + if (register_operand (op, mode)) + return 1; + return 0; + }) + +;; Nonzero if OP is a valid source operand for a compare operation. + +(define_predicate "csky_compare_operand" + (match_code "const_int,reg,subreg") + { + if (register_operand (op, mode)) + return 1; + if (GET_CODE (op) == CONST_INT && INTVAL (op) == 0) + return 1; + return 0; + }) + +(define_predicate "csky_literal_K_Uh_operand" + (match_code "const_int") + { + if (CONST_INT_P (op) + && (CSKY_CONST_OK_FOR_K (INTVAL (op)) + || CSKY_CONST_OK_FOR_Uh (INTVAL (op)))) + return 1; + return 0; + }) + +;; True if OP is a mem with an reg + optional displacement address. + +(define_predicate "csky_simple_mem_operand" + (and (match_operand 0 "memory_operand") + (match_test "csky_simple_addr_operand_p (XEXP (op, 0))"))) + +(define_predicate "csky_arith_any_imm_operand" + (match_code "const_int,reg,subreg") + { + if (register_operand (op, mode)) + return 1; + if (CONST_INT_P (op)) + return 1; + return 0; + }) + +(define_predicate "csky_arith_O_operand" + (match_code "reg,subreg,const_int") + { + if (register_operand (op, mode)) + return 1; + if (CONST_INT_P (op) && CSKY_CONST_OK_FOR_O (INTVAL (op))) + return 1; + return 0; + }) + +(define_predicate "csky_unspec_operand" + (match_code "unspec") + { + if (op == NULL || GET_CODE(op) != UNSPEC) + return 0; + return 1; + } +) + +(define_predicate "csky_const_float1_operand" + (and (match_code "const_double") + (match_test "(op == CONST1_RTX (mode))"))) + +(define_predicate "csky_arith_float1_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "csky_const_float1_operand"))) + +(define_predicate "csky_const_float0_operand" + (and (match_code "const_double") + (match_test "(op == CONST0_RTX (mode))"))) + +(define_predicate "csky_compare_operand_float" + (ior (match_operand 0 "register_operand") + (match_operand 0 "csky_const_float0_operand"))) + +(define_special_predicate "registers_push" + (match_code "parallel") +{ + if ((GET_CODE (XVECEXP (op, 0, 0)) != SET) + || (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != UNSPEC) + || (XINT (SET_SRC (XVECEXP (op, 0, 0)), 1) != UNSPEC_PUSHPOP_MULT)) + return false; + return true; +}) + +(define_special_predicate "registers_pop" + (match_code "parallel") +{ + if ((GET_CODE (XVECEXP (op, 0, 1)) != SET) + || (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) != UNSPEC) + || (XINT (SET_SRC (XVECEXP (op, 0, 1)), 1) != UNSPEC_PUSHPOP_MULT)) + return false; + return true; +}) + +(define_predicate "push_memory_operand" + (match_code "mem") +{ + rtx x = XEXP (op, 0); + if (GET_CODE (x) != PRE_MODIFY) + return false; + if (XEXP (x, 0) != stack_pointer_rtx) + return false; + x = XEXP (x, 1); + if (GET_CODE (x) != PLUS) + return false; + if (XEXP (x, 0) != stack_pointer_rtx) + return false; + return CONST_INT_P (XEXP (x, 1)); +}) + +(define_predicate "pop_memory_operand" + (match_code "mem") +{ + rtx x = XEXP (op, 0); + if (GET_CODE (x) != POST_MODIFY) + return false; + if (XEXP (x, 0) != stack_pointer_rtx) + return false; + x = XEXP (x, 1); + if (GET_CODE (x) != PLUS) + return false; + if (XEXP (x, 0) != stack_pointer_rtx) + return false; + return CONST_INT_P (XEXP (x, 1)); +}) + +(define_special_predicate "csky_float_comparison_operator" + (match_code "eq,ne,le,lt,ge,gt,geu,gtu,leu,ltu, + unordered,ordered")) diff --git a/gcc/config/csky/print-sysroot-suffix.sh b/gcc/config/csky/print-sysroot-suffix.sh new file mode 100644 index 00000000000..5cbdc3fc743 --- /dev/null +++ b/gcc/config/csky/print-sysroot-suffix.sh @@ -0,0 +1,147 @@ +#! /bin/sh +# Script to generate SYSROOT_SUFFIX_SPEC equivalent to MULTILIB_OSDIRNAMES +# Arguments are MULTILIB_OSDIRNAMES, MULTILIB_OPTIONS and MULTILIB_MATCHES. + +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. + +# 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 +# . + +# This shell script produces a header file fragment that defines +# SYSROOT_SUFFIX_SPEC. It assumes that the sysroots will have the same +# structure and names used by the multilibs. + +# Invocation: +# print-sysroot-suffix.sh \ +# MULTILIB_OSDIRNAMES \ +# MULTILIB_OPTIONS \ +# MULTILIB_MATCHES \ +# > t-sysroot-suffix.h + +# The three options exactly correspond to the variables of the same +# names defined in the tmake_file fragments. + +# Example: +# sh ./gcc/config/print-sysroot-suffix.sh "a=A" "a b/c/d" "" +# => +# #undef SYSROOT_SUFFIX_SPEC +# #define SYSROOT_SUFFIX_SPEC "" \ +# "%{a:" \ +# "%{b:A/b/;" \ +# "c:A/c/;" \ +# "d:A/d/;" \ +# ":A/};" \ +# ":}" + +# The script uses temporary subscripts in order to permit a recursive +# algorithm without the use of functions. + +set -e + +dirnames="$1" +options="$2" +matches="$3" + +cat > print-sysroot-suffix3.sh <<\EOF +#! /bin/sh +# Print all the multilib matches for this option +result="$1" +EOF +for x in $matches; do + l=`echo $x | sed -e 's/=.*$//' -e 's/?/=/g'` + r=`echo $x | sed -e 's/^.*=//' -e 's/?/=/g'` + echo "[ \"\$1\" = \"$l\" ] && result=\"\$result|$r\"" >> print-sysroot-suffix3.sh +done +echo 'echo $result' >> print-sysroot-suffix3.sh +chmod +x print-sysroot-suffix3.sh + +cat > print-sysroot-suffix2.sh <<\EOF +#! /bin/sh +# Recursive script to enumerate all multilib combinations, match against +# multilib directories and output a spec string of the result. +# Will fold identical trees. + +padding="$1" +optstring="$2" +shift 2 +n="\" \\ +$padding\"" +if [ $# = 0 ]; then +EOF + +pat= +for x in $dirnames; do +# p=`echo $x | sed -e 's,=!,/$=/,'` + p=`echo $x | sed -e 's/=//g'` +# pat="$pat -e 's=^//$p='" + pat="$pat -e 's/$p/g'" +done +echo ' optstring=`echo "/$optstring" | sed '"$pat\`" >> print-sysroot-suffix2.sh +cat >> print-sysroot-suffix2.sh <<\EOF + case $optstring in + //*) + ;; + *) + echo "$optstring" + ;; + esac +else + thisopt="$1" + shift + bit= + lastcond= + result= + for x in `echo "$thisopt" | sed -e 's,/, ,g'`; do + case $x in +EOF +for x in `echo "$options" | sed -e 's,/, ,g'`; do + match=`./print-sysroot-suffix3.sh "$x"` + echo "$x) optmatch=\"$match\" ;;" >> print-sysroot-suffix2.sh +done +cat >> print-sysroot-suffix2.sh <<\EOF + esac + bit=`"$0" "$padding " "$optstring$x/" "$@"` + if [ -z "$lastopt" ]; then + lastopt="$optmatch" + else + if [ "$lastbit" = "$bit" ]; then + lastopt="$lastopt|$optmatch" + else + result="$result$lastopt:$lastbit;$n" + lastopt="$optmatch" + fi + fi + lastbit="$bit" + done + bit=`"$0" "$padding " "$optstring" "$@"` + if [ "$bit" = "$lastbit" ]; then + if [ -z "$result" ]; then + echo "$bit" + else + echo "$n%{$result:$bit}" + fi + else + echo "$n%{$result$lastopt:$lastbit;$n:$bit}" + fi +fi +EOF +chmod +x ./print-sysroot-suffix2.sh +result=`./print-sysroot-suffix2.sh \"\" \"\" $options` +echo "#undef SYSROOT_SUFFIX_SPEC" +echo "#define SYSROOT_SUFFIX_SPEC \"$result\"" +rm print-sysroot-suffix2.sh +rm print-sysroot-suffix3.sh diff --git a/gcc/config/csky/t-csky b/gcc/config/csky/t-csky new file mode 100644 index 00000000000..ab9be184552 --- /dev/null +++ b/gcc/config/csky/t-csky @@ -0,0 +1,29 @@ +# Make rules for all C-SKY targets. +# +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. +# +# 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 +# . + +TM_H += $(srcdir)/config/csky/csky_cores.def +OPTIONS_H_EXTRA += $(srcdir)/config/csky/csky_cores.def + + +$(srcdir)/config/csky/csky_tables.opt: $(srcdir)/config/csky/csky_genopt.sh \ + $(srcdir)/config/csky/csky_cores.def + $(SHELL) $(srcdir)/config/csky/csky_genopt.sh $(srcdir)/config/csky > \ + $(srcdir)/config/csky/csky_tables.opt diff --git a/gcc/config/csky/t-csky-elf b/gcc/config/csky/t-csky-elf new file mode 100644 index 00000000000..6864544da45 --- /dev/null +++ b/gcc/config/csky/t-csky-elf @@ -0,0 +1,107 @@ +# Multilib configuration for csky*-elf. +# +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. +# +# 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 +# . + +# Endiannesses. +MULTILIB_OPTIONS = mlittle-endian/mbig-endian +MULTILIB_DIRNAMES = little big +MULTILIB_MATCHES = mlittle-endian=EL +MULTILIB_MATCHES = mbig-endian=EB +MULTILIB_EXCEPTIONS = + +# Arch variants. +MULTILIB_OPTIONS += mcpu=ck802/mcpu=ck801/mcpu=ck803f/mcpu=ck807f/mcpu=ck810f +MULTILIB_DIRNAMES += ck802 ck801 ck803 ck807 ck810 + +# For arch ck802. +MULTILIB_MATCHES += mcpu?ck802=march?ck802 +MULTILIB_MATCHES += mcpu?ck802=mcpu?ck802t +MULTILIB_MATCHES += mcpu?ck802=mcpu?ck802j + +# For arch ck801. +MULTILIB_MATCHES += mcpu?ck801=march?ck801 +MULTILIB_MATCHES += mcpu?ck801=mcpu?ck801t + +# For arch ck803. +MULTILIB_MATCHES += mcpu?ck803f=march?ck803 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803fh +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803h +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803t +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ht +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803e +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803eh +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803et +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803eht +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ef +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803efh +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ft +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803eft +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803efht +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803r1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803fr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803fhr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803hr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803tr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803htr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803er1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ehr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803etr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ehtr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803efr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803efhr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803ftr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803eftr1 +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803efhtr1 + +# For arch ck803s. +MULTILIB_MATCHES += mcpu?ck803f=march?ck803s +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803s +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803st +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803se +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803sf +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803sef +MULTILIB_MATCHES += mcpu?ck803f=mcpu?ck803seft + +# For arch ck810. +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810e +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810et +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810ef +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810eft +MULTILIB_MATCHES += mcpu?ck810f=march?ck810 +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810 +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810v +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810t +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810vf +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810tv +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810ft +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810ftv + +# For arch ck807. +MULTILIB_MATCHES += mcpu?ck807f=march?ck807e +MULTILIB_MATCHES += mcpu?ck807f=march?ck807ef +MULTILIB_MATCHES += mcpu?ck807f=march?ck807 +MULTILIB_MATCHES += mcpu?ck807f=mcpu?ck807 + +# For option -msoft-float/-mhard-float. +MULTILIB_OPTIONS += msoft-float/mhard-float +MULTILIB_DIRNAMES += soft-fp hard-fp +MULTILIB_EXCEPTIONS += *mcpu=ck801/*mhard-float* +MULTILIB_EXCEPTIONS += *mcpu=ck802/*mhard-float* diff --git a/gcc/config/csky/t-csky-linux b/gcc/config/csky/t-csky-linux new file mode 100644 index 00000000000..4a145a6fbf3 --- /dev/null +++ b/gcc/config/csky/t-csky-linux @@ -0,0 +1,52 @@ +# Multilib configuration for csky*-linux-*. +# +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. +# +# 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 +# . + + +# Endiannesses. +MULTILIB_OPTIONS = mlittle-endian/mbig-endian +MULTILIB_DIRNAMES = little big +MULTILIB_MATCHES = mlittle-endian=EL +MULTILIB_MATCHES = mbig-endian=EB + +MULTILIB_EXCEPTIONS = +CSKY_MULTILIB_OSDIRNAMES = mbig-endian=/big mlittle-endian=/. mhard-float=/hard-fp msoft-float=/. mcpu.ck810f=/. mcpu.ck807f=/ck807 + +# Arch variants. +MULTILIB_OPTIONS += mcpu=ck810f/mcpu=ck807f +MULTILIB_DIRNAMES += ck810 ck807 + +# For ck807. +MULTILIB_MATCHES += mcpu?ck807f=march?ck807 +MULTILIB_MATCHES += mcpu?ck807f=mcpu?ck807 + +# For arch ck810. +MULTILIB_MATCHES += mcpu?ck810f=march?ck810 +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810 +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810v +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810t +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810vt +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810vf +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810ft +MULTILIB_MATCHES += mcpu?ck810f=mcpu?ck810vft + +# For option -msoft-float/-mhard-float. +MULTILIB_OPTIONS += msoft-float/mhard-float +MULTILIB_DIRNAMES += soft-fp hard-fp diff --git a/gcc/config/csky/t-sysroot-suffix b/gcc/config/csky/t-sysroot-suffix new file mode 100644 index 00000000000..97c03d30bce --- /dev/null +++ b/gcc/config/csky/t-sysroot-suffix @@ -0,0 +1,28 @@ +# Makefile fragment for C-SKY sysroot suffix. +# +# Copyright (C) 2018 Free Software Foundation, Inc. +# Contributed by C-SKY Microsystems and Mentor Graphics. +# +# 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 +# . + +# Generate SYSROOT_SUFFIX_SPEC from MULTILIB_OSDIRNAMES. + +sysroot-suffix.h: $(srcdir)/config/csky/print-sysroot-suffix.sh + $(SHELL) $(srcdir)/config/csky/print-sysroot-suffix.sh \ + "$(CSKY_MULTILIB_OSDIRNAMES)" "$(MULTILIB_OPTIONS)" \ + "$(MULTILIB_MATCHES)" > tmp-sysroot-suffix.h + mv tmp-sysroot-suffix.h $@ -- 2.30.2