diff options
Diffstat (limited to 'gcc/config/arc/arc.c')
-rwxr-xr-x | gcc/config/arc/arc.c | 2203 |
1 files changed, 0 insertions, 2203 deletions
diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c deleted file mode 100755 index 64aec44..0000000 --- a/gcc/config/arc/arc.c +++ /dev/null @@ -1,2203 +0,0 @@ -/* Subroutines used for code generation on the Argonaut ARC cpu. - Copyright (C) 1994, 1995, 1997 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* ??? This is an old port, and is undoubtedly suffering from bit rot. */ - -#include <stdio.h> -#include "config.h" -#include "tree.h" -#include "rtl.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "real.h" -#include "insn-config.h" -#include "conditions.h" -#include "insn-flags.h" -#include "output.h" -#include "insn-attr.h" -#include "flags.h" -#include "expr.h" -#include "recog.h" - -/* Which cpu we're compiling for (NULL(=base), ???). */ -char *arc_cpu_string; -int arc_cpu_type; - -/* Name of mangle string to add to symbols to separate code compiled for each - cpu (or NULL). */ -char *arc_mangle_cpu; - -/* Save the operands last given to a compare for use when we - generate a scc or bcc insn. */ -rtx arc_compare_op0, arc_compare_op1; - -/* Name of text, data, and rodata sections, as specified on command line. - Selected by -m{text,data,rodata} flags. */ -char *arc_text_string = ARC_DEFAULT_TEXT_SECTION; -char *arc_data_string = ARC_DEFAULT_DATA_SECTION; -char *arc_rodata_string = ARC_DEFAULT_RODATA_SECTION; - -/* Name of text, data, and rodata sections used in varasm.c. */ -char *arc_text_section; -char *arc_data_section; -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 void record_cc_ref (); - -void arc_init_reg_tables (); - -/* Called by OVERRIDE_OPTIONS to initialize various things. */ - -void -arc_init (void) -{ - if (arc_cpu_string == 0 - || !strcmp (arc_cpu_string, "base")) - { - /* Ensure we have a printable value for the .cpu pseudo-op. */ - arc_cpu_string = "base"; - arc_cpu_type = 0; - arc_mangle_cpu = NULL; - } - else if (ARC_EXTENSION_CPU (arc_cpu_string)) - ; /* nothing to do */ - else - { - error ("bad value (%s) for -mcpu switch", arc_cpu_string); - arc_cpu_string = "base"; - arc_cpu_type = 0; - arc_mangle_cpu = NULL; - } - - /* Set the pseudo-ops for the various standard sections. */ - arc_text_section = xmalloc (strlen (arc_text_string) + sizeof (ARC_SECTION_FORMAT) + 1); - sprintf (arc_text_section, ARC_SECTION_FORMAT, arc_text_string); - arc_data_section = xmalloc (strlen (arc_data_string) + sizeof (ARC_SECTION_FORMAT) + 1); - sprintf (arc_data_section, ARC_SECTION_FORMAT, arc_data_string); - arc_rodata_section = xmalloc (strlen (arc_rodata_string) + sizeof (ARC_SECTION_FORMAT) + 1); - sprintf (arc_rodata_section, 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 char *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 (comparison) - 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 : abort (); - } - /*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 (op, x, y) - enum rtx_code op; - rtx x, y; -{ - 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; - } - } - 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. */ - -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]; - -void -arc_init_reg_tables () -{ - 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: - default: - /* mode_class hasn't been initialized yet for EXTRA_CC_MODES, so - we must explicitly check for them here. */ - if (i == (int) CCmode || i == (int) CCZNmode || i == (int) CCZNCmode) - arc_mode_class[i] = 1 << (int) C_MODE; - else - 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 -*/ - -/* Return nonzero if IDENTIFIER is a valid decl attribute. */ - -int -arc_valid_machine_decl_attribute (type, attributes, identifier, args) - tree type; - tree attributes; - tree identifier; - tree args; -{ - if (identifier == 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") - || !strcmp (TREE_STRING_POINTER (value), "ilink2")) - return 1; - } - return 0; -} - -/* Return zero if TYPE1 and TYPE are incompatible, one if they are compatible, - and two if they are nearly compatible (which causes a warning to be - generated). */ - -int -arc_comp_type_attributes (type1, type2) - tree type1, type2; -{ - return 1; -} - -/* Set the default attributes for TYPE. */ - -void -arc_set_default_type_attributes (type) - tree type; -{ -} - -/* Acceptable arguments to the call insn. */ - -int -call_address_operand (op, mode) - 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 (op, mode) - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - 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; - } - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - 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; - } - return 0; -} - -/* Return true if OP is an acceptable argument for a single word - move source. */ - -int -move_src_operand (op, mode) - 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 (op, mode) - 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 (op, mode) - 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 (op, mode) - 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 (op, mode) - 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 (op, mode) - 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 (op, mode) - rtx op; - enum machine_mode mode; -{ - /* 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 (op, mode) - rtx op; - enum machine_mode mode; -{ -#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 (op, mode) - rtx op; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (op); - - if (GET_RTX_CLASS (code) != '<') - return 0; - - 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. Emit the compare insn and - return the rtx for the cc reg in the proper mode. */ - -rtx -gen_compare_reg (code, x, y) - enum rtx_code code; - rtx x, y; -{ - enum machine_mode mode = SELECT_CC_MODE (code, x, y); - rtx cc_reg; - - cc_reg = gen_rtx (REG, mode, 61); - - emit_insn (gen_rtx (SET, VOIDmode, cc_reg, - gen_rtx (COMPARE, mode, x, y))); - - return cc_reg; -} - -/* 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 (value) - rtx value; -{ - HOST_WIDE_INT low, high; - - if (GET_CODE (value) != CONST_DOUBLE) - abort (); - - 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-arc.h - to compensate. */ - -void -arc_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) - CUMULATIVE_ARGS *cum; - enum machine_mode mode; - tree type; - int *pretend_size; - int no_rtl; -{ - int first_anon_arg; - - /* All BLKmode values are passed by reference. */ - if (mode == BLKmode) - abort (); - - /* We must treat `__builtin_va_alist' as an anonymous arg. */ - if (current_function_varargs) - first_anon_arg = *cum; - else - 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)); - move_block_from_reg (first_reg_offset, regblock, - MAX_ARC_PARM_REGS - first_reg_offset, - ((MAX_ARC_PARM_REGS - first_reg_offset) - * UNITS_PER_WORD)); - - *pretend_size = ((MAX_ARC_PARM_REGS - first_reg_offset + align_slop) - * UNITS_PER_WORD); - } -} - -/* Cost functions. */ - -/* Provide the costs of an addressing mode that contains ADDR. - If ADDR is not a valid address, its cost is irrelevant. */ - -int -arc_address_cost (addr) - rtx addr; -{ - switch (GET_CODE (addr)) - { - case REG : - /* This is handled in the macro that calls us. - It's here for documentation. */ - 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 (plus1) ? 1 : 2; - case CONST : - case SYMBOL_REF : - case LABEL_REF : - return 2; - default: - break; - } - 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 (decl) - 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_MACHINE_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 - abort (); - 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 \ - && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p))) - -#define MUST_SAVE_RETURN_ADDR (regs_ever_live[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 (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 = current_function_outgoing_args_size; - pretend_size = current_function_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) + current_function_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, base_reg, offset, gmask, op) - FILE *file; - char *base_reg; - unsigned int offset; - unsigned int gmask; - 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; - } - } -} - -/* Set up the stack and frame pointer (if desired) for the function. */ - -void -arc_output_function_prologue (file, size) - FILE *file; - int size; -{ - char *sp_str = reg_names[STACK_POINTER_REGNUM]; - 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. */ - if (size == 0 && gmask) - abort (); - - /* 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,%d\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. */ - -void -arc_output_function_epilogue (file, size) - FILE *file; - int size; -{ - rtx epilogue_delay = current_function_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 = !current_function_calls_alloca; - char *sp_str = reg_names[STACK_POINTER_REGNUM]; - 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) - { - if (!frame_pointer_needed) - abort (); - 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, -2, 1); - 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, -2, 1); - } - } - - /* Emit the return instruction. */ - { - static int regs[4] = { - 0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM - }; - 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) - { - if (frame_pointer_needed && !fp_restored_p) - abort (); - if (restored < size) - abort (); - final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1); - } - else if (frame_pointer_needed && !fp_restored_p) - { - if (!SMALL_INT (frame_size)) - abort (); - /* 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) - { - if (!SMALL_INT (size - restored)) - abort (); - fprintf (file, "\tadd %s,%s,%d\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 () -{ - 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 (trial, slot) - rtx trial; - int slot; -{ - if (slot != 0) - abort (); - - 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; -} - -/* PIC */ - -/* Emit special PIC prologues and epilogues. */ - -void -arc_finalize_pic () -{ - /* nothing to do */ -} - -/* Return true if OP is a shift operator. */ - -int -shift_operator (op, mode) - rtx op; - enum machine_mode mode; -{ - 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. */ - -char * -output_shift (operands) - rtx *operands; -{ - static int loopend_lab; - rtx shift = operands[3]; - enum machine_mode mode = GET_MODE (shift); - enum rtx_code code = GET_CODE (shift); - char *shift_one; - - if (mode != SImode) - abort (); - - 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: abort (); - } - - if (GET_CODE (operands[2]) != CONST_INT) - { - if (optimize) - output_asm_insn ("mov lp_count,%2", operands); - else - output_asm_insn ("mov %4,%2", operands); - goto shiftloop; - } - else - { - int n = INTVAL (operands[2]); - - /* If the count is negative, make it 0. */ - 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; - } - } - /* 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 ("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) - asm_fprintf (asm_out_file, "\t%s single insn loop\n", - ASM_COMMENT_START); - else - asm_fprintf (asm_out_file, "1:\t%s single insn loop\n", - ASM_COMMENT_START); - output_asm_insn (shift_one, operands); - } - else - { - asm_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); - asm_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 (tramp, fnaddr, cxt) - rtx tramp, fnaddr, cxt; -{ -} - -/* Set the cpu type and print out other fancy things, - at the top of the file. */ - -void -arc_asm_file_start (file) - FILE *file; -{ - fprintf (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, x, code) - 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_FLAG (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", - code == 'L' ? INTVAL (first) : INTVAL (second)); - } - else - output_operand_lossage ("invalid operand to %H/%L code"); - return; - case 'A' : - { - REAL_VALUE_TYPE d; - char str[30]; - - if (GET_CODE (x) != CONST_DOUBLE - || GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT) - abort (); - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - REAL_VALUE_TO_DECIMAL (d, "%.20e", str); - 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, addr) - 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_FLAG (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); - if (GET_CODE (base) != REG) - abort (); - fputs (reg_names[REGNO (base)], file); - if (index == 0) - { - if (offset != 0) - fprintf (file, ",%d", offset); - } - else if (GET_CODE (index) == REG) - fprintf (file, ",%s", reg_names[REGNO (index)]); - else if (GET_CODE (index) == SYMBOL_REF) - fputc (',', file), output_addr_const (file, index); - else - abort (); - 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. */ - abort (); - break; - default : - output_addr_const (file, addr); - break; - } -} - -/* Update compare/branch separation marker. */ - -static void -record_cc_ref (insn) - 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 ASM_OUTPUT_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 (insn, opvec, noperands) - rtx insn; - rtx *opvec; - int noperands; -{ - /* BODY will hold the body of INSN. */ - register rtx body = PATTERN (insn); - - /* This will be 1 if trying to repeat the trick (ie: 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 - abort (); - - /* 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 if (seeking_return || arc_ccfsm_state == 2) - { - while (this_insn && GET_CODE (PATTERN (this_insn)) == USE) - { - this_insn = next_nonnote_insn (this_insn); - if (this_insn && (GET_CODE (this_insn) == BARRIER - || GET_CODE (this_insn) == CODE_LABEL)) - abort (); - } - if (!this_insn) - { - /* Oh dear! we ran off the end, give up. */ - insn_extract (insn); - arc_ccfsm_state = 0; - arc_ccfsm_target_insn = NULL; - return; - } - arc_ccfsm_target_insn = this_insn; - } - else - abort (); - - /* 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_operand. Getting the attributes of other insns can - destroy this array, but final.c assumes that it remains intact - across this call; since the insn has been recognized already we - call insn_extract direct. */ - insn_extract (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 ASM_OUTPUT_INTERNAL_LABEL. */ - -void -arc_ccfsm_at_label (prefix, num) - 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 () -{ - 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 () -{ - /* 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; -} |