diff options
Diffstat (limited to 'gcc/config/arc')
-rw-r--r-- | gcc/config/arc/arc-modes.def | 24 | ||||
-rw-r--r-- | gcc/config/arc/arc-protos.h | 63 | ||||
-rw-r--r-- | gcc/config/arc/arc.c | 2491 | ||||
-rw-r--r-- | gcc/config/arc/arc.h | 935 | ||||
-rw-r--r-- | gcc/config/arc/arc.md | 1376 | ||||
-rw-r--r-- | gcc/config/arc/arc.opt | 60 | ||||
-rw-r--r-- | gcc/config/arc/initfini.c | 155 | ||||
-rw-r--r-- | gcc/config/arc/lib1funcs.asm | 266 | ||||
-rw-r--r-- | gcc/config/arc/t-arc | 60 |
9 files changed, 5430 insertions, 0 deletions
diff --git a/gcc/config/arc/arc-modes.def b/gcc/config/arc/arc-modes.def new file mode 100644 index 000000000..c2d2ceaf8 --- /dev/null +++ b/gcc/config/arc/arc-modes.def @@ -0,0 +1,24 @@ +/* Definitions of target machine for GNU compiler, Argonaut ARC cpu. + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Some insns set all condition code flags, some only set the ZNC flags, and + some only set the ZN flags. */ + +CC_MODE (CCZNC); +CC_MODE (CCZN); diff --git a/gcc/config/arc/arc-protos.h b/gcc/config/arc/arc-protos.h new file mode 100644 index 000000000..5550ebfbe --- /dev/null +++ b/gcc/config/arc/arc-protos.h @@ -0,0 +1,63 @@ +/* Definitions of target machine for GNU compiler, Argonaut ARC cpu. + Copyright (C) 2000, 2004, 2007, 2010 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifdef RTX_CODE +extern enum machine_mode arc_select_cc_mode (enum rtx_code, rtx, rtx); + +/* Define the function that build the compare insn for scc and bcc. */ +extern struct rtx_def *gen_compare_reg (enum rtx_code, rtx, rtx); +#endif + +/* Declarations for various fns used in the .md file. */ +extern const char *output_shift (rtx *); + +extern int symbolic_operand (rtx, enum machine_mode); +extern int arc_double_limm_p (rtx); +extern int arc_eligible_for_epilogue_delay (rtx, int); +extern void arc_initialize_trampoline (rtx, rtx, rtx); +extern void arc_print_operand (FILE *, rtx, int); +extern void arc_print_operand_address (FILE *, rtx); +extern void arc_final_prescan_insn (rtx, rtx *, int); +extern int call_address_operand (rtx, enum machine_mode); +extern int call_operand (rtx, enum machine_mode); +extern int symbolic_memory_operand (rtx, enum machine_mode); +extern int short_immediate_operand (rtx, enum machine_mode); +extern int long_immediate_operand (rtx, enum machine_mode); +extern int long_immediate_loadstore_operand (rtx, enum machine_mode); +extern int move_src_operand (rtx, enum machine_mode); +extern int move_double_src_operand (rtx, enum machine_mode); +extern int move_dest_operand (rtx, enum machine_mode); +extern int load_update_operand (rtx, enum machine_mode); +extern int store_update_operand (rtx, enum machine_mode); +extern int nonvol_nonimm_operand (rtx, enum machine_mode); +extern int const_sint32_operand (rtx, enum machine_mode); +extern int const_uint32_operand (rtx, enum machine_mode); +extern int proper_comparison_operator (rtx, enum machine_mode); +extern int shift_operator (rtx, enum machine_mode); + +extern enum arc_function_type arc_compute_function_type (tree); + + +extern unsigned int arc_compute_frame_size (int); +extern void arc_save_restore (FILE *, const char *, unsigned int, + unsigned int, const char *); +extern int arc_delay_slots_for_epilogue (void); +extern void arc_ccfsm_at_label (const char *, int); +extern int arc_ccfsm_branch_deleted_p (void); +extern void arc_ccfsm_record_branch_deleted (void); diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c new file mode 100644 index 000000000..f1afda20f --- /dev/null +++ b/gcc/config/arc/arc.c @@ -0,0 +1,2491 @@ +/* Subroutines used for code generation on the Argonaut ARC cpu. + Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* ??? This is an old port, and is undoubtedly suffering from bit rot. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "function.h" +#include "expr.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "df.h" +#include "tm_p.h" +#include "target.h" +#include "target-def.h" + +/* Which cpu we're compiling for. */ +int arc_cpu_type; + +/* Name of mangle string to add to symbols to separate code compiled for each + cpu (or NULL). */ +const char *arc_mangle_cpu; + +/* Name of text, data, and rodata sections used in varasm.c. */ +const char *arc_text_section; +const char *arc_data_section; +const char *arc_rodata_section; + +/* Array of valid operand punctuation characters. */ +char arc_punct_chars[256]; + +/* Variables used by arc_final_prescan_insn to implement conditional + execution. */ +static int arc_ccfsm_state; +static int arc_ccfsm_current_cc; +static rtx arc_ccfsm_target_insn; +static int arc_ccfsm_target_label; + +/* The maximum number of insns skipped which will be conditionalised if + possible. */ +#define MAX_INSNS_SKIPPED 3 + +/* A nop is needed between a 4 byte insn that sets the condition codes and + a branch that uses them (the same isn't true for an 8 byte insn that sets + the condition codes). Set by arc_final_prescan_insn. Used by + arc_print_operand. */ +static int last_insn_set_cc_p; +static int current_insn_set_cc_p; +static bool arc_handle_option (size_t, const char *, int); +static void record_cc_ref (rtx); +static void arc_init_reg_tables (void); +static int get_arc_condition_code (rtx); +static tree arc_handle_interrupt_attribute (tree *, tree, tree, int, bool *); +static bool arc_assemble_integer (rtx, unsigned int, int); +static void arc_output_function_prologue (FILE *, HOST_WIDE_INT); +static void arc_output_function_epilogue (FILE *, HOST_WIDE_INT); +static void arc_file_start (void); +static void arc_internal_label (FILE *, const char *, unsigned long); +static void arc_va_start (tree, rtx); +static void arc_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, + tree, int *, int); +static bool arc_rtx_costs (rtx, int, int, int *, bool); +static int arc_address_cost (rtx, bool); +static void arc_external_libcall (rtx); +static bool arc_return_in_memory (const_tree, const_tree); +static bool arc_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static rtx arc_function_arg (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static void arc_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static unsigned int arc_function_arg_boundary (enum machine_mode, const_tree); +static void arc_trampoline_init (rtx, tree, rtx); +static void arc_option_override (void); +static void arc_conditional_register_usage (void); + + +/* ARC specific attributs. */ + +static const struct attribute_spec arc_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "interrupt", 1, 1, true, false, false, arc_handle_interrupt_attribute }, + { NULL, 0, 0, false, false, false, NULL } +}; + +/* Initialize the GCC target structure. */ +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER arc_assemble_integer + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE arc_output_function_prologue +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE arc_output_function_epilogue +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START arc_file_start +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE arc_attribute_table +#undef TARGET_ASM_INTERNAL_LABEL +#define TARGET_ASM_INTERNAL_LABEL arc_internal_label +#undef TARGET_ASM_EXTERNAL_LIBCALL +#define TARGET_ASM_EXTERNAL_LIBCALL arc_external_libcall + +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION arc_handle_option + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE arc_option_override + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS arc_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST arc_address_cost + +#undef TARGET_PROMOTE_FUNCTION_MODE +#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY arc_return_in_memory +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE arc_pass_by_reference +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG arc_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE arc_function_arg_advance +#undef TARGET_FUNCTION_ARG_BOUNDARY +#define TARGET_FUNCTION_ARG_BOUNDARY arc_function_arg_boundary +#undef TARGET_CALLEE_COPIES +#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true + +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS arc_setup_incoming_varargs + +#undef TARGET_EXPAND_BUILTIN_VA_START +#define TARGET_EXPAND_BUILTIN_VA_START arc_va_start + +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT arc_trampoline_init + +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE arc_conditional_register_usage + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Implement TARGET_HANDLE_OPTION. */ + +static bool +arc_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) +{ + switch (code) + { + case OPT_mcpu_: + return strcmp (arg, "base") == 0 || ARC_EXTENSION_CPU (arg); + + default: + return true; + } +} + +/* Implement TARGET_OPTION_OVERRIDE. + These need to be done at start up. It's convenient to do them here. */ + +static void +arc_option_override (void) +{ + char *tmp; + + /* Set the pseudo-ops for the various standard sections. */ + arc_text_section = tmp = XNEWVEC (char, strlen (arc_text_string) + sizeof (ARC_SECTION_FORMAT) + 1); + sprintf (tmp, ARC_SECTION_FORMAT, arc_text_string); + arc_data_section = tmp = XNEWVEC (char, strlen (arc_data_string) + sizeof (ARC_SECTION_FORMAT) + 1); + sprintf (tmp, ARC_SECTION_FORMAT, arc_data_string); + arc_rodata_section = tmp = XNEWVEC (char, strlen (arc_rodata_string) + sizeof (ARC_SECTION_FORMAT) + 1); + sprintf (tmp, ARC_SECTION_FORMAT, arc_rodata_string); + + arc_init_reg_tables (); + + /* Initialize array for PRINT_OPERAND_PUNCT_VALID_P. */ + memset (arc_punct_chars, 0, sizeof (arc_punct_chars)); + arc_punct_chars['#'] = 1; + arc_punct_chars['*'] = 1; + arc_punct_chars['?'] = 1; + arc_punct_chars['!'] = 1; + arc_punct_chars['~'] = 1; +} + +/* The condition codes of the ARC, and the inverse function. */ +static const char *const arc_condition_codes[] = +{ + "al", 0, "eq", "ne", "p", "n", "c", "nc", "v", "nv", + "gt", "le", "ge", "lt", "hi", "ls", "pnz", 0 +}; + +#define ARC_INVERSE_CONDITION_CODE(X) ((X) ^ 1) + +/* Returns the index of the ARC condition code string in + `arc_condition_codes'. COMPARISON should be an rtx like + `(eq (...) (...))'. */ + +static int +get_arc_condition_code (rtx comparison) +{ + switch (GET_CODE (comparison)) + { + case EQ : return 2; + case NE : return 3; + case GT : return 10; + case LE : return 11; + case GE : return 12; + case LT : return 13; + case GTU : return 14; + case LEU : return 15; + case LTU : return 6; + case GEU : return 7; + default : gcc_unreachable (); + } + /*NOTREACHED*/ + return (42); +} + +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. */ + +enum machine_mode +arc_select_cc_mode (enum rtx_code op, + rtx x ATTRIBUTE_UNUSED, + rtx y ATTRIBUTE_UNUSED) +{ + switch (op) + { + case EQ : + case NE : + return CCZNmode; + default : + switch (GET_CODE (x)) + { + case AND : + case IOR : + case XOR : + case SIGN_EXTEND : + case ZERO_EXTEND : + return CCZNmode; + case ASHIFT : + case ASHIFTRT : + case LSHIFTRT : + return CCZNCmode; + default: + break; + } + } + return CCmode; +} + +/* Vectors to keep interesting information about registers where it can easily + be got. We use to use the actual mode value as the bit number, but there + is (or may be) more than 32 modes now. Instead we use two tables: one + indexed by hard register number, and one indexed by mode. */ + +/* The purpose of arc_mode_class is to shrink the range of modes so that + they all fit (as bit numbers) in a 32-bit word (again). Each real mode is + mapped into one arc_mode_class mode. */ + +enum arc_mode_class { + C_MODE, + S_MODE, D_MODE, T_MODE, O_MODE, + SF_MODE, DF_MODE, TF_MODE, OF_MODE +}; + +/* Modes for condition codes. */ +#define C_MODES (1 << (int) C_MODE) + +/* Modes for single-word and smaller quantities. */ +#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE)) + +/* Modes for double-word and smaller quantities. */ +#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE)) + +/* Modes for quad-word and smaller quantities. */ +#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE)) + +/* Value is 1 if register/mode pair is acceptable on arc. */ + +const unsigned int arc_hard_regno_mode_ok[] = { + T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, + T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, + T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, D_MODES, + D_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, + + /* ??? Leave these as S_MODES for now. */ + S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, + S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, + S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, + S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, C_MODES +}; + +unsigned int arc_mode_class [NUM_MACHINE_MODES]; + +enum reg_class arc_regno_reg_class[FIRST_PSEUDO_REGISTER]; + +static void +arc_init_reg_tables (void) +{ + int i; + + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + switch (GET_MODE_CLASS (i)) + { + case MODE_INT: + case MODE_PARTIAL_INT: + case MODE_COMPLEX_INT: + if (GET_MODE_SIZE (i) <= 4) + arc_mode_class[i] = 1 << (int) S_MODE; + else if (GET_MODE_SIZE (i) == 8) + arc_mode_class[i] = 1 << (int) D_MODE; + else if (GET_MODE_SIZE (i) == 16) + arc_mode_class[i] = 1 << (int) T_MODE; + else if (GET_MODE_SIZE (i) == 32) + arc_mode_class[i] = 1 << (int) O_MODE; + else + arc_mode_class[i] = 0; + break; + case MODE_FLOAT: + case MODE_COMPLEX_FLOAT: + if (GET_MODE_SIZE (i) <= 4) + arc_mode_class[i] = 1 << (int) SF_MODE; + else if (GET_MODE_SIZE (i) == 8) + arc_mode_class[i] = 1 << (int) DF_MODE; + else if (GET_MODE_SIZE (i) == 16) + arc_mode_class[i] = 1 << (int) TF_MODE; + else if (GET_MODE_SIZE (i) == 32) + arc_mode_class[i] = 1 << (int) OF_MODE; + else + arc_mode_class[i] = 0; + break; + case MODE_CC: + arc_mode_class[i] = 1 << (int) C_MODE; + break; + default: + arc_mode_class[i] = 0; + break; + } + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (i < 60) + arc_regno_reg_class[i] = GENERAL_REGS; + else if (i == 60) + arc_regno_reg_class[i] = LPCOUNT_REG; + else if (i == 61) + arc_regno_reg_class[i] = NO_REGS /* CC_REG: must be NO_REGS */; + else + arc_regno_reg_class[i] = NO_REGS; + } +} + +/* ARC specific attribute support. + + The ARC has these attributes: + interrupt - for interrupt functions +*/ + +/* Handle an "interrupt" attribute; arguments as in + struct attribute_spec.handler. */ +static tree +arc_handle_interrupt_attribute (tree *node ATTRIBUTE_UNUSED, + tree name, + tree args, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + tree value = TREE_VALUE (args); + + if (TREE_CODE (value) != STRING_CST) + { + warning (OPT_Wattributes, + "argument of %qE attribute is not a string constant", + name); + *no_add_attrs = true; + } + else if (strcmp (TREE_STRING_POINTER (value), "ilink1") + && strcmp (TREE_STRING_POINTER (value), "ilink2")) + { + warning (OPT_Wattributes, + "argument of %qE attribute is not \"ilink1\" or \"ilink2\"", + name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + + +/* Acceptable arguments to the call insn. */ + +int +call_address_operand (rtx op, enum machine_mode mode) +{ + return (symbolic_operand (op, mode) + || (GET_CODE (op) == CONST_INT && LEGITIMATE_CONSTANT_P (op)) + || (GET_CODE (op) == REG)); +} + +int +call_operand (rtx op, enum machine_mode mode) +{ + if (GET_CODE (op) != MEM) + return 0; + op = XEXP (op, 0); + return call_address_operand (op, mode); +} + +/* Returns 1 if OP is a symbol reference. */ + +int +symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case LABEL_REF: + case CONST : + return 1; + default: + return 0; + } +} + +/* Return truth value of statement that OP is a symbolic memory + operand of mode MODE. */ + +int +symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + if (GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + if (GET_CODE (op) != MEM) + return 0; + op = XEXP (op, 0); + return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST + || GET_CODE (op) == LABEL_REF); +} + +/* Return true if OP is a short immediate (shimm) value. */ + +int +short_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + if (GET_CODE (op) != CONST_INT) + return 0; + return SMALL_INT (INTVAL (op)); +} + +/* Return true if OP will require a long immediate (limm) value. + This is currently only used when calculating length attributes. */ + +int +long_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF : + case LABEL_REF : + case CONST : + return 1; + case CONST_INT : + return !SMALL_INT (INTVAL (op)); + case CONST_DOUBLE : + /* These can happen because large unsigned 32-bit constants are + represented this way (the multiplication patterns can cause these + to be generated). They also occur for SFmode values. */ + return 1; + default: + break; + } + return 0; +} + +/* Return true if OP is a MEM that when used as a load or store address will + require an 8 byte insn. + Load and store instructions don't allow the same possibilities but they're + similar enough that this one function will do. + This is currently only used when calculating length attributes. */ + +int +long_immediate_loadstore_operand (rtx op, + enum machine_mode mode ATTRIBUTE_UNUSED) +{ + if (GET_CODE (op) != MEM) + return 0; + + op = XEXP (op, 0); + switch (GET_CODE (op)) + { + case SYMBOL_REF : + case LABEL_REF : + case CONST : + return 1; + case CONST_INT : + /* This must be handled as "st c,[limm]". Ditto for load. + Technically, the assembler could translate some possibilities to + "st c,[limm/2 + limm/2]" if limm/2 will fit in a shimm, but we don't + assume that it does. */ + return 1; + case CONST_DOUBLE : + /* These can happen because large unsigned 32-bit constants are + represented this way (the multiplication patterns can cause these + to be generated). They also occur for SFmode values. */ + return 1; + case REG : + return 0; + case PLUS : + if (GET_CODE (XEXP (op, 1)) == CONST_INT + && !SMALL_INT (INTVAL (XEXP (op, 1)))) + return 1; + return 0; + default: + break; + } + return 0; +} + +/* Return true if OP is an acceptable argument for a single word + move source. */ + +int +move_src_operand (rtx op, enum machine_mode mode) +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF : + case LABEL_REF : + case CONST : + return 1; + case CONST_INT : + return (LARGE_INT (INTVAL (op))); + case CONST_DOUBLE : + /* We can handle DImode integer constants in SImode if the value + (signed or unsigned) will fit in 32 bits. This is needed because + large unsigned 32-bit constants are represented as CONST_DOUBLEs. */ + if (mode == SImode) + return arc_double_limm_p (op); + /* We can handle 32-bit floating point constants. */ + if (mode == SFmode) + return GET_MODE (op) == SFmode; + return 0; + case REG : + return register_operand (op, mode); + case SUBREG : + /* (subreg (mem ...) ...) can occur here if the inner part was once a + pseudo-reg and is now a stack slot. */ + if (GET_CODE (SUBREG_REG (op)) == MEM) + return address_operand (XEXP (SUBREG_REG (op), 0), mode); + else + return register_operand (op, mode); + case MEM : + return address_operand (XEXP (op, 0), mode); + default : + return 0; + } +} + +/* Return true if OP is an acceptable argument for a double word + move source. */ + +int +move_double_src_operand (rtx op, enum machine_mode mode) +{ + switch (GET_CODE (op)) + { + case REG : + return register_operand (op, mode); + case SUBREG : + /* (subreg (mem ...) ...) can occur here if the inner part was once a + pseudo-reg and is now a stack slot. */ + if (GET_CODE (SUBREG_REG (op)) == MEM) + return move_double_src_operand (SUBREG_REG (op), mode); + else + return register_operand (op, mode); + case MEM : + /* Disallow auto inc/dec for now. */ + if (GET_CODE (XEXP (op, 0)) == PRE_DEC + || GET_CODE (XEXP (op, 0)) == PRE_INC) + return 0; + return address_operand (XEXP (op, 0), mode); + case CONST_INT : + case CONST_DOUBLE : + return 1; + default : + return 0; + } +} + +/* Return true if OP is an acceptable argument for a move destination. */ + +int +move_dest_operand (rtx op, enum machine_mode mode) +{ + switch (GET_CODE (op)) + { + case REG : + return register_operand (op, mode); + case SUBREG : + /* (subreg (mem ...) ...) can occur here if the inner part was once a + pseudo-reg and is now a stack slot. */ + if (GET_CODE (SUBREG_REG (op)) == MEM) + return address_operand (XEXP (SUBREG_REG (op), 0), mode); + else + return register_operand (op, mode); + case MEM : + return address_operand (XEXP (op, 0), mode); + default : + return 0; + } +} + +/* Return true if OP is valid load with update operand. */ + +int +load_update_operand (rtx op, enum machine_mode mode) +{ + if (GET_CODE (op) != MEM + || GET_MODE (op) != mode) + return 0; + op = XEXP (op, 0); + if (GET_CODE (op) != PLUS + || GET_MODE (op) != Pmode + || !register_operand (XEXP (op, 0), Pmode) + || !nonmemory_operand (XEXP (op, 1), Pmode)) + return 0; + return 1; +} + +/* Return true if OP is valid store with update operand. */ + +int +store_update_operand (rtx op, enum machine_mode mode) +{ + if (GET_CODE (op) != MEM + || GET_MODE (op) != mode) + return 0; + op = XEXP (op, 0); + if (GET_CODE (op) != PLUS + || GET_MODE (op) != Pmode + || !register_operand (XEXP (op, 0), Pmode) + || !(GET_CODE (XEXP (op, 1)) == CONST_INT + && SMALL_INT (INTVAL (XEXP (op, 1))))) + return 0; + return 1; +} + +/* Return true if OP is a non-volatile non-immediate operand. + Volatile memory refs require a special "cache-bypass" instruction + and only the standard movXX patterns are set up to handle them. */ + +int +nonvol_nonimm_operand (rtx op, enum machine_mode mode) +{ + if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op)) + return 0; + return nonimmediate_operand (op, mode); +} + +/* Accept integer operands in the range -0x80000000..0x7fffffff. We have + to check the range carefully since this predicate is used in DImode + contexts. */ + +int +const_sint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + /* All allowed constants will fit a CONST_INT. */ + return (GET_CODE (op) == CONST_INT + && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff)); +} + +/* Accept integer operands in the range 0..0xffffffff. We have to check the + range carefully since this predicate is used in DImode contexts. Also, we + need some extra crud to make it work when hosted on 64-bit machines. */ + +int +const_uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ +#if HOST_BITS_PER_WIDE_INT > 32 + /* All allowed constants will fit a CONST_INT. */ + return (GET_CODE (op) == CONST_INT + && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL)); +#else + return ((GET_CODE (op) == CONST_INT && INTVAL (op) >= 0) + || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0)); +#endif +} + +/* Return 1 if OP is a comparison operator valid for the mode of CC. + This allows the use of MATCH_OPERATOR to recognize all the branch insns. + + Some insns only set a few bits in the condition code. So only allow those + comparisons that use the bits that are valid. */ + +int +proper_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + enum rtx_code code; + if (!COMPARISON_P (op)) + return 0; + + code = GET_CODE (op); + if (GET_MODE (XEXP (op, 0)) == CCZNmode) + return (code == EQ || code == NE); + if (GET_MODE (XEXP (op, 0)) == CCZNCmode) + return (code == EQ || code == NE + || code == LTU || code == GEU || code == GTU || code == LEU); + return 1; +} + +/* Misc. utilities. */ + +/* X and Y are two things to compare using CODE. Return the rtx + for the cc reg in the proper mode. */ + +rtx +gen_compare_reg (enum rtx_code code, rtx x, rtx y) +{ + enum machine_mode mode = SELECT_CC_MODE (code, x, y); + return gen_rtx_REG (mode, 61); +} + +/* Return 1 if VALUE, a const_double, will fit in a limm (4 byte number). + We assume the value can be either signed or unsigned. */ + +int +arc_double_limm_p (rtx value) +{ + HOST_WIDE_INT low, high; + + gcc_assert (GET_CODE (value) == CONST_DOUBLE); + + low = CONST_DOUBLE_LOW (value); + high = CONST_DOUBLE_HIGH (value); + + if (low & 0x80000000) + { + return (((unsigned HOST_WIDE_INT) low <= 0xffffffff && high == 0) + || (((low & - (unsigned HOST_WIDE_INT) 0x80000000) + == - (unsigned HOST_WIDE_INT) 0x80000000) + && high == -1)); + } + else + { + return (unsigned HOST_WIDE_INT) low <= 0x7fffffff && high == 0; + } +} + +/* Do any needed setup for a variadic function. For the ARC, we must + create a register parameter block, and then copy any anonymous arguments + in registers to memory. + + CUM has not been updated for the last named argument which has type TYPE + and mode MODE, and we rely on this fact. + + We do things a little weird here. We're supposed to only allocate space + for the anonymous arguments. However we need to keep the stack eight byte + aligned. So we round the space up if necessary, and leave it to va_start + to compensate. */ + +static void +arc_setup_incoming_varargs (CUMULATIVE_ARGS *cum, + enum machine_mode mode, + tree type ATTRIBUTE_UNUSED, + int *pretend_size, + int no_rtl) +{ + int first_anon_arg; + + /* All BLKmode values are passed by reference. */ + gcc_assert (mode != BLKmode); + + first_anon_arg = *cum + ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + + if (first_anon_arg < MAX_ARC_PARM_REGS && !no_rtl) + { + /* Note that first_reg_offset < MAX_ARC_PARM_REGS. */ + int first_reg_offset = first_anon_arg; + /* Size in words to "pretend" allocate. */ + int size = MAX_ARC_PARM_REGS - first_reg_offset; + /* Extra slop to keep stack eight byte aligned. */ + int align_slop = size & 1; + rtx regblock; + + regblock = gen_rtx_MEM (BLKmode, + plus_constant (arg_pointer_rtx, + FIRST_PARM_OFFSET (0) + + align_slop * UNITS_PER_WORD)); + set_mem_alias_set (regblock, get_varargs_alias_set ()); + set_mem_align (regblock, BITS_PER_WORD); + move_block_from_reg (first_reg_offset, regblock, + MAX_ARC_PARM_REGS - first_reg_offset); + + *pretend_size = ((MAX_ARC_PARM_REGS - first_reg_offset + align_slop) + * UNITS_PER_WORD); + } +} + +/* Cost functions. */ + +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. */ + +static bool +arc_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total, + bool speed ATTRIBUTE_UNUSED) +{ + switch (code) + { + /* Small integers are as cheap as registers. 4 byte values can + be fetched as immediate constants - let's give that the cost + of an extra insn. */ + case CONST_INT: + if (SMALL_INT (INTVAL (x))) + { + *total = 0; + return true; + } + /* FALLTHRU */ + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = COSTS_N_INSNS (1); + return true; + + case CONST_DOUBLE: + { + rtx high, low; + split_double (x, &high, &low); + *total = COSTS_N_INSNS (!SMALL_INT (INTVAL (high)) + + !SMALL_INT (INTVAL (low))); + return true; + } + + /* Encourage synth_mult to find a synthetic multiply when reasonable. + If we need more than 12 insns to do a multiply, then go out-of-line, + since the call overhead will be < 10% of the cost of the multiply. */ + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (TARGET_SHIFTER) + *total = COSTS_N_INSNS (1); + else if (GET_CODE (XEXP (x, 1)) != CONST_INT) + *total = COSTS_N_INSNS (16); + else + *total = COSTS_N_INSNS (INTVAL (XEXP ((x), 1))); + return false; + + default: + return false; + } +} + + +/* Provide the costs of an addressing mode that contains ADDR. + If ADDR is not a valid address, its cost is irrelevant. */ + +static int +arc_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (addr)) + { + case REG : + return 1; + + case LABEL_REF : + case SYMBOL_REF : + case CONST : + return 2; + + case PLUS : + { + register rtx plus0 = XEXP (addr, 0); + register rtx plus1 = XEXP (addr, 1); + + if (GET_CODE (plus0) != REG) + break; + + switch (GET_CODE (plus1)) + { + case CONST_INT : + return SMALL_INT (INTVAL (plus1)) ? 1 : 2; + case CONST : + case SYMBOL_REF : + case LABEL_REF : + return 2; + default: + break; + } + break; + } + default: + break; + } + + return 4; +} + +/* Function prologue/epilogue handlers. */ + +/* ARC stack frames look like: + + Before call After call + +-----------------------+ +-----------------------+ + | | | | + high | local variables, | | local variables, | + mem | reg save area, etc. | | reg save area, etc. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack. | | arguments on stack. | + | | | | + SP+16->+-----------------------+FP+48->+-----------------------+ + | 4 word save area for | | reg parm save area, | + | return addr, prev %fp | | only created for | + SP+0->+-----------------------+ | variable argument | + | functions | + FP+16->+-----------------------+ + | 4 word save area for | + | return addr, prev %fp | + FP+0->+-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | + | register save area | + | | + +-----------------------+ + | | + | alloca allocations | + | | + +-----------------------+ + | | + | arguments on stack | + | | + SP+16->+-----------------------+ + low | 4 word save area for | + memory | return addr, prev %fp | + SP+0->+-----------------------+ + +Notes: +1) The "reg parm save area" does not exist for non variable argument fns. + The "reg parm save area" can be eliminated completely if we created our + own va-arc.h, but that has tradeoffs as well (so it's not done). */ + +/* Structure to be filled in by arc_compute_frame_size with register + save masks, and offsets for the current function. */ +struct arc_frame_info +{ + unsigned int total_size; /* # bytes that the entire frame takes up. */ + unsigned int extra_size; /* # bytes of extra stuff. */ + unsigned int pretend_size; /* # bytes we push and pretend caller did. */ + unsigned int args_size; /* # bytes that outgoing arguments take up. */ + unsigned int reg_size; /* # bytes needed to store regs. */ + unsigned int var_size; /* # bytes that variables take up. */ + unsigned int reg_offset; /* Offset from new sp to store regs. */ + unsigned int gmask; /* Mask of saved gp registers. */ + int initialized; /* Nonzero if frame size already calculated. */ +}; + +/* Current frame information calculated by arc_compute_frame_size. */ +static struct arc_frame_info current_frame_info; + +/* Zero structure to initialize current_frame_info. */ +static struct arc_frame_info zero_frame_info; + +/* Type of function DECL. + + The result is cached. To reset the cache at the end of a function, + call with DECL = NULL_TREE. */ + +enum arc_function_type +arc_compute_function_type (tree decl) +{ + tree a; + /* Cached value. */ + static enum arc_function_type fn_type = ARC_FUNCTION_UNKNOWN; + /* Last function we were called for. */ + static tree last_fn = NULL_TREE; + + /* Resetting the cached value? */ + if (decl == NULL_TREE) + { + fn_type = ARC_FUNCTION_UNKNOWN; + last_fn = NULL_TREE; + return fn_type; + } + + if (decl == last_fn && fn_type != ARC_FUNCTION_UNKNOWN) + return fn_type; + + /* Assume we have a normal function (not an interrupt handler). */ + fn_type = ARC_FUNCTION_NORMAL; + + /* Now see if this is an interrupt handler. */ + for (a = DECL_ATTRIBUTES (current_function_decl); + a; + a = TREE_CHAIN (a)) + { + tree name = TREE_PURPOSE (a), args = TREE_VALUE (a); + + if (name == get_identifier ("__interrupt__") + && list_length (args) == 1 + && TREE_CODE (TREE_VALUE (args)) == STRING_CST) + { + tree value = TREE_VALUE (args); + + if (!strcmp (TREE_STRING_POINTER (value), "ilink1")) + fn_type = ARC_FUNCTION_ILINK1; + else if (!strcmp (TREE_STRING_POINTER (value), "ilink2")) + fn_type = ARC_FUNCTION_ILINK2; + else + gcc_unreachable (); + break; + } + } + + last_fn = decl; + return fn_type; +} + +#define ILINK1_REGNUM 29 +#define ILINK2_REGNUM 30 +#define RETURN_ADDR_REGNUM 31 +#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) +#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM)) + +/* Tell prologue and epilogue if register REGNO should be saved / restored. + The return address and frame pointer are treated separately. + Don't consider them here. */ +#define MUST_SAVE_REGISTER(regno, interrupt_p) \ +((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \ + && (df_regs_ever_live_p (regno) && (!call_used_regs[regno] || interrupt_p))) + +#define MUST_SAVE_RETURN_ADDR (df_regs_ever_live_p (RETURN_ADDR_REGNUM)) + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. + + SIZE is the size needed for local variables. */ + +unsigned int +arc_compute_frame_size (int size /* # of var. bytes allocated. */) +{ + int regno; + unsigned int total_size, var_size, args_size, pretend_size, extra_size; + unsigned int reg_size, reg_offset; + unsigned int gmask; + enum arc_function_type fn_type; + int interrupt_p; + + var_size = size; + args_size = crtl->outgoing_args_size; + pretend_size = crtl->args.pretend_args_size; + extra_size = FIRST_PARM_OFFSET (0); + total_size = extra_size + pretend_size + args_size + var_size; + reg_offset = FIRST_PARM_OFFSET(0) + crtl->outgoing_args_size; + reg_size = 0; + gmask = 0; + + /* See if this is an interrupt handler. Call used registers must be saved + for them too. */ + fn_type = arc_compute_function_type (current_function_decl); + interrupt_p = ARC_INTERRUPT_P (fn_type); + + /* Calculate space needed for registers. + ??? We ignore the extension registers for now. */ + + for (regno = 0; regno <= 31; regno++) + { + if (MUST_SAVE_REGISTER (regno, interrupt_p)) + { + reg_size += UNITS_PER_WORD; + gmask |= 1 << regno; + } + } + + total_size += reg_size; + + /* If the only space to allocate is the fp/blink save area this is an + empty frame. However, if we'll be making a function call we need to + allocate a stack frame for our callee's fp/blink save area. */ + if (total_size == extra_size + && !MUST_SAVE_RETURN_ADDR) + total_size = extra_size = 0; + + total_size = ARC_STACK_ALIGN (total_size); + + /* Save computed information. */ + current_frame_info.total_size = total_size; + current_frame_info.extra_size = extra_size; + current_frame_info.pretend_size = pretend_size; + current_frame_info.var_size = var_size; + current_frame_info.args_size = args_size; + current_frame_info.reg_size = reg_size; + current_frame_info.reg_offset = reg_offset; + current_frame_info.gmask = gmask; + current_frame_info.initialized = reload_completed; + + /* Ok, we're done. */ + return total_size; +} + +/* Common code to save/restore registers. */ + +void +arc_save_restore (FILE *file, + const char *base_reg, + unsigned int offset, + unsigned int gmask, + const char *op) +{ + int regno; + + if (gmask == 0) + return; + + for (regno = 0; regno <= 31; regno++) + { + if ((gmask & (1L << regno)) != 0) + { + fprintf (file, "\t%s %s,[%s,%d]\n", + op, reg_names[regno], base_reg, offset); + offset += UNITS_PER_WORD; + } + } +} + +/* Target hook to assemble an integer object. The ARC version needs to + emit a special directive for references to labels and function + symbols. */ + +static bool +arc_assemble_integer (rtx x, unsigned int size, int aligned_p) +{ + if (size == UNITS_PER_WORD && aligned_p + && ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x)) + || GET_CODE (x) == LABEL_REF)) + { + fputs ("\t.word\t%st(", asm_out_file); + output_addr_const (asm_out_file, x); + fputs (")\n", asm_out_file); + return true; + } + return default_assemble_integer (x, size, aligned_p); +} + +/* Set up the stack and frame pointer (if desired) for the function. */ + +static void +arc_output_function_prologue (FILE *file, HOST_WIDE_INT size) +{ + const char *sp_str = reg_names[STACK_POINTER_REGNUM]; + const char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + unsigned int gmask = current_frame_info.gmask; + enum arc_function_type fn_type = arc_compute_function_type (current_function_decl); + + /* If this is an interrupt handler, set up our stack frame. + ??? Optimize later. */ + if (ARC_INTERRUPT_P (fn_type)) + { + fprintf (file, "\t%s interrupt handler\n", + ASM_COMMENT_START); + fprintf (file, "\tsub %s,%s,16\n", sp_str, sp_str); + } + + /* This is only for the human reader. */ + fprintf (file, "\t%s BEGIN PROLOGUE %s vars= %d, regs= %d, args= %d, extra= %d\n", + ASM_COMMENT_START, ASM_COMMENT_START, + current_frame_info.var_size, + current_frame_info.reg_size / 4, + current_frame_info.args_size, + current_frame_info.extra_size); + + size = ARC_STACK_ALIGN (size); + size = (! current_frame_info.initialized + ? arc_compute_frame_size (size) + : current_frame_info.total_size); + + /* These cases shouldn't happen. Catch them now. */ + gcc_assert (size || !gmask); + + /* Allocate space for register arguments if this is a variadic function. */ + if (current_frame_info.pretend_size != 0) + fprintf (file, "\tsub %s,%s,%d\n", + sp_str, sp_str, current_frame_info.pretend_size); + + /* The home-grown ABI says link register is saved first. */ + if (MUST_SAVE_RETURN_ADDR) + fprintf (file, "\tst %s,[%s,%d]\n", + reg_names[RETURN_ADDR_REGNUM], sp_str, UNITS_PER_WORD); + + /* Set up the previous frame pointer next (if we need to). */ + if (frame_pointer_needed) + { + fprintf (file, "\tst %s,[%s]\n", fp_str, sp_str); + fprintf (file, "\tmov %s,%s\n", fp_str, sp_str); + } + + /* ??? We don't handle the case where the saved regs are more than 252 + bytes away from sp. This can be handled by decrementing sp once, saving + the regs, and then decrementing it again. The epilogue doesn't have this + problem as the `ld' insn takes reg+limm values (though it would be more + efficient to avoid reg+limm). */ + + /* Allocate the stack frame. */ + if (size - current_frame_info.pretend_size > 0) + fprintf (file, "\tsub %s,%s," HOST_WIDE_INT_PRINT_DEC "\n", + sp_str, sp_str, size - current_frame_info.pretend_size); + + /* Save any needed call-saved regs (and call-used if this is an + interrupt handler). */ + arc_save_restore (file, sp_str, current_frame_info.reg_offset, + /* The zeroing of these two bits is unnecessary, + but leave this in for clarity. */ + gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + "st"); + + fprintf (file, "\t%s END PROLOGUE\n", ASM_COMMENT_START); +} + +/* Do any necessary cleanup after a function to restore stack, frame, + and regs. */ + +static void +arc_output_function_epilogue (FILE *file, HOST_WIDE_INT size) +{ + rtx epilogue_delay = crtl->epilogue_delay_list; + int noepilogue = FALSE; + enum arc_function_type fn_type = arc_compute_function_type (current_function_decl); + + /* This is only for the human reader. */ + fprintf (file, "\t%s EPILOGUE\n", ASM_COMMENT_START); + + size = ARC_STACK_ALIGN (size); + size = (!current_frame_info.initialized + ? arc_compute_frame_size (size) + : current_frame_info.total_size); + + if (size == 0 && epilogue_delay == 0) + { + rtx insn = get_last_insn (); + + /* If the last insn was a BARRIER, we don't have to write any code + because a jump (aka return) was put there. */ + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + if (insn && GET_CODE (insn) == BARRIER) + noepilogue = TRUE; + } + + if (!noepilogue) + { + unsigned int pretend_size = current_frame_info.pretend_size; + unsigned int frame_size = size - pretend_size; + int restored, fp_restored_p; + int can_trust_sp_p = !cfun->calls_alloca; + const char *sp_str = reg_names[STACK_POINTER_REGNUM]; + const char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + + /* ??? There are lots of optimizations that can be done here. + EG: Use fp to restore regs if it's closer. + Maybe in time we'll do them all. For now, always restore regs from + sp, but don't restore sp if we don't have to. */ + + if (!can_trust_sp_p) + { + gcc_assert (frame_pointer_needed); + fprintf (file,"\tsub %s,%s,%d\t\t%s sp not trusted here\n", + sp_str, fp_str, frame_size, ASM_COMMENT_START); + } + + /* Restore any saved registers. */ + arc_save_restore (file, sp_str, current_frame_info.reg_offset, + /* The zeroing of these two bits is unnecessary, + but leave this in for clarity. */ + current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + "ld"); + + if (MUST_SAVE_RETURN_ADDR) + fprintf (file, "\tld %s,[%s,%d]\n", + reg_names[RETURN_ADDR_REGNUM], + frame_pointer_needed ? fp_str : sp_str, + UNITS_PER_WORD + (frame_pointer_needed ? 0 : frame_size)); + + /* Keep track of how much of the stack pointer we've restored. + It makes the following a lot more readable. */ + restored = 0; + fp_restored_p = 0; + + /* We try to emit the epilogue delay slot insn right after the load + of the return address register so that it can execute with the + stack intact. Secondly, loads are delayed. */ + /* ??? If stack intactness is important, always emit now. */ + if (MUST_SAVE_RETURN_ADDR && epilogue_delay != NULL_RTX) + { + final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL); + epilogue_delay = NULL_RTX; + } + + if (frame_pointer_needed) + { + /* Try to restore the frame pointer in the delay slot. We can't, + however, if any of these is true. */ + if (epilogue_delay != NULL_RTX + || !SMALL_INT (frame_size) + || pretend_size + || ARC_INTERRUPT_P (fn_type)) + { + /* Note that we restore fp and sp here! */ + fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size); + restored += frame_size; + fp_restored_p = 1; + } + } + else if (!SMALL_INT (size /* frame_size + pretend_size */) + || ARC_INTERRUPT_P (fn_type)) + { + fprintf (file, "\tadd %s,%s,%d\n", sp_str, sp_str, frame_size); + restored += frame_size; + } + + /* These must be done before the return insn because the delay slot + does the final stack restore. */ + if (ARC_INTERRUPT_P (fn_type)) + { + if (epilogue_delay) + { + final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL); + } + } + + /* Emit the return instruction. */ + { + static const int regs[4] = { + 0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM + }; + + /* Update the flags, if returning from an interrupt handler. */ + if (ARC_INTERRUPT_P (fn_type)) + fprintf (file, "\tj.d.f %s\n", reg_names[regs[fn_type]]); + else + fprintf (file, "\tj.d %s\n", reg_names[regs[fn_type]]); + } + + /* If the only register saved is the return address, we need a + nop, unless we have an instruction to put into it. Otherwise + we don't since reloading multiple registers doesn't reference + the register being loaded. */ + + if (ARC_INTERRUPT_P (fn_type)) + fprintf (file, "\tadd %s,%s,16\n", sp_str, sp_str); + else if (epilogue_delay != NULL_RTX) + { + gcc_assert (!frame_pointer_needed || fp_restored_p); + gcc_assert (restored >= size); + final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL); + } + else if (frame_pointer_needed && !fp_restored_p) + { + gcc_assert (SMALL_INT (frame_size)); + /* Note that we restore fp and sp here! */ + fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size); + } + else if (restored < size) + { + gcc_assert (SMALL_INT (size - restored)); + fprintf (file, "\tadd %s,%s," HOST_WIDE_INT_PRINT_DEC "\n", + sp_str, sp_str, size - restored); + } + else + fprintf (file, "\tnop\n"); + } + + /* Reset state info for each function. */ + current_frame_info = zero_frame_info; + arc_compute_function_type (NULL_TREE); +} + +/* Define the number of delay slots needed for the function epilogue. + + Interrupt handlers can't have any epilogue delay slots (it's always needed + for something else, I think). For normal functions, we have to worry about + using call-saved regs as they'll be restored before the delay slot insn. + Functions with non-empty frames already have enough choices for the epilogue + delay slot so for now we only consider functions with empty frames. */ + +int +arc_delay_slots_for_epilogue (void) +{ + if (arc_compute_function_type (current_function_decl) != ARC_FUNCTION_NORMAL) + return 0; + if (!current_frame_info.initialized) + (void) arc_compute_frame_size (get_frame_size ()); + if (current_frame_info.total_size == 0) + return 1; + return 0; +} + +/* Return true if TRIAL is a valid insn for the epilogue delay slot. + Any single length instruction which doesn't reference the stack or frame + pointer or any call-saved register is OK. SLOT will always be 0. */ + +int +arc_eligible_for_epilogue_delay (rtx trial, int slot) +{ + gcc_assert (!slot); + + if (get_attr_length (trial) == 1 + /* If registers where saved, presumably there's more than enough + possibilities for the delay slot. The alternative is something + more complicated (of course, if we expanded the epilogue as rtl + this problem would go away). */ + /* ??? Note that this will always be true since only functions with + empty frames have epilogue delay slots. See + arc_delay_slots_for_epilogue. */ + && current_frame_info.gmask == 0 + && ! reg_mentioned_p (stack_pointer_rtx, PATTERN (trial)) + && ! reg_mentioned_p (frame_pointer_rtx, PATTERN (trial))) + return 1; + return 0; +} + +/* Return true if OP is a shift operator. */ + +int +shift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) +{ + switch (GET_CODE (op)) + { + case ASHIFTRT: + case LSHIFTRT: + case ASHIFT: + return 1; + default: + return 0; + } +} + +/* Output the assembler code for doing a shift. + We go to a bit of trouble to generate efficient code as the ARC only has + single bit shifts. This is taken from the h8300 port. We only have one + mode of shifting and can't access individual bytes like the h8300 can, so + this is greatly simplified (at the expense of not generating hyper- + efficient code). + + This function is not used if the variable shift insns are present. */ + +/* ??? We assume the output operand is the same as operand 1. + This can be optimized (deleted) in the case of 1 bit shifts. */ +/* ??? We use the loop register here. We don't use it elsewhere (yet) and + using it here will give us a chance to play with it. */ + +const char * +output_shift (rtx *operands) +{ + rtx shift = operands[3]; + enum machine_mode mode = GET_MODE (shift); + enum rtx_code code = GET_CODE (shift); + const char *shift_one; + + gcc_assert (mode == SImode); + + switch (code) + { + case ASHIFT: shift_one = "asl %0,%0"; break; + case ASHIFTRT: shift_one = "asr %0,%0"; break; + case LSHIFTRT: shift_one = "lsr %0,%0"; break; + default: gcc_unreachable (); + } + + if (GET_CODE (operands[2]) != CONST_INT) + { + if (optimize) + { + output_asm_insn ("sub.f 0,%2,0", operands); + output_asm_insn ("mov lp_count,%2", operands); + output_asm_insn ("bz 2f", operands); + } + else + output_asm_insn ("mov %4,%2", operands); + goto shiftloop; + } + else + { + int n; + + /* If the count is negative, make it 0. */ + n = INTVAL (operands[2]); + if (n < 0) + n = 0; + /* If the count is too big, truncate it. + ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to + do the intuitive thing. */ + else if (n > GET_MODE_BITSIZE (mode)) + n = GET_MODE_BITSIZE (mode); + + /* First see if we can do them inline. */ + if (n <= 8) + { + while (--n >= 0) + output_asm_insn (shift_one, operands); + } + /* See if we can use a rotate/and. */ + else if (n == BITS_PER_WORD - 1) + { + switch (code) + { + case ASHIFT : + output_asm_insn ("and %0,%0,1\n\tror %0,%0", operands); + break; + case ASHIFTRT : + /* The ARC doesn't have a rol insn. Use something else. */ + output_asm_insn ("asl.f 0,%0\n\tsbc %0,0,0", operands); + break; + case LSHIFTRT : + /* The ARC doesn't have a rol insn. Use something else. */ + output_asm_insn ("asl.f 0,%0\n\tadc %0,0,0", operands); + break; + default: + break; + } + } + /* Must loop. */ + else + { + char buf[100]; + + if (optimize) + output_asm_insn ("mov lp_count,%c2", operands); + else + output_asm_insn ("mov %4,%c2", operands); + shiftloop: + if (optimize) + { + if (flag_pic) + sprintf (buf, "lr %%4,[status]\n\tadd %%4,%%4,6\t%s single insn loop start", + ASM_COMMENT_START); + else + sprintf (buf, "mov %%4,%%%%st(1f)\t%s (single insn loop start) >> 2", + ASM_COMMENT_START); + output_asm_insn (buf, operands); + output_asm_insn ("sr %4,[lp_start]", operands); + output_asm_insn ("add %4,%4,1", operands); + output_asm_insn ("sr %4,[lp_end]", operands); + output_asm_insn ("nop\n\tnop", operands); + if (flag_pic) + fprintf (asm_out_file, "\t%s single insn loop\n", + ASM_COMMENT_START); + else + fprintf (asm_out_file, "1:\t%s single insn loop\n", + ASM_COMMENT_START); + output_asm_insn (shift_one, operands); + fprintf (asm_out_file, "2:\t%s end single insn loop\n", + ASM_COMMENT_START); + } + else + { + fprintf (asm_out_file, "1:\t%s begin shift loop\n", + ASM_COMMENT_START); + output_asm_insn ("sub.f %4,%4,1", operands); + output_asm_insn ("nop", operands); + output_asm_insn ("bn.nd 2f", operands); + output_asm_insn (shift_one, operands); + output_asm_insn ("b.nd 1b", operands); + fprintf (asm_out_file, "2:\t%s end shift loop\n", + ASM_COMMENT_START); + } + } + } + + return ""; +} + +/* Nested function support. */ + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. */ + +void +arc_initialize_trampoline (rtx tramp ATTRIBUTE_UNUSED, + rtx fnaddr ATTRIBUTE_UNUSED, + rtx cxt ATTRIBUTE_UNUSED) +{ +} + +/* Set the cpu type and print out other fancy things, + at the top of the file. */ + +static void +arc_file_start (void) +{ + default_file_start (); + fprintf (asm_out_file, "\t.cpu %s\n", arc_cpu_string); +} + +/* Print operand X (an rtx) in assembler syntax to file FILE. + CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. + For `%' followed by punctuation, CODE is the punctuation and X is null. */ + +void +arc_print_operand (FILE *file, rtx x, int code) +{ + switch (code) + { + case '#' : + /* Conditional branches. For now these are equivalent. */ + case '*' : + /* Unconditional branches. Output the appropriate delay slot suffix. */ + if (!final_sequence || XVECLEN (final_sequence, 0) == 1) + { + /* There's nothing in the delay slot. */ + fputs (".nd", file); + } + else + { + rtx jump = XVECEXP (final_sequence, 0, 0); + rtx delay = XVECEXP (final_sequence, 0, 1); + if (INSN_ANNULLED_BRANCH_P (jump)) + fputs (INSN_FROM_TARGET_P (delay) ? ".jd" : ".nd", file); + else + fputs (".d", file); + } + return; + case '?' : /* with leading "." */ + case '!' : /* without leading "." */ + /* This insn can be conditionally executed. See if the ccfsm machinery + says it should be conditionalized. */ + if (arc_ccfsm_state == 3 || arc_ccfsm_state == 4) + { + /* Is this insn in a delay slot? */ + if (final_sequence && XVECLEN (final_sequence, 0) == 2) + { + rtx insn = XVECEXP (final_sequence, 0, 1); + + /* If the insn is annulled and is from the target path, we need + to inverse the condition test. */ + if (INSN_ANNULLED_BRANCH_P (insn)) + { + if (INSN_FROM_TARGET_P (insn)) + fprintf (file, "%s%s", + code == '?' ? "." : "", + arc_condition_codes[ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc)]); + else + fprintf (file, "%s%s", + code == '?' ? "." : "", + arc_condition_codes[arc_ccfsm_current_cc]); + } + else + { + /* This insn is executed for either path, so don't + conditionalize it at all. */ + ; /* nothing to do */ + } + } + else + { + /* This insn isn't in a delay slot. */ + fprintf (file, "%s%s", + code == '?' ? "." : "", + arc_condition_codes[arc_ccfsm_current_cc]); + } + } + return; + case '~' : + /* Output a nop if we're between a set of the condition codes, + and a conditional branch. */ + if (last_insn_set_cc_p) + fputs ("nop\n\t", file); + return; + case 'd' : + fputs (arc_condition_codes[get_arc_condition_code (x)], file); + return; + case 'D' : + fputs (arc_condition_codes[ARC_INVERSE_CONDITION_CODE + (get_arc_condition_code (x))], + file); + return; + case 'R' : + /* Write second word of DImode or DFmode reference, + register or memory. */ + if (GET_CODE (x) == REG) + fputs (reg_names[REGNO (x)+1], file); + else if (GET_CODE (x) == MEM) + { + fputc ('[', file); + /* Handle possible auto-increment. Since it is pre-increment and + we have already done it, we can just use an offset of four. */ + /* ??? This is taken from rs6000.c I think. I don't think it is + currently necessary, but keep it around. */ + if (GET_CODE (XEXP (x, 0)) == PRE_INC + || GET_CODE (XEXP (x, 0)) == PRE_DEC) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4)); + else + output_address (plus_constant (XEXP (x, 0), 4)); + fputc (']', file); + } + else + output_operand_lossage ("invalid operand to %%R code"); + return; + case 'S' : + if ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x)) + || GET_CODE (x) == LABEL_REF) + { + fprintf (file, "%%st("); + output_addr_const (file, x); + fprintf (file, ")"); + return; + } + break; + case 'H' : + case 'L' : + if (GET_CODE (x) == REG) + { + /* L = least significant word, H = most significant word */ + if ((TARGET_BIG_ENDIAN != 0) ^ (code == 'L')) + fputs (reg_names[REGNO (x)], file); + else + fputs (reg_names[REGNO (x)+1], file); + } + else if (GET_CODE (x) == CONST_INT + || GET_CODE (x) == CONST_DOUBLE) + { + rtx first, second; + + split_double (x, &first, &second); + fprintf (file, "0x%08lx", + (long)(code == 'L' ? INTVAL (first) : INTVAL (second))); + } + else + output_operand_lossage ("invalid operand to %%H/%%L code"); + return; + case 'A' : + { + char str[30]; + + gcc_assert (GET_CODE (x) == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT); + + real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), sizeof (str), 0, 1); + fprintf (file, "%s", str); + return; + } + case 'U' : + /* Output a load/store with update indicator if appropriate. */ + if (GET_CODE (x) == MEM) + { + if (GET_CODE (XEXP (x, 0)) == PRE_INC + || GET_CODE (XEXP (x, 0)) == PRE_DEC) + fputs (".a", file); + } + else + output_operand_lossage ("invalid operand to %%U code"); + return; + case 'V' : + /* Output cache bypass indicator for a load/store insn. Volatile memory + refs are defined to use the cache bypass mechanism. */ + if (GET_CODE (x) == MEM) + { + if (MEM_VOLATILE_P (x)) + fputs (".di", file); + } + else + output_operand_lossage ("invalid operand to %%V code"); + return; + case 0 : + /* Do nothing special. */ + break; + default : + /* Unknown flag. */ + output_operand_lossage ("invalid operand output code"); + } + + switch (GET_CODE (x)) + { + case REG : + fputs (reg_names[REGNO (x)], file); + break; + case MEM : + fputc ('[', file); + if (GET_CODE (XEXP (x, 0)) == PRE_INC) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), + GET_MODE_SIZE (GET_MODE (x)))); + else if (GET_CODE (XEXP (x, 0)) == PRE_DEC) + output_address (plus_constant (XEXP (XEXP (x, 0), 0), + - GET_MODE_SIZE (GET_MODE (x)))); + else + output_address (XEXP (x, 0)); + fputc (']', file); + break; + case CONST_DOUBLE : + /* We handle SFmode constants here as output_addr_const doesn't. */ + if (GET_MODE (x) == SFmode) + { + REAL_VALUE_TYPE d; + long l; + + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + REAL_VALUE_TO_TARGET_SINGLE (d, l); + fprintf (file, "0x%08lx", l); + break; + } + /* Fall through. Let output_addr_const deal with it. */ + default : + output_addr_const (file, x); + break; + } +} + +/* Print a memory address as an operand to reference that memory location. */ + +void +arc_print_operand_address (FILE *file, rtx addr) +{ + register rtx base, index = 0; + int offset = 0; + + switch (GET_CODE (addr)) + { + case REG : + fputs (reg_names[REGNO (addr)], file); + break; + case SYMBOL_REF : + if (/*???*/ 0 && SYMBOL_REF_FUNCTION_P (addr)) + { + fprintf (file, "%%st("); + output_addr_const (file, addr); + fprintf (file, ")"); + } + else + output_addr_const (file, addr); + break; + case PLUS : + if (GET_CODE (XEXP (addr, 0)) == CONST_INT) + offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1); + else if (GET_CODE (XEXP (addr, 1)) == CONST_INT) + offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0); + else + base = XEXP (addr, 0), index = XEXP (addr, 1); + gcc_assert (GET_CODE (base) == REG); + fputs (reg_names[REGNO (base)], file); + if (index == 0) + { + if (offset != 0) + fprintf (file, ",%d", offset); + } + else + { + switch (GET_CODE (index)) + { + case REG: + fprintf (file, ",%s", reg_names[REGNO (index)]); + break; + case SYMBOL_REF: + fputc (',', file), output_addr_const (file, index); + break; + default: + gcc_unreachable (); + } + } + break; + case PRE_INC : + case PRE_DEC : + /* We shouldn't get here as we've lost the mode of the memory object + (which says how much to inc/dec by. */ + gcc_unreachable (); + break; + default : + output_addr_const (file, addr); + break; + } +} + +/* Update compare/branch separation marker. */ + +static void +record_cc_ref (rtx insn) +{ + last_insn_set_cc_p = current_insn_set_cc_p; + + switch (get_attr_cond (insn)) + { + case COND_SET : + case COND_SET_ZN : + case COND_SET_ZNC : + if (get_attr_length (insn) == 1) + current_insn_set_cc_p = 1; + else + current_insn_set_cc_p = 0; + break; + default : + current_insn_set_cc_p = 0; + break; + } +} + +/* Conditional execution support. + + This is based on the ARM port but for now is much simpler. + + A finite state machine takes care of noticing whether or not instructions + can be conditionally executed, and thus decrease execution time and code + size by deleting branch instructions. The fsm is controlled by + final_prescan_insn, and controls the actions of PRINT_OPERAND. The patterns + in the .md file for the branch insns also have a hand in this. */ + +/* The state of the fsm controlling condition codes are: + 0: normal, do nothing special + 1: don't output this insn + 2: don't output this insn + 3: make insns conditional + 4: make insns conditional + + State transitions (state->state by whom, under what condition): + 0 -> 1 final_prescan_insn, if insn is conditional branch + 0 -> 2 final_prescan_insn, if the `target' is an unconditional branch + 1 -> 3 branch patterns, after having not output the conditional branch + 2 -> 4 branch patterns, after having not output the conditional branch + 3 -> 0 (*targetm.asm_out.internal_label), if the `target' label is reached + (the target label has CODE_LABEL_NUMBER equal to + arc_ccfsm_target_label). + 4 -> 0 final_prescan_insn, if `target' unconditional branch is reached + + If the jump clobbers the conditions then we use states 2 and 4. + + A similar thing can be done with conditional return insns. + + We also handle separating branches from sets of the condition code. + This is done here because knowledge of the ccfsm state is required, + we may not be outputting the branch. */ + +void +arc_final_prescan_insn (rtx insn, + rtx *opvec ATTRIBUTE_UNUSED, + int noperands ATTRIBUTE_UNUSED) +{ + /* BODY will hold the body of INSN. */ + register rtx body = PATTERN (insn); + + /* This will be 1 if trying to repeat the trick (i.e.: do the `else' part of + an if/then/else), and things need to be reversed. */ + int reverse = 0; + + /* If we start with a return insn, we only succeed if we find another one. */ + int seeking_return = 0; + + /* START_INSN will hold the insn from where we start looking. This is the + first insn after the following code_label if REVERSE is true. */ + rtx start_insn = insn; + + /* Update compare/branch separation marker. */ + record_cc_ref (insn); + + /* Allow -mdebug-ccfsm to turn this off so we can see how well it does. + We can't do this in macro FINAL_PRESCAN_INSN because its called from + final_scan_insn which has `optimize' as a local. */ + if (optimize < 2 || TARGET_NO_COND_EXEC) + return; + + /* If in state 4, check if the target branch is reached, in order to + change back to state 0. */ + if (arc_ccfsm_state == 4) + { + if (insn == arc_ccfsm_target_insn) + { + arc_ccfsm_target_insn = NULL; + arc_ccfsm_state = 0; + } + return; + } + + /* If in state 3, it is possible to repeat the trick, if this insn is an + unconditional branch to a label, and immediately following this branch + is the previous target label which is only used once, and the label this + branch jumps to is not too far off. Or in other words "we've done the + `then' part, see if we can do the `else' part." */ + if (arc_ccfsm_state == 3) + { + if (simplejump_p (insn)) + { + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == BARRIER) + { + /* ??? Isn't this always a barrier? */ + start_insn = next_nonnote_insn (start_insn); + } + if (GET_CODE (start_insn) == CODE_LABEL + && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label + && LABEL_NUSES (start_insn) == 1) + reverse = TRUE; + else + return; + } + else if (GET_CODE (body) == RETURN) + { + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == BARRIER) + start_insn = next_nonnote_insn (start_insn); + if (GET_CODE (start_insn) == CODE_LABEL + && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label + && LABEL_NUSES (start_insn) == 1) + { + reverse = TRUE; + seeking_return = 1; + } + else + return; + } + else + return; + } + + if (GET_CODE (insn) != JUMP_INSN) + return; + + /* This jump might be paralleled with a clobber of the condition codes, + the jump should always come first. */ + if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0) + body = XVECEXP (body, 0, 0); + + if (reverse + || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC + && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE)) + { + int insns_skipped = 0, fail = FALSE, succeed = FALSE; + /* Flag which part of the IF_THEN_ELSE is the LABEL_REF. */ + int then_not_else = TRUE; + /* Nonzero if next insn must be the target label. */ + int next_must_be_target_label_p; + rtx this_insn = start_insn, label = 0; + + /* Register the insn jumped to. */ + if (reverse) + { + if (!seeking_return) + label = XEXP (SET_SRC (body), 0); + } + else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF) + label = XEXP (XEXP (SET_SRC (body), 1), 0); + else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF) + { + label = XEXP (XEXP (SET_SRC (body), 2), 0); + then_not_else = FALSE; + } + else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN) + seeking_return = 1; + else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN) + { + seeking_return = 1; + then_not_else = FALSE; + } + else + gcc_unreachable (); + + /* See how many insns this branch skips, and what kind of insns. If all + insns are okay, and the label or unconditional branch to the same + label is not too far away, succeed. */ + for (insns_skipped = 0, next_must_be_target_label_p = FALSE; + !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED; + insns_skipped++) + { + rtx scanbody; + + this_insn = next_nonnote_insn (this_insn); + if (!this_insn) + break; + + if (next_must_be_target_label_p) + { + if (GET_CODE (this_insn) == BARRIER) + continue; + if (GET_CODE (this_insn) == CODE_LABEL + && this_insn == label) + { + arc_ccfsm_state = 1; + succeed = TRUE; + } + else + fail = TRUE; + break; + } + + scanbody = PATTERN (this_insn); + + switch (GET_CODE (this_insn)) + { + case CODE_LABEL: + /* Succeed if it is the target label, otherwise fail since + control falls in from somewhere else. */ + if (this_insn == label) + { + arc_ccfsm_state = 1; + succeed = TRUE; + } + else + fail = TRUE; + break; + + case BARRIER: + /* Succeed if the following insn is the target label. + Otherwise fail. + If return insns are used then the last insn in a function + will be a barrier. */ + next_must_be_target_label_p = TRUE; + break; + + case CALL_INSN: + /* Can handle a call insn if there are no insns after it. + IE: The next "insn" is the target label. We don't have to + worry about delay slots as such insns are SEQUENCE's inside + INSN's. ??? It is possible to handle such insns though. */ + if (get_attr_cond (this_insn) == COND_CANUSE) + next_must_be_target_label_p = TRUE; + else + fail = TRUE; + break; + + case JUMP_INSN: + /* If this is an unconditional branch to the same label, succeed. + If it is to another label, do nothing. If it is conditional, + fail. */ + /* ??? Probably, the test for the SET and the PC are unnecessary. */ + + if (GET_CODE (scanbody) == SET + && GET_CODE (SET_DEST (scanbody)) == PC) + { + if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF + && XEXP (SET_SRC (scanbody), 0) == label && !reverse) + { + arc_ccfsm_state = 2; + succeed = TRUE; + } + else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE) + fail = TRUE; + } + else if (GET_CODE (scanbody) == RETURN + && seeking_return) + { + arc_ccfsm_state = 2; + succeed = TRUE; + } + else if (GET_CODE (scanbody) == PARALLEL) + { + if (get_attr_cond (this_insn) != COND_CANUSE) + fail = TRUE; + } + break; + + case INSN: + /* We can only do this with insns that can use the condition + codes (and don't set them). */ + if (GET_CODE (scanbody) == SET + || GET_CODE (scanbody) == PARALLEL) + { + if (get_attr_cond (this_insn) != COND_CANUSE) + fail = TRUE; + } + /* We can't handle other insns like sequences. */ + else + fail = TRUE; + break; + + default: + break; + } + } + + if (succeed) + { + if ((!seeking_return) && (arc_ccfsm_state == 1 || reverse)) + arc_ccfsm_target_label = CODE_LABEL_NUMBER (label); + else + { + gcc_assert (seeking_return || arc_ccfsm_state == 2); + while (this_insn && GET_CODE (PATTERN (this_insn)) == USE) + { + this_insn = next_nonnote_insn (this_insn); + gcc_assert (!this_insn + || (GET_CODE (this_insn) != BARRIER + && GET_CODE (this_insn) != CODE_LABEL)); + } + if (!this_insn) + { + /* Oh dear! we ran off the end, give up. */ + extract_insn_cached (insn); + arc_ccfsm_state = 0; + arc_ccfsm_target_insn = NULL; + return; + } + arc_ccfsm_target_insn = this_insn; + } + + /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from + what it was. */ + if (!reverse) + arc_ccfsm_current_cc = get_arc_condition_code (XEXP (SET_SRC (body), + 0)); + + if (reverse || then_not_else) + arc_ccfsm_current_cc = ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc); + } + + /* Restore recog_data. Getting the attributes of other insns can + destroy this array, but final.c assumes that it remains intact + across this call. */ + extract_insn_cached (insn); + } +} + +/* Record that we are currently outputting label NUM with prefix PREFIX. + It it's the label we're looking for, reset the ccfsm machinery. + + Called from (*targetm.asm_out.internal_label). */ + +void +arc_ccfsm_at_label (const char *prefix, int num) +{ + if (arc_ccfsm_state == 3 && arc_ccfsm_target_label == num + && !strcmp (prefix, "L")) + { + arc_ccfsm_state = 0; + arc_ccfsm_target_insn = NULL_RTX; + } +} + +/* See if the current insn, which is a conditional branch, is to be + deleted. */ + +int +arc_ccfsm_branch_deleted_p (void) +{ + if (arc_ccfsm_state == 1 || arc_ccfsm_state == 2) + return 1; + return 0; +} + +/* Record a branch isn't output because subsequent insns can be + conditionalized. */ + +void +arc_ccfsm_record_branch_deleted (void) +{ + /* Indicate we're conditionalizing insns now. */ + arc_ccfsm_state += 2; + + /* If the next insn is a subroutine call, we still need a nop between the + cc setter and user. We need to undo the effect of calling record_cc_ref + for the just deleted branch. */ + current_insn_set_cc_p = last_insn_set_cc_p; +} + +static void +arc_va_start (tree valist, rtx nextarg) +{ + /* See arc_setup_incoming_varargs for reasons for this oddity. */ + if (crtl->args.info < 8 + && (crtl->args.info & 1)) + nextarg = plus_constant (nextarg, UNITS_PER_WORD); + + std_expand_builtin_va_start (valist, nextarg); +} + +/* This is how to output a definition of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +static void +arc_internal_label (FILE *stream, const char *prefix, unsigned long labelno) +{ + arc_ccfsm_at_label (prefix, labelno); + default_internal_label (stream, prefix, labelno); +} + +/* Worker function for TARGET_ASM_EXTERNAL_LIBCALL. */ + +static void +arc_external_libcall (rtx fun ATTRIBUTE_UNUSED) +{ +#if 0 +/* On the ARC we want to have libgcc's for multiple cpus in one binary. + We can't use `assemble_name' here as that will call ASM_OUTPUT_LABELREF + and we'll get another suffix added on if -mmangle-cpu. */ + if (TARGET_MANGLE_CPU_LIBGCC) + { + fprintf (FILE, "\t.rename\t_%s, _%s%s\n", + XSTR (SYMREF, 0), XSTR (SYMREF, 0), + arc_mangle_suffix); + } +#endif +} + +/* Worker function for TARGET_RETURN_IN_MEMORY. */ + +static bool +arc_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +{ + if (AGGREGATE_TYPE_P (type)) + return true; + else + { + HOST_WIDE_INT size = int_size_in_bytes (type); + return (size == -1 || size > 8); + } +} + +/* For ARC, All aggregates and arguments greater than 8 bytes are + passed by reference. */ + +static bool +arc_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + unsigned HOST_WIDE_INT size; + + if (type) + { + if (AGGREGATE_TYPE_P (type)) + return true; + size = int_size_in_bytes (type); + } + else + size = GET_MODE_SIZE (mode); + + return size > 8; +} + +/* Round SIZE up to a word boundary. */ +#define ROUND_ADVANCE(SIZE) \ +(((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Round arg MODE/TYPE up to the next word boundary. */ +#define ROUND_ADVANCE_ARG(MODE, TYPE) \ +((MODE) == BLKmode \ + ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) \ + : ROUND_ADVANCE (GET_MODE_SIZE (MODE))) + +/* Round CUM up to the necessary point for argument MODE/TYPE. */ +#define ROUND_ADVANCE_CUM(CUM, MODE, TYPE) \ +((((MODE) == BLKmode ? TYPE_ALIGN (TYPE) : GET_MODE_BITSIZE (MODE)) \ + > BITS_PER_WORD) \ + ? (((CUM) + 1) & ~1) \ + : (CUM)) + +/* Return boolean indicating arg of type TYPE and mode MODE will be passed in + a reg. This includes arguments that have to be passed by reference as the + pointer to them is passed in a reg if one is available (and that is what + we're given). */ +#define PASS_IN_REG_P(CUM, MODE, TYPE) \ +((CUM) < MAX_ARC_PARM_REGS \ + && ((ROUND_ADVANCE_CUM ((CUM), (MODE), (TYPE)) \ + + ROUND_ADVANCE_ARG ((MODE), (TYPE)) \ + <= MAX_ARC_PARM_REGS))) + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ +/* On the ARC the first MAX_ARC_PARM_REGS args are normally in registers + and the rest are pushed. */ + +static rtx +arc_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + return (PASS_IN_REG_P (*cum, mode, type) + ? gen_rtx_REG (mode, ROUND_ADVANCE_CUM (*cum, mode, type)) + : NULL_RTX); +} + +/* Worker function for TARGET_FUNCTION_ARG_ADVANCE. */ + +static void +arc_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + *cum = (ROUND_ADVANCE_CUM (*cum, mode, type) + + ROUND_ADVANCE_ARG (mode, type)); +} + +/* Worker function for TARGET_FUNCTION_ARG_BOUNDARY. */ + +static unsigned int +arc_function_arg_boundary (enum machine_mode mode, const_tree type) +{ + return (type != NULL_TREE + ? TYPE_ALIGN (type) + : (GET_MODE_BITSIZE (mode) <= PARM_BOUNDARY + ? PARM_BOUNDARY + : 2 * PARM_BOUNDARY)); +} + +/* Trampolines. */ +/* ??? This doesn't work yet because GCC will use as the address of a nested + function the address of the trampoline. We need to use that address + right shifted by 2. It looks like we'll need PSImode after all. :-( + + ??? The above comment sounds like it's doable via + TARGET_TRAMPOLINE_ADJUST_ADDRESS; no PSImode needed. + + On the ARC, the trampoline is quite simple as we have 32-bit immediate + constants. + + mov r24,STATIC + j.nd FUNCTION +*/ + +static void +arc_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) +{ + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + rtx mem; + + mem = adjust_address (m_tramp, SImode, 0); + emit_move_insn (mem, GEN_INT (0x631f7c00)); + + mem = adjust_address (m_tramp, SImode, 4); + emit_move_insn (mem, chain_value); + + mem = adjust_address (m_tramp, SImode, 8); + emit_move_insn (mem, GEN_INT (0x381f0000)); + + mem = adjust_address (m_tramp, SImode, 12); + emit_move_insn (mem, fnaddr); + + emit_insn (gen_flush_icache (m_tramp)); +} + +/* Worker function for TARGET_CONDITIONAL_REGISTER_USAGE. */ + +static void +arc_conditional_register_usage (void) +{ + if (PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM) + { + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + } +} + diff --git a/gcc/config/arc/arc.h b/gcc/config/arc/arc.h new file mode 100644 index 000000000..0f2b99c19 --- /dev/null +++ b/gcc/config/arc/arc.h @@ -0,0 +1,935 @@ +/* Definitions of target machine for GNU compiler, Argonaut ARC cpu. + Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, + 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* ??? This is an old port, and is undoubtedly suffering from bit rot. */ + +/* Things to do: + + - incscc, decscc? + - print active compiler options in assembler output +*/ + + +#undef ASM_SPEC +#undef LINK_SPEC +#undef LIB_SPEC +#undef STARTFILE_SPEC +#undef ENDFILE_SPEC +#undef SIZE_TYPE +#undef PTRDIFF_TYPE +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE +#undef ASM_OUTPUT_LABELREF + +/* Print subsidiary information on the compiler version in use. */ +#define TARGET_VERSION fprintf (stderr, " (arc)") + +/* Names to predefine in the preprocessor for this target machine. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__arc__"); \ + if (TARGET_BIG_ENDIAN) \ + builtin_define ("__big_endian__"); \ + if (arc_cpu_type == 0) \ + builtin_define ("__base__"); \ + builtin_assert ("cpu=arc"); \ + builtin_assert ("machine=arc"); \ + } while (0) + +/* Pass -mmangle-cpu if we get -mcpu=*. + Doing it this way lets one have it on as default with -mcpu=*, + but also lets one turn it off with -mno-mangle-cpu. */ +#define CC1_SPEC "\ +%{mcpu=*:-mmangle-cpu} \ +%{EB:%{EL:%emay not use both -EB and -EL}} \ +%{EB:-mbig-endian} %{EL:-mlittle-endian} \ +" + +#define ASM_SPEC "%{EB} %{EL}" + +#define LINK_SPEC "%{v} %{EB} %{EL}" + +#define LIB_SPEC "-lc" + +#define STARTFILE_SPEC "%{!shared:crt0.o%s} crtinit.o%s" + +#define ENDFILE_SPEC "crtfini.o%s" + +/* Instruction set characteristics. + These are internal macros, set by the appropriate -mcpu= option. */ + +/* Nonzero means the cpu has a barrel shifter. */ +#define TARGET_SHIFTER 0 + +/* Which cpu we're compiling for. */ +extern int arc_cpu_type; + +/* Check if CPU is an extension and set `arc_cpu_type' and `arc_mangle_cpu' + appropriately. The result should be nonzero if the cpu is recognized, + otherwise zero. This is intended to be redefined in a cover file. + This is used by arc_handle_option. */ +#define ARC_EXTENSION_CPU(cpu) 0 + + +/* Target machine storage layout. */ + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. */ +#define BITS_BIG_ENDIAN 1 + +/* Define this if most significant byte of a word is the lowest numbered. */ +#define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN) + +/* Define this if most significant word of a multiword number is the lowest + numbered. */ +#define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN) + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Define this macro if it is advisable to hold scalars in registers + in a wider mode than that declared by the program. In such cases, + the value is constrained to be within the bounds of the declared + type, but kept valid in the wider mode. The signedness of the + extension may differ from that of the type. */ +#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \ +if (GET_MODE_CLASS (MODE) == MODE_INT \ + && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \ +{ \ + (MODE) = SImode; \ +} + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY 32 + +/* Boundary (in *bits*) on which stack pointer should be aligned. */ +#define STACK_BOUNDARY 64 + +/* ALIGN FRAMES on word boundaries */ +#define ARC_STACK_ALIGN(LOC) (((LOC)+7) & ~7) + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 32 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Every structure's size must be a multiple of this. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* A bit-field declared as `int' forces `int' alignment for the struct. */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* No data type wants to be aligned rounder than this. */ +/* This is bigger than currently necessary for the ARC. If 8 byte floats are + ever added it's not clear whether they'll need such alignment or not. For + now we assume they will. We can always relax it if necessary but the + reverse isn't true. */ +#define BIGGEST_ALIGNMENT 64 + +/* The best alignment to use in cases where we have a choice. */ +#define FASTEST_ALIGNMENT 32 + +/* Make strings word-aligned so strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + ((TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < FASTEST_ALIGNMENT) \ + ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Make arrays of chars word-aligned for the same reasons. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (TREE_CODE (TYPE) == ARRAY_TYPE \ + && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ + && (ALIGN) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +/* On the ARC the lower address bits are masked to 0 as necessary. The chip + won't croak when given an unaligned address, but the insn will still fail + to produce the correct result. */ +#define STRICT_ALIGNMENT 1 + +/* Layout of source language data types. */ + +#define SHORT_TYPE_SIZE 16 +#define INT_TYPE_SIZE 32 +#define LONG_TYPE_SIZE 32 +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE 64 + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#define DEFAULT_SIGNED_CHAR 1 + +#define SIZE_TYPE "long unsigned int" +#define PTRDIFF_TYPE "long int" +#define WCHAR_TYPE "short unsigned int" +#define WCHAR_TYPE_SIZE 16 + +/* Standard register usage. */ + +/* Number of actual hardware registers. + The hardware registers are assigned numbers for the compiler + from 0 to just below FIRST_PSEUDO_REGISTER. + All registers that the compiler knows about must be given numbers, + even those that are not normally considered general registers. */ +/* Registers 61, 62, and 63 are not really registers and we needn't treat + them as such. We still need a register for the condition code. */ +#define FIRST_PSEUDO_REGISTER 62 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + + 0-28 - general purpose registers + 29 - ilink1 (interrupt link register) + 30 - ilink2 (interrupt link register) + 31 - blink (branch link register) + 32-59 - reserved for extensions + 60 - LP_COUNT + 61 - condition code + + For doc purposes: + 61 - short immediate data indicator (setting flags) + 62 - long immediate data indicator + 63 - short immediate data indicator (not setting flags). + + The general purpose registers are further broken down into: + 0-7 - arguments/results + 8-15 - call used + 16-23 - call saved + 24 - call used, static chain pointer + 25 - call used, gptmp + 26 - global pointer + 27 - frame pointer + 28 - stack pointer + + By default, the extension registers are not available. */ + +#define FIXED_REGISTERS \ +{ 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1 } + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. */ + +#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, 1, 1, \ + \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1 } + +/* If defined, an initializer for a vector of integers, containing the + numbers of hard registers in the order in which GCC should + prefer to use them (from most preferred to least). */ +#define REG_ALLOC_ORDER \ +{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, \ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 31, \ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, \ + 27, 28, 29, 30 } + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ +((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ +extern const unsigned int arc_hard_regno_mode_ok[]; +extern unsigned int arc_mode_class[]; +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ +((arc_hard_regno_mode_ok[REGNO] & arc_mode_class[MODE]) != 0) + +/* A C expression that is nonzero if it is desirable to choose + register allocation so as to avoid move instructions between a + value of mode MODE1 and a value of mode MODE2. + + If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, + MODE2)' are ever different for any R, then `MODES_TIEABLE_P (MODE1, + MODE2)' must be zero. */ + +/* Tie QI/HI/SI modes together. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ +(GET_MODE_CLASS (MODE1) == MODE_INT \ + && GET_MODE_CLASS (MODE2) == MODE_INT \ + && GET_MODE_SIZE (MODE1) <= UNITS_PER_WORD \ + && GET_MODE_SIZE (MODE2) <= UNITS_PER_WORD) + +/* Register classes and constants. */ + +/* Define the classes of registers for register constraints in the + machine description. Also define ranges of constants. + + One of the classes must always be named ALL_REGS and include all hard regs. + If there is more than one class, another class must be named NO_REGS + and contain no registers. + + The name GENERAL_REGS must be the name of a class (or an alias for + another name such as ALL_REGS). This is the class of registers + that is allowed by "g" or "r" in a register constraint. + Also, registers outside this class are allocated only when + instructions express preferences for them. + + The classes must be numbered in nondecreasing order; that is, + a larger-numbered class must never be contained completely + in a smaller-numbered class. + + For any two classes, it is very desirable that there be another + class that represents their union. + + It is important that any condition codes have class NO_REGS. + See `register_operand'. */ + +enum reg_class { + NO_REGS, LPCOUNT_REG, GENERAL_REGS, ALL_REGS, LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ +#define REG_CLASS_NAMES \ +{ "NO_REGS", "LPCOUNT_REG", "GENERAL_REGS", "ALL_REGS" } + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. */ + +#define REG_CLASS_CONTENTS \ +{ {0, 0}, {0, 0x10000000}, {0xffffffff, 0xfffffff}, \ + {0xffffffff, 0x1fffffff} } + +/* The same information, inverted: + Return the class number of the smallest class containing + reg number REGNO. This could be a conditional expression + or could index an array. */ +extern enum reg_class arc_regno_reg_class[FIRST_PSEUDO_REGISTER]; +#define REGNO_REG_CLASS(REGNO) \ +(arc_regno_reg_class[REGNO]) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS GENERAL_REGS +#define BASE_REG_CLASS GENERAL_REGS + +/* Get reg_class from a letter such as appears in the machine description. */ +#define REG_CLASS_FROM_LETTER(C) \ +((C) == 'l' ? LPCOUNT_REG /* ??? needed? */ \ + : NO_REGS) + +/* 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. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ +#define REGNO_OK_FOR_BASE_P(REGNO) \ +((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32) +#define REGNO_OK_FOR_INDEX_P(REGNO) \ +((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ +((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* The letters I, J, K, L, M, N, O, P in a register constraint string + can be used to stand for particular ranges of immediate operands. + This macro defines what the ranges are. + C is the letter, and VALUE is a constant value. + Return 1 if VALUE is in the range specified by C. */ +/* 'I' is used for short immediates (always signed). + 'J' is used for long immediates. + 'K' is used for any constant up to 64 bits (for 64x32 situations?). */ + +/* local to this file */ +#define SMALL_INT(X) ((unsigned) ((X) + 0x100) < 0x200) +/* local to this file */ +#define LARGE_INT(X) \ +((X) >= (-(HOST_WIDE_INT) 0x7fffffff - 1) \ + && (unsigned HOST_WIDE_INT)(X) <= (unsigned HOST_WIDE_INT) 0xffffffff) + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ +((C) == 'I' ? SMALL_INT (VALUE) \ + : (C) == 'J' ? LARGE_INT (VALUE) \ + : (C) == 'K' ? 1 \ + : 0) + +/* Similar, but for floating constants, and defining letters G and H. + Here VALUE is the CONST_DOUBLE rtx itself. */ +/* 'G' is used for integer values for the multiplication insns where the + operands are extended from 4 bytes to 8 bytes. + 'H' is used when any 64-bit constant is allowed. */ +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ +((C) == 'G' ? arc_double_limm_p (VALUE) \ + : (C) == 'H' ? 1 \ + : 0) + +/* A C expression that defines the optional machine-dependent constraint + letters that can be used to segregate specific types of operands, + usually memory references, for the target machine. It should return 1 if + VALUE corresponds to the operand type represented by the constraint letter + C. If C is not defined as an extra constraint, the value returned should + be 0 regardless of VALUE. */ +/* ??? This currently isn't used. Waiting for PIC. */ +#if 0 +#define EXTRA_CONSTRAINT(VALUE, C) \ +((C) == 'R' ? (SYMBOL_REF_FUNCTION_P (VALUE) || GET_CODE (VALUE) == LABEL_REF) \ + : 0) +#endif + +/* Stack layout and stack pointer usage. */ + +/* Define this macro if pushing a word onto the stack moves the stack + pointer to a smaller address. */ +#define STACK_GROWS_DOWNWARD + +/* Define this to nonzero if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +#define FRAME_GROWS_DOWNWARD 1 + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET 0 + +/* Offset from the stack pointer register to the first location at which + outgoing arguments are placed. */ +#define STACK_POINTER_OFFSET FIRST_PARM_OFFSET (0) + +/* Offset of first parameter from the argument pointer register value. */ +/* 4 bytes for each of previous fp, return address, and previous gp. + 4 byte reserved area for future considerations. */ +#define FIRST_PARM_OFFSET(FNDECL) 16 + +/* A C expression whose value is RTL representing the address in a + stack frame where the pointer to the caller's frame is stored. + Assume that FRAMEADDR is an RTL expression for the address of the + stack frame itself. + + If you don't define this macro, the default is to return the value + of FRAMEADDR--that is, the stack frame address is also the address + of the stack word that points to the previous frame. */ +/* ??? unfinished */ +/*define DYNAMIC_CHAIN_ADDRESS (FRAMEADDR)*/ + +/* A C expression whose value is RTL representing the value of the + return address for the frame COUNT steps up from the current frame. + FRAMEADDR is the frame pointer of the COUNT frame, or the frame + pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' + is defined. */ +/* The current return address is in r31. The return address of anything + farther back is at [%fp,4]. */ +#if 0 /* The default value should work. */ +#define RETURN_ADDR_RTX(COUNT, FRAME) \ +(((COUNT) == -1) \ + ? gen_rtx_REG (Pmode, 31) \ + : copy_to_reg (gen_rtx_MEM (Pmode, \ + memory_address (Pmode, \ + plus_constant ((FRAME), \ + UNITS_PER_WORD))))) +#endif + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM 28 + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 27 + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM FRAME_POINTER_REGNUM + +/* Register in which static-chain is passed to a function. This must + not be a register used by the prologue. */ +#define STATIC_CHAIN_REGNUM 24 + +/* C statement to store the difference between the frame pointer + and the stack pointer values immediately after the function prologue. */ +#define INITIAL_FRAME_POINTER_OFFSET(VAR) \ +((VAR) = arc_compute_frame_size (get_frame_size ())) + +/* Function argument passing. */ + +/* If defined, the maximum amount of space required for outgoing + arguments will be computed and placed into the variable + `crtl->outgoing_args_size'. No space will be pushed + onto the stack for each call; instead, the function prologue should + increase the stack frame size by this amount. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* Define a data type for recording info about an argument list + during the scan of that argument list. This data type should + hold all necessary information about the function itself + and about the args processed so far, enough to enable macros + such as FUNCTION_ARG to determine where the next arg should go. */ +#define CUMULATIVE_ARGS int + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ +((CUM) = 0) + +/* The number of registers used for parameter passing. Local to this file. */ +#define MAX_ARC_PARM_REGS 8 + +/* 1 if N is a possible register number for function argument passing. */ +#define FUNCTION_ARG_REGNO_P(N) \ +((unsigned) (N) < MAX_ARC_PARM_REGS) + + +/* Function results. */ + +/* Define how to find the value returned by a function. + VALTYPE is the data type of the value (as a tree). + If the precise function being called is known, FUNC is its FUNCTION_DECL; + otherwise, FUNC is 0. */ +#define FUNCTION_VALUE(VALTYPE, FUNC) gen_rtx_REG (TYPE_MODE (VALTYPE), 0) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ +#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, 0) + +/* 1 if N is a possible register number for a function value + as seen by the caller. */ +/* ??? What about r1 in DI/DF values. */ +#define FUNCTION_VALUE_REGNO_P(N) ((N) == 0) + +/* Tell GCC to use TARGET_RETURN_IN_MEMORY. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. */ +#define EXIT_IGNORE_STACK 0 + +/* Epilogue delay slots. */ +#define DELAY_SLOTS_FOR_EPILOGUE arc_delay_slots_for_epilogue () + +#define ELIGIBLE_FOR_EPILOGUE_DELAY(TRIAL, SLOTS_FILLED) \ +arc_eligible_for_epilogue_delay (TRIAL, SLOTS_FILLED) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ +#define FUNCTION_PROFILER(FILE, LABELNO) + +#define TRAMPOLINE_ALIGNMENT 32 +#define TRAMPOLINE_SIZE 16 + +/* Addressing modes, and classification of registers for them. */ + +/* Maximum number of registers that can appear in a valid memory address. */ +/* The `ld' insn allows 2, but the `st' insn only allows 1. */ +#define MAX_REGS_PER_ADDRESS 1 + +/* We have pre inc/dec (load/store with update). */ +#define HAVE_PRE_INCREMENT 1 +#define HAVE_PRE_DECREMENT 1 + +/* Recognize any constant value that is a valid address. */ +#define CONSTANT_ADDRESS_P(X) \ +(GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST) + +/* Nonzero if the constant value X is a legitimate general operand. + We can handle any 32- or 64-bit constant. */ +/* "1" should work since the largest constant should be a 64 bit critter. */ +/* ??? Not sure what to do for 64x32 compiler. */ +#define LEGITIMATE_CONSTANT_P(X) 1 + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. */ + +#ifndef REG_OK_STRICT + +/* Nonzero if X is a hard reg that can be used as an index + or if it is a pseudo reg. */ +#define REG_OK_FOR_INDEX_P(X) \ +((unsigned) REGNO (X) - 32 >= FIRST_PSEUDO_REGISTER - 32) +/* Nonzero if X is a hard reg that can be used as a base reg + or if it is a pseudo reg. */ +#define REG_OK_FOR_BASE_P(X) \ +((unsigned) REGNO (X) - 32 >= FIRST_PSEUDO_REGISTER - 32) + +#else + +/* Nonzero if X is a hard reg that can be used as an index. */ +#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) +/* Nonzero if X is a hard reg that can be used as a base reg. */ +#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) + +#endif + +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. */ +/* The `ld' insn allows [reg],[reg+shimm],[reg+limm],[reg+reg],[limm] + but the `st' insn only allows [reg],[reg+shimm],[limm]. + The only thing we can do is only allow the most strict case `st' and hope + other parts optimize out the restrictions for `ld'. */ + +/* local to this file */ +#define RTX_OK_FOR_BASE_P(X) \ +(REG_P (X) && REG_OK_FOR_BASE_P (X)) + +/* local to this file */ +#define RTX_OK_FOR_INDEX_P(X) \ +(0 && /*???*/ REG_P (X) && REG_OK_FOR_INDEX_P (X)) + +/* local to this file */ +/* ??? Loads can handle any constant, stores can only handle small ones. */ +#define RTX_OK_FOR_OFFSET_P(X) \ +(GET_CODE (X) == CONST_INT && SMALL_INT (INTVAL (X))) + +#define LEGITIMATE_OFFSET_ADDRESS_P(MODE, X) \ +(GET_CODE (X) == PLUS \ + && RTX_OK_FOR_BASE_P (XEXP (X, 0)) \ + && (RTX_OK_FOR_INDEX_P (XEXP (X, 1)) \ + || RTX_OK_FOR_OFFSET_P (XEXP (X, 1)))) + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ if (RTX_OK_FOR_BASE_P (X)) \ + goto ADDR; \ + if (LEGITIMATE_OFFSET_ADDRESS_P ((MODE), (X))) \ + goto ADDR; \ + if (GET_CODE (X) == CONST_INT && LARGE_INT (INTVAL (X))) \ + goto ADDR; \ + if (GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == LABEL_REF \ + || GET_CODE (X) == CONST) \ + goto ADDR; \ + if ((GET_CODE (X) == PRE_DEC || GET_CODE (X) == PRE_INC) \ + /* We're restricted here by the `st' insn. */ \ + && RTX_OK_FOR_BASE_P (XEXP ((X), 0))) \ + goto ADDR; \ +} + +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. */ +#define SELECT_CC_MODE(OP, X, Y) \ +arc_select_cc_mode (OP, X, Y) + +/* Return nonzero if SELECT_CC_MODE will never return MODE for a + floating point inequality comparison. */ +#define REVERSIBLE_CC_MODE(MODE) 1 /*???*/ + +/* Costs. */ + +/* Compute extra cost of moving data between one register class + and another. */ +#define REGISTER_MOVE_COST(MODE, CLASS1, CLASS2) 2 + +/* Compute the cost of moving data between registers and memory. */ +/* Memory is 3 times as expensive as registers. + ??? Is that the right way to look at it? */ +#define MEMORY_MOVE_COST(MODE,CLASS,IN) \ +(GET_MODE_SIZE (MODE) <= UNITS_PER_WORD ? 6 : 12) + +/* The cost of a branch insn. */ +/* ??? What's the right value here? Branches are certainly more + expensive than reg->reg moves. */ +#define BRANCH_COST(speed_p, predictable_p) 2 + +/* Nonzero if access to memory by bytes is slow and undesirable. + For RISC chips, it means that access to memory by bytes is no + better than access by words when possible, so grab a whole word + and maybe make use of that. */ +#define SLOW_BYTE_ACCESS 1 + +/* Define this macro if it is as good or better to call a constant + function address than to call an address kept in a register. */ +/* On the ARC, calling through registers is slow. */ +#define NO_FUNCTION_CSE + +/* Section selection. */ +/* WARNING: These section names also appear in dwarfout.c. */ + +/* The names of the text, data, and readonly-data sections are runtime + selectable. */ + +#define ARC_SECTION_FORMAT "\t.section %s" +#define ARC_DEFAULT_TEXT_SECTION ".text" +#define ARC_DEFAULT_DATA_SECTION ".data" +#define ARC_DEFAULT_RODATA_SECTION ".rodata" + +extern const char *arc_text_section, *arc_data_section, *arc_rodata_section; + +/* initfini.c uses this in an asm. */ +#if defined (CRT_INIT) || defined (CRT_FINI) +#define TEXT_SECTION_ASM_OP "\t.section .text" +#else +#define TEXT_SECTION_ASM_OP arc_text_section +#endif +#define DATA_SECTION_ASM_OP arc_data_section + +#undef READONLY_DATA_SECTION_ASM_OP +#define READONLY_DATA_SECTION_ASM_OP arc_rodata_section + +#define BSS_SECTION_ASM_OP "\t.section .bss" + +/* Define this macro if jump tables (for tablejump insns) should be + output in the text section, along with the assembler instructions. + Otherwise, the readonly data section is used. + This macro is irrelevant if there is no separate readonly data section. */ +/*#define JUMP_TABLES_IN_TEXT_SECTION*/ + +/* For DWARF. Marginally different than default so output is "prettier" + (and consistent with above). */ +#define PUSHSECTION_ASM_OP "\t.section " + +/* Tell crtstuff.c we're using ELF. */ +#define OBJECT_FORMAT_ELF + +/* PIC */ + +/* The register number of the register used to address a table of static + data addresses in memory. In some cases this register is defined by a + processor's ``application binary interface'' (ABI). When this macro + is defined, RTL is generated for this register once, as with the stack + pointer and frame pointer registers. If this macro is not defined, it + is up to the machine-dependent files to allocate such a register (if + necessary). */ +#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 26 : INVALID_REGNUM) + +/* Define this macro if the register defined by PIC_OFFSET_TABLE_REGNUM is + clobbered by calls. Do not define this macro if PIC_OFFSET_TABLE_REGNUM + is not defined. */ +/* This register is call-saved on the ARC. */ +/*#define PIC_OFFSET_TABLE_REG_CALL_CLOBBERED*/ + +/* A C expression that is nonzero if X is a legitimate immediate + operand on the target machine when generating position independent code. + You can assume that X satisfies CONSTANT_P, so you need not + check this. You can also assume `flag_pic' is true, so you need not + check it either. You need not define this macro if all constants + (including SYMBOL_REF) can be immediate operands when generating + position independent code. */ +/*#define LEGITIMATE_PIC_OPERAND_P(X)*/ + +/* Control the assembler format that we output. */ + +/* A C string constant describing how to begin a comment in the target + assembler language. The compiler assumes that the comment will + end at the end of the line. */ +#define ASM_COMMENT_START ";" + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ +#define ASM_APP_ON "" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ +#define ASM_APP_OFF "" + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP "\t.global\t" + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. */ +/* We mangle all user labels to provide protection from linking code + compiled for different cpus. */ +/* We work around a dwarfout.c deficiency by watching for labels from it and + not adding the '_' prefix nor the cpu suffix. There is a comment in + dwarfout.c that says it should be using (*targetm.asm_out.internal_label). */ +extern const char *arc_mangle_cpu; +#define ASM_OUTPUT_LABELREF(FILE, NAME) \ +do { \ + if ((NAME)[0] == '.' && (NAME)[1] == 'L') \ + fprintf (FILE, "%s", NAME); \ + else \ + { \ + fputc ('_', FILE); \ + if (TARGET_MANGLE_CPU && arc_mangle_cpu != NULL) \ + fprintf (FILE, "%s_", arc_mangle_cpu); \ + fprintf (FILE, "%s", NAME); \ + } \ +} while (0) + +/* Assembler pseudo-op to equate one value with another. */ +/* ??? This is needed because dwarfout.c provides a default definition too + late for defaults.h (which contains the default definition of ASM_OUTPUT_DEF + that we use). */ +#define SET_ASM_OP "\t.set\t" + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ +#define REGISTER_NAMES \ +{"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \ + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", \ + "r24", "r25", "r26", "fp", "sp", "ilink1", "ilink2", "blink", \ + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", \ + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", \ + "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", \ + "r56", "r57", "r58", "r59", "lp_count", "cc"} + +/* Entry to the insn conditionalizer. */ +#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \ +arc_final_prescan_insn (INSN, OPVEC, NOPERANDS) + +/* A C expression which evaluates to true if CODE is a valid + punctuation character for use in the `PRINT_OPERAND' macro. */ +extern char arc_punct_chars[256]; +#define PRINT_OPERAND_PUNCT_VALID_P(CHAR) \ +arc_punct_chars[(unsigned char) (CHAR)] + +/* Print operand X (an rtx) in assembler syntax to file FILE. + CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. + For `%' followed by punctuation, CODE is the punctuation and X is null. */ +#define PRINT_OPERAND(FILE, X, CODE) \ +arc_print_operand (FILE, X, CODE) + +/* 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. */ +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ +arc_print_operand_address (FILE, ADDR) + +/* This is how to output an element of a case-vector that is absolute. */ +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ +do { \ + char label[30]; \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", VALUE); \ + fprintf (FILE, "\t.word %%st("); \ + assemble_name (FILE, label); \ + fprintf (FILE, ")\n"); \ +} while (0) + +/* This is how to output an element of a case-vector that is relative. */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ +do { \ + char label[30]; \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", VALUE); \ + fprintf (FILE, "\t.word %%st("); \ + assemble_name (FILE, label); \ + fprintf (FILE, "-"); \ + ASM_GENERATE_INTERNAL_LABEL (label, "L", REL); \ + assemble_name (FILE, label); \ + fprintf (FILE, ")\n"); \ +} while (0) + +/* The desired alignment for the location counter at the beginning + of a loop. */ +/* On the ARC, align loops to 32 byte boundaries (cache line size) + if -malign-loops. */ +#define LOOP_ALIGN(LABEL) (TARGET_ALIGN_LOOPS ? 5 : 0) + +/* This is how to output an assembler line + that says to advance the location counter + to a multiple of 2**LOG bytes. */ +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ +do { if ((LOG) != 0) fprintf (FILE, "\t.align %d\n", 1 << (LOG)); } while (0) + +/* Debugging information. */ + +/* Generate DBX and DWARF debugging information. */ +#define DBX_DEBUGGING_INFO 1 + +/* Prefer STABS (for now). */ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +/* Turn off splitting of long stabs. */ +#define DBX_CONTIN_LENGTH 0 + +/* Miscellaneous. */ + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE Pmode + +/* Define if operations between registers always perform the operation + on the full register even if a narrower mode is specified. */ +#define WORD_REGISTER_OPERATIONS + +/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD + will either zero-extend or sign-extend. The value of this macro should + be the code that says which one of the two operations is implicitly + done, UNKNOWN if none. */ +#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 4 + +/* Define this to be nonzero if shift instructions ignore all but the low-order + few bits. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +/* ??? The arc doesn't have full 32-bit pointers, but making this PSImode has + its own problems (you have to add extendpsisi2 and trucnsipsi2 but how does + one do it without getting excess code?). Try to avoid it. */ +#define Pmode SImode + +/* A function address in a call instruction. */ +#define FUNCTION_MODE SImode + +/* alloca should avoid clobbering the old register save area. */ +/* ??? Not defined in tm.texi. */ +#define SETJMP_VIA_SAVE_AREA + +/* ARC function types. */ +enum arc_function_type { + ARC_FUNCTION_UNKNOWN, ARC_FUNCTION_NORMAL, + /* These are interrupt handlers. The name corresponds to the register + name that contains the return address. */ + ARC_FUNCTION_ILINK1, ARC_FUNCTION_ILINK2 +}; +#define ARC_INTERRUPT_P(TYPE) \ +((TYPE) == ARC_FUNCTION_ILINK1 || (TYPE) == ARC_FUNCTION_ILINK2) +/* Compute the type of a function from its DECL. */ diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md new file mode 100644 index 000000000..09e47daf1 --- /dev/null +++ b/gcc/config/arc/arc.md @@ -0,0 +1,1376 @@ +;; Machine description of the Argonaut ARC cpu for GNU C compiler +;; Copyright (C) 1994, 1997, 1998, 1999, 2000, 2004, 2005, 2007, 2008 +;; Free Software Foundation, Inc. + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; GCC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; ??? This is an old port, and is undoubtedly suffering from bit rot. + +;; Insn type. Used to default other attribute values. + +(define_attr "type" + "move,load,store,cmove,unary,binary,compare,shift,mul,uncond_branch,branch,call,call_no_delay_slot,multi,misc" + (const_string "binary")) + +;; Length (in # of insns, long immediate constants counted too). +;; ??? There's a nasty interaction between the conditional execution fsm +;; and insn lengths: insns with shimm values cannot be conditionally executed. +(define_attr "length" "" + (cond [(eq_attr "type" "load") + (if_then_else (match_operand 1 "long_immediate_loadstore_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "store") + (if_then_else (match_operand 0 "long_immediate_loadstore_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "move,unary,compare") + (if_then_else (match_operand 1 "long_immediate_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "binary,mul") + (if_then_else (match_operand 2 "long_immediate_operand" "") + (const_int 2) (const_int 1)) + + (eq_attr "type" "cmove") + (if_then_else (match_operand 2 "register_operand" "") + (const_int 1) (const_int 2)) + + (eq_attr "type" "multi") (const_int 2) + ] + + (const_int 1))) + +;; The length here is the length of a single asm. Unfortunately it might be +;; 1 or 2 so we must allow for 2. That's ok though. How often will users +;; lament asm's not being put in delay slots? +(define_asm_attributes + [(set_attr "length" "2") + (set_attr "type" "multi")]) + +;; Condition codes: this one is used by final_prescan_insn to speed up +;; conditionalizing instructions. It saves having to scan the rtl to see if +;; it uses or alters the condition codes. + +;; USE: This insn uses the condition codes (e.g.: a conditional branch). +;; CANUSE: This insn can use the condition codes (for conditional execution). +;; SET: All condition codes are set by this insn. +;; SET_ZN: the Z and N flags are set by this insn. +;; SET_ZNC: the Z, N, and C flags are set by this insn. +;; CLOB: The condition codes are set to unknown values by this insn. +;; NOCOND: This insn can't use and doesn't affect the condition codes. + +(define_attr "cond" "use,canuse,set,set_zn,set_znc,clob,nocond" + (cond [(and (eq_attr "type" "unary,binary,move") + (eq_attr "length" "1")) + (const_string "canuse") + + (eq_attr "type" "compare") + (const_string "set") + + (eq_attr "type" "cmove,branch") + (const_string "use") + + (eq_attr "type" "multi,misc") + (const_string "clob") + ] + + (const_string "nocond"))) + +;; Delay slots. + +(define_attr "in_delay_slot" "false,true" + (cond [(eq_attr "type" "uncond_branch,branch,call,call_no_delay_slot,multi") + (const_string "false") + ] + + (if_then_else (eq_attr "length" "1") + (const_string "true") + (const_string "false")))) + +(define_delay (eq_attr "type" "call") + [(eq_attr "in_delay_slot" "true") + (eq_attr "in_delay_slot" "true") + (eq_attr "in_delay_slot" "true")]) + +(define_delay (eq_attr "type" "branch,uncond_branch") + [(eq_attr "in_delay_slot" "true") + (eq_attr "in_delay_slot" "true") + (eq_attr "in_delay_slot" "true")]) + +;; Scheduling description for the ARC + +(define_cpu_unit "branch") + +(define_insn_reservation "any_insn" 1 (eq_attr "type" "!load,compare,branch") + "nothing") + +;; 1) A conditional jump cannot immediately follow the insn setting the flags. +;; This isn't a complete solution as it doesn't come with guarantees. That +;; is done in the branch patterns and in arc_print_operand. This exists to +;; avoid inserting a nop when we can. + +(define_insn_reservation "compare" 1 (eq_attr "type" "compare") + "nothing,branch") + +(define_insn_reservation "branch" 1 (eq_attr "type" "branch") + "branch") + +;; 2) References to loaded registers should wait a cycle. + +;; Memory with load-delay of 1 (i.e., 2 cycle load). + +(define_insn_reservation "memory" 2 (eq_attr "type" "load") + "nothing") + +;; Move instructions. + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (QImode, operands[1]); +}") + +(define_insn "*movqi_insn" + [(set (match_operand:QI 0 "move_dest_operand" "=r,r,r,m") + (match_operand:QI 1 "move_src_operand" "rI,Ji,m,r"))] +;; ??? Needed? + "register_operand (operands[0], QImode) + || register_operand (operands[1], QImode)" + "@ + mov%? %0,%1 + mov%? %0,%1 + ldb%U1%V1 %0,%1 + stb%U0%V0 %1,%0" + [(set_attr "type" "move,move,load,store")]) + +;; ??? This may never match since there's no cmpqi insn. + +(define_insn "*movqi_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (sign_extend:SI (match_operand:QI 1 "move_src_operand" "rIJi")) + (const_int 0))) + (set (match_operand:QI 0 "move_dest_operand" "=r") + (match_dup 1))] + "" + "mov%?.f %0,%1" + [(set_attr "type" "move") + (set_attr "cond" "set_zn")]) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (HImode, operands[1]); +}") + +(define_insn "*movhi_insn" + [(set (match_operand:HI 0 "move_dest_operand" "=r,r,r,m") + (match_operand:HI 1 "move_src_operand" "rI,Ji,m,r"))] + "register_operand (operands[0], HImode) + || register_operand (operands[1], HImode)" + "@ + mov%? %0,%1 + mov%? %0,%1 + ldw%U1%V1 %0,%1 + stw%U0%V0 %1,%0" + [(set_attr "type" "move,move,load,store")]) + +;; ??? Will this ever match? + +(define_insn "*movhi_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (sign_extend:SI (match_operand:HI 1 "move_src_operand" "rIJi")) + (const_int 0))) + (set (match_operand:HI 0 "move_dest_operand" "=r") + (match_dup 1))] +;; ??? Needed? + "register_operand (operands[0], HImode) + || register_operand (operands[1], HImode)" + "mov%?.f %0,%1" + [(set_attr "type" "move") + (set_attr "cond" "set_zn")]) + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (SImode, operands[1]); +}") + +(define_insn "*movsi_insn" + [(set (match_operand:SI 0 "move_dest_operand" "=r,r,r,m") + (match_operand:SI 1 "move_src_operand" "rI,GJi,m,r"))] + "register_operand (operands[0], SImode) + || register_operand (operands[1], SImode)" + "@ + mov%? %0,%1 + mov%? %0,%S1 + ld%U1%V1 %0,%1 + st%U0%V0 %1,%0" + [(set_attr "type" "move,move,load,store")]) + +(define_insn "*movsi_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (match_operand:SI 1 "move_src_operand" "rIJi") + (const_int 0))) + (set (match_operand:SI 0 "move_dest_operand" "=r") + (match_dup 1))] + "register_operand (operands[0], SImode) + || register_operand (operands[1], SImode)" + "mov%?.f %0,%S1" + [(set_attr "type" "move") + (set_attr "cond" "set_zn")]) + +(define_expand "movdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (DImode, operands[1]); +}") + +(define_insn "*movdi_insn" + [(set (match_operand:DI 0 "move_dest_operand" "=r,r,r,m") + (match_operand:DI 1 "move_double_src_operand" "r,HK,m,r"))] + "register_operand (operands[0], DImode) + || register_operand (operands[1], DImode)" + "* +{ + switch (which_alternative) + { + case 0 : + /* We normally copy the low-numbered register first. However, if + the first register operand 0 is the same as the second register of + operand 1, we must copy in the opposite order. */ + if (REGNO (operands[0]) == REGNO (operands[1]) + 1) + return \"mov %R0,%R1\;mov %0,%1\"; + else + return \"mov %0,%1\;mov %R0,%R1\"; + case 1 : + return \"mov %0,%L1\;mov %R0,%H1\"; + case 2 : + /* If the low-address word is used in the address, we must load it + last. Otherwise, load it first. Note that we cannot have + auto-increment in that case since the address register is known to be + dead. */ + if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, + operands [1], 0)) + return \"ld%V1 %R0,%R1\;ld%V1 %0,%1\"; + else + return \"ld%V1 %0,%1\;ld%V1 %R0,%R1\"; + case 3 : + return \"st%V0 %1,%0\;st%V0 %R1,%R0\"; + default: + gcc_unreachable (); + } +}" + [(set_attr "type" "move,move,load,store") + ;; ??? The ld/st values could be 4 if it's [reg,bignum]. + (set_attr "length" "2,4,2,2")]) + +;(define_expand "movdi" +; [(set (match_operand:DI 0 "general_operand" "") +; (match_operand:DI 1 "general_operand" ""))] +; "" +; " +;{ +; /* Flow doesn't understand that this is effectively a DFmode move. +; It doesn't know that all of `operands[0]' is set. */ +; emit_clobber (operands[0]); +; +; /* Emit insns that movsi_insn can handle. */ +; emit_insn (gen_movsi (operand_subword (operands[0], 0, 0, DImode), +; operand_subword (operands[1], 0, 0, DImode))); +; emit_insn (gen_movsi (operand_subword (operands[0], 1, 0, DImode), +; operand_subword (operands[1], 1, 0, DImode))); +; DONE; +;}") + +;; Floating point move insns. + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (SFmode, operands[1]); +}") + +(define_insn "*movsf_insn" + [(set (match_operand:SF 0 "move_dest_operand" "=r,r,r,m") + (match_operand:SF 1 "move_src_operand" "r,E,m,r"))] + "register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode)" + "@ + mov%? %0,%1 + mov%? %0,%1 ; %A1 + ld%U1%V1 %0,%1 + st%U0%V0 %1,%0" + [(set_attr "type" "move,move,load,store")]) + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " +{ + /* Everything except mem = const or mem = mem can be done easily. */ + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (DFmode, operands[1]); +}") + +(define_insn "*movdf_insn" + [(set (match_operand:DF 0 "move_dest_operand" "=r,r,r,m") + (match_operand:DF 1 "move_double_src_operand" "r,E,m,r"))] + "register_operand (operands[0], DFmode) + || register_operand (operands[1], DFmode)" + "* +{ + switch (which_alternative) + { + case 0 : + /* We normally copy the low-numbered register first. However, if + the first register operand 0 is the same as the second register of + operand 1, we must copy in the opposite order. */ + if (REGNO (operands[0]) == REGNO (operands[1]) + 1) + return \"mov %R0,%R1\;mov %0,%1\"; + else + return \"mov %0,%1\;mov %R0,%R1\"; + case 1 : + return \"mov %0,%L1\;mov %R0,%H1 ; %A1\"; + case 2 : + /* If the low-address word is used in the address, we must load it + last. Otherwise, load it first. Note that we cannot have + auto-increment in that case since the address register is known to be + dead. */ + if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, + operands [1], 0)) + return \"ld%V1 %R0,%R1\;ld%V1 %0,%1\"; + else + return \"ld%V1 %0,%1\;ld%V1 %R0,%R1\"; + case 3 : + return \"st%V0 %1,%0\;st%V0 %R1,%R0\"; + default: + gcc_unreachable (); + } +}" + [(set_attr "type" "move,move,load,store") + ;; ??? The ld/st values could be 4 if it's [reg,bignum]. + (set_attr "length" "2,4,2,2")]) + +;(define_expand "movdf" +; [(set (match_operand:DF 0 "general_operand" "") +; (match_operand:DF 1 "general_operand" ""))] +; "" +; " +;{ +; /* Flow doesn't understand that this is effectively a DFmode move. +; It doesn't know that all of `operands[0]' is set. */ +; emit_clobber (operands[0]); +; +; /* Emit insns that movsi_insn can handle. */ +; emit_insn (gen_movsi (operand_subword (operands[0], 0, 0, DFmode), +; operand_subword (operands[1], 0, 0, DFmode))); +; emit_insn (gen_movsi (operand_subword (operands[0], 1, 0, DFmode), +; operand_subword (operands[1], 1, 0, DFmode))); +; DONE; +;}") + +;; Load/Store with update instructions. +;; +;; Some of these we can get by using pre-decrement or pre-increment, but the +;; hardware can also do cases where the increment is not the size of the +;; object. +;; +;; In all these cases, we use operands 0 and 1 for the register being +;; incremented because those are the operands that local-alloc will +;; tie and these are the pair most likely to be tieable (and the ones +;; that will benefit the most). +;; +;; We use match_operator here because we need to know whether the memory +;; object is volatile or not. + +(define_insn "*loadqi_update" + [(set (match_operand:QI 3 "register_operand" "=r,r") + (match_operator:QI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")])) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldb.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*load_zeroextendqisi_update" + [(set (match_operand:SI 3 "register_operand" "=r,r") + (zero_extend:SI (match_operator:QI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldb.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*load_signextendqisi_update" + [(set (match_operand:SI 3 "register_operand" "=r,r") + (sign_extend:SI (match_operator:QI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldb.x.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*storeqi_update" + [(set (match_operator:QI 4 "store_update_operand" + [(match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "short_immediate_operand" "I")]) + (match_operand:QI 3 "register_operand" "r")) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "stb.a%V4 %3,[%0,%2]" + [(set_attr "type" "store") + (set_attr "length" "1")]) + +(define_insn "*loadhi_update" + [(set (match_operand:HI 3 "register_operand" "=r,r") + (match_operator:HI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")])) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldw.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*load_zeroextendhisi_update" + [(set (match_operand:SI 3 "register_operand" "=r,r") + (zero_extend:SI (match_operator:HI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldw.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*load_signextendhisi_update" + [(set (match_operand:SI 3 "register_operand" "=r,r") + (sign_extend:SI (match_operator:HI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ldw.x.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*storehi_update" + [(set (match_operator:HI 4 "store_update_operand" + [(match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "short_immediate_operand" "I")]) + (match_operand:HI 3 "register_operand" "r")) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "stw.a%V4 %3,[%0,%2]" + [(set_attr "type" "store") + (set_attr "length" "1")]) + +(define_insn "*loadsi_update" + [(set (match_operand:SI 3 "register_operand" "=r,r") + (match_operator:SI 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")])) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ld.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*storesi_update" + [(set (match_operator:SI 4 "store_update_operand" + [(match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "short_immediate_operand" "I")]) + (match_operand:SI 3 "register_operand" "r")) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "st.a%V4 %3,[%0,%2]" + [(set_attr "type" "store") + (set_attr "length" "1")]) + +(define_insn "*loadsf_update" + [(set (match_operand:SF 3 "register_operand" "=r,r") + (match_operator:SF 4 "load_update_operand" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "nonmemory_operand" "rI,J")])) + (set (match_operand:SI 0 "register_operand" "=r,r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "ld.a%V4 %3,[%0,%2]" + [(set_attr "type" "load,load") + (set_attr "length" "1,2")]) + +(define_insn "*storesf_update" + [(set (match_operator:SF 4 "store_update_operand" + [(match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "short_immediate_operand" "I")]) + (match_operand:SF 3 "register_operand" "r")) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "st.a%V4 %3,[%0,%2]" + [(set_attr "type" "store") + (set_attr "length" "1")]) + +;; Conditional move instructions. + +(define_expand "movsicc" + [(set (match_operand:SI 0 "register_operand" "") + (if_then_else:SI (match_operand 1 "comparison_operator" "") + (match_operand:SI 2 "nonmemory_operand" "") + (match_operand:SI 3 "register_operand" "")))] + "" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + rtx cc_reg = gen_compare_reg (code, XEXP (operands[1], 0), + XEXP (operands[1], 1)); + operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); +}") + +(define_expand "movsfcc" + [(set (match_operand:SF 0 "register_operand" "") + (if_then_else:SF (match_operand 1 "comparison_operator" "") + (match_operand:SF 2 "nonmemory_operand" "") + (match_operand:SF 3 "register_operand" "")))] + "" + " +{ + enum rtx_code code = GET_CODE (operands[1]); + rtx cc_reg = gen_compare_reg (code, XEXP (operands[1], 0), + XEXP (operands[1], 1)); + operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); +}") + +(define_insn "*movsicc_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI (match_operand 1 "comparison_operator" "") + (match_operand:SI 2 "nonmemory_operand" "rJi") + (match_operand:SI 3 "register_operand" "0")))] + "" + "mov.%d1 %0,%S2" + [(set_attr "type" "cmove")]) + +(define_insn "*movsfcc_insn" + [(set (match_operand:SF 0 "register_operand" "=r,r") + (if_then_else:SF (match_operand 1 "comparison_operator" "") + (match_operand:SF 2 "nonmemory_operand" "r,E") + (match_operand:SF 3 "register_operand" "0,0")))] + "" + "@ + mov.%d1 %0,%2 + mov.%d1 %0,%2 ; %A2" + [(set_attr "type" "cmove,cmove")]) + + +;; Zero extension instructions. +;; ??? We don't support volatile memrefs here, but I'm not sure why. + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (zero_extend:HI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + extb%? %0,%1 + ldb%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*zero_extendqihi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (match_dup 1)))] + "" + "extb%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + extb%? %0,%1 + ldb%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*zero_extendqisi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_dup 1)))] + "" + "extb%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (zero_extend:SI (match_operand:HI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + extw%? %0,%1 + ldw%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*zero_extendhisi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (zero_extend:SI (match_operand:HI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_dup 1)))] + "" + "extw%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +;; Sign extension instructions. + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (sign_extend:HI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + sexb%? %0,%1 + ldb.x%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*extendqihi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (sign_extend:SI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI (match_dup 1)))] + "" + "sexb%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (sign_extend:SI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + sexb%? %0,%1 + ldb.x%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*extendqisi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (sign_extend:SI (match_operand:QI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_dup 1)))] + "" + "sexb%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (sign_extend:SI (match_operand:HI 1 "nonvol_nonimm_operand" "r,m")))] + "" + "@ + sexw%? %0,%1 + ldw.x%U1 %0,%1" + [(set_attr "type" "unary,load")]) + +(define_insn "*extendhisi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (sign_extend:SI (match_operand:HI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_dup 1)))] + "" + "sexw%?.f %0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +;; Arithmetic instructions. + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")))] + "" + "add%? %0,%1,%2") + +(define_insn "*addsi3_set_cc_insn" + [(set (reg:CC 61) (compare:CC + (plus:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "" + "add%?.f %0,%1,%2" + [(set_attr "cond" "set")]) + +(define_insn "adddi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "nonmemory_operand" "%r") + (match_operand:DI 2 "nonmemory_operand" "ri"))) + (clobber (reg:CC 61))] + "" + "* +{ + rtx op2 = operands[2]; + + if (GET_CODE (op2) == CONST_INT) + { + int sign = INTVAL (op2); + if (sign < 0) + return \"add.f %L0,%L1,%2\;adc %H0,%H1,-1\"; + else + return \"add.f %L0,%L1,%2\;adc %H0,%H1,0\"; + } + else + return \"add.f %L0,%L1,%L2\;adc %H0,%H1,%H2\"; +}" + [(set_attr "length" "2")]) + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")))] + "" + "sub%? %0,%1,%2") + +(define_insn "*subsi3_set_cc_insn" + [(set (reg:CC 61) (compare:CC + (minus:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_dup 1) + (match_dup 2)))] + "" + "sub%?.f %0,%1,%2" + [(set_attr "cond" "set")]) + +(define_insn "subdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "nonmemory_operand" "r") + (match_operand:DI 2 "nonmemory_operand" "ri"))) + (clobber (reg:CC 61))] + "" + "* +{ + rtx op2 = operands[2]; + + if (GET_CODE (op2) == CONST_INT) + { + int sign = INTVAL (op2); + if (sign < 0) + return \"sub.f %L0,%L1,%2\;sbc %H0,%H1,-1\"; + else + return \"sub.f %L0,%L1,%2\;sbc %H0,%H1,0\"; + } + else + return \"sub.f %L0,%L1,%L2\;sbc %H0,%H1,%H2\"; +}" + [(set_attr "length" "2")]) + +;; Boolean instructions. +;; +;; We don't define the DImode versions as expand_binop does a good enough job. + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")))] + "" + "and%? %0,%1,%2") + +(define_insn "*andsi3_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (and:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (and:SI (match_dup 1) + (match_dup 2)))] + "" + "and%?.f %0,%1,%2" + [(set_attr "cond" "set_zn")]) + +(define_insn "*bicsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (and:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") + (not:SI (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r"))))] + "" + "bic%? %0,%1,%2" + [(set_attr "length" "1,2,1,2")]) + +(define_insn "*bicsi3_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (and:SI (match_operand:SI 1 "register_operand" "%r") + (not:SI (match_operand:SI 2 "nonmemory_operand" "rIJ"))) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (and:SI (match_dup 1) + (not:SI (match_dup 2))))] + "" + "bic%?.f %0,%1,%2" + [(set_attr "cond" "set_zn")]) + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")))] + "" + "or%? %0,%1,%2") + +(define_insn "*iorsi3_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (ior:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_dup 1) + (match_dup 2)))] + "" + "or%?.f %0,%1,%2" + [(set_attr "cond" "set_zn")]) + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (xor:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")))] + "" + "xor%? %0,%1,%2") + +(define_insn "*xorsi3_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (xor:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rIJ")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (xor:SI (match_dup 1) + (match_dup 2)))] + "" + "xor%?.f %0,%1,%2" + [(set_attr "cond" "set_zn")]) + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "sub%? %0,0,%1" + [(set_attr "type" "unary")]) + +(define_insn "*negsi2_set_cc_insn" + [(set (reg:CC 61) (compare:CC + (neg:SI (match_operand:SI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_dup 1)))] + "" + "sub%?.f %0,0,%1" + [(set_attr "type" "unary") + (set_attr "cond" "set")]) + +(define_insn "negdi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (neg:DI (match_operand:DI 1 "register_operand" "r"))) + (clobber (reg:SI 61))] + "" + "sub.f %L0,0,%L1\;sbc %H0,0,%H1" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "xor%? %0,%1,-1" + [(set_attr "type" "unary")]) + +(define_insn "*one_cmplsi2_set_cc_insn" + [(set (reg:CCZN 61) (compare:CCZN + (not:SI (match_operand:SI 1 "register_operand" "r")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_dup 1)))] + "" + "xor%?.f %0,%1,-1" + [(set_attr "type" "unary") + (set_attr "cond" "set_zn")]) + +;; Shift instructions. + +(define_expand "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashift:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")))] + "" + " +{ + if (! TARGET_SHIFTER) + { + emit_insn (gen_rtx_PARALLEL + (VOIDmode, + gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_ASHIFT (SImode, operands[1], + operands[2])), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_SCRATCH (SImode))))); + DONE; + } +}") + +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")))] + "" + " +{ + if (! TARGET_SHIFTER) + { + emit_insn (gen_rtx_PARALLEL + (VOIDmode, + gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_ASHIFTRT (SImode, + operands[1], + operands[2])), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_SCRATCH (SImode))))); + DONE; + } +}") + +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")))] + "" + " +{ + if (! TARGET_SHIFTER) + { + emit_insn (gen_rtx_PARALLEL + (VOIDmode, + gen_rtvec (2, + gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_LSHIFTRT (SImode, + operands[1], + operands[2])), + gen_rtx_CLOBBER (VOIDmode, + gen_rtx_SCRATCH (SImode))))); + DONE; + } +}") + +(define_insn "*ashlsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (ashift:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") + (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] + "TARGET_SHIFTER" + "asl%? %0,%1,%2" + [(set_attr "type" "shift") + (set_attr "length" "1,2,1,2")]) + +(define_insn "*ashrsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (ashiftrt:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") + (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] + "TARGET_SHIFTER" + "asr%? %0,%1,%2" + [(set_attr "type" "shift") + (set_attr "length" "1,2,1,2")]) + +(define_insn "*lshrsi3_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (lshiftrt:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") + (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] + "TARGET_SHIFTER" + "lsr%? %0,%1,%2" + [(set_attr "type" "shift") + (set_attr "length" "1,2,1,2")]) + +(define_insn "*shift_si3" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 3 "shift_operator" + [(match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "nonmemory_operand" "rIJ")])) + (clobber (match_scratch:SI 4 "=&r"))] + "! TARGET_SHIFTER" + "* return output_shift (operands);" + [(set_attr "type" "shift") + (set_attr "length" "8")]) + +;; Compare instructions. +;; This controls RTL generation and register allocation. + +;; ??? We may be able to relax this a bit by adding a new constant 'K' for 0. +;; This assumes sub.f 0,symbol,0 is a valid insn. +;; Note that "sub.f 0,r0,1" is an 8 byte insn. To avoid unnecessarily +;; creating 8 byte insns we duplicate %1 in the destination reg of the insn +;; if it's a small constant. + +(define_insn "*cmpsi_cc_insn" + [(set (reg:CC 61) + (compare:CC (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] + "" + "@ + sub.f 0,%0,%1 + sub.f %1,%0,%1 + sub.f 0,%0,%1" + [(set_attr "type" "compare,compare,compare")]) + +(define_insn "*cmpsi_cczn_insn" + [(set (reg:CCZN 61) + (compare:CCZN (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] + "" + "@ + sub.f 0,%0,%1 + sub.f %1,%0,%1 + sub.f 0,%0,%1" + [(set_attr "type" "compare,compare,compare")]) + +(define_insn "*cmpsi_ccznc_insn" + [(set (reg:CCZNC 61) + (compare:CCZNC (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] + "" + "@ + sub.f 0,%0,%1 + sub.f %1,%0,%1 + sub.f 0,%0,%1" + [(set_attr "type" "compare,compare,compare")]) + +;; Next come the scc insn and its expander. + +(define_expand "cstoresi4" + [(set (match_dup 4) + (match_op_dup 5 + [(match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "nonmemory_operand" "")])) + (set (match_operand:SI 0 "register_operand") + (match_operator:SI 1 "ordered_comparison_operator" + [(match_dup 4) + (const_int 0)]))] + "" + " +{ + operands[4] = gen_compare_reg (GET_CODE (operands[1]), + operands[2], operands[3]); + operands[5] = gen_rtx_fmt_ee (COMPARE, + GET_MODE (operands[4]), + operands[2], operands[3]); +}") + +(define_insn "*scc_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operator:SI 1 "comparison_operator" [(reg 61) (const_int 0)]))] + "" + "mov %0,1\;sub.%D1 %0,%0,%0" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +;; ??? Look up negscc insn. See pa.md for example. +(define_insn "*neg_scc_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operator:SI 1 "comparison_operator" + [(reg 61) (const_int 0)])))] + "" + "mov %0,-1\;sub.%D1 %0,%0,%0" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +(define_insn "*not_scc_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operator:SI 1 "comparison_operator" + [(reg 61) (const_int 0)])))] + "" + "mov %0,1\;sub.%d1 %0,%0,%0" + [(set_attr "type" "unary") + (set_attr "length" "2")]) + +;; These control RTL generation for conditional jump insns + +(define_expand "cbranchsi4" + [(set (match_dup 4) + (match_op_dup 5 + [(match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "nonmemory_operand" "")])) + (set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_dup 4) + (const_int 0)]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + " +{ + operands[4] = gen_compare_reg (GET_CODE (operands[0]), + operands[1], operands[2]); + operands[5] = gen_rtx_fmt_ee (COMPARE, + GET_MODE (operands[4]), + operands[1], operands[2]); +}") + +;; Now match both normal and inverted jump. + +(define_insn "*branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "proper_comparison_operator" + [(reg 61) (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + if (arc_ccfsm_branch_deleted_p ()) + { + arc_ccfsm_record_branch_deleted (); + return \"; branch deleted, next insns conditionalized\"; + } + else + return \"%~b%d1%# %l0\"; +}" + [(set_attr "type" "branch")]) + +(define_insn "*rev_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "proper_comparison_operator" + [(reg 61) (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "REVERSIBLE_CC_MODE (GET_MODE (XEXP (operands[1], 0)))" + "* +{ + if (arc_ccfsm_branch_deleted_p ()) + { + arc_ccfsm_record_branch_deleted (); + return \"; branch deleted, next insns conditionalized\"; + } + else + return \"%~b%D1%# %l0\"; +}" + [(set_attr "type" "branch")]) + +;; Unconditional and other jump instructions. + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "b%* %l0" + [(set_attr "type" "uncond_branch")]) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "address_operand" "p"))] + "" + "j%* %a0" + [(set_attr "type" "uncond_branch")]) + +;; Implement a switch statement. +;; This wouldn't be necessary in the non-pic case if we could distinguish +;; label refs of the jump table from other label refs. The problem is that +;; label refs are output as "%st(.LL42)" but we don't want the %st - we want +;; the real address since it's the address of the table. + +(define_expand "casesi" + [(set (match_dup 5) + (minus:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonmemory_operand" ""))) + (set (reg:CC 61) + (compare:CC (match_dup 5) + (match_operand:SI 2 "nonmemory_operand" ""))) + (set (pc) + (if_then_else (gtu (reg:CC 61) + (const_int 0)) + (label_ref (match_operand 4 "" "")) + (pc))) + (parallel + [(set (pc) + (mem:SI (plus:SI (mult:SI (match_dup 5) + (const_int 4)) + (label_ref (match_operand 3 "" ""))))) + (clobber (match_scratch:SI 6 "")) + (clobber (match_scratch:SI 7 ""))])] + "" + " +{ + operands[5] = gen_reg_rtx (SImode); +}") + +(define_insn "*casesi_insn" + [(set (pc) + (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "r") + (const_int 4)) + (label_ref (match_operand 1 "" ""))))) + (clobber (match_scratch:SI 2 "=r")) + (clobber (match_scratch:SI 3 "=r"))] + "" + "* +{ + output_asm_insn (\"mov %2,%1\", operands); + if (TARGET_SHIFTER) + output_asm_insn (\"asl %3,%0,2\", operands); + else + output_asm_insn (\"asl %3,%0\;asl %3,%3\", operands); + output_asm_insn (\"ld %2,[%2,%3]\", operands); + output_asm_insn (\"j.nd %a2\", operands); + return \"\"; +}" + [(set_attr "type" "uncond_branch") + (set_attr "length" "6")]) + +(define_insn "tablejump" + [(set (pc) (match_operand:SI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))] + "0 /* disabled -> using casesi now */" + "j%* %a0" + [(set_attr "type" "uncond_branch")]) + +(define_expand "call" + ;; operands[1] is stack_size_rtx + ;; operands[2] is next_arg_register + [(parallel [(call (match_operand:SI 0 "call_operand" "") + (match_operand 1 "" "")) + (clobber (reg:SI 31))])] + "" + "") + +(define_insn "*call_via_reg" + [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) + (match_operand 1 "" "")) + (clobber (reg:SI 31))] + "" + "lr blink,[status]\;j.d %0\;add blink,blink,2" + [(set_attr "type" "call_no_delay_slot") + (set_attr "length" "3")]) + +(define_insn "*call_via_label" + [(call (mem:SI (match_operand:SI 0 "call_address_operand" "")) + (match_operand 1 "" "")) + (clobber (reg:SI 31))] + "" + ; The %~ is necessary in case this insn gets conditionalized and the previous + ; insn is the cc setter. + "%~bl%!%* %0" + [(set_attr "type" "call") + (set_attr "cond" "canuse")]) + +(define_expand "call_value" + ;; operand 2 is stack_size_rtx + ;; operand 3 is next_arg_register + [(parallel [(set (match_operand 0 "register_operand" "=r") + (call (match_operand:SI 1 "call_operand" "") + (match_operand 2 "" ""))) + (clobber (reg:SI 31))])] + "" + "") + +(define_insn "*call_value_via_reg" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand 2 "" ""))) + (clobber (reg:SI 31))] + "" + "lr blink,[status]\;j.d %1\;add blink,blink,2" + [(set_attr "type" "call_no_delay_slot") + (set_attr "length" "3")]) + +(define_insn "*call_value_via_label" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:SI (match_operand:SI 1 "call_address_operand" "")) + (match_operand 2 "" ""))) + (clobber (reg:SI 31))] + "" + ; The %~ is necessary in case this insn gets conditionalized and the previous + ; insn is the cc setter. + "%~bl%!%* %1" + [(set_attr "type" "call") + (set_attr "cond" "canuse")]) + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "misc")]) + +;; Special pattern to flush the icache. +;; ??? Not sure what to do here. Some ARC's are known to support this. + +(define_insn "flush_icache" + [(unspec_volatile [(match_operand 0 "memory_operand" "m")] 0)] + "" + "* return \"\";" + [(set_attr "type" "misc")]) + +;; Split up troublesome insns for better scheduling. + +;; Peepholes go at the end. diff --git a/gcc/config/arc/arc.opt b/gcc/config/arc/arc.opt new file mode 100644 index 000000000..e5381f95a --- /dev/null +++ b/gcc/config/arc/arc.opt @@ -0,0 +1,60 @@ +; Options for the Argonaut ARC port of the compiler +; +; Copyright (C) 2005, 2007, 2011 Free Software Foundation, Inc. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it under +; the terms of the GNU General Public License as published by the Free +; Software Foundation; either version 3, or (at your option) any later +; version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT +; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +; License for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; <http://www.gnu.org/licenses/>. + +EB +Driver + +EL +Driver + +malign-loops +Target Undocumented Report Mask(ALIGN_LOOPS) + +mbig-endian +Target Undocumented Report RejectNegative Mask(BIG_ENDIAN) + +mlittle-endian +Target Undocumented Report RejectNegative InverseMask(BIG_ENDIAN) + +mmangle-cpu +Target Report Mask(MANGLE_CPU) +Prepend the name of the cpu to all public symbol names + +; mmangle-cpu-libgcc +; Target Undocumented Mask(MANGLE_CPU_LIBGC) + +mno-cond-exec +Target Undocumented Report RejectNegative Mask(NO_COND_EXEC) + +mcpu= +Target RejectNegative Joined Var(arc_cpu_string) Init("base") +-mcpu=CPU Compile code for ARC variant CPU + +mtext= +Target RejectNegative Joined Var(arc_text_string) Init(ARC_DEFAULT_TEXT_SECTION) +-mtext=SECTION Put functions in SECTION + +mdata= +Target RejectNegative Joined Var(arc_data_string) Init(ARC_DEFAULT_DATA_SECTION) +-mdata=SECTION Put data in SECTION + +mrodata= +Target RejectNegative Joined Var(arc_rodata_string) Init(ARC_DEFAULT_RODATA_SECTION) +-mrodata=SECTION Put read-only data in SECTION diff --git a/gcc/config/arc/initfini.c b/gcc/config/arc/initfini.c new file mode 100644 index 000000000..d7514133a --- /dev/null +++ b/gcc/config/arc/initfini.c @@ -0,0 +1,155 @@ +/* .init/.fini section handling + C++ global constructor/destructor handling. + This file is based on crtstuff.c, sol2-crti.asm, sol2-crtn.asm. + +Copyright (C) 1995, 1997, 1998, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* Declare a pointer to void function type. */ +typedef void (*func_ptr) (void); + +#ifdef CRT_INIT + +/* NOTE: In order to be able to support SVR4 shared libraries, we arrange + to have one set of symbols { __CTOR_LIST__, __DTOR_LIST__, __CTOR_END__, + __DTOR_END__ } per root executable and also one set of these symbols + per shared library. So in any given whole process image, we may have + multiple definitions of each of these symbols. In order to prevent + these definitions from conflicting with one another, and in order to + ensure that the proper lists are used for the initialization/finalization + of each individual shared library (respectively), we give these symbols + only internal (i.e. `static') linkage, and we also make it a point to + refer to only the __CTOR_END__ symbol in crtfini.o and the __DTOR_LIST__ + symbol in crtinit.o, where they are defined. */ + +static func_ptr __CTOR_LIST__[1] __attribute__ ((section (".ctors"))) + = { (func_ptr) (-1) }; + +static func_ptr __DTOR_LIST__[1] __attribute__ ((section (".dtors"))) + = { (func_ptr) (-1) }; + +/* Run all the global destructors on exit from the program. */ + +/* Some systems place the number of pointers in the first word of the + table. On SVR4 however, that word is -1. In all cases, the table is + null-terminated. On SVR4, we start from the beginning of the list and + invoke each per-compilation-unit destructor routine in order + until we find that null. + + Note that this function MUST be static. There will be one of these + functions in each root executable and one in each shared library, but + although they all have the same code, each one is unique in that it + refers to one particular associated `__DTOR_LIST__' which belongs to the + same particular root executable or shared library file. */ + +static void __do_global_dtors (void) +asm ("__do_global_dtors") __attribute__ ((section (".text"))); + +static void +__do_global_dtors (void) +{ + func_ptr *p; + for (p = __DTOR_LIST__ + 1; *p; p++) + (*p) (); +} + +/* .init section start. + This must appear at the start of the .init section. */ + +asm ("\n\ + .section .init\n\ + .global init\n\ + .word 0\n\ +init:\n\ + st blink,[sp,4]\n\ + st fp,[sp]\n\ + mov fp,sp\n\ + sub sp,sp,16\n\ +"); + +/* .fini section start. + This must appear at the start of the .init section. */ + +asm ("\n\ + .section .fini\n\ + .global fini\n\ + .word 0\n\ +fini:\n\ + st blink,[sp,4]\n\ + st fp,[sp]\n\ + mov fp,sp\n\ + sub sp,sp,16\n\ + bl.nd __do_global_dtors\n\ +"); + +#endif /* CRT_INIT */ + +#ifdef CRT_FINI + +/* Put a word containing zero at the end of each of our two lists of function + addresses. Note that the words defined here go into the .ctors and .dtors + sections of the crtend.o file, and since that file is always linked in + last, these words naturally end up at the very ends of the two lists + contained in these two sections. */ + +static func_ptr __CTOR_END__[1] __attribute__ ((section (".ctors"))) + = { (func_ptr) 0 }; + +static func_ptr __DTOR_END__[1] __attribute__ ((section (".dtors"))) + = { (func_ptr) 0 }; + +/* Run all global constructors for the program. + Note that they are run in reverse order. */ + +static void __do_global_ctors (void) +asm ("__do_global_ctors") __attribute__ ((section (".text"))); + +static void +__do_global_ctors (void) +{ + func_ptr *p; + for (p = __CTOR_END__ - 1; *p != (func_ptr) -1; p--) + (*p) (); +} + +/* .init section end. + This must live at the end of the .init section. */ + +asm ("\n\ + .section .init\n\ + bl.nd __do_global_ctors\n\ + ld blink,[fp,4]\n\ + j.d blink\n\ + ld.a fp,[sp,16]\n\ +"); + +/* .fini section end. + This must live at the end of the .fini section. */ + +asm ("\n\ + .section .fini\n\ + ld blink,[fp,4]\n\ + j.d blink\n\ + ld.a fp,[sp,16]\n\ +"); + +#endif /* CRT_FINI */ diff --git a/gcc/config/arc/lib1funcs.asm b/gcc/config/arc/lib1funcs.asm new file mode 100644 index 000000000..c61f39a5c --- /dev/null +++ b/gcc/config/arc/lib1funcs.asm @@ -0,0 +1,266 @@ +; libgcc routines for ARC cpu. + +/* Copyright (C) 1995, 1997,2004, 2009 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#ifdef L_mulsi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___mulsi3 +___mulsi3: + +/* This the simple version. + + while (a) + { + if (a & 1) + r += b; + a >>= 1; + b <<= 1; + } +*/ + mov r2,0 ; Accumulate result here. +.Lloop: + sub.f 0,r0,0 ; while (a) + nop + beq.nd .Ldone + and.f 0,r0,1 ; if (a & 1) + add.nz r2,r2,r1 ; r += b + lsr r0,r0 ; a >>= 1 + b.d .Lloop + lsl r1,r1 ; b <<= 1 +.Ldone: + j.d blink + mov r0,r2 +#endif + +#endif /* L_mulsi3 */ + +#ifdef L_umulsidi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___umulsidi3 +___umulsidi3: + +/* This the simple version. + + while (a) + { + if (a & 1) + r += b; + a >>= 1; + b <<= 1; + } +*/ + mov r2,0 ; Top part of b. + mov r3,0 ; Accumulate result here. + mov r4,0 +.Lloop: + sub.f 0,r0,0 ; while (a) + nop + beq.nd .Ldone + and.f 0,r0,1 ; if (a & 1) + sub.f 0,r0,0 + nop + beq .Ldontadd + add.f r4,r4,r1 ; r += b + adc r3,r3,r2 +.Ldontadd: + lsr r0,r0 ; a >>= 1 + lsl.f r1,r1 ; b <<= 1 + b.d .Lloop + rlc r2,r2 +.Ldone: +#ifdef __big_endian__ + mov r1,r4 + j.d blink + mov r0,r3 +#else + mov r0,r4 + j.d blink + mov r1,r3 +#endif +#endif + +#endif /* L_umulsidi3 */ + +#ifdef L_divmod_tools + +; Utilities used by all routines. + + .section .text + .align 4 + +; inputs: r0 = numerator, r1 = denominator +; outputs: positive r0/r1, +; r6.bit1 = sign of numerator, r6.bit0 = sign of result + + .global ___divnorm +___divnorm: + mov r6,0 ; keep sign in r6 + sub.f 0,r0,0 ; is numerator -ve? + sub.lt r0,0,r0 ; negate numerator + mov.lt r6,3 ; sign is -ve + sub.f 0,r1,0 ; is denominator -ve? + sub.lt r1,0,r1 ; negate denominator + xor.lt r6,r6,1 ; toggle sign + j.nd blink + +/* +unsigned long +udivmodsi4(int modwanted, unsigned long num, unsigned long den) +{ + unsigned long bit = 1; + unsigned long res = 0; + + while (den < num && bit && !(den & (1L<<31))) + { + den <<=1; + bit <<=1; + } + while (bit) + { + if (num >= den) + { + num -= den; + res |= bit; + } + bit >>=1; + den >>=1; + } + if (modwanted) return num; + return res; +} +*/ + +; inputs: r0 = numerator, r1 = denominator +; outputs: r0 = quotient, r1 = remainder, r2/r3 trashed + + .global ___udivmodsi4 +___udivmodsi4: + mov r2,1 ; bit = 1 + mov r3,0 ; res = 0 +.Lloop1: + sub.f 0,r1,r0 ; while (den < num + nop + bnc.nd .Lloop2 + sub.f 0,r2,0 ; && bit + nop + bz.nd .Lloop2 + lsl.f 0,r1 ; && !(den & (1<<31)) + nop + bc.nd .Lloop2 + lsl r1,r1 ; den <<= 1 + b.d .Lloop1 + lsl r2,r2 ; bit <<= 1 +.Lloop2: + sub.f 0,r2,0 ; while (bit) + nop + bz.nd .Ldivmodend + sub.f 0,r0,r1 ; if (num >= den) + nop + bc.nd .Lshiftdown + sub r0,r0,r1 ; num -= den + or r3,r3,r2 ; res |= bit +.Lshiftdown: + lsr r2,r2 ; bit >>= 1 + b.d .Lloop2 + lsr r1,r1 ; den >>= 1 +.Ldivmodend: + mov r1,r0 ; r1 = mod + j.d blink + mov r0,r3 ; r0 = res + +#endif + +#ifdef L_udivsi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___udivsi3 +___udivsi3: + mov r7,blink + bl.nd ___udivmodsi4 + j.nd r7 +#endif + +#endif /* L_udivsi3 */ + +#ifdef L_divsi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___divsi3 +___divsi3: + mov r7,blink + bl.nd ___divnorm + bl.nd ___udivmodsi4 + and.f 0,r6,1 + sub.nz r0,0,r0 ; cannot go in delay slot, has limm value + j.nd r7 +#endif + +#endif /* L_divsi3 */ + +#ifdef L_umodsi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___umodsi3 +___umodsi3: + mov r7,blink + bl.nd ___udivmodsi4 + j.d r7 + mov r0,r1 +#endif + +#endif /* L_umodsi3 */ + +#ifdef L_modsi3 + .section .text + .align 4 + +#ifdef __base__ + .cpu base + .global ___modsi3 +___modsi3: + mov r7,blink + bl.nd ___divnorm + bl.nd ___udivmodsi4 + and.f 0,r6,2 + sub.nz r1,0,r1 + j.d r7 + mov r0,r1 +#endif + +#endif /* L_modsi3 */ diff --git a/gcc/config/arc/t-arc b/gcc/config/arc/t-arc new file mode 100644 index 000000000..a923479ca --- /dev/null +++ b/gcc/config/arc/t-arc @@ -0,0 +1,60 @@ +# Copyright (C) 1997, 1998, 1999, 2001, 2002, 2003, +# 2004 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +LIB1ASMSRC = arc/lib1funcs.asm +LIB1ASMFUNCS = _mulsi3 _umulsidi3 _udivsi3 _divsi3 _umodsi3 _modsi3 _divmod_tools + +# We need libgcc routines to be mangled according to which cpu they +# were compiled for. +# ??? -mmangle-cpu passed by default for now. +#LIBGCC2_CFLAGS = -g1 -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) -mmangle-cpu + +# 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 + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#ifndef __big_endian__' > dp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c + echo '#endif' >> dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + echo '#ifndef __big_endian__' >> fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c + echo '#endif' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +# .init/.fini section routines + +$(T)crtinit.o: $(srcdir)/config/arc/initfini.c $(GCC_PASSES) $(CONFIG_H) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(CRTSTUFF_T_CFLAGS) \ + $(MULTILIB_CFLAGS) -DCRT_INIT -finhibit-size-directive -fno-inline-functions \ + -g0 -c $(srcdir)/config/arc/initfini.c -o $(T)crtinit.o + +$(T)crtfini.o: $(srcdir)/config/arc/initfini.c $(GCC_PASSES) $(CONFIG_H) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(CRTSTUFF_T_CFLAGS) \ + -DCRT_FINI $(MULTILIB_CFLAGS) -finhibit-size-directive -fno-inline-functions \ + -g0 -c $(srcdir)/config/arc/initfini.c -o $(T)crtfini.o + +MULTILIB_OPTIONS = EB +MULTILIB_DIRNAMES = be +EXTRA_MULTILIB_PARTS = crtinit.o crtfini.o |