From 6b3d1e471a4de2a93320d1fc14549b4a49223b9d Mon Sep 17 00:00:00 2001 From: Stan Cox Date: Fri, 8 Aug 2003 11:30:25 +0000 Subject: [PATCH] iq2000: New port. * config/iq2000: New port. * config.gcc (iq2000-*-elf): Added. * doc/install.texi (Specific): Add iq2000 description. From-SVN: r70245 --- gcc/ChangeLog | 6 + gcc/config.gcc | 7 + gcc/config/iq2000/abi | 232 +++ gcc/config/iq2000/iq2000-protos.h | 64 + gcc/config/iq2000/iq2000.c | 3745 +++++++++++++++++++++++++++++++++++ gcc/config/iq2000/iq2000.h | 1486 ++++++++++++++ gcc/config/iq2000/iq2000.md | 2553 ++++++++++++++++++++++++ gcc/config/iq2000/lib2extra-funcs.c | 17 + gcc/config/iq2000/t-iq2000 | 36 + gcc/config/iq2000/xm-iq2000.h | 35 + gcc/doc/install.texi | 9 + 11 files changed, 8190 insertions(+) create mode 100644 gcc/config/iq2000/abi create mode 100644 gcc/config/iq2000/iq2000-protos.h create mode 100644 gcc/config/iq2000/iq2000.c create mode 100644 gcc/config/iq2000/iq2000.h create mode 100644 gcc/config/iq2000/iq2000.md create mode 100644 gcc/config/iq2000/lib2extra-funcs.c create mode 100644 gcc/config/iq2000/t-iq2000 create mode 100644 gcc/config/iq2000/xm-iq2000.h diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a8f1f91..0645031 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2003-08-08 Stan Cox + + * config/iq2000: New port. + * config.gcc (iq2000-*-elf): Added. + * doc/install.texi (Specific): Add iq2000 description. + 2003-08-08 Andreas Schwab * configure.in (gcc_cv_as_ia64_ltoffx_ldxmov_relocs): Fix quoting diff --git a/gcc/config.gcc b/gcc/config.gcc index 15ff2ce..0cb8938 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -1296,6 +1296,13 @@ ia64*-*-hpux*) ip2k-*-elf) tm_file="elfos.h ${tm_file}" ;; +iq2000*-*-elf*) + tm_file="iq2000/iq2000.h" + tmake_file=iq2000/t-iq2000 + out_file=iq2000/iq2000.c + xm_file=iq2000/xm-iq2000.h + md_file=iq2000/iq2000.md + ;; m32r-*-elf*) tm_file="dbxelf.h elfos.h svr4.h ${tm_file}" extra_parts="crtinit.o crtfini.o" diff --git a/gcc/config/iq2000/abi b/gcc/config/iq2000/abi new file mode 100644 index 0000000..fa49a82 --- /dev/null +++ b/gcc/config/iq2000/abi @@ -0,0 +1,232 @@ + IQ2000 ABI + ========= + +Sizes and alignments +-------------------- + + Type Size (bytes) Alignment (bytes) + + char 1 1 + short 2 2 + int 4 4 + unsigned 4 4 + long 4 4 + long long 8 8 + float 4 4 + double 8 8 + pointers 4 4 + +* alignment within aggregates (structs and unions) is as above, with + padding added if needed +* aggregates have alignment equal to that of their most aligned + member +* aggregates have sizes which are a multiple of their alignment + + +Floating point +-------------- + +All emulated using IEEE floating point conventions. + +Registers +---------------- + +%0 always zero +%1 call clobbered +%2 return value +%3 return value +%4 argument register 1 +%5 argument register 2 +%6 argument register 3 +%7 argument register 4 +%8 argument register 5 +%9 argument register 6 +%10 argument register 7 +%11 argument register 8 +%12 call clobbered +%13 call clobbered +%14 call clobbered +%15 call clobbered +%16 call saved +%17 call saved +%18 call saved +%19 call saved +%20 call saved +%21 call saved +%22 call saved +%23 call saved +%24 call clobbered +%25 call clobbered +%26 reserved +%27 frame ptr +%28 global ptr +%29 stack ptr +%30 reserved +%31 return address + +Stack alignment 8 bytes + +Structures passed <= 32 bits as values, else as pointers + +The IQ2000 Stack +--------------- + +Space is allocated as needed in the stack frame for the following at compile +time: + +* Outgoing parameters beyond the eighth + +* All automatic arrays, automatic data aggregates, automatic + scalars which must be addressable, and automatic scalars for + which there is no room in registers + +* Compiler-generated temporary values (typically when there are + too many for the compiler to keep them all in registers) + +Space can be allocated dynamically (at runtime) in the stack frame for the +following: + +* Memory allocated using the alloca() function of the C library + +Addressable automatic variables on the stack are addressed with positive +offsets relative to %27; dynamically allocated space is addressed with positive +offsets from the pointer returned by alloca(). + +Stack Frame +----------- + + +-----------------------+ + | Caller memory args | + +-----------------------+ <-sp + | Return address | + +-----------------------+ + | Previous FP | + +-----------------------+ + | Saved Registers | + +-----------------------+ + | ... | + +-----------------------+ + | Local Variables | + +-----------------------+ <-fp + | Alloca | + +-----------------------+ + | ... | + +-----------------------+ + | Parameter Word 2 | + +-----------------------+ + | Parameter Word 1 | + +-----------------------+ <-sp + + +Parameter Assignment to Registers +--------------------------------- + +Consider the parameters in a function call as ordered from left (first +parameter) to right. GR contains the number of the next available +general-purpose register. STARG is the address of the next available stack +parameter word. + +INITIALIZE: + Set GR=r4 and STARG to point to parameter word 1. + +SCAN: + If there are no more parameters, terminate. + Otherwise, select one of the following depending on the type + of the next parameter: + + SIMPLE ARG: + + A SIMPLE ARG is one of the following: + + * One of the simple integer types which will fit into a + general-purpose register, + * A pointer to an object of any type, + * A struct or union small enough to fit in a register (<= 32 bits) + * A larger struct or union, which shall be treated as a + pointer to the object or to a copy of the object. + (See below for when copies are made.) + + If GR > r11, go to STACK. Otherwise, load the parameter value into + general-purpose register GR and advance GR to the next general-purpose + register. Values shorter than the register size are sign-extended or + zero-extended depending on whether they are signed or unsigned. Then + go to SCAN. + + DOUBLE or LONG LONG + + If GR > r10, go to STACK. Otherwise, if GR is odd, advance GR to the + next register. Load the 64-bit long long or double value into register + pair GR and GR+1. Advance GR to GR+2 and go to SCAN. + + STACK: + + Parameters not otherwise handled above are passed in the parameter + words of the caller's stack frame. SIMPLE ARGs, as defined above, are + considered to have size and alignment equal to the size of a + general-purpose register, with simple argument types shorter than this + sign- or zero-extended to this width. Round STARG up to a multiple of + the alignment requirement of the parameter and copy the argument + byte-for-byte into STARG, STARG+1, ... STARG+size-1. Set STARG to + STARG+size and go to SCAN. + + +Structure passing +----------------- + +As noted above, code which passes structures and unions by value is implemented +specially. (In this section, "struct" will refer to structs and unions +inclusively.) Structs small enough to fit in a register are passed by value in +a single register or in a stack frame slot the size of a register. Structs +containing a single double or long long component are passed by value in two +registers or in a stack frame slot the size of two registers. Other structs +are handled by passing the address of the structure. In this case, a copy of +the structure will be made if necessary in order to preserve the pass-by-value +semantics. + +Copies of large structs are made under the following rules: + + ANSI mode K&R Mode + --------- -------- +Normal param Callee copies if needed Caller copies +Varargs (...) param Caller copies Caller copies + +In the case of normal (non-varargs) large-struct parameters in ANSI mode, the +callee is responsible for producing the same effect as if a copy of the +structure were passed, preserving the pass-by-value semantics. This may be +accomplished by having the callee make a copy, but in some cases the callee may +be able to determine that a copy is not necessary in order to produce the same +results. In such cases, the callee may choose to avoid making a copy of the +parameter. + + +Varargs handling +---------------- + +No special changes are needed for handling varargs parameters other than the +caller knowing that a copy is needed on struct parameters larger than a +register (see above). + +The varargs macros set up a register save area for the general-purpose +registers to be saved. Because the save area lies between the caller and +callee stack frames, the saved register parameters are contiguous with +parameters passed on the stack. A pointer advances from the register save area +into the caller's stack frame. + + +Function return values +---------------------- + + Type Register + ---- -------- + int r2 + short r2 + long r2 + long long r2-r3 + float r2 + double r2-r3 + struct/union see below + +Structs/unions which will fit into two general-purpose registers are returned +in r2, or in r2-r3 if necessary. Larger structs/unions are handled by the +caller passing as a "hidden" first argument a pointer to space allocated to +receive the return value. diff --git a/gcc/config/iq2000/iq2000-protos.h b/gcc/config/iq2000/iq2000-protos.h new file mode 100644 index 0000000..6a28074 --- /dev/null +++ b/gcc/config/iq2000/iq2000-protos.h @@ -0,0 +1,64 @@ +/* Definitions of target machine for GNU compiler for iq2000. + Copyright (C) 2003 Free Software Foundation, Inc. + + This file is part of GNU CC. + + GNU CC 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 2, or (at your option) + any later version. + + GNU CC 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 GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef GCC_IQ2000_PROTOS_H +#define GCC_IQ2000_PROTOS_H + +extern int iq2000_check_split PARAMS ((rtx, enum machine_mode)); +extern int iq2000_reg_mode_ok_for_base_p PARAMS ((rtx, enum machine_mode, int)); +extern int iq2000_legitimate_address_p PARAMS ((enum machine_mode, rtx, int)); +extern const char* iq2000_fill_delay_slot PARAMS ((const char*, enum delay_type, rtx*, rtx)); +extern const char *iq2000_move_1word PARAMS ((rtx *, rtx, int)); +extern int iq2000_address_cost PARAMS ((rtx)); +extern void override_options PARAMS ((void)); +extern HOST_WIDE_INT iq2000_debugger_offset PARAMS ((rtx, HOST_WIDE_INT)); +extern void final_prescan_insn PARAMS ((rtx, rtx*, int)); +extern HOST_WIDE_INT compute_frame_size PARAMS ((HOST_WIDE_INT)); +extern int iq2000_initial_elimination_offset (int, int); +extern void iq2000_expand_prologue PARAMS ((void)); +extern void iq2000_expand_epilogue PARAMS ((void)); +extern void iq2000_expand_eh_return PARAMS ((rtx)); +extern int iq2000_can_use_return_insn PARAMS ((void)); +int function_arg_pass_by_reference PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int)); +int iq2000_adjust_insn_length PARAMS ((rtx, int)); +char *iq2000_output_conditional_branch PARAMS ((rtx, rtx*, int, int, int, int)); +extern void iq2000_init_builtins PARAMS ((void)); +extern void iq2000_setup_incoming_varargs PARAMS ((CUMULATIVE_ARGS, int, tree, int*, int)); +extern void print_operand_address PARAMS ((FILE*, rtx)); +extern void print_operand PARAMS ((FILE*, rtx, int)); + +#ifdef RTX_CODE +extern rtx gen_int_relational PARAMS ((enum rtx_code, rtx, rtx, rtx, int*)); +extern void gen_conditional_branch PARAMS ((rtx *, enum rtx_code)); +#endif + +#ifdef TREE_CODE +extern void init_cumulative_args PARAMS ((CUMULATIVE_ARGS*, tree, rtx)); +extern void function_arg_advance PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int)); +extern struct rtx_def* function_arg PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int)); +extern int function_arg_partial_nregs PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int)); +extern void iq2000_va_start PARAMS ((tree, rtx)); +extern rtx iq2000_va_arg PARAMS ((tree, tree)); +extern rtx iq2000_function_value PARAMS ((tree, tree)); +extern rtx iq2000_expand_builtin PARAMS ((tree, rtx, rtx, + enum machine_mode, int)); +#endif + +#endif /* ! GCC_IQ2000_PROTOS_H */ diff --git a/gcc/config/iq2000/iq2000.c b/gcc/config/iq2000/iq2000.c new file mode 100644 index 0000000..f9deb8a --- /dev/null +++ b/gcc/config/iq2000/iq2000.c @@ -0,0 +1,3745 @@ +/* Subroutines used for code generation on Vitesse IQ2000 processors + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "optabs.h" +#include "libfuncs.h" +#include "recog.h" +#include "toplev.h" +#include "reload.h" +#include "ggc.h" +#include "tm_p.h" +#include "debug.h" +#include "target.h" +#include "target-def.h" + +/* Enumeration for all of the relational tests, so that we can build + arrays indexed by the test type, and not worry about the order + of EQ, NE, etc. */ + +enum internal_test { + ITEST_EQ, + ITEST_NE, + ITEST_GT, + ITEST_GE, + ITEST_LT, + ITEST_LE, + ITEST_GTU, + ITEST_GEU, + ITEST_LTU, + ITEST_LEU, + ITEST_MAX + }; + +struct constant; + +static void iq2000_count_memory_refs PARAMS ((rtx, int)); +static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code)); +static rtx iq2000_add_large_offset_to_sp PARAMS ((HOST_WIDE_INT)); +static void iq2000_annotate_frame_insn PARAMS ((rtx, rtx)); +static void iq2000_emit_frame_related_store PARAMS ((rtx, rtx, + HOST_WIDE_INT)); +static struct machine_function * iq2000_init_machine_status PARAMS ((void)); +static void save_restore_insns PARAMS ((int)); +static void abort_with_insn PARAMS ((rtx, const char *)) + ATTRIBUTE_NORETURN; +static int symbolic_expression_p PARAMS ((rtx)); +static enum processor_type iq2000_parse_cpu PARAMS ((const char *)); +static void iq2000_select_rtx_section PARAMS ((enum machine_mode, rtx, + unsigned HOST_WIDE_INT)); +static void iq2000_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT)); +static rtx expand_one_builtin PARAMS ((enum insn_code, rtx, tree, enum rtx_code*, + int)); + +/* Structure to be filled in by compute_frame_size with register + save masks, and offsets for the current function. */ + +struct iq2000_frame_info +{ + long total_size; /* # bytes that the entire frame takes up */ + long var_size; /* # bytes that variables take up */ + long args_size; /* # bytes that outgoing arguments take up */ + long extra_size; /* # bytes of extra gunk */ + int gp_reg_size; /* # bytes needed to store gp regs */ + int fp_reg_size; /* # bytes needed to store fp regs */ + long mask; /* mask of saved gp registers */ + long gp_save_offset; /* offset from vfp to store gp registers */ + long fp_save_offset; /* offset from vfp to store fp registers */ + long gp_sp_offset; /* offset from new sp to store gp registers */ + long fp_sp_offset; /* offset from new sp to store fp registers */ + int initialized; /* != 0 if frame size already calculated */ + int num_gp; /* number of gp registers saved */ +}; + +struct machine_function +{ + /* Current frame information, calculated by compute_frame_size. */ + struct iq2000_frame_info frame; +}; + +/* Global variables for machine-dependent things. */ + +/* Count the number of .file directives, so that .loc is up to date. */ +int num_source_filenames = 0; + +/* Files to separate the text and the data output, so that all of the data + can be emitted before the text, which will mean that the assembler will + generate smaller code, based on the global pointer. */ +FILE *asm_out_data_file; +FILE *asm_out_text_file; + +/* The next branch instruction is a branch likely, not branch normal. */ +int iq2000_branch_likely; + +/* Count of delay slots and how many are filled. */ +int dslots_load_total; +int dslots_load_filled; +int dslots_jump_total; +int dslots_jump_filled; + +/* # of nops needed by previous insn */ +int dslots_number_nops; + +/* Number of 1/2/3 word references to data items (ie, not jal's). */ +int num_refs[3]; + +/* registers to check for load delay */ +rtx iq2000_load_reg, iq2000_load_reg2, iq2000_load_reg3, iq2000_load_reg4; + +/* Cached operands, and operator to compare for use in set/branch/trap + on condition codes. */ +rtx branch_cmp[2]; + +/* what type of branch to use */ +enum cmp_type branch_type; + +/* The target cpu for code generation. */ +enum processor_type iq2000_arch; + +/* The target cpu for optimization and scheduling. */ +enum processor_type iq2000_tune; + +/* which instruction set architecture to use. */ +int iq2000_isa; + +/* Strings to hold which cpu and instruction set architecture to use. */ +const char *iq2000_cpu_string; /* for -mcpu= */ +const char *iq2000_arch_string; /* for -march= */ + +/* Mode used for saving/restoring general purpose registers. */ +static enum machine_mode gpr_mode; + +/* List of all IQ2000 punctuation characters used by print_operand. */ +char iq2000_print_operand_punct[256]; + +/* Initialize the GCC target structure. */ +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS iq2000_init_builtins + +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN iq2000_expand_builtin + +#undef TARGET_ASM_SELECT_RTX_SECTION +#define TARGET_ASM_SELECT_RTX_SECTION iq2000_select_rtx_section + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Return 1 if OP can be used as an operand where a register or 16 bit unsigned + integer is needed. */ + +int +uns_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op)) + return 1; + + return register_operand (op, mode); +} + +/* Return 1 if OP can be used as an operand where a 16 bit integer is needed. */ + +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == CONST_INT && SMALL_INT (op)) + return 1; + + return register_operand (op, mode); +} + +/* Return 1 if OP is a integer which fits in 16 bits */ + +int +small_int (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); +} + +/* Return 1 if OP is a 32 bit integer which is too big to be loaded with one + instruction. */ + +int +large_int (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + HOST_WIDE_INT value; + + if (GET_CODE (op) != CONST_INT) + return 0; + + value = INTVAL (op); + + /* ior reg,$r0,value */ + if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0) + return 0; + + /* subu reg,$r0,value */ + if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767) + return 0; + + /* lui reg,value>>16 */ + if ((value & 0x0000ffff) == 0) + return 0; + + return 1; +} + +/* Return 1 if OP is a register or the constant 0. */ + +int +reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case CONST_INT: + return INTVAL (op) == 0; + + case CONST_DOUBLE: + return op == CONST0_RTX (mode); + + case REG: + case SUBREG: + return register_operand (op, mode); + + default: + break; + } + + return 0; +} + +/* Return 1 if OP is a memory operand that fits in a single instruction + (ie, register + small offset). */ + +int +simple_memory_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + rtx addr, plus0, plus1; + + /* Eliminate non-memory operations */ + if (GET_CODE (op) != MEM) + return 0; + + /* dword operations really put out 2 instructions, so eliminate them. */ + if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD) + return 0; + + /* Decode the address now. */ + addr = XEXP (op, 0); + switch (GET_CODE (addr)) + { + case REG: + case LO_SUM: + return 1; + + case CONST_INT: + return SMALL_INT (addr); + + case PLUS: + plus0 = XEXP (addr, 0); + plus1 = XEXP (addr, 1); + if (GET_CODE (plus0) == REG + && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1) + && SMALL_INT_UNSIGNED (plus1) /* No negative offsets */) + return 1; + + else if (GET_CODE (plus1) == REG + && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0) + && SMALL_INT_UNSIGNED (plus1) /* No negative offsets */) + return 1; + + else + return 0; + + case SYMBOL_REF: + return 0; + + default: + break; + } + + return 0; +} + +/* Return nonzero if the code of this rtx pattern is EQ or NE. */ + +int +equality_op (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != GET_MODE (op)) + return 0; + + return GET_CODE (op) == EQ || GET_CODE (op) == NE; +} + +/* Return nonzero if the code is a relational operations (EQ, LE, etc.) */ + +int +cmp_op (op, mode) + rtx op; + enum machine_mode mode; +{ + if (mode != GET_MODE (op)) + return 0; + + return GET_RTX_CLASS (GET_CODE (op)) == '<'; +} + +/* Return nonzero if the operand is either the PC or a label_ref. */ + +int +pc_or_label_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + if (op == pc_rtx) + return 1; + + if (GET_CODE (op) == LABEL_REF) + return 1; + + return 0; +} + +/* Return nonzero if OP is a valid operand for a call instruction. */ + +int +call_insn_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + return (CONSTANT_ADDRESS_P (op) + || (GET_CODE (op) == REG && op != arg_pointer_rtx + && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER + && REGNO (op) <= LAST_VIRTUAL_REGISTER))); +} + +/* Return nonzero if OP is valid as a source operand for a move instruction. */ + +int +move_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* Accept any general operand after reload has started; doing so + avoids losing if reload does an in-place replacement of a register + with a SYMBOL_REF or CONST. */ + return (general_operand (op, mode) + && (! (iq2000_check_split (op, mode)) + || reload_in_progress || reload_completed)); +} + +/* Return nonzero if OP is a constant power of 2. */ + +int +power_of_2_operand (op, mode) + rtx op; + enum machine_mode mode ATTRIBUTE_UNUSED; +{ + int intval; + + if (GET_CODE (op) != CONST_INT) + return 0; + else + intval = INTVAL (op); + + return ((intval & ((unsigned)(intval) - 1)) == 0); +} + +/* Return nonzero if we split the address into high and low parts. */ + +int +iq2000_check_split (address, mode) + rtx address; + enum machine_mode mode; +{ + /* This is the same check used in simple_memory_operand. + We use it here because LO_SUM is not offsettable. */ + if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD) + return 0; + + if ((GET_CODE (address) == SYMBOL_REF) + || (GET_CODE (address) == CONST + && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF) + || GET_CODE (address) == LABEL_REF) + return 1; + + return 0; +} + +/* Return nonzero if REG is valid for MODE. */ + +int +iq2000_reg_mode_ok_for_base_p (reg, mode, strict) + rtx reg; + enum machine_mode mode ATTRIBUTE_UNUSED; + int strict; +{ + return (strict + ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode) + : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode)); +} + +/* Return a nonzero value if XINSN is a legitimate address for a + memory operand of the indicated MODE. STRICT is non-zero if this + function is called during reload. */ + +int +iq2000_legitimate_address_p (mode, xinsn, strict) + enum machine_mode mode; + rtx xinsn; + int strict; +{ + if (TARGET_DEBUG_A_MODE) + { + GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n", + strict ? "" : "not "); + GO_DEBUG_RTX (xinsn); + } + + /* Check for constant before stripping off SUBREG, so that we don't + accept (subreg (const_int)) which will fail to reload. */ + if (CONSTANT_ADDRESS_P (xinsn) + && ! (iq2000_check_split (xinsn, mode)) + && ! (GET_CODE (xinsn) == CONST_INT && ! SMALL_INT (xinsn))) + return 1; + + while (GET_CODE (xinsn) == SUBREG) + xinsn = SUBREG_REG (xinsn); + + if (GET_CODE (xinsn) == REG + && iq2000_reg_mode_ok_for_base_p (xinsn, mode, strict)) + return 1; + + if (GET_CODE (xinsn) == LO_SUM) + { + register rtx xlow0 = XEXP (xinsn, 0); + register rtx xlow1 = XEXP (xinsn, 1); + + while (GET_CODE (xlow0) == SUBREG) + xlow0 = SUBREG_REG (xlow0); + if (GET_CODE (xlow0) == REG + && iq2000_reg_mode_ok_for_base_p (xlow0, mode, strict) + && iq2000_check_split (xlow1, mode)) + return 1; + } + + if (GET_CODE (xinsn) == PLUS) + { + register rtx xplus0 = XEXP (xinsn, 0); + register rtx xplus1 = XEXP (xinsn, 1); + register enum rtx_code code0; + register enum rtx_code code1; + + while (GET_CODE (xplus0) == SUBREG) + xplus0 = SUBREG_REG (xplus0); + code0 = GET_CODE (xplus0); + + while (GET_CODE (xplus1) == SUBREG) + xplus1 = SUBREG_REG (xplus1); + code1 = GET_CODE (xplus1); + + if (code0 == REG + && iq2000_reg_mode_ok_for_base_p (xplus0, mode, strict)) + { + if (code1 == CONST_INT && SMALL_INT (xplus1) + && SMALL_INT_UNSIGNED (xplus1) /* No negative offsets */) + return 1; + } + } + + if (TARGET_DEBUG_A_MODE) + GO_PRINTF ("Not a legitimate address\n"); + + /* The address was not legitimate. */ + return 0; +} + +/* Returns an operand string for the given instruction's delay slot, + after updating filled delay slot statistics. + + We assume that operands[0] is the target register that is set. + + In order to check the next insn, most of this functionality is moved + to FINAL_PRESCAN_INSN, and we just set the global variables that + it needs. */ + +const char * +iq2000_fill_delay_slot (ret, type, operands, cur_insn) + const char *ret; /* normal string to return */ + enum delay_type type; /* type of delay */ + rtx operands[]; /* operands to use */ + rtx cur_insn; /* current insn */ +{ + register rtx set_reg; + register enum machine_mode mode; + register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX; + register int num_nops; + + if (type == DELAY_LOAD || type == DELAY_FCMP) + num_nops = 1; + + else + num_nops = 0; + + /* Make sure that we don't put nop's after labels. */ + next_insn = NEXT_INSN (cur_insn); + while (next_insn != 0 + && (GET_CODE (next_insn) == NOTE + || GET_CODE (next_insn) == CODE_LABEL)) + next_insn = NEXT_INSN (next_insn); + + dslots_load_total += num_nops; + if (TARGET_DEBUG_C_MODE + || type == DELAY_NONE + || operands == 0 + || cur_insn == 0 + || next_insn == 0 + || GET_CODE (next_insn) == CODE_LABEL + || (set_reg = operands[0]) == 0) + { + dslots_number_nops = 0; + iq2000_load_reg = 0; + iq2000_load_reg2 = 0; + iq2000_load_reg3 = 0; + iq2000_load_reg4 = 0; + return ret; + } + + set_reg = operands[0]; + if (set_reg == 0) + return ret; + + while (GET_CODE (set_reg) == SUBREG) + set_reg = SUBREG_REG (set_reg); + + mode = GET_MODE (set_reg); + dslots_number_nops = num_nops; + iq2000_load_reg = set_reg; + if (GET_MODE_SIZE (mode) + > (unsigned) (UNITS_PER_WORD)) + iq2000_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1); + else + iq2000_load_reg2 = 0; + + return ret; +} + +/* Determine whether a memory reference takes one (based off of the GP + pointer), two (normal), or three (label + reg) instructions, and bump the + appropriate counter for -mstats. */ + +static void +iq2000_count_memory_refs (op, num) + rtx op; + int num; +{ + int additional = 0; + int n_words = 0; + rtx addr, plus0, plus1; + enum rtx_code code0, code1; + int looping; + + if (TARGET_DEBUG_B_MODE) + { + fprintf (stderr, "\n========== iq2000_count_memory_refs:\n"); + debug_rtx (op); + } + + /* Skip MEM if passed, otherwise handle movsi of address. */ + addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0); + + /* Loop, going through the address RTL. */ + do + { + looping = FALSE; + switch (GET_CODE (addr)) + { + case REG: + case CONST_INT: + case LO_SUM: + break; + + case PLUS: + plus0 = XEXP (addr, 0); + plus1 = XEXP (addr, 1); + code0 = GET_CODE (plus0); + code1 = GET_CODE (plus1); + + if (code0 == REG) + { + additional++; + addr = plus1; + looping = 1; + continue; + } + + if (code0 == CONST_INT) + { + addr = plus1; + looping = 1; + continue; + } + + if (code1 == REG) + { + additional++; + addr = plus0; + looping = 1; + continue; + } + + if (code1 == CONST_INT) + { + addr = plus0; + looping = 1; + continue; + } + + if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST) + { + addr = plus0; + looping = 1; + continue; + } + + if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST) + { + addr = plus1; + looping = 1; + continue; + } + + break; + + case LABEL_REF: + n_words = 2; /* always 2 words */ + break; + + case CONST: + addr = XEXP (addr, 0); + looping = 1; + continue; + + case SYMBOL_REF: + n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2; + break; + + default: + break; + } + } + while (looping); + + if (n_words == 0) + return; + + n_words += additional; + if (n_words > 3) + n_words = 3; + + num_refs[n_words-1] += num; +} + +/* Return the appropriate instructions to move one operand to another. */ + +const char * +iq2000_move_1word (operands, insn, unsignedp) + rtx operands[]; + rtx insn; + int unsignedp; +{ + const char *ret = 0; + rtx op0 = operands[0]; + rtx op1 = operands[1]; + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); + enum machine_mode mode = GET_MODE (op0); + int subreg_offset0 = 0; + int subreg_offset1 = 0; + enum delay_type delay = DELAY_NONE; + + while (code0 == SUBREG) + { + subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)), + GET_MODE (SUBREG_REG (op0)), + SUBREG_BYTE (op0), + GET_MODE (op0)); + op0 = SUBREG_REG (op0); + code0 = GET_CODE (op0); + } + + while (code1 == SUBREG) + { + subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)), + GET_MODE (SUBREG_REG (op1)), + SUBREG_BYTE (op1), + GET_MODE (op1)); + op1 = SUBREG_REG (op1); + code1 = GET_CODE (op1); + } + + /* For our purposes, a condition code mode is the same as SImode. */ + if (mode == CCmode) + mode = SImode; + + if (code0 == REG) + { + int regno0 = REGNO (op0) + subreg_offset0; + + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_offset1; + + /* Do not do anything for assigning a register to itself */ + if (regno0 == regno1) + ret = ""; + + else if (GP_REG_P (regno0)) + { + if (GP_REG_P (regno1)) + ret = "or\t%0,%%0,%1"; + } + + } + + else if (code1 == MEM) + { + delay = DELAY_LOAD; + + if (TARGET_STATS) + iq2000_count_memory_refs (op1, 1); + + if (GP_REG_P (regno0)) + { + /* For loads, use the mode of the memory item, instead of the + target, so zero/sign extend can use this code as well. */ + switch (GET_MODE (op1)) + { + default: + break; + case SFmode: + ret = "lw\t%0,%1"; + break; + case SImode: + case CCmode: + ret = "lw\t%0,%1"; + break; + case HImode: + ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; + break; + case QImode: + ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; + break; + } + } + } + + else if (code1 == CONST_INT + || (code1 == CONST_DOUBLE + && GET_MODE (op1) == VOIDmode)) + { + if (code1 == CONST_DOUBLE) + { + /* This can happen when storing constants into long long + bitfields. Just store the least significant word of + the value. */ + operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); + } + + if (INTVAL (op1) == 0) + { + if (GP_REG_P (regno0)) + ret = "or\t%0,%%0,%z1"; + } + else if (GP_REG_P (regno0)) + { + if (SMALL_INT_UNSIGNED (op1)) + ret = "ori\t%0,%%0,%x1\t\t\t# %1"; + else if (SMALL_INT (op1)) + ret = "addiu\t%0,%%0,%1\t\t\t# %1"; + else + ret = "lui\t%0,%X1\t\t\t# %1\n\tori\t%0,%0,%x1"; + } + } + + else if (code1 == CONST_DOUBLE && mode == SFmode) + { + if (op1 == CONST0_RTX (SFmode)) + { + if (GP_REG_P (regno0)) + ret = "or\t%0,%%0,%."; + } + + else + { + delay = DELAY_LOAD; + ret = "li.s\t%0,%1"; + } + } + + else if (code1 == LABEL_REF) + { + if (TARGET_STATS) + iq2000_count_memory_refs (op1, 1); + + ret = "la\t%0,%a1"; + } + + else if (code1 == SYMBOL_REF || code1 == CONST) + { + if (TARGET_STATS) + iq2000_count_memory_refs (op1, 1); + + ret = "la\t%0,%a1"; + } + + else if (code1 == PLUS) + { + rtx add_op0 = XEXP (op1, 0); + rtx add_op1 = XEXP (op1, 1); + + if (GET_CODE (XEXP (op1, 1)) == REG + && GET_CODE (XEXP (op1, 0)) == CONST_INT) + add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0); + + operands[2] = add_op0; + operands[3] = add_op1; + ret = "add%:\t%0,%2,%3"; + } + + else if (code1 == HIGH) + { + operands[1] = XEXP (op1, 0); + ret = "lui\t%0,%%hi(%1)"; + } + } + + else if (code0 == MEM) + { + if (TARGET_STATS) + iq2000_count_memory_refs (op0, 1); + + if (code1 == REG) + { + int regno1 = REGNO (op1) + subreg_offset1; + + if (GP_REG_P (regno1)) + { + switch (mode) + { + case SFmode: ret = "sw\t%1,%0"; break; + case SImode: ret = "sw\t%1,%0"; break; + case HImode: ret = "sh\t%1,%0"; break; + case QImode: ret = "sb\t%1,%0"; break; + default: break; + } + } + } + + else if (code1 == CONST_INT && INTVAL (op1) == 0) + { + switch (mode) + { + case SFmode: ret = "sw\t%z1,%0"; break; + case SImode: ret = "sw\t%z1,%0"; break; + case HImode: ret = "sh\t%z1,%0"; break; + case QImode: ret = "sb\t%z1,%0"; break; + default: break; + } + } + + else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode)) + { + switch (mode) + { + case SFmode: ret = "sw\t%.,%0"; break; + case SImode: ret = "sw\t%.,%0"; break; + case HImode: ret = "sh\t%.,%0"; break; + case QImode: ret = "sb\t%.,%0"; break; + default: break; + } + } + } + + if (ret == 0) + { + abort_with_insn (insn, "Bad move"); + return 0; + } + + if (delay != DELAY_NONE) + return iq2000_fill_delay_slot (ret, delay, operands, insn); + + return ret; +} + +/* Provide the costs of an addressing mode that contains ADDR. */ + +int +iq2000_address_cost (addr) + rtx addr; +{ + switch (GET_CODE (addr)) + { + case LO_SUM: + return 1; + + case LABEL_REF: + return 2; + + case CONST: + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (XEXP (addr, 0), &offset); + if (GET_CODE (addr) == LABEL_REF) + return 2; + + if (GET_CODE (addr) != SYMBOL_REF) + return 4; + + if (! SMALL_INT (offset)) + return 2; + } + + /* ... fall through ... */ + + case SYMBOL_REF: + return SYMBOL_REF_FLAG (addr) ? 1 : 2; + + case PLUS: + { + register rtx plus0 = XEXP (addr, 0); + register rtx plus1 = XEXP (addr, 1); + + if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG) + plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0); + + if (GET_CODE (plus0) != REG) + break; + + switch (GET_CODE (plus1)) + { + case CONST_INT: + return SMALL_INT (plus1) ? 1 : 2; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case HIGH: + case LO_SUM: + return iq2000_address_cost (plus1) + 1; + + default: + break; + } + } + + default: + break; + } + + return 4; +} + +/* Make normal rtx_code into something we can index from an array. */ + +static enum internal_test +map_test_to_internal_test (test_code) + enum rtx_code test_code; +{ + enum internal_test test = ITEST_MAX; + + switch (test_code) + { + case EQ: test = ITEST_EQ; break; + case NE: test = ITEST_NE; break; + case GT: test = ITEST_GT; break; + case GE: test = ITEST_GE; break; + case LT: test = ITEST_LT; break; + case LE: test = ITEST_LE; break; + case GTU: test = ITEST_GTU; break; + case GEU: test = ITEST_GEU; break; + case LTU: test = ITEST_LTU; break; + case LEU: test = ITEST_LEU; break; + default: break; + } + + return test; +} + +/* Generate the code to compare two integer values. The return value is: + (reg:SI xx) The pseudo register the comparison is in + 0 No register, generate a simple branch. +*/ + +rtx +gen_int_relational (test_code, result, cmp0, cmp1, p_invert) + enum rtx_code test_code; /* relational test (EQ, etc) */ + rtx result; /* result to store comp. or 0 if branch */ + rtx cmp0; /* first operand to compare */ + rtx cmp1; /* second operand to compare */ + int *p_invert; /* NULL or ptr to hold whether branch needs */ + /* to reverse its test */ +{ + struct cmp_info + { + enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */ + int const_low; /* low bound of constant we can accept */ + int const_high; /* high bound of constant we can accept */ + int const_add; /* constant to add (convert LE -> LT) */ + int reverse_regs; /* reverse registers in test */ + int invert_const; /* != 0 if invert value if cmp1 is constant */ + int invert_reg; /* != 0 if invert value if cmp1 is register */ + int unsignedp; /* != 0 for unsigned comparisons. */ + }; + + static struct cmp_info info[ (int)ITEST_MAX ] = { + + { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */ + { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */ + { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */ + { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */ + { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */ + { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */ + { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */ + { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */ + { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */ + { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */ + }; + + enum internal_test test; + enum machine_mode mode; + struct cmp_info *p_info; + int branch_p; + int eqne_p; + int invert; + rtx reg; + rtx reg2; + + test = map_test_to_internal_test (test_code); + if (test == ITEST_MAX) + abort (); + + p_info = &info[(int) test]; + eqne_p = (p_info->test_code == XOR); + + mode = GET_MODE (cmp0); + if (mode == VOIDmode) + mode = GET_MODE (cmp1); + + /* Eliminate simple branches */ + branch_p = (result == 0); + if (branch_p) + { + if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG) + { + /* Comparisons against zero are simple branches */ + if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + return 0; + + /* Test for beq/bne. */ + if (eqne_p) + return 0; + } + + /* allocate a pseudo to calculate the value in. */ + result = gen_reg_rtx (mode); + } + + /* Make sure we can handle any constants given to us. */ + if (GET_CODE (cmp0) == CONST_INT) + cmp0 = force_reg (mode, cmp0); + + if (GET_CODE (cmp1) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (cmp1); + + if (value < p_info->const_low + || value > p_info->const_high) + cmp1 = force_reg (mode, cmp1); + } + + /* See if we need to invert the result. */ + invert = (GET_CODE (cmp1) == CONST_INT + ? p_info->invert_const : p_info->invert_reg); + + if (p_invert != (int *)0) + { + *p_invert = invert; + invert = 0; + } + + /* Comparison to constants, may involve adding 1 to change a LT into LE. + Comparison between two registers, may involve switching operands. */ + if (GET_CODE (cmp1) == CONST_INT) + { + if (p_info->const_add != 0) + { + HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; + + /* If modification of cmp1 caused overflow, + we would get the wrong answer if we follow the usual path; + thus, x > 0xffffffffU would turn into x > 0U. */ + if ((p_info->unsignedp + ? (unsigned HOST_WIDE_INT) new > + (unsigned HOST_WIDE_INT) INTVAL (cmp1) + : new > INTVAL (cmp1)) + != (p_info->const_add > 0)) + { + /* This test is always true, but if INVERT is true then + the result of the test needs to be inverted so 0 should + be returned instead. */ + emit_move_insn (result, invert ? const0_rtx : const_true_rtx); + return result; + } + else + cmp1 = GEN_INT (new); + } + } + + else if (p_info->reverse_regs) + { + rtx temp = cmp0; + cmp0 = cmp1; + cmp1 = temp; + } + + if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + reg = cmp0; + else + { + reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result; + convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0); + } + + if (test == ITEST_NE) + { + convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0); + if (p_invert != NULL) + *p_invert = 0; + invert = 0; + } + + else if (test == ITEST_EQ) + { + reg2 = invert ? gen_reg_rtx (mode) : result; + convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0); + reg = reg2; + } + + if (invert) + { + rtx one; + + one = const1_rtx; + convert_move (result, gen_rtx (XOR, mode, reg, one), 0); + } + + return result; +} + +/* Emit the common code for doing conditional branches. + operand[0] is the label to jump to. + The comparison operands are saved away by cmp{si,di,sf,df}. */ + +void +gen_conditional_branch (operands, test_code) + rtx operands[]; + enum rtx_code test_code; +{ + enum cmp_type type = branch_type; + rtx cmp0 = branch_cmp[0]; + rtx cmp1 = branch_cmp[1]; + enum machine_mode mode; + rtx reg; + int invert; + rtx label1, label2; + + switch (type) + { + case CMP_SI: + case CMP_DI: + mode = type == CMP_SI ? SImode : DImode; + invert = 0; + reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert); + + if (reg) + { + cmp0 = reg; + cmp1 = const0_rtx; + test_code = NE; + } + else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) + /* We don't want to build a comparison against a non-zero + constant. */ + cmp1 = force_reg (mode, cmp1); + + break; + + case CMP_SF: + case CMP_DF: + reg = gen_reg_rtx (CCmode); + + /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result == 0 */ + emit_insn (gen_rtx_SET (VOIDmode, reg, + gen_rtx (test_code == NE ? EQ : test_code, + CCmode, cmp0, cmp1))); + + test_code = test_code == NE ? EQ : NE; + mode = CCmode; + cmp0 = reg; + cmp1 = const0_rtx; + invert = 0; + break; + + default: + abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test"); + } + + /* Generate the branch. */ + + label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]); + label2 = pc_rtx; + + if (invert) + { + label2 = label1; + label1 = pc_rtx; + } + + emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, + gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx (test_code, mode, + cmp0, cmp1), + label1, label2))); +} + +/* Initialize CUMULATIVE_ARGS for a function. */ + +void +init_cumulative_args (cum, fntype, libname) + CUMULATIVE_ARGS *cum; /* argument info to initialize */ + tree fntype; /* tree ptr for function decl */ + rtx libname ATTRIBUTE_UNUSED; /* SYMBOL_REF of library name or 0 */ +{ + static CUMULATIVE_ARGS zero_cum; + tree param, next_param; + + if (TARGET_DEBUG_D_MODE) + { + fprintf (stderr, + "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype); + + if (!fntype) + fputc ('\n', stderr); + + else + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, ", fntype code = %s, ret code = %s\n", + tree_code_name[(int)TREE_CODE (fntype)], + tree_code_name[(int)TREE_CODE (ret_type)]); + } + } + + *cum = zero_cum; + + /* Determine if this function has variable arguments. This is + indicated by the last argument being 'void_type_mode' if there + are no variable arguments. The standard IQ2000 calling sequence + passes all arguments in the general purpose registers in this case. */ + + for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0; + param != 0; param = next_param) + { + next_param = TREE_CHAIN (param); + if (next_param == 0 && TREE_VALUE (param) != void_type_node) + cum->gp_reg_found = 1; + } +} + +/* Advance the argument to the next argument position. */ + +void +function_arg_advance (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* whether or not the argument was named */ +{ + if (TARGET_DEBUG_D_MODE) + { + fprintf (stderr, + "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ", + cum->gp_reg_found, cum->arg_number, cum->arg_words, + GET_MODE_NAME (mode)); + fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type); + fprintf (stderr, ", %d )\n\n", named); + } + + cum->arg_number++; + switch (mode) + { + case VOIDmode: + break; + + default: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + + cum->gp_reg_found = 1; + cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + break; + + case BLKmode: + cum->gp_reg_found = 1; + cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + break; + + case SFmode: + cum->arg_words++; + if (! cum->gp_reg_found && cum->arg_number <= 2) + cum->fp_code += 1 << ((cum->arg_number - 1) * 2); + break; + + case DFmode: + cum->arg_words += 2; + if (! cum->gp_reg_found && cum->arg_number <= 2) + cum->fp_code += 2 << ((cum->arg_number - 1) * 2); + break; + + case DImode: + cum->gp_reg_found = 1; + cum->arg_words += 2; + break; + + case QImode: + case HImode: + case SImode: + cum->gp_reg_found = 1; + cum->arg_words++; + break; + } +} + +/* Return an RTL expression containing the register for the given mode, + or 0 if the argument is to be passed on the stack. */ + +struct rtx_def * +function_arg (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* != 0 for normal args, == 0 for ... args */ +{ + rtx ret; + int regbase = -1; + int bias = 0; + unsigned int *arg_words = &cum->arg_words; + int struct_p = (type != 0 + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE + || TREE_CODE (type) == QUAL_UNION_TYPE)); + + if (TARGET_DEBUG_D_MODE) + { + fprintf (stderr, + "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ", + cum->gp_reg_found, cum->arg_number, cum->arg_words, + GET_MODE_NAME (mode)); + fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type); + fprintf (stderr, ", %d ) = ", named); + } + + + cum->last_arg_fp = 0; + switch (mode) + { + case SFmode: + regbase = GP_ARG_FIRST; + break; + + case DFmode: + cum->arg_words += cum->arg_words & 1; + + regbase = GP_ARG_FIRST; + break; + + default: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + + /* Drops through. */ + case BLKmode: + if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD) + cum->arg_words += (cum->arg_words & 1); + regbase = GP_ARG_FIRST; + break; + + case VOIDmode: + case QImode: + case HImode: + case SImode: + regbase = GP_ARG_FIRST; + break; + + case DImode: + cum->arg_words += (cum->arg_words & 1); + regbase = GP_ARG_FIRST; + } + + if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS) + { + if (TARGET_DEBUG_D_MODE) + fprintf (stderr, "%s\n", struct_p ? ", [struct]" : ""); + + ret = 0; + } + else + { + if (regbase == -1) + abort (); + + if (! type || TREE_CODE (type) != RECORD_TYPE + || ! named || ! TYPE_SIZE_UNIT (type) + || ! host_integerp (TYPE_SIZE_UNIT (type), 1)) + ret = gen_rtx_REG (mode, regbase + *arg_words + bias); + else + { + tree field; + + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD + && host_integerp (bit_position (field), 0) + && int_bit_position (field) % BITS_PER_WORD == 0) + break; + + /* If the whole struct fits a DFmode register, + we don't need the PARALLEL. */ + if (! field || mode == DFmode) + ret = gen_rtx_REG (mode, regbase + *arg_words + bias); + else + { + unsigned int chunks; + HOST_WIDE_INT bitpos; + unsigned int regno; + unsigned int i; + + /* ??? If this is a packed structure, then the last hunk won't + be 64 bits. */ + + chunks + = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD; + if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS) + chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias; + + /* assign_parms checks the mode of ENTRY_PARM, so we must + use the actual mode here. */ + ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks)); + + bitpos = 0; + regno = regbase + *arg_words + bias; + field = TYPE_FIELDS (type); + for (i = 0; i < chunks; i++) + { + rtx reg; + + for (; field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && int_bit_position (field) >= bitpos) + break; + + if (field + && int_bit_position (field) == bitpos + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD) + reg = gen_rtx_REG (DFmode, regno++); + else + reg = gen_rtx_REG (word_mode, regno); + + XVECEXP (ret, 0, i) + = gen_rtx_EXPR_LIST (VOIDmode, reg, + GEN_INT (bitpos / BITS_PER_UNIT)); + + bitpos += 64; + regno++; + } + } + } + + if (TARGET_DEBUG_D_MODE) + fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias], + struct_p ? ", [struct]" : ""); + } + + /* We will be called with a mode of VOIDmode after the last argument + has been seen. Whatever we return will be passed to the call + insn. If we need any shifts for small structures, return them in + a PARALLEL. */ + if (mode == VOIDmode) + { + if (cum->num_adjusts > 0) + ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code, + gen_rtvec_v (cum->num_adjusts, cum->adjust)); + } + + return ret; +} + +int +function_arg_partial_nregs (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type ATTRIBUTE_UNUSED;/* type of the argument or 0 if lib support */ + int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */ +{ + if (mode == DImode + && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1) + { + if (TARGET_DEBUG_D_MODE) + fprintf (stderr, "function_arg_partial_nregs = 1\n"); + + return 1; + } + + return 0; +} + +/* Implement va_start. */ + +void +iq2000_va_start (valist, nextarg) + tree valist; + rtx nextarg; +{ + int int_arg_words; + + /* Find out how many non-float named formals */ + int gpr_save_area_size; + /* Note UNITS_PER_WORD is 4 bytes */ + int_arg_words = current_function_args_info.arg_words; + if (int_arg_words < 8 ) + /* Adjust for the prologue's economy measure */ + gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD; + else + gpr_save_area_size = 0; + + /* Everything is in the GPR save area, or in the overflow + area which is contiguous with it. */ + + nextarg = plus_constant (nextarg, -gpr_save_area_size); + std_expand_builtin_va_start (valist, nextarg); +} + +/* Implement va_arg. */ + +rtx +iq2000_va_arg (valist, type) + tree valist, type; +{ + HOST_WIDE_INT size, rsize; + rtx addr_rtx; + tree t; + + int indirect; + rtx r, lab_over = NULL_RTX, lab_false; + tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff; + tree ovfl, gtop, ftop, goff, foff; + + size = int_size_in_bytes (type); + rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; + indirect + = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0); + if (indirect) + { + size = POINTER_SIZE / BITS_PER_UNIT; + rsize = UNITS_PER_WORD; + } + + addr_rtx = gen_reg_rtx (Pmode); + + { + /* Case of all args in a merged stack. No need to check bounds, + just advance valist along the stack. */ + + tree gpr = valist; + if (! indirect + && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD) + { + t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr, + build_int_2 (2*UNITS_PER_WORD - 1, 0)); + t = build (BIT_AND_EXPR, TREE_TYPE (t), t, + build_int_2 (-2*UNITS_PER_WORD, -1)); + t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr, + size_int (rsize)); + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + /* flush the POSTINCREMENT */ + emit_queue(); + + if (indirect) + { + r = gen_rtx_MEM (Pmode, addr_rtx); + set_mem_alias_set (r, get_varargs_alias_set ()); + emit_move_insn (addr_rtx, r); + } + else + { + if (BYTES_BIG_ENDIAN && rsize != size) + addr_rtx = plus_constant (addr_rtx, rsize - size); + } + return addr_rtx; + } + + /* Not a simple merged stack. Need ptrs and indexes left by va_start. */ + + f_ovfl = TYPE_FIELDS (va_list_type_node); + f_gtop = TREE_CHAIN (f_ovfl); + f_ftop = TREE_CHAIN (f_gtop); + f_goff = TREE_CHAIN (f_ftop); + f_foff = TREE_CHAIN (f_goff); + + ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl); + gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop); + ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop); + goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff); + foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff); + + lab_false = gen_label_rtx (); + lab_over = gen_label_rtx (); + + if (TREE_CODE (type) == REAL_TYPE) + { + + /* Emit code to branch if foff == 0. */ + r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)), + EXPAND_NORMAL); + emit_cmp_and_jump_insns (r, const0_rtx, EQ, + const1_rtx, GET_MODE (r), 1, lab_false); + + /* Emit code for addr_rtx = ftop - foff */ + t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff ); + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + /* Emit code for foff-=8. + Advances the offset up FPR save area by one double */ + t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0)); + t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + emit_queue(); + emit_jump (lab_over); + emit_barrier (); + emit_label (lab_false); + + /* If a 4-byte int is followed by an 8-byte float, then + natural alignment causes a 4 byte gap. + So, dynamically adjust ovfl up to a multiple of 8. */ + t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl, + build_int_2 (7, 0)); + t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t); + t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + /* Emit code for addr_rtx = the ovfl pointer into overflow area. + Postincrement the ovfl pointer by 8. */ + t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl, + size_int (8)); + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + emit_queue(); + emit_label (lab_over); + return addr_rtx; + } + else + { + /* not REAL_TYPE */ + int step_size; + + if (TREE_CODE (type) == INTEGER_TYPE + && TYPE_PRECISION (type) == 64) + { + /* int takes 32 bits of the GPR save area, but + longlong takes an aligned 64 bits. So, emit code + to zero the low order bits of goff, thus aligning + the later calculation of (gtop-goff) upwards. */ + t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff, + build_int_2 (-8, -1)); + t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + /* Emit code to branch if goff == 0. */ + r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)), + EXPAND_NORMAL); + emit_cmp_and_jump_insns (r, const0_rtx, EQ, + const1_rtx, GET_MODE (r), 1, lab_false); + + /* Emit code for addr_rtx = gtop - goff. */ + t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff); + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + if (TYPE_PRECISION (type) == 64) + step_size = 8; + else + step_size = UNITS_PER_WORD; + + /* Emit code for goff = goff - step_size. + Advances the offset up GPR save area over the item. */ + t = build (MINUS_EXPR, TREE_TYPE (goff), goff, + build_int_2 (step_size, 0)); + t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t); + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + emit_queue(); + emit_jump (lab_over); + emit_barrier (); + emit_label (lab_false); + + /* Emit code for addr_rtx -> overflow area, postinc by step_size */ + t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl, + size_int (step_size)); + r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL); + if (r != addr_rtx) + emit_move_insn (addr_rtx, r); + + emit_queue(); + emit_label (lab_over); + + if (indirect) + { + r = gen_rtx_MEM (Pmode, addr_rtx); + set_mem_alias_set (r, get_varargs_alias_set ()); + emit_move_insn (addr_rtx, r); + } + else + { + if (BYTES_BIG_ENDIAN && rsize != size) + addr_rtx = plus_constant (addr_rtx, rsize - size); + } + return addr_rtx; + } +} + +/* Abort after printing out a specific insn. */ + +static void +abort_with_insn (insn, reason) + rtx insn; + const char *reason; +{ + error (reason); + debug_rtx (insn); + abort (); +} + +/* Detect any conflicts in the switches. */ + +void +override_options () +{ + register enum processor_type iq2000_cpu; + + target_flags &= ~MASK_GPOPT; + + iq2000_isa = IQ2000_ISA_DEFAULT; + + /* Identify the processor type. */ + + if (iq2000_cpu_string != 0) + { + iq2000_cpu = iq2000_parse_cpu (iq2000_cpu_string); + if (iq2000_cpu == PROCESSOR_DEFAULT) + { + error ("bad value (%s) for -mcpu= switch", iq2000_arch_string); + iq2000_cpu_string = "default"; + } + iq2000_arch = iq2000_cpu; + iq2000_tune = iq2000_cpu; + } + + if (iq2000_arch_string == 0 + || ! strcmp (iq2000_arch_string, "default") + || ! strcmp (iq2000_arch_string, "DEFAULT")) + { + switch (iq2000_isa) + { + default: + iq2000_arch_string = "iq2000"; + iq2000_arch = PROCESSOR_IQ2000; + break; + } + } + else + { + iq2000_arch = iq2000_parse_cpu (iq2000_arch_string); + if (iq2000_arch == PROCESSOR_DEFAULT) + { + error ("bad value (%s) for -march= switch", iq2000_arch_string); + iq2000_arch_string = "default"; + } + if (iq2000_arch == PROCESSOR_IQ10) + { + error ("The compiler does not support -march=%s.", iq2000_arch_string); + iq2000_arch_string = "default"; + } + } + + iq2000_print_operand_punct['?'] = 1; + iq2000_print_operand_punct['#'] = 1; + iq2000_print_operand_punct['&'] = 1; + iq2000_print_operand_punct['!'] = 1; + iq2000_print_operand_punct['*'] = 1; + iq2000_print_operand_punct['@'] = 1; + iq2000_print_operand_punct['.'] = 1; + iq2000_print_operand_punct['('] = 1; + iq2000_print_operand_punct[')'] = 1; + iq2000_print_operand_punct['['] = 1; + iq2000_print_operand_punct[']'] = 1; + iq2000_print_operand_punct['<'] = 1; + iq2000_print_operand_punct['>'] = 1; + iq2000_print_operand_punct['{'] = 1; + iq2000_print_operand_punct['}'] = 1; + iq2000_print_operand_punct['^'] = 1; + iq2000_print_operand_punct['$'] = 1; + iq2000_print_operand_punct['+'] = 1; + iq2000_print_operand_punct['~'] = 1; + + /* Save GPR registers in word_mode sized hunks. word_mode hasn't been + initialized yet, so we can't use that here. */ + gpr_mode = SImode; + + /* Function to allocate machine-dependent function status. */ + init_machine_status = &iq2000_init_machine_status; +} + +/* Allocate a chunk of memory for per-function machine-dependent data. */ + +static struct machine_function * +iq2000_init_machine_status () +{ + return ((struct machine_function *) + ggc_alloc_cleared (sizeof (struct machine_function))); +} + +/* The arg pointer (which is eliminated) points to the virtual frame pointer, + while the frame pointer (which may be eliminated) points to the stack + pointer after the initial adjustments. */ + +HOST_WIDE_INT +iq2000_debugger_offset (addr, offset) + rtx addr; + HOST_WIDE_INT offset; +{ + rtx offset2 = const0_rtx; + rtx reg = eliminate_constant_term (addr, &offset2); + + if (offset == 0) + offset = INTVAL (offset2); + + if (reg == stack_pointer_rtx || reg == frame_pointer_rtx + || reg == hard_frame_pointer_rtx) + { + HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized) + ? compute_frame_size (get_frame_size ()) + : cfun->machine->frame.total_size; + + offset = offset - frame_size; + } + + return offset; +} + +/* If defined, a C statement to be executed just prior to the output of + assembler code for INSN, to modify the extracted operands so they will be + output differently. + + Here the argument OPVEC is the vector containing the operands extracted + from INSN, and NOPERANDS is the number of elements of the vector which + contain meaningful data for this insn. The contents of this vector are + what will be used to convert the insn template into assembler code, so you + can change the assembler output by changing the contents of the vector. + + We use it to check if the current insn needs a nop in front of it because + of load delays, and also to update the delay slot statistics. */ + +void +final_prescan_insn (insn, opvec, noperands) + rtx insn; + rtx opvec[] ATTRIBUTE_UNUSED; + int noperands ATTRIBUTE_UNUSED; +{ + if (dslots_number_nops > 0) + { + rtx pattern = PATTERN (insn); + int length = get_attr_length (insn); + + /* Do we need to emit a NOP? */ + if (length == 0 + || (iq2000_load_reg != 0 && reg_mentioned_p (iq2000_load_reg, pattern)) + || (iq2000_load_reg2 != 0 && reg_mentioned_p (iq2000_load_reg2, pattern)) + || (iq2000_load_reg3 != 0 && reg_mentioned_p (iq2000_load_reg3, pattern)) + || (iq2000_load_reg4 != 0 + && reg_mentioned_p (iq2000_load_reg4, pattern))) + fputs ("\tnop\n", asm_out_file); + + else + dslots_load_filled++; + + while (--dslots_number_nops > 0) + fputs ("\tnop\n", asm_out_file); + + iq2000_load_reg = 0; + iq2000_load_reg2 = 0; + iq2000_load_reg3 = 0; + iq2000_load_reg4 = 0; + } + + if ((GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN + || (GET_CODE (PATTERN (insn)) == RETURN)) + && NEXT_INSN (PREV_INSN (insn)) == insn) + { + rtx nop_insn = emit_insn_after (gen_nop (), insn); + INSN_ADDRESSES_NEW (nop_insn, -1); + } + + if (TARGET_STATS + && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN)) + dslots_jump_total++; +} + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. + + IQ2000 stack frames look like: + + Before call After call + +-----------------------+ +-----------------------+ + high | | | | + mem. | | | | + | caller's temps. | | caller's temps. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack. | | arguments on stack. | + | | | | + +-----------------------+ +-----------------------+ + | 4 words to save | | 4 words to save | + | arguments passed | | arguments passed | + | in registers, even | | in registers, even | + SP->| if not passed. | VFP->| if not passed. | + +-----------------------+ +-----------------------+ + | | + | fp register save | + | | + +-----------------------+ + | | + | gp register save | + | | + +-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | + | alloca allocations | + | | + +-----------------------+ + | | + | GP save for V.4 abi | + | | + +-----------------------+ + | | + | arguments on stack | + | | + +-----------------------+ + | 4 words to save | + | arguments passed | + | in registers, even | + low SP->| if not passed. | + memory +-----------------------+ + +*/ + +HOST_WIDE_INT +compute_frame_size (size) + HOST_WIDE_INT size; /* # of var. bytes allocated */ +{ + int regno; + HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ + HOST_WIDE_INT var_size; /* # bytes that variables take up */ + HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ + HOST_WIDE_INT extra_size; /* # extra bytes */ + HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */ + HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ + HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ + long mask; /* mask of saved gp registers */ + int fp_inc; /* 1 or 2 depending on the size of fp regs */ + long fp_bits; /* bitmask to use for each fp register */ + + gp_reg_size = 0; + fp_reg_size = 0; + mask = 0; + extra_size = IQ2000_STACK_ALIGN ((0)); + var_size = IQ2000_STACK_ALIGN (size); + args_size = IQ2000_STACK_ALIGN (current_function_outgoing_args_size); + + /* If a function dynamically allocates the stack and + has 0 for STACK_DYNAMIC_OFFSET then allocate some stack space */ + + if (args_size == 0 && current_function_calls_alloca) + args_size = 4 * UNITS_PER_WORD; + + total_size = var_size + args_size + extra_size; + + /* Calculate space needed for gp registers. */ + for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) + { + if (MUST_SAVE_REGISTER (regno)) + { + gp_reg_size += GET_MODE_SIZE (gpr_mode); + mask |= 1L << (regno - GP_REG_FIRST); + } + } + + /* We need to restore these for the handler. */ + if (current_function_calls_eh_return) + { + int i; + for (i = 0; ; ++i) + { + regno = EH_RETURN_DATA_REGNO (i); + if (regno == (signed int) INVALID_REGNUM) + break; + gp_reg_size += GET_MODE_SIZE (gpr_mode); + mask |= 1L << (regno - GP_REG_FIRST); + } + } + + fp_inc = 2; + fp_bits = 3; + gp_reg_rounded = IQ2000_STACK_ALIGN (gp_reg_size); + total_size += gp_reg_rounded + IQ2000_STACK_ALIGN (fp_reg_size); + + /* The gp reg is caller saved, so there is no need for leaf routines + (total_size == extra_size) to save the gp reg. */ + if (total_size == extra_size + && ! profile_flag) + total_size = extra_size = 0; + + total_size += IQ2000_STACK_ALIGN (current_function_pretend_args_size); + + /* Save other computed information. */ + cfun->machine->frame.total_size = total_size; + cfun->machine->frame.var_size = var_size; + cfun->machine->frame.args_size = args_size; + cfun->machine->frame.extra_size = extra_size; + cfun->machine->frame.gp_reg_size = gp_reg_size; + cfun->machine->frame.fp_reg_size = fp_reg_size; + cfun->machine->frame.mask = mask; + cfun->machine->frame.initialized = reload_completed; + cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD; + + if (mask) + { + unsigned long offset; + + offset = (args_size + extra_size + var_size + + gp_reg_size - GET_MODE_SIZE (gpr_mode)); + + cfun->machine->frame.gp_sp_offset = offset; + cfun->machine->frame.gp_save_offset = offset - total_size; + } + else + { + cfun->machine->frame.gp_sp_offset = 0; + cfun->machine->frame.gp_save_offset = 0; + } + + cfun->machine->frame.fp_sp_offset = 0; + cfun->machine->frame.fp_save_offset = 0; + + /* Ok, we're done. */ + return total_size; +} + +/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame + pointer, argument pointer, or return address pointer. TO is either + the stack pointer or hard frame pointer. */ + +int +iq2000_initial_elimination_offset (from, to) + int from; + int to ATTRIBUTE_UNUSED; +{ + int offset; + + compute_frame_size (get_frame_size ()); + if ((from) == FRAME_POINTER_REGNUM) + (offset) = 0; + else if ((from) == ARG_POINTER_REGNUM) + (offset) = (cfun->machine->frame.total_size); + else if ((from) == RETURN_ADDRESS_POINTER_REGNUM) + { + if (leaf_function_p ()) + (offset) = 0; + else (offset) = cfun->machine->frame.gp_sp_offset + + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)) + * (BYTES_BIG_ENDIAN != 0)); + } + + return offset; +} + +/* Common code to emit the insns (or to write the instructions to a file) + to save/restore registers. + Other parts of the code assume that IQ2000_TEMP1_REGNUM (aka large_reg) + is not modified within save_restore_insns. */ + +#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0) + +/* Emit instructions to load the value (SP + OFFSET) into IQ2000_TEMP2_REGNUM + and return an rtl expression for the register. Write the assembly + instructions directly to FILE if it is not null, otherwise emit them as + rtl. + + This function is a subroutine of save_restore_insns. It is used when + OFFSET is too large to add in a single instruction. */ + +static rtx +iq2000_add_large_offset_to_sp (offset) + HOST_WIDE_INT offset; +{ + rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM); + rtx offset_rtx = GEN_INT (offset); + + emit_move_insn (reg, offset_rtx); + emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx)); + return reg; +} + +/* Make INSN frame related and note that it performs the frame-related + operation DWARF_PATTERN. */ + +static void +iq2000_annotate_frame_insn (insn, dwarf_pattern) + rtx insn, dwarf_pattern; +{ + RTX_FRAME_RELATED_P (insn) = 1; + REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, + dwarf_pattern, + REG_NOTES (insn)); +} + +/* Emit a move instruction that stores REG in MEM. Make the instruction + frame related and note that it stores REG at (SP + OFFSET). */ + +static void +iq2000_emit_frame_related_store (mem, reg, offset) + rtx mem; + rtx reg; + HOST_WIDE_INT offset; +{ + rtx dwarf_address = plus_constant (stack_pointer_rtx, offset); + rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address); + + iq2000_annotate_frame_insn (emit_move_insn (mem, reg), + gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg)); +} + +static void +save_restore_insns (store_p) + int store_p; /* true if this is prologue */ +{ + long mask = cfun->machine->frame.mask; + int regno; + rtx base_reg_rtx; + HOST_WIDE_INT base_offset; + HOST_WIDE_INT gp_offset; + HOST_WIDE_INT end_offset; + + if (frame_pointer_needed + && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST)) + abort (); + + if (mask == 0) + { + base_reg_rtx = 0, base_offset = 0; + return; + } + + /* Save registers starting from high to low. The debuggers prefer at least + the return register be stored at func+4, and also it allows us not to + need a nop in the epilog if at least one register is reloaded in + addition to return address. */ + + /* Save GP registers if needed. */ + /* Pick which pointer to use as a base register. For small frames, just + use the stack pointer. Otherwise, use a temporary register. Save 2 + cycles if the save area is near the end of a large frame, by reusing + the constant created in the prologue/epilogue to adjust the stack + frame. */ + + gp_offset = cfun->machine->frame.gp_sp_offset; + end_offset + = gp_offset - (cfun->machine->frame.gp_reg_size + - GET_MODE_SIZE (gpr_mode)); + + if (gp_offset < 0 || end_offset < 0) + internal_error + ("gp_offset (%ld) or end_offset (%ld) is less than zero.", + (long) gp_offset, (long) end_offset); + + else if (gp_offset < 32768) + base_reg_rtx = stack_pointer_rtx, base_offset = 0; + else + { + int regno; + int reg_save_count = 0; + for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) + if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1; + base_offset = gp_offset - ((reg_save_count - 1) * 4); + base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset); + } + + for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) + { + if (BITSET_P (mask, regno - GP_REG_FIRST)) + { + rtx reg_rtx; + rtx mem_rtx + = gen_rtx (MEM, gpr_mode, + gen_rtx (PLUS, Pmode, base_reg_rtx, + GEN_INT (gp_offset - base_offset))); + + if (! current_function_calls_eh_return) + RTX_UNCHANGING_P (mem_rtx) = 1; + + reg_rtx = gen_rtx (REG, gpr_mode, regno); + + if (store_p) + iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset); + else + { + emit_move_insn (reg_rtx, mem_rtx); + } + gp_offset -= GET_MODE_SIZE (gpr_mode); + } + } +} + +/* Expand the prologue into a bunch of separate insns. */ + +void +iq2000_expand_prologue () +{ + int regno; + HOST_WIDE_INT tsize; + int last_arg_is_vararg_marker = 0; + tree fndecl = current_function_decl; + tree fntype = TREE_TYPE (fndecl); + tree fnargs = DECL_ARGUMENTS (fndecl); + rtx next_arg_reg; + int i; + tree next_arg; + tree cur_arg; + CUMULATIVE_ARGS args_so_far; + int store_args_on_stack = (iq2000_can_use_return_insn ()); + + /* If struct value address is treated as the first argument. */ + if (aggregate_value_p (DECL_RESULT (fndecl)) + && ! current_function_returns_pcc_struct + && struct_value_incoming_rtx == 0) + { + tree type = build_pointer_type (fntype); + tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); + + DECL_ARG_TYPE (function_result_decl) = type; + TREE_CHAIN (function_result_decl) = fnargs; + fnargs = function_result_decl; + } + + /* For arguments passed in registers, find the register number + of the first argument in the variable part of the argument list, + otherwise GP_ARG_LAST+1. Note also if the last argument is + the varargs special argument, and treat it as part of the + variable arguments. + + This is only needed if store_args_on_stack is true. */ + + INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0); + regno = GP_ARG_FIRST; + + for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg) + { + tree passed_type = DECL_ARG_TYPE (cur_arg); + enum machine_mode passed_mode = TYPE_MODE (passed_type); + rtx entry_parm; + + if (TREE_ADDRESSABLE (passed_type)) + { + passed_type = build_pointer_type (passed_type); + passed_mode = Pmode; + } + + entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1); + + FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1); + next_arg = TREE_CHAIN (cur_arg); + + if (entry_parm && store_args_on_stack) + { + if (next_arg == 0 + && DECL_NAME (cur_arg) + && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), + "__builtin_va_alist")) + || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), + "va_alist")))) + { + last_arg_is_vararg_marker = 1; + break; + } + else + { + int words; + + if (GET_CODE (entry_parm) != REG) + abort (); + + /* passed in a register, so will get homed automatically */ + if (GET_MODE (entry_parm) == BLKmode) + words = (int_size_in_bytes (passed_type) + 3) / 4; + else + words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; + + regno = REGNO (entry_parm) + words - 1; + } + } + else + { + regno = GP_ARG_LAST+1; + break; + } + } + + /* In order to pass small structures by value in registers we need to + shift the value into the high part of the register. + Function_arg has encoded a PARALLEL rtx, holding a vector of + adjustments to be made as the next_arg_reg variable, so we split up the + insns, and emit them separately. */ + + next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1); + if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL) + { + rtvec adjust = XVEC (next_arg_reg, 0); + int num = GET_NUM_ELEM (adjust); + + for (i = 0; i < num; i++) + { + rtx insn, pattern; + + pattern = RTVEC_ELT (adjust, i); + if (GET_CODE (pattern) != SET + || GET_CODE (SET_SRC (pattern)) != ASHIFT) + abort_with_insn (pattern, "Insn is not a shift"); + PUT_CODE (SET_SRC (pattern), ASHIFTRT); + + insn = emit_insn (pattern); + + /* Global life information isn't valid at this point, so we + can't check whether these shifts are actually used. Mark + them MAYBE_DEAD so that flow2 will remove them, and not + complain about dead code in the prologue. */ + REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, + REG_NOTES (insn)); + } + } + + tsize = compute_frame_size (get_frame_size ()); + + /* If this function is a varargs function, store any registers that + would normally hold arguments ($4 - $7) on the stack. */ + if (store_args_on_stack + && ((TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node)) + || last_arg_is_vararg_marker)) + { + int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD; + rtx ptr = stack_pointer_rtx; + + for (; regno <= GP_ARG_LAST; regno++) + { + if (offset != 0) + ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset)); + emit_move_insn (gen_rtx (MEM, gpr_mode, ptr), + gen_rtx (REG, gpr_mode, regno)); + + offset += GET_MODE_SIZE (gpr_mode); + } + } + + if (tsize > 0) + { + rtx tsize_rtx = GEN_INT (tsize); + rtx adjustment_rtx, insn, dwarf_pattern; + + if (tsize > 32767) + { + adjustment_rtx = gen_rtx (REG, Pmode, IQ2000_TEMP1_REGNUM); + emit_move_insn (adjustment_rtx, tsize_rtx); + } + else + adjustment_rtx = tsize_rtx; + + insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, + adjustment_rtx)); + + dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx, + plus_constant (stack_pointer_rtx, -tsize)); + + iq2000_annotate_frame_insn (insn, dwarf_pattern); + + save_restore_insns (1); + + if (frame_pointer_needed) + { + rtx insn = 0; + + insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, + stack_pointer_rtx)); + + if (insn) + RTX_FRAME_RELATED_P (insn) = 1; + } + } + + emit_insn (gen_blockage ()); +} + +/* Expand the epilogue into a bunch of separate insns. */ + +void +iq2000_expand_epilogue () +{ + HOST_WIDE_INT tsize = cfun->machine->frame.total_size; + rtx tsize_rtx = GEN_INT (tsize); + rtx tmp_rtx = (rtx)0; + + if (iq2000_can_use_return_insn ()) + { + emit_insn (gen_return ()); + return; + } + + if (tsize > 32767) + { + tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM); + emit_move_insn (tmp_rtx, tsize_rtx); + tsize_rtx = tmp_rtx; + } + + if (tsize > 0) + { + if (frame_pointer_needed) + { + emit_insn (gen_blockage ()); + + emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); + } + + save_restore_insns (0); + + if (current_function_calls_eh_return) + { + rtx eh_ofs = EH_RETURN_STACKADJ_RTX; + emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx)); + tsize_rtx = eh_ofs; + } + + emit_insn (gen_blockage ()); + + if (tsize != 0 || current_function_calls_eh_return) + { + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + } + } + + if (current_function_calls_eh_return) + { + /* Perform the additional bump for __throw. */ + emit_move_insn (gen_rtx (REG, Pmode, HARD_FRAME_POINTER_REGNUM), + stack_pointer_rtx); + emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, + HARD_FRAME_POINTER_REGNUM))); + emit_jump_insn (gen_eh_return_internal ()); + } + else + emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, + GP_REG_FIRST + 31))); +} + +void +iq2000_expand_eh_return (address) + rtx address; +{ + HOST_WIDE_INT gp_offset = cfun->machine->frame.gp_sp_offset; + rtx scratch; + + scratch = plus_constant (stack_pointer_rtx, gp_offset); + emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address); +} + +/* Return nonzero if this function is known to have a null epilogue. + This allows the optimizer to omit jumps to jumps if no stack + was created. */ + +int +iq2000_can_use_return_insn () +{ + if (! reload_completed) + return 0; + + if (regs_ever_live[31] || profile_flag) + return 0; + + if (cfun->machine->frame.initialized) + return cfun->machine->frame.total_size == 0; + + return compute_frame_size (get_frame_size ()) == 0; +} + +/* Returns non-zero if X contains a SYMBOL_REF. */ + +static int +symbolic_expression_p (x) + rtx x; +{ + if (GET_CODE (x) == SYMBOL_REF) + return 1; + + if (GET_CODE (x) == CONST) + return symbolic_expression_p (XEXP (x, 0)); + + if (GET_RTX_CLASS (GET_CODE (x)) == '1') + return symbolic_expression_p (XEXP (x, 0)); + + if (GET_RTX_CLASS (GET_CODE (x)) == 'c' + || GET_RTX_CLASS (GET_CODE (x)) == '2') + return (symbolic_expression_p (XEXP (x, 0)) + || symbolic_expression_p (XEXP (x, 1))); + + return 0; +} + +/* Choose the section to use for the constant rtx expression X that has + mode MODE. */ + +static void +iq2000_select_rtx_section (mode, x, align) + enum machine_mode mode; + rtx x ATTRIBUTE_UNUSED; + unsigned HOST_WIDE_INT align; +{ + /* For embedded applications, always put constants in read-only data, + in order to reduce RAM usage. */ + /* For embedded applications, always put constants in read-only data, + in order to reduce RAM usage. */ + mergeable_constant_section (mode, align, 0); +} + +/* Choose the section to use for DECL. RELOC is true if its value contains + any relocatable expression. + + Some of the logic used here needs to be replicated in + ENCODE_SECTION_INFO in iq2000.h so that references to these symbols + are done correctly. */ + +static void +iq2000_select_section (decl, reloc, align) + tree decl; + int reloc ATTRIBUTE_UNUSED; + unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED; +{ + if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put an object in read-only data + if possible, in order to reduce RAM usage. */ + + if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings)))) + readonly_data_section (); + else + data_section (); + } + else + { + /* For hosted applications, always put an object in small data if + possible, as this gives the best performance. */ + + if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + && DECL_INITIAL (decl) + && (DECL_INITIAL (decl) == error_mark_node + || TREE_CONSTANT (DECL_INITIAL (decl)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings)))) + readonly_data_section (); + else + data_section (); + } +} +/* Return register to use for a function return value with VALTYPE for function + FUNC. */ + +rtx +iq2000_function_value (valtype, func) + tree valtype; + tree func ATTRIBUTE_UNUSED; +{ + int reg = GP_RETURN; + enum machine_mode mode = TYPE_MODE (valtype); + int unsignedp = TREE_UNSIGNED (valtype); + + /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode + just as PROMOTE_MODE does. */ + mode = promote_mode (valtype, mode, &unsignedp, 1); + + return gen_rtx_REG (mode, reg); +} + +/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return + nonzero when an argument must be passed by reference. */ + +int +function_arg_pass_by_reference (cum, mode, type, named) + CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; + enum machine_mode mode; + tree type; + int named ATTRIBUTE_UNUSED; +{ + int size; + + /* We must pass by reference if we would be both passing in registers + and the stack. This is because any subsequent partial arg would be + handled incorrectly in this case. */ + + if (cum && MUST_PASS_IN_STACK (mode, type)) + { + /* Don't pass the actual CUM to FUNCTION_ARG, because we would + get double copies of any offsets generated for small structs + passed in registers. */ + CUMULATIVE_ARGS temp; + temp = *cum; + if (FUNCTION_ARG (temp, mode, type, named) != 0) + return 1; + } + + if (type == NULL_TREE || mode == DImode || mode == DFmode) + return 0; + + size = int_size_in_bytes (type); + return size == -1 || size > UNITS_PER_WORD; +} + +/* Return the length of INSN. LENGTH is the initial length computed by + attributes in the machine-description file. */ + +int +iq2000_adjust_insn_length (insn, length) + rtx insn; + int length; +{ + /* A unconditional jump has an unfilled delay slot if it is not part + of a sequence. A conditional jump normally has a delay slot */ + if (simplejump_p (insn) + || ((GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN))) + length += 4; + + return length; +} + +/* Output assembly instructions to perform a conditional branch. + + INSN is the branch instruction. OPERANDS[0] is the condition. + OPERANDS[1] is the target of the branch. OPERANDS[2] is the target + of the first operand to the condition. If TWO_OPERANDS_P is + non-zero the comparison takes two operands; OPERANDS[3] will be the + second operand. + + If INVERTED_P is non-zero we are to branch if the condition does + not hold. If FLOAT_P is non-zero this is a floating-point comparison. + + LENGTH is the length (in bytes) of the sequence we are to generate. + That tells us whether to generate a simple conditional branch, or a + reversed conditional branch around a `jr' instruction. */ + +char * +iq2000_output_conditional_branch (insn, + operands, + two_operands_p, + float_p, + inverted_p, + length) + rtx insn; + rtx *operands; + int two_operands_p; + int float_p; + int inverted_p; + int length; +{ + static char buffer[200]; + /* The kind of comparison we are doing. */ + enum rtx_code code = GET_CODE (operands[0]); + /* Non-zero if the opcode for the comparison needs a `z' indicating + that it is a comparision against zero. */ + int need_z_p; + /* A string to use in the assembly output to represent the first + operand. */ + const char *op1 = "%z2"; + /* A string to use in the assembly output to represent the second + operand. Use the hard-wired zero register if there's no second + operand. */ + const char *op2 = (two_operands_p ? ",%z3" : ",%."); + /* The operand-printing string for the comparison. */ + const char *comp = (float_p ? "%F0" : "%C0"); + /* The operand-printing string for the inverted comparison. */ + const char *inverted_comp = (float_p ? "%W0" : "%N0"); + + /* likely variants of each branch instruction annul the instruction + in the delay slot if the branch is not taken. */ + iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); + + if (!two_operands_p) + { + /* To compute whether than A > B, for example, we normally + subtract B from A and then look at the sign bit. But, if we + are doing an unsigned comparison, and B is zero, we don't + have to do the subtraction. Instead, we can just check to + see if A is non-zero. Thus, we change the CODE here to + reflect the simpler comparison operation. */ + switch (code) + { + case GTU: + code = NE; + break; + + case LEU: + code = EQ; + break; + + case GEU: + /* A condition which will always be true. */ + code = EQ; + op1 = "%."; + break; + + case LTU: + /* A condition which will always be false. */ + code = NE; + op1 = "%."; + break; + + default: + /* Not a special case. */ + break; + } + } + + /* Relative comparisons are always done against zero. But + equality comparisons are done between two operands, and therefore + do not require a `z' in the assembly language output. */ + need_z_p = (!float_p && code != EQ && code != NE); + /* For comparisons against zero, the zero is not provided + explicitly. */ + if (need_z_p) + op2 = ""; + + /* Begin by terminating the buffer. That way we can always use + strcat to add to it. */ + buffer[0] = '\0'; + + switch (length) + { + case 4: + case 8: + /* Just a simple conditional branch. */ + if (float_p) + sprintf (buffer, "b%s%%?\t%%Z2%%1", + inverted_p ? inverted_comp : comp); + else + sprintf (buffer, "b%s%s%%?\t%s%s,%%1", + inverted_p ? inverted_comp : comp, + need_z_p ? "z" : "", + op1, + op2); + return buffer; + + case 12: + case 16: + { + /* Generate a reversed conditional branch around ` j' + instruction: + + .set noreorder + .set nomacro + bc l + nop + j target + .set macro + .set reorder + l: + + Because we have to jump four bytes *past* the following + instruction if this branch was annulled, we can't just use + a label, as in the picture above; there's no way to put the + label after the next instruction, as the assembler does not + accept `.L+4' as the target of a branch. (We can't just + wait until the next instruction is output; it might be a + macro and take up more than four bytes. Once again, we see + why we want to eliminate macros.) + + If the branch is annulled, we jump four more bytes that we + would otherwise; that way we skip the annulled instruction + in the delay slot. */ + + const char *target + = ((iq2000_branch_likely || length == 16) ? ".+16" : ".+12"); + char *c; + + c = strchr (buffer, '\0'); + /* Generate the reversed comparision. This takes four + bytes. */ + if (float_p) + sprintf (c, "b%s\t%%Z2%s", + inverted_p ? comp : inverted_comp, + target); + else + sprintf (c, "b%s%s\t%s%s,%s", + inverted_p ? comp : inverted_comp, + need_z_p ? "z" : "", + op1, + op2, + target); + strcat (c, "\n\tnop\n\tj\t%1"); + if (length == 16) + /* The delay slot was unfilled. Since we're inside + .noreorder, the assembler will not fill in the NOP for + us, so we must do it ourselves. */ + strcat (buffer, "\n\tnop"); + return buffer; + } + + default: + abort (); + } + + /* NOTREACHED */ + return 0; +} + +static enum processor_type +iq2000_parse_cpu (cpu_string) + const char *cpu_string; +{ + const char *p = cpu_string; + enum processor_type cpu; + + cpu = PROCESSOR_DEFAULT; + switch (p[2]) + { + case '1': + if (!strcmp (p, "iq10")) + cpu = PROCESSOR_IQ10; + break; + case '2': + if (!strcmp (p, "iq2000")) + cpu = PROCESSOR_IQ2000; + break; + } + + return cpu; +} + +#define def_builtin(NAME, TYPE, CODE) \ + builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, NULL_TREE) + +void +iq2000_init_builtins () +{ + tree endlink = void_list_node; + tree void_ftype, void_ftype_int, void_ftype_int_int; + tree void_ftype_int_int_int; + tree int_ftype_int, int_ftype_int_int, int_ftype_int_int_int; + tree int_ftype_int_int_int_int; + + /* func () */ + void_ftype + = build_function_type (void_type_node, + tree_cons (NULL_TREE, void_type_node, endlink)); + + /* func (int) */ + void_ftype_int + = build_function_type (void_type_node, + tree_cons (NULL_TREE, integer_type_node, endlink)); + + /* void func (int, int) */ + void_ftype_int_int + = build_function_type (void_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + endlink))); + + /* int func (int) */ + int_ftype_int + = build_function_type (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, endlink)); + + /* int func (int, int) */ + int_ftype_int_int + = build_function_type (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + endlink))); + + /* void func (int, int, int) */ +void_ftype_int_int_int + = build_function_type + (void_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)))); + + /* int func (int, int, int, int) */ + int_ftype_int_int_int_int + = build_function_type + (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink))))); + + /* int func (int, int, int) */ + int_ftype_int_int_int + = build_function_type + (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink)))); + + /* int func (int, int, int, int) */ + int_ftype_int_int_int_int + = build_function_type + (integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + tree_cons (NULL_TREE, + integer_type_node, + endlink))))); + + def_builtin ("__builtin_ado16", int_ftype_int_int, IQ2000_BUILTIN_ADO16); + def_builtin ("__builtin_ram", int_ftype_int_int_int_int, IQ2000_BUILTIN_RAM); + def_builtin ("__builtin_chkhdr", void_ftype_int_int, IQ2000_BUILTIN_CHKHDR); + def_builtin ("__builtin_pkrl", void_ftype_int_int, IQ2000_BUILTIN_PKRL); + def_builtin ("__builtin_cfc0", int_ftype_int, IQ2000_BUILTIN_CFC0); + def_builtin ("__builtin_cfc1", int_ftype_int, IQ2000_BUILTIN_CFC1); + def_builtin ("__builtin_cfc2", int_ftype_int, IQ2000_BUILTIN_CFC2); + def_builtin ("__builtin_cfc3", int_ftype_int, IQ2000_BUILTIN_CFC3); + def_builtin ("__builtin_ctc0", void_ftype_int_int, IQ2000_BUILTIN_CTC0); + def_builtin ("__builtin_ctc1", void_ftype_int_int, IQ2000_BUILTIN_CTC1); + def_builtin ("__builtin_ctc2", void_ftype_int_int, IQ2000_BUILTIN_CTC2); + def_builtin ("__builtin_ctc3", void_ftype_int_int, IQ2000_BUILTIN_CTC3); + def_builtin ("__builtin_mfc0", int_ftype_int, IQ2000_BUILTIN_MFC0); + def_builtin ("__builtin_mfc1", int_ftype_int, IQ2000_BUILTIN_MFC1); + def_builtin ("__builtin_mfc2", int_ftype_int, IQ2000_BUILTIN_MFC2); + def_builtin ("__builtin_mfc3", int_ftype_int, IQ2000_BUILTIN_MFC3); + def_builtin ("__builtin_mtc0", void_ftype_int_int, IQ2000_BUILTIN_MTC0); + def_builtin ("__builtin_mtc1", void_ftype_int_int, IQ2000_BUILTIN_MTC1); + def_builtin ("__builtin_mtc2", void_ftype_int_int, IQ2000_BUILTIN_MTC2); + def_builtin ("__builtin_mtc3", void_ftype_int_int, IQ2000_BUILTIN_MTC3); + def_builtin ("__builtin_lur", void_ftype_int_int, IQ2000_BUILTIN_LUR); + def_builtin ("__builtin_rb", void_ftype_int_int, IQ2000_BUILTIN_RB); + def_builtin ("__builtin_rx", void_ftype_int_int, IQ2000_BUILTIN_RX); + def_builtin ("__builtin_srrd", void_ftype_int, IQ2000_BUILTIN_SRRD); + def_builtin ("__builtin_srwr", void_ftype_int_int, IQ2000_BUILTIN_SRWR); + def_builtin ("__builtin_wb", void_ftype_int_int, IQ2000_BUILTIN_WB); + def_builtin ("__builtin_wx", void_ftype_int_int, IQ2000_BUILTIN_WX); + def_builtin ("__builtin_luc32l", void_ftype_int_int, IQ2000_BUILTIN_LUC32L); + def_builtin ("__builtin_luc64", void_ftype_int_int, IQ2000_BUILTIN_LUC64); + def_builtin ("__builtin_luc64l", void_ftype_int_int, IQ2000_BUILTIN_LUC64L); + def_builtin ("__builtin_luk", void_ftype_int_int, IQ2000_BUILTIN_LUK); + def_builtin ("__builtin_lulck", void_ftype_int, IQ2000_BUILTIN_LULCK); + def_builtin ("__builtin_lum32", void_ftype_int_int, IQ2000_BUILTIN_LUM32); + def_builtin ("__builtin_lum32l", void_ftype_int_int, IQ2000_BUILTIN_LUM32L); + def_builtin ("__builtin_lum64", void_ftype_int_int, IQ2000_BUILTIN_LUM64); + def_builtin ("__builtin_lum64l", void_ftype_int_int, IQ2000_BUILTIN_LUM64L); + def_builtin ("__builtin_lurl", void_ftype_int_int, IQ2000_BUILTIN_LURL); + def_builtin ("__builtin_mrgb", int_ftype_int_int_int, IQ2000_BUILTIN_MRGB); + def_builtin ("__builtin_srrdl", void_ftype_int, IQ2000_BUILTIN_SRRDL); + def_builtin ("__builtin_srulck", void_ftype_int, IQ2000_BUILTIN_SRULCK); + def_builtin ("__builtin_srwru", void_ftype_int_int, IQ2000_BUILTIN_SRWRU); + def_builtin ("__builtin_trapqfl", void_ftype, IQ2000_BUILTIN_TRAPQFL); + def_builtin ("__builtin_trapqne", void_ftype, IQ2000_BUILTIN_TRAPQNE); + def_builtin ("__builtin_traprel", void_ftype_int, IQ2000_BUILTIN_TRAPREL); + def_builtin ("__builtin_wbu", void_ftype_int_int_int, IQ2000_BUILTIN_WBU); + def_builtin ("__builtin_syscall", void_ftype, IQ2000_BUILTIN_SYSCALL); +} + +/* Builtin for ICODE having ARGCOUNT args in ARGLIST where each arg + has an rtx CODE */ + +static rtx +expand_one_builtin (icode, target, arglist, code, argcount) + enum insn_code icode; + rtx target; + tree arglist; + enum rtx_code *code; + int argcount; +{ + rtx pat; + tree arg [5]; + rtx op [5]; + enum machine_mode mode [5]; + int i; + + mode[0] = insn_data[icode].operand[0].mode; + for (i = 0; i < argcount; i++) + { + arg[i] = TREE_VALUE (arglist); + arglist = TREE_CHAIN (arglist); + op[i] = expand_expr (arg[i], NULL_RTX, VOIDmode, 0); + mode[i] = insn_data[icode].operand[i].mode; + if (code[i] == CONST_INT && GET_CODE (op[i]) != CONST_INT) + error ("argument `%d' is not a constant", i + 1); + if (code[i] == REG + && ! (*insn_data[icode].operand[i].predicate) (op[i], mode[i])) + op[i] = copy_to_mode_reg (mode[i], op[i]); + } + + if (insn_data[icode].operand[0].constraint[0] == '=') + { + if (target == 0 + || GET_MODE (target) != mode[0] + || ! (*insn_data[icode].operand[0].predicate) (target, mode[0])) + target = gen_reg_rtx (mode[0]); + } + else + target = 0; + + switch (argcount) + { + case 0: + pat = GEN_FCN (icode) (target); + case 1: + if (target) + pat = GEN_FCN (icode) (target, op[0]); + else + pat = GEN_FCN (icode) (op[0]); + break; + case 2: + if (target) + pat = GEN_FCN (icode) (target, op[0], op[1]); + else + pat = GEN_FCN (icode) (op[0], op[1]); + break; + case 3: + if (target) + pat = GEN_FCN (icode) (target, op[0], op[1], op[2]); + else + pat = GEN_FCN (icode) (op[0], op[1], op[2]); + break; + case 4: + if (target) + pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]); + else + pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]); + break; + default: + abort (); + } + + if (! pat) + return 0; + emit_insn (pat); + return target; +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +rtx +iq2000_expand_builtin (exp, target, subtarget, mode, ignore) + tree exp; + rtx target; + rtx subtarget ATTRIBUTE_UNUSED; + enum machine_mode mode ATTRIBUTE_UNUSED; + int ignore ATTRIBUTE_UNUSED; +{ + tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + tree arglist = TREE_OPERAND (exp, 1); + int fcode = DECL_FUNCTION_CODE (fndecl); + enum rtx_code code [5]; + + code[0] = REG; + code[1] = REG; + code[2] = REG; + code[3] = REG; + code[4] = REG; + switch (fcode) + { + default: + break; + + case IQ2000_BUILTIN_ADO16: + return expand_one_builtin (CODE_FOR_ado16, target, arglist, code, 2); + + case IQ2000_BUILTIN_RAM: + code[1] = CONST_INT; + code[2] = CONST_INT; + code[3] = CONST_INT; + return expand_one_builtin (CODE_FOR_ram, target, arglist, code, 4); + + case IQ2000_BUILTIN_CHKHDR: + return expand_one_builtin (CODE_FOR_chkhdr, target, arglist, code, 2); + + case IQ2000_BUILTIN_PKRL: + return expand_one_builtin (CODE_FOR_pkrl, target, arglist, code, 2); + + case IQ2000_BUILTIN_CFC0: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_cfc0, target, arglist, code, 1); + + case IQ2000_BUILTIN_CFC1: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_cfc1, target, arglist, code, 1); + + case IQ2000_BUILTIN_CFC2: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_cfc2, target, arglist, code, 1); + + case IQ2000_BUILTIN_CFC3: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_cfc3, target, arglist, code, 1); + + case IQ2000_BUILTIN_CTC0: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_ctc0, target, arglist, code, 2); + + case IQ2000_BUILTIN_CTC1: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_ctc1, target, arglist, code, 2); + + case IQ2000_BUILTIN_CTC2: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_ctc2, target, arglist, code, 2); + + case IQ2000_BUILTIN_CTC3: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_ctc3, target, arglist, code, 2); + + case IQ2000_BUILTIN_MFC0: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_mfc0, target, arglist, code, 1); + + case IQ2000_BUILTIN_MFC1: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_mfc1, target, arglist, code, 1); + + case IQ2000_BUILTIN_MFC2: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_mfc2, target, arglist, code, 1); + + case IQ2000_BUILTIN_MFC3: + code[0] = CONST_INT; + return expand_one_builtin (CODE_FOR_mfc3, target, arglist, code, 1); + + case IQ2000_BUILTIN_MTC0: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_mtc0, target, arglist, code, 2); + + case IQ2000_BUILTIN_MTC1: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_mtc1, target, arglist, code, 2); + + case IQ2000_BUILTIN_MTC2: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_mtc2, target, arglist, code, 2); + + case IQ2000_BUILTIN_MTC3: + code[1] = CONST_INT; + return expand_one_builtin (CODE_FOR_mtc3, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUR: + return expand_one_builtin (CODE_FOR_lur, target, arglist, code, 2); + + case IQ2000_BUILTIN_RB: + return expand_one_builtin (CODE_FOR_rb, target, arglist, code, 2); + + case IQ2000_BUILTIN_RX: + return expand_one_builtin (CODE_FOR_rx, target, arglist, code, 2); + + case IQ2000_BUILTIN_SRRD: + return expand_one_builtin (CODE_FOR_srrd, target, arglist, code, 1); + + case IQ2000_BUILTIN_SRWR: + return expand_one_builtin (CODE_FOR_srwr, target, arglist, code, 2); + + case IQ2000_BUILTIN_WB: + return expand_one_builtin (CODE_FOR_wb, target, arglist, code, 2); + + case IQ2000_BUILTIN_WX: + return expand_one_builtin (CODE_FOR_wx, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUC32L: + return expand_one_builtin (CODE_FOR_luc32l, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUC64: + return expand_one_builtin (CODE_FOR_luc64, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUC64L: + return expand_one_builtin (CODE_FOR_luc64l, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUK: + return expand_one_builtin (CODE_FOR_luk, target, arglist, code, 2); + + case IQ2000_BUILTIN_LULCK: + return expand_one_builtin (CODE_FOR_lulck, target, arglist, code, 1); + + case IQ2000_BUILTIN_LUM32: + return expand_one_builtin (CODE_FOR_lum32, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUM32L: + return expand_one_builtin (CODE_FOR_lum32l, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUM64: + return expand_one_builtin (CODE_FOR_lum64, target, arglist, code, 2); + + case IQ2000_BUILTIN_LUM64L: + return expand_one_builtin (CODE_FOR_lum64l, target, arglist, code, 2); + + case IQ2000_BUILTIN_LURL: + return expand_one_builtin (CODE_FOR_lurl, target, arglist, code, 2); + + case IQ2000_BUILTIN_MRGB: + code[2] = CONST_INT; + return expand_one_builtin (CODE_FOR_mrgb, target, arglist, code, 3); + + case IQ2000_BUILTIN_SRRDL: + return expand_one_builtin (CODE_FOR_srrdl, target, arglist, code, 1); + + case IQ2000_BUILTIN_SRULCK: + return expand_one_builtin (CODE_FOR_srulck, target, arglist, code, 1); + + case IQ2000_BUILTIN_SRWRU: + return expand_one_builtin (CODE_FOR_srwru, target, arglist, code, 2); + + case IQ2000_BUILTIN_TRAPQFL: + return expand_one_builtin (CODE_FOR_trapqfl, target, arglist, code, 0); + + case IQ2000_BUILTIN_TRAPQNE: + return expand_one_builtin (CODE_FOR_trapqne, target, arglist, code, 0); + + case IQ2000_BUILTIN_TRAPREL: + return expand_one_builtin (CODE_FOR_traprel, target, arglist, code, 1); + + case IQ2000_BUILTIN_WBU: + return expand_one_builtin (CODE_FOR_wbu, target, arglist, code, 3); + + case IQ2000_BUILTIN_SYSCALL: + return expand_one_builtin (CODE_FOR_syscall, target, arglist, code, 0); + } + + return NULL_RTX; +} + +void +iq2000_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) + CUMULATIVE_ARGS cum; + int mode ATTRIBUTE_UNUSED; + tree type ATTRIBUTE_UNUSED; + int * pretend_size; + int no_rtl; +{ + unsigned int iq2000_off = (! (cum).last_arg_fp); + unsigned int iq2000_fp_off = ((cum).last_arg_fp); + if (((cum).arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off)) + { + int iq2000_save_gp_regs + = MAX_ARGS_IN_REGISTERS - (cum).arg_words - iq2000_off; + int iq2000_save_fp_regs + = (MAX_ARGS_IN_REGISTERS - (cum).fp_arg_words - iq2000_fp_off); + + if (iq2000_save_gp_regs < 0) + iq2000_save_gp_regs = 0; + if (iq2000_save_fp_regs < 0) + iq2000_save_fp_regs = 0; + + *pretend_size = ((iq2000_save_gp_regs * UNITS_PER_WORD) + + (iq2000_save_fp_regs * UNITS_PER_FPREG)); + + if (! (no_rtl)) + { + if ((cum).arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off) + { + rtx ptr, mem; + ptr = plus_constant (virtual_incoming_args_rtx, + - (iq2000_save_gp_regs + * UNITS_PER_WORD)); + mem = gen_rtx_MEM (BLKmode, ptr); + move_block_from_reg + ((cum).arg_words + GP_ARG_FIRST + iq2000_off, + mem, + iq2000_save_gp_regs); + } + } + } +} + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand that is a memory + reference whose address is ADDR. ADDR is an RTL expression. +*/ + +void +print_operand_address (file, addr) + FILE *file; + rtx addr; +{ + if (!addr) + error ("PRINT_OPERAND_ADDRESS, null pointer"); + + else + switch (GET_CODE (addr)) + { + case REG: + if (REGNO (addr) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + + fprintf (file, "0(%s)", reg_names [REGNO (addr)]); + break; + + case LO_SUM: + { + register rtx arg0 = XEXP (addr, 0); + register rtx arg1 = XEXP (addr, 1); + + if (GET_CODE (arg0) != REG) + abort_with_insn (addr, + "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG."); + + fprintf (file, "%%lo("); + print_operand_address (file, arg1); + fprintf (file, ")(%s)", reg_names [REGNO (arg0)]); + } + break; + + case PLUS: + { + register rtx reg = 0; + register rtx offset = 0; + register rtx arg0 = XEXP (addr, 0); + register rtx arg1 = XEXP (addr, 1); + + if (GET_CODE (arg0) == REG) + { + reg = arg0; + offset = arg1; + if (GET_CODE (offset) == REG) + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs"); + } + + else if (GET_CODE (arg1) == REG) + reg = arg1, offset = arg0; + else if (CONSTANT_P (arg0) && CONSTANT_P (arg1)) + { + output_addr_const (file, addr); + break; + } + else + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs"); + + if (! CONSTANT_P (offset)) + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2"); + + if (REGNO (reg) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + + output_addr_const (file, offset); + fprintf (file, "(%s)", reg_names [REGNO (reg)]); + } + break; + + case LABEL_REF: + case SYMBOL_REF: + case CONST_INT: + case CONST: + output_addr_const (file, addr); + if (GET_CODE (addr) == CONST_INT) + fprintf (file, "(%s)", reg_names [0]); + break; + + default: + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1"); + break; + } +} + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand X. X is an RTL + expression. + + CODE is a value that can be used to specify one of several ways + of printing the operand. It is used when identical operands + must be printed differently depending on the context. CODE + comes from the `%' specification that was used to request + printing of the operand. If the specification was just `%DIGIT' + then CODE is 0; if the specification was `%LTR DIGIT' then CODE + is the ASCII code for LTR. + + If X is a register, this macro should print the register's name. + The names can be found in an array `reg_names' whose type is + `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. + + When the machine description has a specification `%PUNCT' (a `%' + followed by a punctuation character), this macro is called with + a null pointer for X and the punctuation character for CODE. + + The IQ2000 specific codes are: + + 'X' X is CONST_INT, prints upper 16 bits in hexadecimal format = "0x%04x", + 'x' X is CONST_INT, prints lower 16 bits in hexadecimal format = "0x%04x", + 'd' output integer constant in decimal, + 'z' if the operand is 0, use $0 instead of normal operand. + 'D' print second part of double-word register or memory operand. + 'L' print low-order register of double-word register operand. + 'M' print high-order register of double-word register operand. + 'C' print part of opcode for a branch condition. + 'F' print part of opcode for a floating-point branch condition. + 'N' print part of opcode for a branch condition, inverted. + 'W' print part of opcode for a floating-point branch condition, inverted. + 'A' Print part of opcode for a bit test condition. + 'P' Print label for a bit test. + 'p' Print log for a bit test. + 'B' print 'z' for EQ, 'n' for NE + 'b' print 'n' for EQ, 'z' for NE + 'T' print 'f' for EQ, 't' for NE + 't' print 't' for EQ, 'f' for NE + 'Z' print register and a comma, but print nothing for $fcc0 + '?' Print 'l' if we are to use a branch likely instead of normal branch. + '@' Print the name of the assembler temporary register (at or $1). + '.' Print the name of the register with a hard-wired zero (zero or $0). + '$' Print the name of the stack pointer register (sp or $29). + '+' Print the name of the gp register (gp or $28). */ + +void +print_operand (file, op, letter) + FILE *file; /* file to write to */ + rtx op; /* operand to print */ + int letter; /* % or 0 */ +{ + register enum rtx_code code; + + if (PRINT_OPERAND_PUNCT_VALID_P (letter)) + { + switch (letter) + { + case '?': + if (iq2000_branch_likely) + putc ('l', file); + break; + + case '@': + fputs (reg_names [GP_REG_FIRST + 1], file); + break; + + case '.': + fputs (reg_names [GP_REG_FIRST + 0], file); + break; + + case '$': + fputs (reg_names[STACK_POINTER_REGNUM], file); + break; + + case '+': + fputs (reg_names[GP_REG_FIRST + 28], file); + break; + + default: + error ("PRINT_OPERAND: Unknown punctuation '%c'", letter); + break; + } + + return; + } + + if (! op) + { + error ("PRINT_OPERAND null pointer"); + return; + } + + code = GET_CODE (op); + + if (code == SIGN_EXTEND) + op = XEXP (op, 0), code = GET_CODE (op); + + if (letter == 'C') + switch (code) + { + case EQ: fputs ("eq", file); break; + case NE: fputs ("ne", file); break; + case GT: fputs ("gt", file); break; + case GE: fputs ("ge", file); break; + case LT: fputs ("lt", file); break; + case LE: fputs ("le", file); break; + case GTU: fputs ("ne", file); break; + case GEU: fputs ("geu", file); break; + case LTU: fputs ("ltu", file); break; + case LEU: fputs ("eq", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C"); + } + + else if (letter == 'N') + switch (code) + { + case EQ: fputs ("ne", file); break; + case NE: fputs ("eq", file); break; + case GT: fputs ("le", file); break; + case GE: fputs ("lt", file); break; + case LT: fputs ("ge", file); break; + case LE: fputs ("gt", file); break; + case GTU: fputs ("leu", file); break; + case GEU: fputs ("ltu", file); break; + case LTU: fputs ("geu", file); break; + case LEU: fputs ("gtu", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N"); + } + + else if (letter == 'F') + switch (code) + { + case EQ: fputs ("c1f", file); break; + case NE: fputs ("c1t", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F"); + } + + else if (letter == 'W') + switch (code) + { + case EQ: fputs ("c1t", file); break; + case NE: fputs ("c1f", file); break; + default: + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W"); + } + + else if (letter == 'A') + fputs (code == LABEL_REF ? "i" : "in", file); + + else if (letter == 'P') + { + if (code == LABEL_REF) + output_addr_const (file, op); + else if (code != PC) + output_operand_lossage ("invalid %%P operand"); + } + + else if (letter == 'p') + { + int value; + if (code != CONST_INT + || (value = exact_log2 (INTVAL (op))) < 0) + output_operand_lossage ("invalid %%p value"); + fprintf (file, "%d", value); + } + + else if (letter == 'Z') + { + register int regnum; + + if (code != REG) + abort (); + + regnum = REGNO (op); + abort (); + + fprintf (file, "%s,", reg_names[regnum]); + } + + else if (code == REG || code == SUBREG) + { + register int regnum; + + if (code == REG) + regnum = REGNO (op); + else + regnum = true_regnum (op); + + if ((letter == 'M' && ! WORDS_BIG_ENDIAN) + || (letter == 'L' && WORDS_BIG_ENDIAN) + || letter == 'D') + regnum++; + + fprintf (file, "%s", reg_names[regnum]); + } + + else if (code == MEM) + { + if (letter == 'D') + output_address (plus_constant (XEXP (op, 0), 4)); + else + output_address (XEXP (op, 0)); + } + + else if (code == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT) + { + char s[60]; + + real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1); + fputs (s, file); + } + + else if (letter == 'x' && GET_CODE (op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op)); + + else if (letter == 'X' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & (INTVAL (op) >> 16)); + + else if (letter == 'd' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op))); + + else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0) + fputs (reg_names[GP_REG_FIRST], file); + + else if (letter == 'd' || letter == 'x' || letter == 'X') + output_operand_lossage ("invalid use of %%d, %%x, or %%X"); + + else if (letter == 'B') + fputs (code == EQ ? "z" : "n", file); + else if (letter == 'b') + fputs (code == EQ ? "n" : "z", file); + else if (letter == 'T') + fputs (code == EQ ? "f" : "t", file); + else if (letter == 't') + fputs (code == EQ ? "t" : "f", file); + + else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG) + { + print_operand (file, XEXP (op, 0), letter); + } + + else + output_addr_const (file, op); +} diff --git a/gcc/config/iq2000/iq2000.h b/gcc/config/iq2000/iq2000.h new file mode 100644 index 0000000..4f4bf39 --- /dev/null +++ b/gcc/config/iq2000/iq2000.h @@ -0,0 +1,1486 @@ +/* Definitions of target machine for GNU compiler. + Vitesse IQ2000 processors + Copyright (C) 2003 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 2, 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 COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* Set up System V.4 (aka ELF) defaults. */ +#include "svr4.h" +#include "elfos.h" + + +/* Driver configuration. */ + +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G') + +/* The svr4.h LIB_SPEC with -leval and --*group tacked on */ +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{!symbolic:--start-group -lc -leval -lgcc --end-group}}" + +#undef STARTFILE_SPEC +#undef ENDFILE_SPEC + + +/* Run-time target specifications. */ + +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__iq2000__"); \ + builtin_assert ("cpu=iq2000"); \ + builtin_assert ("machine=iq2000"); \ + } \ + while (0) + + +extern int target_flags; + +#define MASK_GPOPT 0x00000008 /* Optimize for global pointer */ +#define MASK_EMBEDDED_DATA 0x00008000 /* Reduce RAM usage, not fast code */ +#define MASK_UNINIT_CONST_IN_RODATA \ + 0x00800000 /* Store uninitialized + consts in rodata */ + +/* Macros used in the machine description to test the flags. */ + +#define TARGET_STATS 0 + + /* for embedded systems, optimize for + reduced RAM space instead of for + fastest code. */ +#define TARGET_EMBEDDED_DATA (target_flags & MASK_EMBEDDED_DATA) + +#define TARGET_DEBUG_MODE (target_flags & 0) +#define TARGET_DEBUG_A_MODE (target_flags & 0) +#define TARGET_DEBUG_B_MODE (target_flags & 0) +#define TARGET_DEBUG_C_MODE (target_flags & 0) +#define TARGET_DEBUG_D_MODE (target_flags & 0) + +#define TARGET_SWITCHES \ +{ \ + {"no-crt0", 0, \ + N_("No default crt0.o") }, \ + {"gpopt", MASK_GPOPT, \ + N_("Use GP relative sdata/sbss sections")}, \ + {"no-gpopt", -MASK_GPOPT, \ + N_("Don't use GP relative sdata/sbss sections")}, \ + {"embedded-data", MASK_EMBEDDED_DATA, \ + N_("Use ROM instead of RAM")}, \ + {"no-embedded-data", -MASK_EMBEDDED_DATA, \ + N_("Don't use ROM instead of RAM")}, \ + {"uninit-const-in-rodata", MASK_UNINIT_CONST_IN_RODATA, \ + N_("Put uninitialized constants in ROM (needs -membedded-data)")}, \ + {"no-uninit-const-in-rodata", -MASK_UNINIT_CONST_IN_RODATA, \ + N_("Don't put uninitialized constants in ROM")}, \ + {"", (TARGET_DEFAULT \ + | TARGET_CPU_DEFAULT), \ + NULL}, \ +} + +/* Default target_flags if no switches are specified. */ + +#define TARGET_DEFAULT 0 + +#ifndef TARGET_CPU_DEFAULT +#define TARGET_CPU_DEFAULT 0 +#endif + +#ifndef IQ2000_ISA_DEFAULT +#define IQ2000_ISA_DEFAULT 1 +#endif + +#define TARGET_OPTIONS \ +{ \ + SUBTARGET_TARGET_OPTIONS \ + { "cpu=", & iq2000_cpu_string, \ + N_("Specify CPU for scheduling purposes")}, \ + { "arch=", & iq2000_arch_string, \ + N_("Specify CPU for code generation purposes")}, \ +} + +/* This is meant to be redefined in the host dependent files. */ +#define SUBTARGET_TARGET_OPTIONS + +#define IQ2000_VERSION "[1.0]" + +#ifndef MACHINE_TYPE +#define MACHINE_TYPE "IQ2000" +#endif + +#ifndef TARGET_VERSION_INTERNAL +#define TARGET_VERSION_INTERNAL(STREAM) \ + fprintf (STREAM, " %s %s", IQ2000_VERSION, MACHINE_TYPE) +#endif + +#ifndef TARGET_VERSION +#define TARGET_VERSION TARGET_VERSION_INTERNAL (stderr) +#endif + +#define OVERRIDE_OPTIONS override_options () + +#define CAN_DEBUG_WITHOUT_FP + +/* Storage Layout. */ + +#define BITS_BIG_ENDIAN 0 + +#define BYTES_BIG_ENDIAN 1 + +#define WORDS_BIG_ENDIAN 1 + +#define LIBGCC2_WORDS_BIG_ENDIAN 1 + +#define BITS_PER_UNIT 8 + +#define BITS_PER_WORD 32 + +#define MAX_BITS_PER_WORD 64 + +#define UNITS_PER_WORD 4 + +#define MIN_UNITS_PER_WORD 4 + +#define POINTER_SIZE 32 + +/* 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. + + We promote any value smaller than SImode up to SImode. */ + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < 4) \ + (MODE) = SImode; + +#define PROMOTE_FUNCTION_ARGS + +#define PROMOTE_FUNCTION_RETURN + +#define PARM_BOUNDARY 32 + +#define STACK_BOUNDARY 64 + +#define FUNCTION_BOUNDARY 32 + +#define BIGGEST_ALIGNMENT 64 + +#undef DATA_ALIGNMENT +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + ((((ALIGN) < BITS_PER_WORD) \ + && (TREE_CODE (TYPE) == ARRAY_TYPE \ + || TREE_CODE (TYPE) == UNION_TYPE \ + || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN)) + +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST || TREE_CODE (EXP) == CONSTRUCTOR) \ + && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) + +#define EMPTY_FIELD_BOUNDARY 32 + +#define STRUCTURE_SIZE_BOUNDARY 8 + +#define STRICT_ALIGNMENT 1 + +#define PCC_BITFIELD_TYPE_MATTERS 1 + +#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT + + +/* Layout of Source Language Data Types. */ + +#define INT_TYPE_SIZE 32 + +#define MAX_INT_TYPE_SIZE 32 + +#define SHORT_TYPE_SIZE 16 + +#define LONG_TYPE_SIZE 32 + +#define LONG_LONG_TYPE_SIZE 64 + +#define CHAR_TYPE_SIZE BITS_PER_UNIT + +#define FLOAT_TYPE_SIZE 32 + +#define DOUBLE_TYPE_SIZE 64 + +#define LONG_DOUBLE_TYPE_SIZE 64 + +#define DEFAULT_SIGNED_CHAR 1 + +#define MAX_WCHAR_TYPE_SIZE MAX_INT_TYPE_SIZE + + +/* Register Basics. */ + +/* On the IQ2000, we have 32 integer registers. */ +#define FIRST_PSEUDO_REGISTER 33 + +#define FIXED_REGISTERS \ +{ \ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1 \ +} + +#define CALL_USED_REGISTERS \ +{ \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1 \ +} + + +/* Order of allocation of registers. */ + +#define REG_ALLOC_ORDER \ +{ 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 \ +} + + +/* How Values Fit in Registers. */ + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + ((REGNO_REG_CLASS (REGNO) == GR_REGS) \ + ? ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) <= 4 \ + : ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) == 4) + +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((GET_MODE_CLASS (MODE1) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT) \ + == (GET_MODE_CLASS (MODE2) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT)) + +#define AVOID_CCMODE_COPIES + + +/* Register Classes. */ + +enum reg_class +{ + NO_REGS, /* no registers in set */ + GR_REGS, /* integer registers */ + ALL_REGS, /* all registers */ + LIM_REG_CLASSES /* max value + 1 */ +}; + +#define GENERAL_REGS GR_REGS + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "GR_REGS", \ + "ALL_REGS" \ +} + +#define REG_CLASS_CONTENTS \ +{ \ + { 0x00000000, 0x00000000 }, /* no registers */ \ + { 0xffffffff, 0x00000000 }, /* integer registers */ \ + { 0xffffffff, 0x00000001 } /* all registers */ \ +} + +#define REGNO_REG_CLASS(REGNO) \ +((REGNO) <= GP_REG_LAST + 1 ? GR_REGS : NO_REGS) + +#define BASE_REG_CLASS (GR_REGS) + +#define INDEX_REG_CLASS NO_REGS + +#define REG_CLASS_FROM_LETTER(C) \ + ((C) == 'd' ? GR_REGS : \ + (C) == 'b' ? ALL_REGS : \ + (C) == 'y' ? GR_REGS : \ + NO_REGS) + +#define REGNO_OK_FOR_INDEX_P(regno) 0 + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + ((CLASS) != ALL_REGS \ + ? (CLASS) \ + : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \ + || GET_MODE_CLASS (GET_MODE (X)) == MODE_COMPLEX_FLOAT) \ + ? (GR_REGS) \ + : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_INT \ + || GET_MODE (X) == VOIDmode) \ + ? (GR_REGS) \ + : (CLASS)))) + +#define SMALL_REGISTER_CLASSES 0 + +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* For IQ2000: + + `I' is used for the range of constants an arithmetic insn can + actually contain (16 bits signed integers). + + `J' is used for the range which is just zero (ie, $r0). + + `K' is used for the range of constants a logical insn can actually + contain (16 bit zero-extended integers). + + `L' is used for the range of constants that be loaded with lui + (ie, the bottom 16 bits are zero). + + `M' is used for the range of constants that take two words to load + (ie, not matched by `I', `K', and `L'). + + `N' is used for constants 0xffffnnnn or 0xnnnnffff + + `O' is a 5 bit zero-extended integer. +*/ + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x8000) < 0x10000) \ + : (C) == 'J' ? ((VALUE) == 0) \ + : (C) == 'K' ? ((unsigned HOST_WIDE_INT) (VALUE) < 0x10000) \ + : (C) == 'L' ? (((VALUE) & 0x0000ffff) == 0 \ + && (((VALUE) & ~2147483647) == 0 \ + || ((VALUE) & ~2147483647) == ~2147483647)) \ + : (C) == 'M' ? ((((VALUE) & ~0x0000ffff) != 0) \ + && (((VALUE) & ~0x0000ffff) != ~0x0000ffff) \ + && (((VALUE) & 0x0000ffff) != 0 \ + || (((VALUE) & ~2147483647) != 0 \ + && ((VALUE) & ~2147483647) != ~2147483647))) \ + : (C) == 'N' ? ((((VALUE) & 0xffff) == 0xffff) \ + || (((VALUE) & 0xffff0000) == 0xffff0000)) \ + : (C) == 'O' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x20) < 0x40) \ + : 0) + +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' \ + && (VALUE) == CONST0_RTX (GET_MODE (VALUE))) + +/* `R' is for memory references which take 1 word for the instruction. */ + +#define EXTRA_CONSTRAINT(OP,CODE) \ + (((CODE) == 'R') ? simple_memory_operand (OP, GET_MODE (OP)) \ + : FALSE) + + +/* Basic Stack Layout. */ + +#define STACK_GROWS_DOWNWARD + +/* #define FRAME_GROWS_DOWNWARD */ + +#define STARTING_FRAME_OFFSET \ + (current_function_outgoing_args_size) + +/* Use the default value zero. */ +/* #define STACK_POINTER_OFFSET 0 */ + +#define FIRST_PARM_OFFSET(FNDECL) 0 + +/* The return address for the current frame is in r31 if this is a leaf + function. Otherwise, it is on the stack. It is at a variable offset + from sp/fp/ap, so we define a fake hard register rap which is a + pointer to the return address on the stack. This always gets eliminated + during reload to be either the frame pointer or the stack pointer plus + an offset. */ + +#define RETURN_ADDR_RTX(count, frame) \ + (((count) == 0) \ + ? (leaf_function_p () \ + ? gen_rtx_REG (Pmode, GP_REG_FIRST + 31) \ + : gen_rtx_MEM (Pmode, gen_rtx_REG (Pmode, \ + RETURN_ADDRESS_POINTER_REGNUM))) \ + : (rtx) 0) + +/* Before the prologue, RA lives in r31. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (VOIDmode, GP_REG_FIRST + 31) + + +/* Register That Address the Stack Frame. */ + +#define STACK_POINTER_REGNUM (GP_REG_FIRST + 29) + +#define FRAME_POINTER_REGNUM (GP_REG_FIRST + 1) + +#define HARD_FRAME_POINTER_REGNUM \ + (GP_REG_FIRST + 27) + +#define ARG_POINTER_REGNUM GP_REG_FIRST + +#define RETURN_ADDRESS_POINTER_REGNUM RAP_REG_NUM + +#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 2) + + +/* Eliminating the Frame Pointer and the Arg Pointer. */ + +#define FRAME_POINTER_REQUIRED 0 + +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \ + { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \ + { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 31}, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}} + + +/* We can always eliminate to the frame pointer. We can eliminate to the + stack pointer unless a frame pointer is needed. */ + +#define CAN_ELIMINATE(FROM, TO) \ + (((FROM) == RETURN_ADDRESS_POINTER_REGNUM && (! leaf_function_p () \ + || (TO == GP_REG_FIRST + 31 && leaf_function_p))) \ + || ((FROM) != RETURN_ADDRESS_POINTER_REGNUM \ + && ((TO) == HARD_FRAME_POINTER_REGNUM \ + || ((TO) == STACK_POINTER_REGNUM && ! frame_pointer_needed)))) + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = iq2000_initial_elimination_offset ((FROM), (TO)) + +/* Passing Function Arguments on the Stack. */ + +#define PROMOTE_PROTOTYPES 1 + +/* #define PUSH_ROUNDING(BYTES) 0 */ + +#define ACCUMULATE_OUTGOING_ARGS 1 + +#define REG_PARM_STACK_SPACE(FNDECL) 0 + +#define OUTGOING_REG_PARM_STACK_SPACE + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + + +/* Function Arguments in Registers. */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + function_arg( &CUM, MODE, TYPE, NAMED) + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ + function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED) + +#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + function_arg_pass_by_reference (&CUM, MODE, TYPE, NAMED) + +#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED) \ + ((NAMED) && FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED)) + +#define MAX_ARGS_IN_REGISTERS 8 + +typedef struct iq2000_args { + int gp_reg_found; /* whether a gp register was found yet */ + unsigned int arg_number; /* argument number */ + unsigned int arg_words; /* # total words the arguments take */ + unsigned int fp_arg_words; /* # words for FP args (IQ2000_EABI only) */ + int last_arg_fp; /* nonzero if last arg was FP (EABI only) */ + int fp_code; /* Mode of FP arguments */ + unsigned int num_adjusts; /* number of adjustments made */ + /* Adjustments made to args pass in regs. */ + struct rtx_def *adjust[MAX_ARGS_IN_REGISTERS*2]; +} CUMULATIVE_ARGS; + +/* 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. */ +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \ + init_cumulative_args (&CUM, FNTYPE, LIBNAME) \ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + function_arg_advance (&CUM, MODE, TYPE, NAMED) + +#define FUNCTION_ARG_PADDING(MODE, TYPE) \ + (! BYTES_BIG_ENDIAN \ + ? upward \ + : (((MODE) == BLKmode \ + ? ((TYPE) && TREE_CODE (TYPE_SIZE (TYPE)) == INTEGER_CST \ + && int_size_in_bytes (TYPE) < (PARM_BOUNDARY / BITS_PER_UNIT))\ + : (GET_MODE_BITSIZE (MODE) < PARM_BOUNDARY \ + && (GET_MODE_CLASS (MODE) == MODE_INT))) \ + ? downward : upward)) + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ + (((TYPE) != 0) \ + ? ((TYPE_ALIGN(TYPE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : TYPE_ALIGN(TYPE)) \ + : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : GET_MODE_ALIGNMENT(MODE))) + +#define FUNCTION_ARG_REGNO_P(N) \ + (((N) >= GP_ARG_FIRST && (N) <= GP_ARG_LAST)) + + +/* How Scalar Function Values are Returned. */ + +#define FUNCTION_VALUE(VALTYPE, FUNC) iq2000_function_value (VALTYPE, FUNC) + +#define LIBCALL_VALUE(MODE) \ + gen_rtx (REG, \ + ((GET_MODE_CLASS (MODE) != MODE_INT \ + || GET_MODE_SIZE (MODE) >= 4) \ + ? (MODE) \ + : SImode), \ + GP_RETURN) + +/* On the IQ2000, R2 and R3 are the only register thus used. */ + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN) + + +/* How Large Values are Returned. */ + +#define RETURN_IN_MEMORY(TYPE) \ + (((int_size_in_bytes (TYPE) \ + > (2 * UNITS_PER_WORD)) \ + || (int_size_in_bytes (TYPE) == -1))) + +#define DEFAULT_PCC_STRUCT_RETURN 0 + +#define STRUCT_VALUE 0 + + +/* Function Entry and Exit. */ + +#define EXIT_IGNORE_STACK 1 + + +/* Generating Code for Profiling. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ +{ \ + fprintf (FILE, "\t.set\tnoreorder\n"); \ + fprintf (FILE, "\t.set\tnoat\n"); \ + fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n", \ + reg_names[GP_REG_FIRST + 1], reg_names[GP_REG_FIRST + 31]); \ + fprintf (FILE, "\tjal\t_mcount\n"); \ + fprintf (FILE, \ + "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from stack\n", \ + "subu", \ + reg_names[STACK_POINTER_REGNUM], \ + reg_names[STACK_POINTER_REGNUM], \ + Pmode == DImode ? 16 : 8); \ + fprintf (FILE, "\t.set\treorder\n"); \ + fprintf (FILE, "\t.set\tat\n"); \ +} + + +/* Implementing the Varargs Macros. */ + +#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \ + iq2000_setup_incoming_varargs (CUM,MODE,TYPE,&PRETEND_SIZE,NO_RTL); + +#define STRICT_ARGUMENT_NAMING 1 + +#define BUILD_VA_LIST_TYPE(VALIST) \ + (VALIST) = ptr_type_node + +#define EXPAND_BUILTIN_VA_START(valist, nextarg) \ + iq2000_va_start (valist, nextarg) + +/* Implement `va_arg'. */ +#define EXPAND_BUILTIN_VA_ARG(valist, type) \ + iq2000_va_arg (valist, type) + + +/* Trampolines for Nested Functions. */ + +/* A C statement to output, on the stream FILE, assembler code for a + block of data that contains the constant parts of a trampoline. + This code should not include a label--the label is taken care of + automatically. */ + +#define TRAMPOLINE_TEMPLATE(STREAM) \ +{ \ + fprintf (STREAM, "\t.word\t0x03e00821\t\t# move $1,$31\n"); \ + fprintf (STREAM, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# nop\n"); \ + if (Pmode == DImode) \ + { \ + fprintf (STREAM, "\t.word\t0xdfe30014\t\t# ld $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0xdfe2001c\t\t# ld $2,28($31)\n"); \ + } \ + else \ + { \ + fprintf (STREAM, "\t.word\t0x8fe30014\t\t# lw $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0x8fe20018\t\t# lw $2,24($31)\n"); \ + } \ + fprintf (STREAM, "\t.word\t0x0060c821\t\t# move $25,$3 (abicalls)\n"); \ + fprintf (STREAM, "\t.word\t0x00600008\t\t# jr $3\n"); \ + fprintf (STREAM, "\t.word\t0x0020f821\t\t# move $31,$1\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# \n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# \n"); \ +} + +#define TRAMPOLINE_SIZE (40) + +#define TRAMPOLINE_ALIGNMENT 32 + +#define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN) \ +{ \ + rtx addr = ADDR; \ + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 32)), FUNC); \ + emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 36)), CHAIN);\ +} + + +/* Implicit Calls to Library Routines. */ + +#define INIT_TARGET_OPTABS \ +do \ + { \ + INIT_SUBTARGET_OPTABS; \ + } \ +while (0) + + +/* Addressing Modes. */ + +#define CONSTANT_ADDRESS_P(X) \ + ((GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == HIGH \ + || (GET_CODE (X) == CONST))) + +#define MAX_REGS_PER_ADDRESS 1 + +#ifdef REG_OK_STRICT +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if (iq2000_legitimate_address_p (MODE, X, 1)) \ + goto ADDR; \ +} +#else +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if (iq2000_legitimate_address_p (MODE, X, 0)) \ + goto ADDR; \ +} +#endif + +#define REG_OK_FOR_INDEX_P(X) 0 + + +/* For the IQ2000, transform: + + memory(X + ) + into: + Y = & ~0x7fff; + Z = X + Y + memory (Z + ( & 0x7fff)); +*/ + +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ +{ \ + register rtx xinsn = (X); \ + \ + if (TARGET_DEBUG_B_MODE) \ + { \ + GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n"); \ + GO_DEBUG_RTX (xinsn); \ + } \ + \ + if (iq2000_check_split (X, MODE)) \ + { \ + X = gen_rtx_LO_SUM (Pmode, \ + copy_to_mode_reg (Pmode, \ + gen_rtx (HIGH, Pmode, X)), \ + X); \ + goto WIN; \ + } \ + \ + if (GET_CODE (xinsn) == PLUS) \ + { \ + register rtx xplus0 = XEXP (xinsn, 0); \ + register rtx xplus1 = XEXP (xinsn, 1); \ + register enum rtx_code code0 = GET_CODE (xplus0); \ + register enum rtx_code code1 = GET_CODE (xplus1); \ + \ + if (code0 != REG && code1 == REG) \ + { \ + xplus0 = XEXP (xinsn, 1); \ + xplus1 = XEXP (xinsn, 0); \ + code0 = GET_CODE (xplus0); \ + code1 = GET_CODE (xplus1); \ + } \ + \ + if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE) \ + && code1 == CONST_INT && !SMALL_INT (xplus1)) \ + { \ + rtx int_reg = gen_reg_rtx (Pmode); \ + rtx ptr_reg = gen_reg_rtx (Pmode); \ + \ + emit_move_insn (int_reg, \ + GEN_INT (INTVAL (xplus1) & ~ 0x7fff)); \ + \ + emit_insn (gen_rtx_SET (VOIDmode, \ + ptr_reg, \ + gen_rtx_PLUS (Pmode, xplus0, int_reg))); \ + \ + X = plus_constant (ptr_reg, INTVAL (xplus1) & 0x7fff); \ + goto WIN; \ + } \ + } \ + \ + if (TARGET_DEBUG_B_MODE) \ + GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n"); \ +} + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {} + +#define LEGITIMATE_CONSTANT_P(X) (1) + + +/* Describing Relative Costs of Operations. */ + +#define CONST_COSTS(X,CODE,OUTER_CODE) \ + case CONST_INT: \ + return 0; \ + \ + case LABEL_REF: \ + return COSTS_N_INSNS (2); \ + \ + case CONST: \ + { \ + rtx offset = const0_rtx; \ + rtx symref = eliminate_constant_term (XEXP (X, 0), &offset); \ + \ + if (GET_CODE (symref) == LABEL_REF) \ + return COSTS_N_INSNS (2); \ + \ + if (GET_CODE (symref) != SYMBOL_REF) \ + return COSTS_N_INSNS (4); \ + \ + /* let's be paranoid.... */ \ + if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767) \ + return COSTS_N_INSNS (2); \ + \ + return COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2); \ + } \ + \ + case SYMBOL_REF: \ + return COSTS_N_INSNS (SYMBOL_REF_FLAG (X) ? 1 : 2); \ + \ + case CONST_DOUBLE: \ + { \ + rtx high, low; \ + split_double (X, &high, &low); \ + return COSTS_N_INSNS ((high == CONST0_RTX (GET_MODE (high)) \ + || low == CONST0_RTX (GET_MODE (low))) \ + ? 2 : 4); \ + } + +#define RTX_COSTS(X,CODE,OUTER_CODE) \ + case MEM: \ + { \ + int num_words = (GET_MODE_SIZE (GET_MODE (X)) > UNITS_PER_WORD) ? 2 : 1; \ + if (simple_memory_operand (X, GET_MODE (X))) \ + return COSTS_N_INSNS (num_words); \ + \ + return COSTS_N_INSNS (2*num_words); \ + } \ + \ + case FFS: \ + return COSTS_N_INSNS (6); \ + \ + case NOT: \ + return COSTS_N_INSNS (GET_MODE (X) == DImode && 2); \ + \ + case AND: \ + case IOR: \ + case XOR: \ + if (GET_MODE (X) == DImode) \ + return COSTS_N_INSNS (2); \ + \ + break; \ + \ + case ASHIFT: \ + case ASHIFTRT: \ + case LSHIFTRT: \ + if (GET_MODE (X) == DImode) \ + return COSTS_N_INSNS ((GET_CODE (XEXP (X, 1)) == CONST_INT) ? 4 : 12); \ + \ + break; \ + \ + case ABS: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode || xmode == DFmode) \ + return COSTS_N_INSNS (1); \ + \ + return COSTS_N_INSNS (4); \ + } \ + \ + case PLUS: \ + case MINUS: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode || xmode == DFmode) \ + { \ + return COSTS_N_INSNS (6); \ + } \ + \ + if (xmode == DImode) \ + return COSTS_N_INSNS (4); \ + \ + break; \ + } \ + \ + case NEG: \ + if (GET_MODE (X) == DImode) \ + return 4; \ + \ + break; \ + \ + case MULT: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode) \ + { \ + return COSTS_N_INSNS (7); \ + } \ + \ + if (xmode == DFmode) \ + { \ + return COSTS_N_INSNS (8); \ + } \ + \ + return COSTS_N_INSNS (10); \ + } \ + \ + case DIV: \ + case MOD: \ + { \ + enum machine_mode xmode = GET_MODE (X); \ + if (xmode == SFmode) \ + { \ + return COSTS_N_INSNS (23); \ + } \ + \ + if (xmode == DFmode) \ + { \ + return COSTS_N_INSNS (36); \ + } \ + } \ + /* fall through */ \ + \ + case UDIV: \ + case UMOD: \ + return COSTS_N_INSNS (69); \ + \ + case SIGN_EXTEND: \ + return COSTS_N_INSNS (2); \ + \ + case ZERO_EXTEND: \ + return COSTS_N_INSNS (1); + +#define ADDRESS_COST(ADDR) (REG_P (ADDR) ? 1 : iq2000_address_cost (ADDR)) + +#define REGISTER_MOVE_COST(MODE, FROM, TO) 2 + +#define MEMORY_MOVE_COST(MODE,CLASS,TO_P) \ + (TO_P ? 2 : 16) + +#define BRANCH_COST 2 + +#define SLOW_BYTE_ACCESS 1 + +#define NO_FUNCTION_CSE 1 + +#define NO_RECURSIVE_FUNCTION_CSE 1 + +#define ADJUST_COST(INSN,LINK,DEP_INSN,COST) \ + if (REG_NOTE_KIND (LINK) != 0) \ + (COST) = 0; /* Anti or output dependence. */ + + +/* Dividing the output into sections. */ + +#define TEXT_SECTION_ASM_OP "\t.text" /* instructions */ + +#define DATA_SECTION_ASM_OP "\t.data" /* large data */ + + +/* The Overall Framework of an Assembler File. */ + +#define ASM_COMMENT_START " #" + +#define ASM_APP_ON "#APP\n" + +#define ASM_APP_OFF "#NO_APP\n" + + +/* Output and Generation of Labels. */ + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(STREAM,PREFIX,NUM) \ + fprintf (STREAM, "%s%s%d:\n", LOCAL_LABEL_PREFIX, PREFIX, NUM) + +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf ((LABEL), "*%s%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long)(NUM)) + +#define GLOBAL_ASM_OP "\t.globl\t" + + +/* Output of Assembler Instructions. */ + +#define REGISTER_NAMES \ +{ \ + "%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", "%rap" \ +}; + +#define ADDITIONAL_REGISTER_NAMES \ +{ \ + { "%0", 0 + GP_REG_FIRST }, \ + { "%1", 1 + GP_REG_FIRST }, \ + { "%2", 2 + GP_REG_FIRST }, \ + { "%3", 3 + GP_REG_FIRST }, \ + { "%4", 4 + GP_REG_FIRST }, \ + { "%5", 5 + GP_REG_FIRST }, \ + { "%6", 6 + GP_REG_FIRST }, \ + { "%7", 7 + GP_REG_FIRST }, \ + { "%8", 8 + GP_REG_FIRST }, \ + { "%9", 9 + GP_REG_FIRST }, \ + { "%10", 10 + GP_REG_FIRST }, \ + { "%11", 11 + GP_REG_FIRST }, \ + { "%12", 12 + GP_REG_FIRST }, \ + { "%13", 13 + GP_REG_FIRST }, \ + { "%14", 14 + GP_REG_FIRST }, \ + { "%15", 15 + GP_REG_FIRST }, \ + { "%16", 16 + GP_REG_FIRST }, \ + { "%17", 17 + GP_REG_FIRST }, \ + { "%18", 18 + GP_REG_FIRST }, \ + { "%19", 19 + GP_REG_FIRST }, \ + { "%20", 20 + GP_REG_FIRST }, \ + { "%21", 21 + GP_REG_FIRST }, \ + { "%22", 22 + GP_REG_FIRST }, \ + { "%23", 23 + GP_REG_FIRST }, \ + { "%24", 24 + GP_REG_FIRST }, \ + { "%25", 25 + GP_REG_FIRST }, \ + { "%26", 26 + GP_REG_FIRST }, \ + { "%27", 27 + GP_REG_FIRST }, \ + { "%28", 28 + GP_REG_FIRST }, \ + { "%29", 29 + GP_REG_FIRST }, \ + { "%30", 27 + GP_REG_FIRST }, \ + { "%31", 31 + GP_REG_FIRST }, \ + { "%rap", 32 + GP_REG_FIRST }, \ +} + +/* Check if the current insn needs a nop in front of it + because of load delays, and also update the delay slot statistics. */ + +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ + final_prescan_insn (INSN, OPVEC, NOPERANDS) + +/* See iq2000.c for the IQ2000 specific codes. */ +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) iq2000_print_operand_punct[CODE] + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) + +#define DBR_OUTPUT_SEQEND(STREAM) \ +do \ + { \ + dslots_jump_filled++; \ + fputs ("\n", STREAM); \ + } \ +while (0) + +#define LOCAL_LABEL_PREFIX "$" + +#define USER_LABEL_PREFIX "" + + +/* Output of dispatch tables. */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \ +do { \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + Pmode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, VALUE); \ +} while (0) + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + Pmode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, \ + VALUE) + + +/* Assembler Commands for Alignment. */ + +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(STREAM,SIZE) \ + fprintf (STREAM, "\t.space\t%u\n", (SIZE)) + +#define ASM_OUTPUT_ALIGN(STREAM,LOG) \ + if ((LOG) != 0) \ + fprintf (STREAM, "\t.balign %d\n", 1<<(LOG)) + + +/* Macros Affecting all Debug Formats. */ + +#define DEBUGGER_AUTO_OFFSET(X) \ + iq2000_debugger_offset (X, (HOST_WIDE_INT) 0) + +#define DEBUGGER_ARG_OFFSET(OFFSET, X) \ + iq2000_debugger_offset (X, (HOST_WIDE_INT) OFFSET) + +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#define DWARF2_DEBUGGING_INFO 1 + + +/* Miscellaneous Parameters. */ + +#define PREDICATE_CODES \ + {"uns_arith_operand", { REG, CONST_INT, SUBREG }}, \ + {"arith_operand", { REG, CONST_INT, SUBREG }}, \ + {"small_int", { CONST_INT }}, \ + {"large_int", { CONST_INT }}, \ + {"reg_or_0_operand", { REG, CONST_INT, CONST_DOUBLE, SUBREG }}, \ + {"simple_memory_operand", { MEM, SUBREG }}, \ + {"equality_op", { EQ, NE }}, \ + {"cmp_op", { EQ, NE, GT, GE, GTU, GEU, LT, LE, \ + LTU, LEU }}, \ + {"pc_or_label_operand", { PC, LABEL_REF }}, \ + {"call_insn_operand", { CONST_INT, CONST, SYMBOL_REF, REG}}, \ + {"move_operand", { CONST_INT, CONST_DOUBLE, CONST, \ + SYMBOL_REF, LABEL_REF, SUBREG, \ + REG, MEM}}, \ + {"power_of_2_operand", { CONST_INT }}, + +#define CASE_VECTOR_MODE SImode + +#define CASE_VECTOR_PC_RELATIVE 0 + +#define WORD_REGISTER_OPERATIONS + +#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND + +#define MOVE_MAX 4 + +#define MAX_MOVE_MAX 8 + +#define SHIFT_COUNT_TRUNCATED 1 + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define STORE_FLAG_VALUE 1 + +#define Pmode SImode + +#define FUNCTION_MODE SImode + +/* Standard GCC variables that we reference. */ + +extern char call_used_regs[]; + +/* IQ2000 external variables defined in iq2000.c. */ + +/* Comparison type. */ +enum cmp_type +{ + CMP_SI, /* compare four byte integers */ + CMP_DI, /* compare eight byte integers */ + CMP_SF, /* compare single precision floats */ + CMP_DF, /* compare double precision floats */ + CMP_MAX /* max comparison type */ +}; + +/* Types of delay slot. */ +enum delay_type +{ + DELAY_NONE, /* no delay slot */ + DELAY_LOAD, /* load from memory delay */ + DELAY_FCMP /* delay after doing c..{d,s} */ +}; + +/* Which processor to schedule for. */ + +enum processor_type +{ + PROCESSOR_DEFAULT, + PROCESSOR_IQ2000, + PROCESSOR_IQ10 +}; + +/* Recast the cpu class to be the cpu attribute. */ +#define iq2000_cpu_attr ((enum attr_cpu)iq2000_tune) + +extern char iq2000_print_operand_punct[]; /* print_operand punctuation chars */ +extern int num_source_filenames; /* current .file # */ +extern int iq2000_branch_likely; /* emit 'l' after br (branch likely) */ +extern struct rtx_def *branch_cmp[2]; /* operands for compare */ +extern enum cmp_type branch_type; /* what type of branch to use */ +extern enum processor_type iq2000_arch; /* which cpu to codegen for */ +extern enum processor_type iq2000_tune; /* which cpu to schedule for */ +extern int iq2000_isa; /* architectural level */ +extern const char *iq2000_cpu_string; /* for -mcpu= */ +extern const char *iq2000_arch_string; /* for -march= */ +extern int dslots_load_total; /* total # load related delay slots */ +extern int dslots_load_filled; /* # filled load delay slots */ +extern int dslots_jump_total; /* total # jump related delay slots */ +extern int dslots_jump_filled; /* # filled jump delay slots */ +extern int dslots_number_nops; /* # of nops needed by previous insn */ +extern int num_refs[3]; /* # 1/2/3 word references */ +extern struct rtx_def *iq2000_load_reg; /* register to check for load delay */ +extern struct rtx_def *iq2000_load_reg2; /* 2nd reg to check for load delay */ +extern struct rtx_def *iq2000_load_reg3; /* 3rd reg to check for load delay */ +extern struct rtx_def *iq2000_load_reg4; /* 4th reg to check for load delay */ + +/* Functions to change what output section we are using. */ +extern void rdata_section PARAMS ((void)); +extern void sdata_section PARAMS ((void)); +extern void sbss_section PARAMS ((void)); + +#define BITMASK_UPPER16 ((unsigned long)0xffff << 16) /* 0xffff0000 */ +#define BITMASK_LOWER16 ((unsigned long)0xffff) /* 0x0000ffff */ + + +#define GENERATE_BRANCHLIKELY (ISA_HAS_BRANCHLIKELY) + +/* Macros to decide whether certain features are available or not, + depending on the instruction set architecture level. */ + +#define BRANCH_LIKELY_P() GENERATE_BRANCHLIKELY + +/* ISA has branch likely instructions. */ +#define ISA_HAS_BRANCHLIKELY (iq2000_isa == 1) + + +#undef ASM_SPEC +#define ASM_SPEC "%{march=iq2000: -m2000} %{march=iq10: -m10} %{!march=*: -m2000}" + + +/* The mapping from gcc register number to DWARF 2 CFA column number. + This mapping does not allow for tracking register 0, since + register 0 is fixed. */ +#define DWARF_FRAME_REGNUM(REG) \ + (REG == GP_REG_FIRST + 31 ? DWARF_FRAME_RETURN_COLUMN : REG) + +/* The DWARF 2 CFA column which tracks the return address. */ +#define DWARF_FRAME_RETURN_COLUMN ( GP_REG_FIRST + 26) + +/* Describe how we implement __builtin_eh_return. */ +#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + GP_ARG_FIRST : INVALID_REGNUM) + +/* The EH_RETURN_STACKADJ_RTX macro returns RTL which describes the + location used to store the amount to adjust the stack. This is + usually a register that is available from end of the function's body + to the end of the epilogue. Thus, this cannot be a register used as a + temporary by the epilogue. + + This must be an integer register. */ +#define EH_RETURN_STACKADJ_REGNO 3 +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, EH_RETURN_STACKADJ_REGNO) + +/* The EH_RETURN_HANDLER_RTX macro returns RTL which describes the + location used to store the address the processor should jump to + catch exception. This is usually a registers that is available from + end of the function's body to the end of the epilogue. Thus, this + cannot be a register used as a temporary by the epilogue. + + This must be an address register. */ +#define EH_RETURN_HANDLER_REGNO 26 +#define EH_RETURN_HANDLER_RTX \ + gen_rtx_REG (Pmode, EH_RETURN_HANDLER_REGNO) + +/* Offsets recorded in opcodes are a multiple of this alignment factor. */ +#define DWARF_CIE_DATA_ALIGNMENT 4 + +/* For IQ2000, width of a floating point register. */ +#define UNITS_PER_FPREG 4 + +/* Force right-alignment for small varargs in 32 bit little_endian mode */ + +#define PAD_VARARGS_DOWN !BYTES_BIG_ENDIAN + +/* Internal macros to classify a register number as to whether it's a + general purpose register, a floating point register, a + multiply/divide register, or a status register. */ + +#define GP_REG_FIRST 0 +#define GP_REG_LAST 31 +#define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1) + +#define RAP_REG_NUM 32 +#define AT_REGNUM (GP_REG_FIRST + 1) + +#define GP_REG_P(REGNO) \ + ((unsigned int) ((int) (REGNO) - GP_REG_FIRST) < GP_REG_NUM) + +/* IQ2000 registers used in prologue/epilogue code when the stack frame + is larger than 32K bytes. These registers must come from the + scratch register set, and not used for passing and returning + arguments and any other information used in the calling sequence. */ + +#define IQ2000_TEMP1_REGNUM (GP_REG_FIRST + 12) +#define IQ2000_TEMP2_REGNUM (GP_REG_FIRST + 13) + +/* This macro is used later on in the file. */ +#define GR_REG_CLASS_P(CLASS) \ + ((CLASS) == GR_REGS) + +#define SMALL_INT(X) ((unsigned HOST_WIDE_INT) (INTVAL (X) + 0x8000) < 0x10000) +#define SMALL_INT_UNSIGNED(X) ((unsigned HOST_WIDE_INT) (INTVAL (X)) < 0x10000) + +/* Certain machines have the property that some registers cannot be + copied to some other registers without using memory. Define this + macro on those machines to be a C expression that is non-zero if + objects of mode MODE in registers of CLASS1 can only be copied to + registers of class CLASS2 by storing a register of CLASS1 into + memory and loading that memory location into a register of CLASS2. + + Do not define this macro if its value would always be zero. */ + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ + +#define CLASS_UNITS(mode, size) \ + ((GET_MODE_SIZE (mode) + (size) - 1) / (size)) + +/* If defined, gives a class of registers that cannot be used as the + operand of a SUBREG that changes the mode of the object illegally. */ + +#define CLASS_CANNOT_CHANGE_MODE 0 + +/* Defines illegal mode changes for CLASS_CANNOT_CHANGE_MODE. */ + +#define CLASS_CANNOT_CHANGE_MODE_P(FROM,TO) \ + (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO)) + +/* Make sure 4 words are always allocated on the stack. */ + +#ifndef STACK_ARGS_ADJUST +#define STACK_ARGS_ADJUST(SIZE) \ +{ \ + if (SIZE.constant < 4 * UNITS_PER_WORD) \ + SIZE.constant = 4 * UNITS_PER_WORD; \ +} +#endif + + +/* Symbolic macros for the registers used to return integer and floating + point values. */ + +#define GP_RETURN (GP_REG_FIRST + 2) + +/* Symbolic macros for the first/last argument registers. */ + +#define GP_ARG_FIRST (GP_REG_FIRST + 4) +#define GP_ARG_LAST (GP_REG_FIRST + 11) + +#define MAX_ARGS_IN_REGISTERS 8 + + +/* Tell prologue and epilogue if register REGNO should be saved / restored. */ + +#define MUST_SAVE_REGISTER(regno) \ + ((regs_ever_live[regno] && !call_used_regs[regno]) \ + || (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) \ + || (regno == (GP_REG_FIRST + 31) && regs_ever_live[GP_REG_FIRST + 31])) + +/* ALIGN FRAMES on double word boundaries */ +#ifndef IQ2000_STACK_ALIGN +#define IQ2000_STACK_ALIGN(LOC) (((LOC) + 7) & ~7) +#endif + + +/* These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + These definitions are NOT overridden anywhere. */ + +#define BASE_REG_P(regno, mode) \ + (GP_REG_P (regno)) + +#define GP_REG_OR_PSEUDO_STRICT_P(regno, mode) \ + BASE_REG_P((regno < FIRST_PSEUDO_REGISTER) ? regno : reg_renumber[regno], \ + (mode)) + +#define GP_REG_OR_PSEUDO_NONSTRICT_P(regno, mode) \ + (((regno) >= FIRST_PSEUDO_REGISTER) || (BASE_REG_P ((regno), (mode)))) + +#define REGNO_MODE_OK_FOR_BASE_P(regno, mode) \ + GP_REG_OR_PSEUDO_STRICT_P ((regno), (mode)) + +/* 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 all. + 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. + Some source files that are used after register allocation + need to be strict. */ + +#ifndef REG_OK_STRICT +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + iq2000_reg_mode_ok_for_base_p (X, MODE, 0) +#else +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + iq2000_reg_mode_ok_for_base_p (X, MODE, 1) +#endif + +#if 1 +#define GO_PRINTF(x) fprintf(stderr, (x)) +#define GO_PRINTF2(x,y) fprintf(stderr, (x), (y)) +#define GO_DEBUG_RTX(x) debug_rtx(x) + +#else +#define GO_PRINTF(x) +#define GO_PRINTF2(x,y) +#define GO_DEBUG_RTX(x) +#endif + +/* Specify the tree operation to be used to convert reals to integers. */ +#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR + +/* This is the kind of divide that is easiest to do in the general case. */ +#define EASY_DIV_EXPR TRUNC_DIV_EXPR + +/* Define this if zero-extension is slow (more than one real instruction). */ +#define SLOW_ZERO_EXTEND + +/* If defined, modifies the length assigned to instruction INSN as a + function of the context in which it is used. LENGTH is an lvalue + that contains the initially computed length of the insn and should + be updated with the correct length of the insn. */ +#define ADJUST_INSN_LENGTH(INSN, LENGTH) \ + ((LENGTH) = iq2000_adjust_insn_length ((INSN), (LENGTH))) + + +/* A list of predicates that do special things with modes, and so + should not elicit warnings for VOIDmode match_operand. */ + +#define SPECIAL_MODE_PREDICATES \ + "pc_or_label_operand", + + + + +/* How to tell the debugger about changes of source files. */ + +#ifndef SET_FILE_NUMBER +#define SET_FILE_NUMBER() ++num_source_filenames +#endif + +/* This is how to output a note the debugger telling it the line number + to which the following sequence of instructions corresponds. */ + +#ifndef LABEL_AFTER_LOC +#define LABEL_AFTER_LOC(STREAM) +#endif + +/* Handle certain cpp directives used in header files on sysV. */ +#define SCCS_DIRECTIVE + + +/* Default to -G 8 */ +#ifndef IQ2000_DEFAULT_GVALUE +#define IQ2000_DEFAULT_GVALUE 8 +#endif + +#define SDATA_SECTION_ASM_OP "\t.sdata" /* small data */ + +/* Given a decl node or constant node, choose the section to output it in + and select that section. */ + +#undef TARGET_ASM_SELECT_SECTION +#define TARGET_ASM_SELECT_SECTION iq2000_select_section + +/* See iq2000_expand_prologue's use of loadgp for when this should be + true. */ + +#define DONT_ACCESS_GBLS_AFTER_EPILOGUE 0 + + +#ifndef INIT_SUBTARGET_OPTABS +#define INIT_SUBTARGET_OPTABS +#endif + +enum iq2000_builtins +{ + IQ2000_BUILTIN_ADO16, + IQ2000_BUILTIN_CFC0, + IQ2000_BUILTIN_CFC1, + IQ2000_BUILTIN_CFC2, + IQ2000_BUILTIN_CFC3, + IQ2000_BUILTIN_CHKHDR, + IQ2000_BUILTIN_CTC0, + IQ2000_BUILTIN_CTC1, + IQ2000_BUILTIN_CTC2, + IQ2000_BUILTIN_CTC3, + IQ2000_BUILTIN_LU, + IQ2000_BUILTIN_LUC32L, + IQ2000_BUILTIN_LUC64, + IQ2000_BUILTIN_LUC64L, + IQ2000_BUILTIN_LUK, + IQ2000_BUILTIN_LULCK, + IQ2000_BUILTIN_LUM32, + IQ2000_BUILTIN_LUM32L, + IQ2000_BUILTIN_LUM64, + IQ2000_BUILTIN_LUM64L, + IQ2000_BUILTIN_LUR, + IQ2000_BUILTIN_LURL, + IQ2000_BUILTIN_MFC0, + IQ2000_BUILTIN_MFC1, + IQ2000_BUILTIN_MFC2, + IQ2000_BUILTIN_MFC3, + IQ2000_BUILTIN_MRGB, + IQ2000_BUILTIN_MTC0, + IQ2000_BUILTIN_MTC1, + IQ2000_BUILTIN_MTC2, + IQ2000_BUILTIN_MTC3, + IQ2000_BUILTIN_PKRL, + IQ2000_BUILTIN_RAM, + IQ2000_BUILTIN_RB, + IQ2000_BUILTIN_RX, + IQ2000_BUILTIN_SRRD, + IQ2000_BUILTIN_SRRDL, + IQ2000_BUILTIN_SRULC, + IQ2000_BUILTIN_SRULCK, + IQ2000_BUILTIN_SRWR, + IQ2000_BUILTIN_SRWRU, + IQ2000_BUILTIN_TRAPQF, + IQ2000_BUILTIN_TRAPQFL, + IQ2000_BUILTIN_TRAPQN, + IQ2000_BUILTIN_TRAPQNE, + IQ2000_BUILTIN_TRAPRE, + IQ2000_BUILTIN_TRAPREL, + IQ2000_BUILTIN_WB, + IQ2000_BUILTIN_WBR, + IQ2000_BUILTIN_WBU, + IQ2000_BUILTIN_WX, + IQ2000_BUILTIN_SYSCALL +}; diff --git a/gcc/config/iq2000/iq2000.md b/gcc/config/iq2000/iq2000.md new file mode 100644 index 0000000..de493b3 --- /dev/null +++ b/gcc/config/iq2000/iq2000.md @@ -0,0 +1,2553 @@ +;; iq2000.md Machine Description for Vitesse IQ2000 processors +;; Copyright (C) 2003 Free Software Foundation, Inc. + +;; This file is part of GNU CC. + +;; GNU CC 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 2, or (at your option) +;; any later version. + +;; GNU CC 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 GNU CC; see the file COPYING. If not, write to +;; the Free Software Foundation, 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;; ??? Currently does not have define_function_unit support for the R8000. +;; Must include new entries for fmadd in addition to existing entries. + +(define_constants + [(UNSPEC_ADO16 0) + (UNSPEC_RAM 1) + (UNSPEC_CHKHDR 2) + (UNSPEC_PKRL 3) + (UNSPEC_CFC0 4) + (UNSPEC_CFC1 5) + (UNSPEC_CFC2 6) + (UNSPEC_CFC3 7) + (UNSPEC_CTC0 8) + (UNSPEC_CTC1 9) + (UNSPEC_CTC2 10) + (UNSPEC_CTC3 11) + (UNSPEC_MFC0 12) + (UNSPEC_MFC1 13) + (UNSPEC_MFC2 14) + (UNSPEC_MFC3 15) + (UNSPEC_MTC0 16) + (UNSPEC_MTC1 17) + (UNSPEC_MTC2 18) + (UNSPEC_MTC3 19) + (UNSPEC_LUR 20) + (UNSPEC_RB 21) + (UNSPEC_RX 22) + (UNSPEC_SRRD 23) + (UNSPEC_SRWR 24) + (UNSPEC_WB 25) + (UNSPEC_WX 26) + (UNSPEC_LUC32 49) + (UNSPEC_LUC32L 27) + (UNSPEC_LUC64 28) + (UNSPEC_LUC64L 29) + (UNSPEC_LUK 30) + (UNSPEC_LULCK 31) + (UNSPEC_LUM32 32) + (UNSPEC_LUM32L 33) + (UNSPEC_LUM64 34) + (UNSPEC_LUM64L 35) + (UNSPEC_LURL 36) + (UNSPEC_MRGB 37) + (UNSPEC_SRRDL 38) + (UNSPEC_SRULCK 39) + (UNSPEC_SRWRU 40) + (UNSPEC_TRAPQFL 41) + (UNSPEC_TRAPQNE 42) + (UNSPEC_TRAPREL 43) + (UNSPEC_WBU 44) + (UNSPEC_SYSCALL 45)] +) +;; UNSPEC values used in iq2000.md +;; Number USE +;; 0 movsi_ul +;; 1 movsi_us, get_fnaddr +;; 3 eh_set_return +;; 20 builtin_setjmp_setup +;; +;; UNSPEC_VOLATILE values +;; 0 blockage +;; 2 loadgp +;; 3 builtin_longjmp +;; 4 exception_receiver +;; 10 consttable_qi +;; 11 consttable_hi +;; 12 consttable_si +;; 13 consttable_di +;; 14 consttable_sf +;; 15 consttable_df +;; 16 align_2 +;; 17 align_4 +;; 18 align_8 + + +;; .................... +;; +;; Attributes +;; +;; .................... + +;; Classification of each insn. +;; branch conditional branch +;; jump unconditional jump +;; call unconditional call +;; load load instruction(s) +;; store store instruction(s) +;; move data movement within same register set +;; xfer transfer to/from coprocessor +;; arith integer arithmetic instruction +;; darith double precision integer arithmetic instructions +;; imul integer multiply +;; idiv integer divide +;; icmp integer compare +;; fadd floating point add/subtract +;; fmul floating point multiply +;; fmadd floating point multiply-add +;; fdiv floating point divide +;; fabs floating point absolute value +;; fneg floating point negation +;; fcmp floating point compare +;; fcvt floating point convert +;; fsqrt floating point square root +;; multi multiword sequence (or user asm statements) +;; nop no operation + +(define_attr "type" + "unknown,branch,jump,call,load,store,move,xfer,arith,darith,imul,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,multi,nop" + (const_string "unknown")) + +;; Main data type used by the insn +(define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF,FPSW" (const_string "unknown")) + +;; Length (in # of bytes). A conditional branch is allowed only to a +;; location within a signed 18-bit offset of the delay slot. If that +;; provides too small a range, we use the `j' instruction. This +;; instruction takes a 28-bit value, but that value is not an offset. +;; Instead, it's bitwise-ored with the high-order four bits of the +;; instruction in the delay slot, which means it cannot be used to +;; cross a 256MB boundary. We could fall back back on the jr, +;; instruction which allows full access to the entire address space, +;; but we do not do so at present. + +(define_attr "length" "" + (cond [(eq_attr "type" "branch") + (cond [(lt (abs (minus (match_dup 1) (plus (pc) (const_int 4)))) + (const_int 131072)) + (const_int 4)] + (const_int 12))] + (const_int 4))) + +(define_attr "cpu" + "default,iq2000" + (const (symbol_ref "iq2000_cpu_attr"))) + +;; Does the instruction have a mandatory delay slot? has_dslot +;; Can the instruction be in a delay slot? ok_in_dslot +;; Can the instruction not be in a delay slot? not_in_dslot +(define_attr "dslot" "has_dslot,ok_in_dslot,not_in_dslot" + (if_then_else (eq_attr "type" "branch,jump,call,xfer,fcmp") + (const_string "has_dslot") + (const_string "ok_in_dslot"))) + +;; Attribute defining whether or not we can use the branch-likely instructions + +(define_attr "branch_likely" "no,yes" + (const + (if_then_else (ne (symbol_ref "GENERATE_BRANCHLIKELY") (const_int 0)) + (const_string "yes") + (const_string "no")))) + + +;; Describe a user's asm statement. +(define_asm_attributes + [(set_attr "type" "multi")]) + + + +;; ......................... +;; +;; Delay slots, can't describe load/fcmp/xfer delay slots here +;; +;; ......................... + +(define_delay (eq_attr "type" "jump") + [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4")) + (nil) + (nil)]) + +(define_delay (eq_attr "type" "branch") + [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4")) + (nil) + (and (eq_attr "branch_likely" "yes") (and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4")))]) + +(define_delay (eq_attr "type" "call") + [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4")) + (nil) + (nil)]) + + + +;; ......................... +;; +;; Functional units +;; +;; ......................... + +; (define_function_unit NAME MULTIPLICITY SIMULTANEITY +; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST]) + +;; Make the default case (PROCESSOR_DEFAULT) handle the worst case + +(define_function_unit "memory" 1 0 + (and (eq_attr "type" "load") + (eq_attr "cpu" "iq2000")) + 3 0) + +(define_function_unit "memory" 1 0 + (and (eq_attr "type" "move") + (eq_attr "cpu" "iq2000")) + 3 0) + +(define_function_unit "memory" 1 0 + (and (eq_attr "type" "arith") + (eq_attr "cpu" "iq2000")) + 3 0) + +(define_function_unit "memory" 1 0 (eq_attr "type" "store") 1 0) + +(define_function_unit "memory" 1 0 (eq_attr "type" "xfer") 2 0) + +;; +;; .................... +;; +;; CONDITIONAL TRAPS +;; +;; .................... +;; + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 0))] + "" + "* +{ + return \"break\"; +}") + +;; +;; .................... +;; +;; ADDITION +;; +;; .................... +;; + +(define_expand "addsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "") + +(define_insn "addsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=d,=d") + (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ,dJ") + (match_operand:SI 2 "arith_operand" "d,I")))] + "" + "@ + addu\\t%0,%z1,%2 + addiu\\t%0,%z1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; +;; .................... +;; +;; SUBTRACTION +;; +;; .................... +;; + +(define_expand "subsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "") + +(define_insn "subsi3_internal" + [(set (match_operand:SI 0 "register_operand" "=d,=d") + (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ,dJ") + (match_operand:SI 2 "arith_operand" "d,I")))] + "" + "@ + subu\\t%0,%z1,%2 + addiu\\t%0,%z1,%n2" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; +;; .................... +;; +;; NEGATION and ONE'S COMPLEMENT +;; +;; .................... + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (neg:SI (match_operand:SI 1 "register_operand" "d")))] + "" + "* +{ + operands[2] = const0_rtx; + return \"subu\\t%0,%z2,%1\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=d") + (not:SI (match_operand:SI 1 "register_operand" "d")))] + "" + "* +{ + operands[2] = const0_rtx; + return \"nor\\t%0,%z2,%1\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; +;; .................... +;; +;; LOGICAL +;; +;; .................... +;; + +(define_expand "andsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d,d") + (match_operand:SI 2 "nonmemory_operand" "d,K,N")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d,d") + (match_operand:SI 2 "nonmemory_operand" "d,K,N")))] + "" + "* +{ + if (which_alternative == 0) + return \"and\\t%0,%1,%2\"; + else if (which_alternative == 1) + return \"andi\\t%0,%1,%x2\"; + else if (which_alternative == 2) + { + if ((INTVAL (operands[2]) & 0xffff) == 0xffff) + { + operands[2] = GEN_INT (INTVAL (operands[2]) >> 16); + return \"andoui\\t%0,%1,%x2\"; + } + else + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); + return \"andoi\\t%0,%1,%x2\"; + } + } +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + "@ + or\\t%0,%1,%2 + ori\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d") + (match_operand:SI 2 "uns_arith_operand" "d,K")))] + "" + "@ + xor\\t%0,%1,%2 + xori\\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_insn "*norsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "d")) + (not:SI (match_operand:SI 2 "register_operand" "d"))))] + "" + "nor\\t%0,%z1,%z2" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; +;; .................... +;; +;; ZERO EXTENSION +;; +;; .................... + +;; Extension insns. +;; Those for integer source operand are ordered widest source type first. + +(define_expand "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))] + "" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0xffff\"; + else + return iq2000_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "SI") + (set_attr "length" "4,4,8")]) + +(define_expand "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=d,d,d") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))] + "" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0x00ff\"; + else + return iq2000_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "HI") + (set_attr "length" "4,4,8")]) + +(define_expand "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + "") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d,d,d") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))] + "" + "* +{ + if (which_alternative == 0) + return \"andi\\t%0,%1,0x00ff\"; + else + return iq2000_move_1word (operands, insn, TRUE); +}" + [(set_attr "type" "arith,load,load") + (set_attr "mode" "SI") + (set_attr "length" "4,4,8")]) + +;; +;; .................... +;; +;; SIGN EXTENSION +;; +;; .................... + +;; Extension insns. +;; Those for integer source operand are ordered widest source type first. + +;; These patterns originally accepted general_operands, however, slightly +;; better code is generated by only accepting register_operands, and then +;; letting combine generate the lh and lb insns. + +(define_expand "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (16); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendhisi2_internal" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))] + "" + "* return iq2000_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4,8")]) + +(define_expand "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (24); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (op0, temp, shift)); + DONE; + } +}") + +(define_insn "extendqihi2_internal" + [(set (match_operand:HI 0 "register_operand" "=d,d") + (sign_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))] + "" + "* return iq2000_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4,8")]) + + +(define_expand "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))] + "" + " +{ + if (optimize && GET_CODE (operands[1]) == MEM) + operands[1] = force_not_mem (operands[1]); + + if (GET_CODE (operands[1]) != MEM) + { + rtx op1 = gen_lowpart (SImode, operands[1]); + rtx temp = gen_reg_rtx (SImode); + rtx shift = GEN_INT (24); + + emit_insn (gen_ashlsi3 (temp, op1, shift)); + emit_insn (gen_ashrsi3 (operands[0], temp, shift)); + DONE; + } +}") + +(define_insn "extendqisi2_insn" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (sign_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))] + "" + "* return iq2000_move_1word (operands, insn, FALSE);" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4,8")]) + +;; +;; ........................ +;; +;; BIT FIELD EXTRACTION +;; +;; ........................ + +(define_insn "extzv" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "O") + (match_operand:SI 3 "const_int_operand" "O")))] + "" + "* +{ + int value[4]; + value[2] = INTVAL (operands[2]); + value[3] = INTVAL (operands[3]); + operands[2] = GEN_INT ((value[3])); + operands[3] = GEN_INT ((32 - value[2])); + return \"ram\\t%0,%1,%2,%3,0x0\"; +}" + [(set_attr "type" "arith")]) + +;; +;; .................... +;; +;; DATA MOVEMENT +;; +;; .................... + +/* Take care of constants that don't fit in single instruction */ +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "(reload_in_progress || reload_completed) + && large_int (operands[1], SImode)" + + [(set (match_dup 0 ) + (high:SI (match_dup 1))) + (set (match_dup 0 ) + (lo_sum:SI (match_dup 0) + (match_dup 1)))] +) + +;; ??? iq2000_move_1word has support for HIGH, so this pattern may be +;; unnecessary. + +(define_insn "high" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand:SI 1 "immediate_operand" "")))] + "" + "lui\\t%0,%%hi(%1) # high" + [(set_attr "type" "move")]) + +(define_insn "low" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "immediate_operand" "")))] + "" + "addiu\\t%0,%1,%%lo(%2) # low" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; 32-bit Integer moves + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "large_int" ""))] + "reload_in_progress | reload_completed" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 0) + (ior:SI (match_dup 0) + (match_dup 3)))] + " +{ + operands[2] = GEN_INT (trunc_int_for_mode (INTVAL (operands[1]) + & BITMASK_UPPER16, + SImode)); + operands[3] = GEN_INT (INTVAL (operands[1]) & BITMASK_LOWER16); +}") + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. + +(define_expand "movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + if (iq2000_check_split (operands[1], SImode)) + { + enum machine_mode mode = GET_MODE (operands[0]); + rtx tem = ((reload_in_progress | reload_completed) + ? operands[0] : gen_reg_rtx (mode)); + + emit_insn (gen_rtx_SET (VOIDmode, tem, + gen_rtx_HIGH (mode, operands[1]))); + + operands[1] = gen_rtx_LO_SUM (mode, tem, operands[1]); + } + + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], SImode) + && !register_operand (operands[1], SImode) + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0)) + { + rtx temp = force_reg (SImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } + + /* Take care of constants that don't fit in single instruction */ + if ((reload_in_progress || reload_completed) + && CONSTANT_P (operands[1]) + && GET_CODE (operands[1]) != HIGH + && GET_CODE (operands[1]) != LO_SUM + && ! SMALL_INT_UNSIGNED (operands[1])) + { + rtx tem = ((reload_in_progress | reload_completed) + ? operands[0] : gen_reg_rtx (SImode)); + + emit_insn (gen_rtx_SET (VOIDmode, tem, + gen_rtx_HIGH (SImode, operands[1]))); + operands[1] = gen_rtx_LO_SUM (SImode, tem, operands[1]); + } +}") + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movsi_internal2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*x,*d,*x,*d") + (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,J,*x,*d,*a"))] + "(register_operand (operands[0], SImode) + || register_operand (operands[1], SImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return iq2000_move_1word (operands, insn, FALSE);" + [(set_attr "type" "move,load,arith,arith,load,load,store,store,xfer,xfer,move,move,move,move") + (set_attr "mode" "SI") + (set_attr "length" "4,8,4,8,4,8,4,8,4,4,4,4,4,4")]) + +;; 16-bit Integer moves + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. +;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined + +(define_expand "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], HImode) + && !register_operand (operands[1], HImode) + && ((GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0))) + { + rtx temp = force_reg (HImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movhi_internal2" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d") + (match_operand:HI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))] + "(register_operand (operands[0], HImode) + || register_operand (operands[1], HImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return iq2000_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,move") + (set_attr "mode" "HI") + (set_attr "length" "4,4,4,8,4,8,4,4,4,4")]) + +;; 8-bit Integer moves + +;; Unlike most other insns, the move insns can't be split with +;; different predicates, because register spilling and other parts of +;; the compiler, have memoized the insn number already. +;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined + +(define_expand "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + if ((reload_in_progress | reload_completed) == 0 + && !register_operand (operands[0], QImode) + && !register_operand (operands[1], QImode) + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) != 0)) + { + rtx temp = force_reg (QImode, operands[1]); + emit_move_insn (operands[0], temp); + DONE; + } +}") + +;; The difference between these two is whether or not ints are allowed +;; in FP registers (off by default, use -mdebugh to enable). + +(define_insn "movqi_internal2" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d") + (match_operand:QI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))] + "(register_operand (operands[0], QImode) + || register_operand (operands[1], QImode) + || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))" + "* return iq2000_move_1word (operands, insn, TRUE);" + [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,move") + (set_attr "mode" "QI") + (set_attr "length" "4,4,4,8,4,8,4,4,4,4")]) + +;; 32-bit floating point moves + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + if (!reload_in_progress + && !reload_completed + && GET_CODE (operands[0]) == MEM + && (GET_CODE (operands[1]) == MEM + || GET_CODE (operands[1]) == CONST_DOUBLE)) + operands[1] = copy_to_mode_reg (SFmode, operands[1]); + + /* Take care of reg <- SF constant */ + if ( const_double_operand (operands[1], GET_MODE (operands[1]) ) ) + { + emit_insn (gen_movsf_high (operands[0], operands[1])); + emit_insn (gen_movsf_lo_sum (operands[0], operands[0], operands[1])); + DONE; + } +}") + +(define_insn "movsf_lo_sum" + [(set (match_operand:SF 0 "register_operand" "=r") + (lo_sum:SF (match_operand:SF 1 "register_operand" "r") + (match_operand:SF 2 "const_double_operand" "")))] + "" + "* +{ + REAL_VALUE_TYPE r; + long i; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[2]); + REAL_VALUE_TO_TARGET_SINGLE (r, i); + operands[2] = GEN_INT (i); + return \"addiu\\t%0,%1,%%lo(%2) # low\"; +}" + [(set_attr "length" "4") + (set_attr "type" "arith")]) + +(define_insn "movsf_high" + [(set (match_operand:SF 0 "register_operand" "=r") + (high:SF (match_operand:SF 1 "const_double_operand" "")))] + "" + "* +{ + REAL_VALUE_TYPE r; + long i; + + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (r, i); + operands[1] = GEN_INT (i); + return \"lui\\t%0,%%hi(%1) # high\"; +}" + [(set_attr "length" "4") + (set_attr "type" "arith")]) + +(define_insn "*movsf_internal" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m") + (match_operand:SF 1 "nonimmediate_operand" "r,m,r"))] + "!memory_operand (operands[0], SFmode) || !memory_operand (operands[1], SFmode)" + "* +{ + iq2000_fill_delay_slot (\"\", DELAY_LOAD, operands, insn); + if (which_alternative == 0) + return \"or\\t%0,%1,%1\"; + else if (which_alternative == 1) + return \"lw\\t%0,%1\"; + else if (which_alternative == 2) + return \"sw\\t%1,%0\"; +}" + [(set_attr "length" "4,4,4") + (set_attr "type" "arith,load,store")] +) + +;; +;; .................... +;; +;; SHIFTS +;; +;; .................... + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashift:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "") + +(define_insn "ashlsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashift:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + return \"sll\\t%0,%1,%2\"; + } + else + return \"sllv\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "") + +(define_insn "ashrsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + return \"sra\\t%0,%1,%2\"; + } + else + return \"srav\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "") + +(define_insn "lshrsi3_internal1" + [(set (match_operand:SI 0 "register_operand" "=d") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "arith_operand" "dI")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); + return \"srl\\t%0,%1,%2\"; + } + else + return \"srlv\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +;; Rotate Right +(define_insn "rotrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (rotatert:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "uns_arith_operand" "O")))] + "" + "ram %0,%1,%2,0x0,0x0" + [(set_attr "type" "arith")]) + + +;; +;; .................... +;; +;; COMPARISONS +;; +;; .................... + +;; Flow here is rather complex: +;; +;; 1) The cmp{si,di,sf,df} routine is called. It deposits the +;; arguments into the branch_cmp array, and the type into +;; branch_type. No RTL is generated. +;; +;; 2) The appropriate branch define_expand is called, which then +;; creates the appropriate RTL for the comparison and branch. +;; Different CC modes are used, based on what type of branch is +;; done, so that we can constrain things appropriately. There +;; are assumptions in the rest of GCC that break if we fold the +;; operands into the branchs for integer operations, and use cc0 +;; for floating point, so we use the fp status register instead. +;; If needed, an appropriate temporary is created to hold the +;; of the integer compare. + +(define_expand "cmpsi" + [(set (cc0) + (compare:CC (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "arith_operand" "")))] + "" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = operands[1]; + branch_type = CMP_SI; + DONE; + } +}") + +(define_expand "tstsi" + [(set (cc0) + (match_operand:SI 0 "register_operand" ""))] + "" + " +{ + if (operands[0]) /* avoid unused code message */ + { + branch_cmp[0] = operands[0]; + branch_cmp[1] = const0_rtx; + branch_type = CMP_SI; + DONE; + } +}") + +;; +;; .................... +;; +;; CONDITIONAL BRANCHES +;; +;; .................... + +;; Conditional branches on comparisons with zero. + +(define_insn "branch_zero" + [(set (pc) + (if_then_else + (match_operator:SI 0 "cmp_op" + [(match_operand:SI 2 "register_operand" "d") + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + "* +{ + return iq2000_output_conditional_branch (insn, + operands, + /*two_operands_p=*/0, + /*float_p=*/0, + /*inverted_p=*/0, + get_attr_length (insn)); +}" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_insn "branch_zero_inverted" + [(set (pc) + (if_then_else + (match_operator:SI 0 "cmp_op" + [(match_operand:SI 2 "register_operand" "d") + (const_int 0)]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "" + "* +{ + return iq2000_output_conditional_branch (insn, + operands, + /*two_operands_p=*/0, + /*float_p=*/0, + /*inverted_p=*/1, + get_attr_length (insn)); +}" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +;; Conditional branch on equality comparision. + +(define_insn "branch_equality" + [(set (pc) + (if_then_else + (match_operator:SI 0 "equality_op" + [(match_operand:SI 2 "register_operand" "d") + (match_operand:SI 3 "register_operand" "d")]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "" + "* +{ + return iq2000_output_conditional_branch (insn, + operands, + /*two_operands_p=*/1, + /*float_p=*/0, + /*inverted_p=*/0, + get_attr_length (insn)); +}" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_insn "branch_equality_inverted" + [(set (pc) + (if_then_else + (match_operator:SI 0 "equality_op" + [(match_operand:SI 2 "register_operand" "d") + (match_operand:SI 3 "register_operand" "d")]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "" + "* +{ + return iq2000_output_conditional_branch (insn, + operands, + /*two_operands_p=*/1, + /*float_p=*/0, + /*inverted_p=*/1, + get_attr_length (insn)); +}" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_expand "beq" + [(set (pc) + (if_then_else (eq:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, EQ); + DONE; + } +}") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, NE); + DONE; + } +}") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GT); + DONE; + } +}") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GE); + DONE; + } +}") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LT); + DONE; + } +}") + +(define_expand "ble" + [(set (pc) + (if_then_else (le:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LE); + DONE; + } +}") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GTU); + DONE; + } +}") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, GEU); + DONE; + } +}") + + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LTU); + DONE; + } +}") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (operands[0]) /* avoid unused code warning */ + { + gen_conditional_branch (operands, LEU); + DONE; + } +}") + +;; Recognize bbi and bbin instructions. These use two unusual template +;; patterns, %Ax and %Px. %Ax outputs an 'i' if operand `x' is a LABEL_REF +;; otherwise it outputs an 'in'. %Px does nothing if `x' is PC +;; and outputs the operand if `x' is a LABEL_REF. + +(define_insn "" + [(set (pc) + (if_then_else + (ne (sign_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "arith_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A2\\t%0(31-%1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (sign_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "arith_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A3\\t%0(31-%1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else + (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "arith_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A2\\t%0(31-%1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r") + (const_int 1) + (match_operand:SI 1 "arith_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A3\\t%0(31-%1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else + (eq (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "power_of_2_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A3\\t%0(%p1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else + (ne (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "power_of_2_operand" "I")) + (const_int 0)) + (match_operand 2 "pc_or_label_operand" "") + (match_operand 3 "pc_or_label_operand" "")))] + "" + "bb%A2\\t%0(%p1),%P2%P3" + [(set_attr "length" "4") + (set_attr "type" "branch")]) + +;; +;; .................... +;; +;; SETTING A REGISTER FROM A COMPARISON +;; +;; .................... + +(define_expand "seq" + [(set (match_operand:SI 0 "register_operand" "=d") + (eq:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (EQ, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + + +(define_insn "seq_si_zero" + [(set (match_operand:SI 0 "register_operand" "=d") + (eq:SI (match_operand:SI 1 "register_operand" "d") + (const_int 0)))] + "" + "sltiu\\t%0,%1,1" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "sne" + [(set (match_operand:SI 0 "register_operand" "=d") + (ne:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (NE, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sne_si_zero" + [(set (match_operand:SI 0 "register_operand" "=d") + (ne:SI (match_operand:SI 1 "register_operand" "d") + (const_int 0)))] + "" + "sltu\\t%0,%.,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "sgt" + [(set (match_operand:SI 0 "register_operand" "=d") + (gt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (GT, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sgt_si" + [(set (match_operand:SI 0 "register_operand" "=d,=d") + (gt:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "reg_or_0_operand" "d,J")))] + "" + "@ + slt\\t%0,%z2,%1 + slt\\t%0,%z2,%1" + [(set_attr "type" "arith,arith") + (set_attr "mode" "SI,SI")]) + +(define_expand "sge" + [(set (match_operand:SI 0 "register_operand" "=d") + (ge:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (GE, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_expand "slt" + [(set (match_operand:SI 0 "register_operand" "=d") + (lt:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (LT, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "slt_si" + [(set (match_operand:SI 0 "register_operand" "=d,=d") + (lt:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "" + "@ + slt\\t%0,%1,%2 + slti\\t%0,%1,%2" + [(set_attr "type" "arith,arith") + (set_attr "mode" "SI,SI")]) + +(define_expand "sle" + [(set (match_operand:SI 0 "register_operand" "=d") + (le:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (LE, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sle_si_const" + [(set (match_operand:SI 0 "register_operand" "=d") + (le:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2])+1); + return \"slti\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "sgtu" + [(set (match_operand:SI 0 "register_operand" "=d") + (gtu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (GTU, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sgtu_si" + [(set (match_operand:SI 0 "register_operand" "=d") + (gtu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "reg_or_0_operand" "dJ")))] + "" + "sltu\\t%0,%z2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=t") + (gtu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d")))] + "" + "sltu\\t%2,%1" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_expand "sgeu" + [(set (match_operand:SI 0 "register_operand" "=d") + (geu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (GEU, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_expand "sltu" + [(set (match_operand:SI 0 "register_operand" "=d") + (ltu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (LTU, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sltu_si" + [(set (match_operand:SI 0 "register_operand" "=d,=d") + (ltu:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "arith_operand" "d,I")))] + "" + "@ + sltu\\t%0,%1,%2 + sltiu\\t%0,%1,%2" + [(set_attr "type" "arith,arith") + (set_attr "mode" "SI,SI")]) + +(define_expand "sleu" + [(set (match_operand:SI 0 "register_operand" "=d") + (leu:SI (match_dup 1) + (match_dup 2)))] + "" + " +{ + if (branch_type != CMP_SI && (branch_type != CMP_DI)) + FAIL; + + /* set up operands from compare. */ + operands[1] = branch_cmp[0]; + operands[2] = branch_cmp[1]; + + gen_int_relational (LEU, operands[0], operands[1], operands[2], (int *)0); + DONE; +}") + +(define_insn "sleu_si_const" + [(set (match_operand:SI 0 "register_operand" "=d") + (leu:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "small_int" "I")))] + "INTVAL (operands[2]) < 32767" + "* +{ + operands[2] = GEN_INT (INTVAL (operands[2]) + 1); + return \"sltiu\\t%0,%1,%2\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + + +;; +;; .................... +;; +;; UNCONDITIONAL BRANCHES +;; +;; .................... + +;; Unconditional branches. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "* +{ + if (GET_CODE (operands[0]) == REG) + return \"j\\t%0\"; + return \"j\\t%l0\"; + /* return \"b\\t%l0\";*/ +}" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +(define_expand "indirect_jump" + [(set (pc) (match_operand 0 "register_operand" "d"))] + "" + " +{ + rtx dest; + + if (operands[0]) /* eliminate unused code warnings */ + { + dest = operands[0]; + if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode) + operands[0] = copy_to_mode_reg (Pmode, dest); + + if (!(Pmode == DImode)) + emit_jump_insn (gen_indirect_jump_internal1 (operands[0])); + else + emit_jump_insn (gen_indirect_jump_internal2 (operands[0])); + + DONE; + } +}") + +(define_insn "indirect_jump_internal1" + [(set (pc) (match_operand:SI 0 "register_operand" "d"))] + "!(Pmode == DImode)" + "j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +(define_expand "tablejump" + [(set (pc) + (match_operand 0 "register_operand" "d")) + (use (label_ref (match_operand 1 "" "")))] + "" + " +{ + if (operands[0]) /* eliminate unused code warnings */ + { + if (GET_MODE (operands[0]) != Pmode) + abort (); + + if (!(Pmode == DImode)) + emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1])); + else + emit_jump_insn (gen_tablejump_internal2 (operands[0], operands[1])); + + DONE; + } +}") + +(define_insn "tablejump_internal1" + [(set (pc) + (match_operand:SI 0 "register_operand" "d")) + (use (label_ref (match_operand 1 "" "")))] + "!(Pmode == DImode)" + "j\\t%0" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +(define_expand "tablejump_internal3" + [(parallel [(set (pc) + (plus:SI (match_operand:SI 0 "register_operand" "d") + (label_ref:SI (match_operand 1 "" "")))) + (use (label_ref:SI (match_dup 1)))])] + "" + "") + +;;; Make sure that this only matches the insn before ADDR_DIFF_VEC. Otherwise +;;; it is not valid. ??? With the USE, the condition tests may not be required +;;; any longer. + +;;; ??? The length depends on the ABI. It is two for o32, and one for n32. +;;; We just use the conservative number here. + +(define_insn "" + [(set (pc) + (plus:SI (match_operand:SI 0 "register_operand" "d") + (label_ref:SI (match_operand 1 "" "")))) + (use (label_ref:SI (match_dup 1)))] + "!(Pmode == DImode) && next_active_insn (insn) != 0 + && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC + && PREV_INSN (next_active_insn (insn)) == operands[1]" + "* +{ + return \"j\\t%0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set_attr "length" "8")]) + +;; +;; .................... +;; +;; Function prologue/epilogue +;; +;; .................... +;; + +(define_expand "prologue" + [(const_int 1)] + "" + " +{ + if (iq2000_isa >= 0) /* avoid unused code warnings */ + { + iq2000_expand_prologue (); + DONE; + } +}") + +;; Block any insns from being moved before this point, since the +;; profiling call to mcount can use various registers that aren't +;; saved or used to pass arguments. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] 0)] + "" + "" + [(set_attr "type" "unknown") + (set_attr "mode" "none") + (set_attr "length" "0")]) + +(define_expand "epilogue" + [(const_int 2)] + "" + " +{ + if (iq2000_isa >= 0) /* avoid unused code warnings */ + { + iq2000_expand_epilogue (); + DONE; + } +}") + +;; Trivial return. Make it look like a normal return insn as that +;; allows jump optimizations to work better . +(define_insn "return" + [(return)] + "iq2000_can_use_return_insn ()" + "j\\t%%31" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +;; Normal return. + +(define_insn "return_internal" + [(use (match_operand 0 "pmode_register_operand" "")) + (return)] + "" + "* +{ + return \"j\\t%0\"; +}" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +(define_insn "eh_return_internal" + [(const_int 4) + (return) + (use (reg:SI 26)) + (use (reg:SI 31))] + "" + "j\\t%%26" + [(set_attr "type" "jump") + (set_attr "mode" "none")]) + +(define_expand "eh_return" + [(use (match_operand:SI 0 "register_operand" "r"))] + "" + " +{ + iq2000_expand_eh_return (operands[0]); + DONE; +}") + + +;; +;; .................... +;; +;; FUNCTION CALLS +;; +;; .................... + +;; calls.c now passes a third argument, make saber happy + +(define_expand "call" + [(parallel [(call (match_operand 0 "memory_operand" "m") + (match_operand 1 "" "i")) + (clobber (reg:SI 31)) + (use (match_operand 2 "" "")) ;; next_arg_reg + (use (match_operand 3 "" ""))])] ;; struct_value_size_rtx + "" + " +{ + rtx addr; + + if (operands[0]) /* eliminate unused code warnings */ + { + addr = XEXP (operands[0], 0); + if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr))) + || ! call_insn_operand (addr, VOIDmode)) + XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr); + + /* In order to pass small structures by value in registers + compatibly with the IQ2000 compiler, we need to shift the value + into the high part of the register. Function_arg has encoded + a PARALLEL rtx, holding a vector of adjustments to be made + as the next_arg_reg variable, so we split up the insns, + and emit them separately. */ + + if (operands[2] != (rtx)0 && GET_CODE (operands[2]) == PARALLEL) + { + rtvec adjust = XVEC (operands[2], 0); + int num = GET_NUM_ELEM (adjust); + int i; + + for (i = 0; i < num; i++) + emit_insn (RTVEC_ELT (adjust, i)); + } + + emit_call_insn (gen_call_internal0 (operands[0], operands[1], + gen_rtx_REG (SImode, + GP_REG_FIRST + 31))); + DONE; + } +}") + +(define_expand "call_internal0" + [(parallel [(call (match_operand 0 "" "") + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "" ""))])] + "" + "") + +(define_insn "call_internal1" + [(call (mem (match_operand 0 "call_insn_operand" "ri")) + (match_operand 1 "" "i")) + (clobber (match_operand:SI 2 "register_operand" "=d"))] + "" + "* +{ + register rtx target = operands[0]; + + if (GET_CODE (target) == CONST_INT) + return \"li\\t%@,%0\\n\\tjalr\\t%2,%@\"; + else if (CONSTANT_ADDRESS_P (target)) + return \"jal\\t%0\"; + else + return \"jalr\\t%2,%0\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none")]) + +;; calls.c now passes a fourth argument, make saber happy + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "register_operand" "=df") + (call (match_operand 1 "memory_operand" "m") + (match_operand 2 "" "i"))) + (clobber (reg:SI 31)) + (use (match_operand 3 "" ""))])] ;; next_arg_reg + "" + " +{ + rtx addr; + + if (operands[0]) /* eliminate unused code warning */ + { + addr = XEXP (operands[1], 0); + if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr))) + || ! call_insn_operand (addr, VOIDmode)) + XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr); + + /* In order to pass small structures by value in registers + compatibly with the IQ2000 compiler, we need to shift the value + into the high part of the register. Function_arg has encoded + a PARALLEL rtx, holding a vector of adjustments to be made + as the next_arg_reg variable, so we split up the insns, + and emit them separately. */ + + if (operands[3] != (rtx)0 && GET_CODE (operands[3]) == PARALLEL) + { + rtvec adjust = XVEC (operands[3], 0); + int num = GET_NUM_ELEM (adjust); + int i; + + for (i = 0; i < num; i++) + emit_insn (RTVEC_ELT (adjust, i)); + } + + if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) > 1) + { + emit_call_insn (gen_call_value_multiple_internal0 + (XEXP (XVECEXP (operands[0], 0, 0), 0), + operands[1], operands[2], + XEXP (XVECEXP (operands[0], 0, 1), 0), + gen_rtx_REG (SImode, GP_REG_FIRST + 31))); + DONE; + } + + /* We have a call returning a DImode structure in an FP reg. + Strip off the now unnecessary PARALLEL. */ + if (GET_CODE (operands[0]) == PARALLEL) + operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0); + + emit_call_insn (gen_call_value_internal0 (operands[0], operands[1], operands[2], + gen_rtx_REG (SImode, + GP_REG_FIRST + 31))); + + DONE; + } +}") + +(define_expand "call_value_internal0" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "" ""))])] + "" + "") + +(define_insn "call_value_internal1" + [(set (match_operand 0 "register_operand" "=df") + (call (mem (match_operand 1 "call_insn_operand" "ri")) + (match_operand 2 "" "i"))) + (clobber (match_operand:SI 3 "register_operand" "=d"))] + "" + "* +{ + register rtx target = operands[1]; + + if (GET_CODE (target) == CONST_INT) + return \"li\\t%@,%1\\n\\tjalr\\t%3,%@\"; + else if (CONSTANT_ADDRESS_P (target)) + return \"jal\\t%1\"; + else + return \"jalr\\t%3,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none")]) + +(define_expand "call_value_multiple_internal0" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (set (match_operand 3 "" "") + (call (match_dup 1) + (match_dup 2))) + (clobber (match_operand:SI 4 "" ""))])] + "" + "") + +;; ??? May eventually need all 6 versions of the call patterns with multiple +;; return values. + +(define_insn "call_value_multiple_internal1" + [(set (match_operand 0 "register_operand" "=df") + (call (mem (match_operand 1 "call_insn_operand" "ri")) + (match_operand 2 "" "i"))) + (set (match_operand 3 "register_operand" "=df") + (call (mem (match_dup 1)) + (match_dup 2))) + (clobber (match_operand:SI 4 "register_operand" "=d"))] + "" + "* +{ + register rtx target = operands[1]; + + if (GET_CODE (target) == CONST_INT) + return \"li\\t%@,%1\\n\\tjalr\\t%4,%@\"; + else if (CONSTANT_ADDRESS_P (target)) + return \"jal\\t%1\"; + else + return \"jalr\\t%4,%1\"; +}" + [(set_attr "type" "call") + (set_attr "mode" "none")]) + +;; Call subroutine returning any type. + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + if (operands[0]) /* silence statement not reached warnings */ + { + int i; + + emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + emit_insn (gen_blockage ()); + DONE; + } +}") + +;; +;; .................... +;; +;; MISC. +;; +;; .................... +;; + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "nop") + (set_attr "mode" "none")]) + + +;; For the rare case where we need to load an address into a register +;; that can not be recognized by the normal movsi/addsi instructions. +;; I have no idea how many insns this can actually generate. It should +;; be rare, so over-estimating as 10 instructions should not have any +;; real performance impact. +(define_insn "leasi" + [(set (match_operand:SI 0 "register_operand" "=d") + (match_operand:SI 1 "address_operand" "p"))] + "Pmode == SImode" + "* +{ + rtx xoperands [3]; + + xoperands[0] = operands[0]; + xoperands[1] = XEXP (operands[1], 0); + xoperands[2] = XEXP (operands[1], 1); + output_asm_insn (\"addiu\\t%0,%1,%2\", xoperands); + return \"\"; +}" + [(set_attr "type" "arith") + (set_attr "mode" "SI") + (set_attr "length" "40")]) + +(define_insn "ado16" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")] + UNSPEC_ADO16))] + "" + "ado16\\t%0, %1, %2" +) + +(define_insn "ram" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "I") + (match_operand:SI 3 "const_int_operand" "I") + (match_operand:SI 4 "const_int_operand" "I")] + UNSPEC_RAM))] + "" + "ram\\t%0, %1, %2, %3, %4" +) + +(define_insn "chkhdr" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_CHKHDR)] + "" + "* return iq2000_fill_delay_slot (\"chkhdr\\t%0, %1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "pkrl" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_PKRL)] + "" + "* return iq2000_fill_delay_slot (\"pkrl\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "cfc0" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CFC0))] + "" + "* return iq2000_fill_delay_slot (\"cfc0\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "cfc1" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CFC1))] + "" + "* return iq2000_fill_delay_slot (\"cfc1\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "cfc2" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CFC2))] + "" + "* return iq2000_fill_delay_slot (\"cfc2\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "cfc3" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CFC3))] + "" + "* return iq2000_fill_delay_slot (\"cfc3\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "ctc0" + [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CTC0)] + "" + "* return iq2000_fill_delay_slot (\"ctc0\\t%z0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "ctc1" + [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CTC1)] + "" + "* return iq2000_fill_delay_slot (\"ctc1\\t%z0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "ctc2" + [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CTC2)] + "" + "* return iq2000_fill_delay_slot (\"ctc2\\t%z0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "ctc3" + [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_CTC3)] + "" + "* return iq2000_fill_delay_slot (\"ctc3\\t%z0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mfc0" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MFC0))] + "" + "* return iq2000_fill_delay_slot (\"mfc0\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mfc1" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MFC1))] + "" + "* return iq2000_fill_delay_slot (\"mfc1\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mfc2" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MFC2))] + "" + "* return iq2000_fill_delay_slot (\"mfc2\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "mfc3" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MFC3))] + "" + "* return iq2000_fill_delay_slot (\"mfc3\\t%0, %%%1\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "mtc0" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MTC0)] + "" + "* return iq2000_fill_delay_slot (\"mtc0\\t%0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mtc1" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MTC1)] + "" + "* return iq2000_fill_delay_slot (\"mtc1\\t%0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mtc2" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MTC2)] + "" + "* return iq2000_fill_delay_slot (\"mtc2\\t%0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "mtc3" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "const_int_operand" "I")] + UNSPEC_MTC3)] + "" + "* return iq2000_fill_delay_slot (\"mtc3\\t%0, %%%1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "lur" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUR)] + "" + "* return iq2000_fill_delay_slot (\"lur\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "rb" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_RB)] + "" + "* return iq2000_fill_delay_slot (\"rb\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "rx" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_RX)] + "" + "* return iq2000_fill_delay_slot (\"rx\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "srrd" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_SRRD)] + "" + "* return iq2000_fill_delay_slot (\"srrd\\t%0\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "srwr" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_SRWR)] + "" + "* return iq2000_fill_delay_slot (\"srwr\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "wb" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_WB)] + "" + "* return iq2000_fill_delay_slot (\"wb\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "wx" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_WX)] + "" + "* return iq2000_fill_delay_slot (\"wx\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "luc32" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUC32)] + "" + "* return iq2000_fill_delay_slot (\"luc32\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "luc32l" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUC32L)] + "" + "* return iq2000_fill_delay_slot (\"luc32l\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "luc64" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUC64)] + "" + "* return iq2000_fill_delay_slot (\"luc64\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "luc64l" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUC64L)] + "" + "* return iq2000_fill_delay_slot (\"luc64l\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "luk" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUK)] + "" + "* return iq2000_fill_delay_slot (\"luk\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "lulck" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_LULCK)] + "" + "* return iq2000_fill_delay_slot (\"lulck\\t%0\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "lum32" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUM32)] + "" + "* return iq2000_fill_delay_slot (\"lum32\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "lum32l" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUM32L)] + "" + "* return iq2000_fill_delay_slot (\"lum32l\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "lum64" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUM64)] + "" + "* return iq2000_fill_delay_slot (\"lum64\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "lum64l" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LUM64L)] + "" + "* return iq2000_fill_delay_slot (\"lum64l\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "lurl" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_LURL)] + "" + "* return iq2000_fill_delay_slot (\"lurl\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "mrgb" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "const_int_operand" "I")] + UNSPEC_MRGB))] + "" + "* return iq2000_fill_delay_slot (\"mrgb\\t%0, %1, %2, %3\", DELAY_LOAD, operands, insn);" + [(set_attr "dslot" "ok_in_dslot")] +) + +(define_insn "srrdl" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_SRRDL)] + "" + "* return iq2000_fill_delay_slot (\"srrdl\\t%0\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "srulck" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_SRULCK)] + "" + "* return iq2000_fill_delay_slot (\"srulck\\t%0\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "srwru" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_SRWRU)] + "" + "* return iq2000_fill_delay_slot (\"srwru\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "trapqfl" + [(unspec_volatile:SI [(const_int 1)] UNSPEC_TRAPQFL)] + "" + "* return iq2000_fill_delay_slot (\"trapqfl\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "trapqne" + [(unspec_volatile:SI [(const_int 2)] UNSPEC_TRAPQNE)] + "" + "* return iq2000_fill_delay_slot (\"trapqne\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "traprel" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")] + UNSPEC_TRAPREL)] + "" + "* return iq2000_fill_delay_slot (\"traprel %0\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "wbu" + [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "register_operand" "r")] + UNSPEC_WBU)] + "" + "* return iq2000_fill_delay_slot (\"wbu\\t%0, %1\", DELAY_NONE, operands, insn);" + [(set_attr "dslot" "not_in_dslot")] +) + +(define_insn "syscall" + [(unspec_volatile:SI [(const_int 2)] UNSPEC_SYSCALL)] + "" + "syscall" + [(set_attr "dslot" "not_in_dslot")] +) diff --git a/gcc/config/iq2000/lib2extra-funcs.c b/gcc/config/iq2000/lib2extra-funcs.c new file mode 100644 index 0000000..e092bab --- /dev/null +++ b/gcc/config/iq2000/lib2extra-funcs.c @@ -0,0 +1,17 @@ +typedef unsigned int USItype __attribute__ ((mode (SI))); + +USItype +__mulsi3 (USItype a, USItype b) +{ + USItype c = 0; + + while (a != 0) + { + if (a & 1) + c += b; + a >>= 1; + b <<= 1; + } + + return c; +} diff --git a/gcc/config/iq2000/t-iq2000 b/gcc/config/iq2000/t-iq2000 new file mode 100644 index 0000000..23dc955 --- /dev/null +++ b/gcc/config/iq2000/t-iq2000 @@ -0,0 +1,36 @@ +# Suppress building libgcc1.a, since the MIPS compiler port is complete +# and does not need anything from libgcc1.a. +LIBGCC1 = +CROSS_LIBGCC1 = + +# We must build libgcc2.a with -G 0, in case the user wants to link +# without the $gp register. +TARGET_LIBGCC2_CFLAGS = -G 0 + +LIB2FUNCS_EXTRA = $(srcdir)/config/udivmod.c $(srcdir)/config/divmod.c $(srcdir)/config/udivmodsi4.c $(srcdir)/config/iq2000/lib2extra-funcs.c + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + cat $(srcdir)/config/fp-bit.c > dp-bit.c + +# Enable the following if multilibs are needed. +# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a +# description of the options and their values. +# +# MULTILIB_OPTIONS = +# MULTILIB_DIRNAMES = +# MULTILIB_MATCHES = +# MULTILIB_EXCEPTIONS = +# MULTILIB_EXTRA_OPTS = +# +# LIBGCC = stmp-multilib +# INSTALL_LIBGCC = install-multilib + diff --git a/gcc/config/iq2000/xm-iq2000.h b/gcc/config/iq2000/xm-iq2000.h new file mode 100644 index 0000000..22e46bc --- /dev/null +++ b/gcc/config/iq2000/xm-iq2000.h @@ -0,0 +1,35 @@ +/* Configuration for IQ based processors + Copyright (C) 2003 Free Software Foundation, Inc. + Contributed by Red Hat Inc. + +This file is part of GNU CC. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index efbe049..1ed6876 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1906,6 +1906,8 @@ GNU Compiler Collection on your machine. @item @uref{#ip2k-*-elf,,ip2k-*-elf} @item +@uref{#iq2000-*-elf,,iq2000-*-elf} +@item @uref{#m32r-*-elf,,m32r-*-elf} @item @uref{#m6811-elf,,m6811-elf} @@ -2759,6 +2761,13 @@ Use @samp{configure --target=ip2k-elf --enable-languages=c} to configure GCC@. @html
@end html +@heading @anchor{iq2000-*-elf}iq2000-*-elf +Vitesse IQ2000 processors. These are used in embedded +applications. There are no standard Unix configurations. + +@html +
+@end html @heading @anchor{m32r-*-elf}m32r-*-elf Renesas M32R processor. This configuration is intended for embedded systems. -- 2.7.4