X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;f=gcc%2Fconfig%2Fc4x%2Fc4x.c;fp=gcc%2Fconfig%2Fc4x%2Fc4x.c;h=0000000000000000000000000000000000000000;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=ee878dd709886e66511486dbeb1b9803e365293f;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/config/c4x/c4x.c b/gcc/config/c4x/c4x.c deleted file mode 100644 index ee878dd7..00000000 --- a/gcc/config/c4x/c4x.c +++ /dev/null @@ -1,5060 +0,0 @@ -/* Subroutines for assembler code output on the TMS320C[34]x - Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 - Free Software Foundation, Inc. - - Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz) - and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl). - -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. */ - -/* Some output-actions in c4x.md need these. */ -#include "config.h" -#include "system.h" -#include "rtl.h" -#include "tree.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "basic-block.h" -#include "real.h" -#include "insn-config.h" -#include "insn-attr.h" -#include "conditions.h" -#include "output.h" -#include "function.h" -#include "expr.h" -#include "optabs.h" -#include "libfuncs.h" -#include "flags.h" -#include "loop.h" -#include "recog.h" -#include "c-tree.h" -#include "ggc.h" -#include "cpplib.h" -#include "toplev.h" -#include "c4x-protos.h" -#include "target.h" -#include "target-def.h" - -rtx smulhi3_libfunc; -rtx umulhi3_libfunc; -rtx fix_truncqfhi2_libfunc; -rtx fixuns_truncqfhi2_libfunc; -rtx fix_trunchfhi2_libfunc; -rtx fixuns_trunchfhi2_libfunc; -rtx floathiqf2_libfunc; -rtx floatunshiqf2_libfunc; -rtx floathihf2_libfunc; -rtx floatunshihf2_libfunc; - -static int c4x_leaf_function; - -static const char *const float_reg_names[] = FLOAT_REGISTER_NAMES; - -/* Array of the smallest class containing reg number REGNO, indexed by - REGNO. Used by REGNO_REG_CLASS in c4x.h. We assume that all these - registers are available and set the class to NO_REGS for registers - that the target switches say are unavailable. */ - -enum reg_class c4x_regclass_map[FIRST_PSEUDO_REGISTER] = -{ - /* Reg Modes Saved. */ - R0R1_REGS, /* R0 QI, QF, HF No. */ - R0R1_REGS, /* R1 QI, QF, HF No. */ - R2R3_REGS, /* R2 QI, QF, HF No. */ - R2R3_REGS, /* R3 QI, QF, HF No. */ - EXT_LOW_REGS, /* R4 QI, QF, HF QI. */ - EXT_LOW_REGS, /* R5 QI, QF, HF QI. */ - EXT_LOW_REGS, /* R6 QI, QF, HF QF. */ - EXT_LOW_REGS, /* R7 QI, QF, HF QF. */ - ADDR_REGS, /* AR0 QI No. */ - ADDR_REGS, /* AR1 QI No. */ - ADDR_REGS, /* AR2 QI No. */ - ADDR_REGS, /* AR3 QI QI. */ - ADDR_REGS, /* AR4 QI QI. */ - ADDR_REGS, /* AR5 QI QI. */ - ADDR_REGS, /* AR6 QI QI. */ - ADDR_REGS, /* AR7 QI QI. */ - DP_REG, /* DP QI No. */ - INDEX_REGS, /* IR0 QI No. */ - INDEX_REGS, /* IR1 QI No. */ - BK_REG, /* BK QI QI. */ - SP_REG, /* SP QI No. */ - ST_REG, /* ST CC No. */ - NO_REGS, /* DIE/IE No. */ - NO_REGS, /* IIE/IF No. */ - NO_REGS, /* IIF/IOF No. */ - INT_REGS, /* RS QI No. */ - INT_REGS, /* RE QI No. */ - RC_REG, /* RC QI No. */ - EXT_REGS, /* R8 QI, QF, HF QI. */ - EXT_REGS, /* R9 QI, QF, HF No. */ - EXT_REGS, /* R10 QI, QF, HF No. */ - EXT_REGS, /* R11 QI, QF, HF No. */ -}; - -enum machine_mode c4x_caller_save_map[FIRST_PSEUDO_REGISTER] = -{ - /* Reg Modes Saved. */ - HFmode, /* R0 QI, QF, HF No. */ - HFmode, /* R1 QI, QF, HF No. */ - HFmode, /* R2 QI, QF, HF No. */ - HFmode, /* R3 QI, QF, HF No. */ - QFmode, /* R4 QI, QF, HF QI. */ - QFmode, /* R5 QI, QF, HF QI. */ - QImode, /* R6 QI, QF, HF QF. */ - QImode, /* R7 QI, QF, HF QF. */ - QImode, /* AR0 QI No. */ - QImode, /* AR1 QI No. */ - QImode, /* AR2 QI No. */ - QImode, /* AR3 QI QI. */ - QImode, /* AR4 QI QI. */ - QImode, /* AR5 QI QI. */ - QImode, /* AR6 QI QI. */ - QImode, /* AR7 QI QI. */ - VOIDmode, /* DP QI No. */ - QImode, /* IR0 QI No. */ - QImode, /* IR1 QI No. */ - QImode, /* BK QI QI. */ - VOIDmode, /* SP QI No. */ - VOIDmode, /* ST CC No. */ - VOIDmode, /* DIE/IE No. */ - VOIDmode, /* IIE/IF No. */ - VOIDmode, /* IIF/IOF No. */ - QImode, /* RS QI No. */ - QImode, /* RE QI No. */ - VOIDmode, /* RC QI No. */ - QFmode, /* R8 QI, QF, HF QI. */ - HFmode, /* R9 QI, QF, HF No. */ - HFmode, /* R10 QI, QF, HF No. */ - HFmode, /* R11 QI, QF, HF No. */ -}; - - -/* Test and compare insns in c4x.md store the information needed to - generate branch and scc insns here. */ - -struct rtx_def *c4x_compare_op0 = NULL_RTX; -struct rtx_def *c4x_compare_op1 = NULL_RTX; - -const char *c4x_rpts_cycles_string; -int c4x_rpts_cycles = 0; /* Max. cycles for RPTS. */ -const char *c4x_cpu_version_string; -int c4x_cpu_version = 40; /* CPU version C30/31/32/33/40/44. */ - -/* Pragma definitions. */ - -tree code_tree = NULL_TREE; -tree data_tree = NULL_TREE; -tree pure_tree = NULL_TREE; -tree noreturn_tree = NULL_TREE; -tree interrupt_tree = NULL_TREE; - -/* Forward declarations */ -static void c4x_add_gc_roots PARAMS ((void)); -static int c4x_isr_reg_used_p PARAMS ((unsigned int)); -static int c4x_leaf_function_p PARAMS ((void)); -static int c4x_assembler_function_p PARAMS ((void)); -static int c4x_immed_float_p PARAMS ((rtx)); -static int c4x_a_register PARAMS ((rtx)); -static int c4x_x_register PARAMS ((rtx)); -static int c4x_immed_int_constant PARAMS ((rtx)); -static int c4x_immed_float_constant PARAMS ((rtx)); -static int c4x_K_constant PARAMS ((rtx)); -static int c4x_N_constant PARAMS ((rtx)); -static int c4x_O_constant PARAMS ((rtx)); -static int c4x_R_indirect PARAMS ((rtx)); -static int c4x_S_indirect PARAMS ((rtx)); -static void c4x_S_address_parse PARAMS ((rtx , int *, int *, int *, int *)); -static int c4x_valid_operands PARAMS ((enum rtx_code, rtx *, - enum machine_mode, int)); -static int c4x_arn_reg_operand PARAMS ((rtx, enum machine_mode, unsigned int)); -static int c4x_arn_mem_operand PARAMS ((rtx, enum machine_mode, unsigned int)); -static void c4x_check_attribute PARAMS ((const char *, tree, tree, tree *)); -static int c4x_r11_set_p PARAMS ((rtx)); -static int c4x_rptb_valid_p PARAMS ((rtx, rtx)); -static int c4x_label_ref_used_p PARAMS ((rtx, rtx)); -static tree c4x_handle_fntype_attribute PARAMS ((tree *, tree, tree, int, bool *)); -const struct attribute_spec c4x_attribute_table[]; -static void c4x_insert_attributes PARAMS ((tree, tree *)); -static void c4x_asm_named_section PARAMS ((const char *, unsigned int)); -static int c4x_adjust_cost PARAMS ((rtx, rtx, rtx, int)); - -/* Initialize the GCC target structure. */ -#undef TARGET_ASM_BYTE_OP -#define TARGET_ASM_BYTE_OP "\t.word\t" -#undef TARGET_ASM_ALIGNED_HI_OP -#define TARGET_ASM_ALIGNED_HI_OP NULL -#undef TARGET_ASM_ALIGNED_SI_OP -#define TARGET_ASM_ALIGNED_SI_OP NULL - -#undef TARGET_ATTRIBUTE_TABLE -#define TARGET_ATTRIBUTE_TABLE c4x_attribute_table - -#undef TARGET_INSERT_ATTRIBUTES -#define TARGET_INSERT_ATTRIBUTES c4x_insert_attributes - -#undef TARGET_INIT_BUILTINS -#define TARGET_INIT_BUILTINS c4x_init_builtins - -#undef TARGET_EXPAND_BUILTIN -#define TARGET_EXPAND_BUILTIN c4x_expand_builtin - -#undef TARGET_SCHED_ADJUST_COST -#define TARGET_SCHED_ADJUST_COST c4x_adjust_cost - -struct gcc_target targetm = TARGET_INITIALIZER; - -/* Called to register all of our global variables with the garbage - collector. */ - -static void -c4x_add_gc_roots () -{ - ggc_add_rtx_root (&c4x_compare_op0, 1); - ggc_add_rtx_root (&c4x_compare_op1, 1); - ggc_add_tree_root (&code_tree, 1); - ggc_add_tree_root (&data_tree, 1); - ggc_add_tree_root (&pure_tree, 1); - ggc_add_tree_root (&noreturn_tree, 1); - ggc_add_tree_root (&interrupt_tree, 1); - ggc_add_rtx_root (&smulhi3_libfunc, 1); - ggc_add_rtx_root (&umulhi3_libfunc, 1); - ggc_add_rtx_root (&fix_truncqfhi2_libfunc, 1); - ggc_add_rtx_root (&fixuns_truncqfhi2_libfunc, 1); - ggc_add_rtx_root (&fix_trunchfhi2_libfunc, 1); - ggc_add_rtx_root (&fixuns_trunchfhi2_libfunc, 1); - ggc_add_rtx_root (&floathiqf2_libfunc, 1); - ggc_add_rtx_root (&floatunshiqf2_libfunc, 1); - ggc_add_rtx_root (&floathihf2_libfunc, 1); - ggc_add_rtx_root (&floatunshihf2_libfunc, 1); -} - - -/* Override command line options. - Called once after all options have been parsed. - Mostly we process the processor - type and sometimes adjust other TARGET_ options. */ - -void -c4x_override_options () -{ - if (c4x_rpts_cycles_string) - c4x_rpts_cycles = atoi (c4x_rpts_cycles_string); - else - c4x_rpts_cycles = 0; - - if (TARGET_C30) - c4x_cpu_version = 30; - else if (TARGET_C31) - c4x_cpu_version = 31; - else if (TARGET_C32) - c4x_cpu_version = 32; - else if (TARGET_C33) - c4x_cpu_version = 33; - else if (TARGET_C40) - c4x_cpu_version = 40; - else if (TARGET_C44) - c4x_cpu_version = 44; - else - c4x_cpu_version = 40; - - /* -mcpu=xx overrides -m40 etc. */ - if (c4x_cpu_version_string) - { - const char *p = c4x_cpu_version_string; - - /* Also allow -mcpu=c30 etc. */ - if (*p == 'c' || *p == 'C') - p++; - c4x_cpu_version = atoi (p); - } - - target_flags &= ~(C30_FLAG | C31_FLAG | C32_FLAG | C33_FLAG | - C40_FLAG | C44_FLAG); - - switch (c4x_cpu_version) - { - case 30: target_flags |= C30_FLAG; break; - case 31: target_flags |= C31_FLAG; break; - case 32: target_flags |= C32_FLAG; break; - case 33: target_flags |= C33_FLAG; break; - case 40: target_flags |= C40_FLAG; break; - case 44: target_flags |= C44_FLAG; break; - default: - warning ("unknown CPU version %d, using 40.\n", c4x_cpu_version); - c4x_cpu_version = 40; - target_flags |= C40_FLAG; - } - - if (TARGET_C30 || TARGET_C31 || TARGET_C32 || TARGET_C33) - target_flags |= C3X_FLAG; - else - target_flags &= ~C3X_FLAG; - - /* Convert foo / 8.0 into foo * 0.125, etc. */ - set_fast_math_flags(); - - /* We should phase out the following at some stage. - This provides compatibility with the old -mno-aliases option. */ - if (! TARGET_ALIASES && ! flag_argument_noalias) - flag_argument_noalias = 1; - - /* Register global variables with the garbage collector. */ - c4x_add_gc_roots (); -} - - -/* This is called before c4x_override_options. */ - -void -c4x_optimization_options (level, size) - int level ATTRIBUTE_UNUSED; - int size ATTRIBUTE_UNUSED; -{ - /* Scheduling before register allocation can screw up global - register allocation, especially for functions that use MPY||ADD - instructions. The benefit we gain we get by scheduling before - register allocation is probably marginal anyhow. */ - flag_schedule_insns = 0; -} - - -/* Write an ASCII string. */ - -#define C4X_ASCII_LIMIT 40 - -void -c4x_output_ascii (stream, ptr, len) - FILE *stream; - const char *ptr; - int len; -{ - char sbuf[C4X_ASCII_LIMIT + 1]; - int s, l, special, first = 1, onlys; - - if (len) - fprintf (stream, "\t.byte\t"); - - for (s = l = 0; len > 0; --len, ++ptr) - { - onlys = 0; - - /* Escape " and \ with a \". */ - special = *ptr == '\"' || *ptr == '\\'; - - /* If printable - add to buff. */ - if ((! TARGET_TI || ! special) && *ptr >= 0x20 && *ptr < 0x7f) - { - if (special) - sbuf[s++] = '\\'; - sbuf[s++] = *ptr; - if (s < C4X_ASCII_LIMIT - 1) - continue; - onlys = 1; - } - if (s) - { - if (first) - first = 0; - else - { - fputc (',', stream); - l++; - } - - sbuf[s] = 0; - fprintf (stream, "\"%s\"", sbuf); - l += s + 2; - if (TARGET_TI && l >= 80 && len > 1) - { - fprintf (stream, "\n\t.byte\t"); - first = 1; - l = 0; - } - - s = 0; - } - if (onlys) - continue; - - if (first) - first = 0; - else - { - fputc (',', stream); - l++; - } - - fprintf (stream, "%d", *ptr); - l += 3; - if (TARGET_TI && l >= 80 && len > 1) - { - fprintf (stream, "\n\t.byte\t"); - first = 1; - l = 0; - } - } - if (s) - { - if (! first) - fputc (',', stream); - - sbuf[s] = 0; - fprintf (stream, "\"%s\"", sbuf); - s = 0; - } - fputc ('\n', stream); -} - - -int -c4x_hard_regno_mode_ok (regno, mode) - unsigned int regno; - enum machine_mode mode; -{ - switch (mode) - { -#if Pmode != QImode - case Pmode: /* Pointer (24/32 bits). */ -#endif - case QImode: /* Integer (32 bits). */ - return IS_INT_REGNO (regno); - - case QFmode: /* Float, Double (32 bits). */ - case HFmode: /* Long Double (40 bits). */ - return IS_EXT_REGNO (regno); - - case CCmode: /* Condition Codes. */ - case CC_NOOVmode: /* Condition Codes. */ - return IS_ST_REGNO (regno); - - case HImode: /* Long Long (64 bits). */ - /* We need two registers to store long longs. Note that - it is much easier to constrain the first register - to start on an even boundary. */ - return IS_INT_REGNO (regno) - && IS_INT_REGNO (regno + 1) - && (regno & 1) == 0; - - default: - return 0; /* We don't support these modes. */ - } - - return 0; -} - -/* Return non-zero if REGNO1 can be renamed to REGNO2. */ -int -c4x_hard_regno_rename_ok (regno1, regno2) - unsigned int regno1; - unsigned int regno2; -{ - /* We can not copy call saved registers from mode QI into QF or from - mode QF into QI. */ - if (IS_FLOAT_CALL_SAVED_REGNO (regno1) && IS_INT_CALL_SAVED_REGNO (regno2)) - return 0; - if (IS_INT_CALL_SAVED_REGNO (regno1) && IS_FLOAT_CALL_SAVED_REGNO (regno2)) - return 0; - /* We cannot copy from an extended (40 bit) register to a standard - (32 bit) register because we only set the condition codes for - extended registers. */ - if (IS_EXT_REGNO (regno1) && ! IS_EXT_REGNO (regno2)) - return 0; - if (IS_EXT_REGNO (regno2) && ! IS_EXT_REGNO (regno1)) - return 0; - return 1; -} - -/* The TI C3x C compiler register argument runtime model uses 6 registers, - AR2, R2, R3, RC, RS, RE. - - The first two floating point arguments (float, double, long double) - that are found scanning from left to right are assigned to R2 and R3. - - The remaining integer (char, short, int, long) or pointer arguments - are assigned to the remaining registers in the order AR2, R2, R3, - RC, RS, RE when scanning left to right, except for the last named - argument prior to an ellipsis denoting variable number of - arguments. We don't have to worry about the latter condition since - function.c treats the last named argument as anonymous (unnamed). - - All arguments that cannot be passed in registers are pushed onto - the stack in reverse order (right to left). GCC handles that for us. - - c4x_init_cumulative_args() is called at the start, so we can parse - the args to see how many floating point arguments and how many - integer (or pointer) arguments there are. c4x_function_arg() is - then called (sometimes repeatedly) for each argument (parsed left - to right) to obtain the register to pass the argument in, or zero - if the argument is to be passed on the stack. Once the compiler is - happy, c4x_function_arg_advance() is called. - - Don't use R0 to pass arguments in, we use 0 to indicate a stack - argument. */ - -static const int c4x_int_reglist[3][6] = -{ - {AR2_REGNO, R2_REGNO, R3_REGNO, RC_REGNO, RS_REGNO, RE_REGNO}, - {AR2_REGNO, R3_REGNO, RC_REGNO, RS_REGNO, RE_REGNO, 0}, - {AR2_REGNO, RC_REGNO, RS_REGNO, RE_REGNO, 0, 0} -}; - -static int c4x_fp_reglist[2] = {R2_REGNO, R3_REGNO}; - - -/* 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. */ - -void -c4x_init_cumulative_args (cum, fntype, libname) - CUMULATIVE_ARGS *cum; /* Argument info to initialize. */ - tree fntype; /* Tree ptr for function decl. */ - rtx libname; /* SYMBOL_REF of library name or 0. */ -{ - tree param, next_param; - - cum->floats = cum->ints = 0; - cum->init = 0; - cum->var = 0; - cum->args = 0; - - if (TARGET_DEBUG) - { - fprintf (stderr, "\nc4x_init_cumulative_args ("); - if (fntype) - { - tree ret_type = TREE_TYPE (fntype); - - fprintf (stderr, "fntype code = %s, ret code = %s", - tree_code_name[(int) TREE_CODE (fntype)], - tree_code_name[(int) TREE_CODE (ret_type)]); - } - else - fprintf (stderr, "no fntype"); - - if (libname) - fprintf (stderr, ", libname = %s", XSTR (libname, 0)); - } - - cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); - - for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0; - param; param = next_param) - { - tree type; - - next_param = TREE_CHAIN (param); - - type = TREE_VALUE (param); - if (type && type != void_type_node) - { - enum machine_mode mode; - - /* If the last arg doesn't have void type then we have - variable arguments. */ - if (! next_param) - cum->var = 1; - - if ((mode = TYPE_MODE (type))) - { - if (! MUST_PASS_IN_STACK (mode, type)) - { - /* Look for float, double, or long double argument. */ - if (mode == QFmode || mode == HFmode) - cum->floats++; - /* Look for integer, enumeral, boolean, char, or pointer - argument. */ - else if (mode == QImode || mode == Pmode) - cum->ints++; - } - } - cum->args++; - } - } - - if (TARGET_DEBUG) - fprintf (stderr, "%s%s, args = %d)\n", - cum->prototype ? ", prototype" : "", - cum->var ? ", variable args" : "", - cum->args); -} - - -/* Update the data in CUM to advance over an argument - of mode MODE and data type TYPE. - (TYPE is null for libcalls where that information may not be available.) */ - -void -c4x_function_arg_advance (cum, mode, type, named) - CUMULATIVE_ARGS *cum; /* Current arg information. */ - enum machine_mode mode; /* Current arg mode. */ - tree type; /* Type of the arg or 0 if lib support. */ - int named; /* Whether or not the argument was named. */ -{ - if (TARGET_DEBUG) - fprintf (stderr, "c4x_function_adv(mode=%s, named=%d)\n\n", - GET_MODE_NAME (mode), named); - if (! TARGET_MEMPARM - && named - && type - && ! MUST_PASS_IN_STACK (mode, type)) - { - /* Look for float, double, or long double argument. */ - if (mode == QFmode || mode == HFmode) - cum->floats++; - /* Look for integer, enumeral, boolean, char, or pointer argument. */ - else if (mode == QImode || mode == Pmode) - cum->ints++; - } - else if (! TARGET_MEMPARM && ! type) - { - /* Handle libcall arguments. */ - if (mode == QFmode || mode == HFmode) - cum->floats++; - else if (mode == QImode || mode == Pmode) - cum->ints++; - } - return; -} - - -/* Define where to put the arguments 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). */ - -struct rtx_def * -c4x_function_arg (cum, mode, type, named) - CUMULATIVE_ARGS *cum; /* Current arg information. */ - enum machine_mode mode; /* Current arg mode. */ - tree type; /* Type of the arg or 0 if lib support. */ - int named; /* != 0 for normal args, == 0 for ... args. */ -{ - int reg = 0; /* Default to passing argument on stack. */ - - if (! cum->init) - { - /* We can handle at most 2 floats in R2, R3. */ - cum->maxfloats = (cum->floats > 2) ? 2 : cum->floats; - - /* We can handle at most 6 integers minus number of floats passed - in registers. */ - cum->maxints = (cum->ints > 6 - cum->maxfloats) ? - 6 - cum->maxfloats : cum->ints; - - /* If there is no prototype, assume all the arguments are integers. */ - if (! cum->prototype) - cum->maxints = 6; - - cum->ints = cum->floats = 0; - cum->init = 1; - } - - /* This marks the last argument. We don't need to pass this through - to the call insn. */ - if (type == void_type_node) - return 0; - - if (! TARGET_MEMPARM - && named - && type - && ! MUST_PASS_IN_STACK (mode, type)) - { - /* Look for float, double, or long double argument. */ - if (mode == QFmode || mode == HFmode) - { - if (cum->floats < cum->maxfloats) - reg = c4x_fp_reglist[cum->floats]; - } - /* Look for integer, enumeral, boolean, char, or pointer argument. */ - else if (mode == QImode || mode == Pmode) - { - if (cum->ints < cum->maxints) - reg = c4x_int_reglist[cum->maxfloats][cum->ints]; - } - } - else if (! TARGET_MEMPARM && ! type) - { - /* We could use a different argument calling model for libcalls, - since we're only calling functions in libgcc. Thus we could - pass arguments for long longs in registers rather than on the - stack. In the meantime, use the odd TI format. We make the - assumption that we won't have more than two floating point - args, six integer args, and that all the arguments are of the - same mode. */ - if (mode == QFmode || mode == HFmode) - reg = c4x_fp_reglist[cum->floats]; - else if (mode == QImode || mode == Pmode) - reg = c4x_int_reglist[0][cum->ints]; - } - - if (TARGET_DEBUG) - { - fprintf (stderr, "c4x_function_arg(mode=%s, named=%d", - GET_MODE_NAME (mode), named); - if (reg) - fprintf (stderr, ", reg=%s", reg_names[reg]); - else - fprintf (stderr, ", stack"); - fprintf (stderr, ")\n"); - } - if (reg) - return gen_rtx_REG (mode, reg); - else - return NULL_RTX; -} - - -void -c4x_va_start (stdarg_p, valist, nextarg) - int stdarg_p; - tree valist; - rtx nextarg; -{ - nextarg = plus_constant (nextarg, stdarg_p ? 0 : UNITS_PER_WORD * 2); - - std_expand_builtin_va_start (stdarg_p, valist, nextarg); -} - - -/* C[34]x arguments grow in weird ways (downwards) that the standard - varargs stuff can't handle.. */ -rtx -c4x_va_arg (valist, type) - tree valist, type; -{ - tree t; - - t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist, - build_int_2 (int_size_in_bytes (type), 0)); - TREE_SIDE_EFFECTS (t) = 1; - - return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL); -} - - -static int -c4x_isr_reg_used_p (regno) - unsigned int regno; -{ - /* Don't save/restore FP or ST, we handle them separately. */ - if (regno == FRAME_POINTER_REGNUM - || IS_ST_REGNO (regno)) - return 0; - - /* We could be a little smarter abut saving/restoring DP. - We'll only save if for the big memory model or if - we're paranoid. ;-) */ - if (IS_DP_REGNO (regno)) - return ! TARGET_SMALL || TARGET_PARANOID; - - /* Only save/restore regs in leaf function that are used. */ - if (c4x_leaf_function) - return regs_ever_live[regno] && fixed_regs[regno] == 0; - - /* Only save/restore regs that are used by the ISR and regs - that are likely to be used by functions the ISR calls - if they are not fixed. */ - return IS_EXT_REGNO (regno) - || ((regs_ever_live[regno] || call_used_regs[regno]) - && fixed_regs[regno] == 0); -} - - -static int -c4x_leaf_function_p () -{ - /* A leaf function makes no calls, so we only need - to save/restore the registers we actually use. - For the global variable leaf_function to be set, we need - to define LEAF_REGISTERS and all that it entails. - Let's check ourselves... */ - - if (lookup_attribute ("leaf_pretend", - TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) - return 1; - - /* Use the leaf_pretend attribute at your own risk. This is a hack - to speed up ISRs that call a function infrequently where the - overhead of saving and restoring the additional registers is not - warranted. You must save and restore the additional registers - required by the called function. Caveat emptor. Here's enough - rope... */ - - if (leaf_function_p ()) - return 1; - - return 0; -} - - -static int -c4x_assembler_function_p () -{ - tree type; - - type = TREE_TYPE (current_function_decl); - return (lookup_attribute ("assembler", TYPE_ATTRIBUTES (type)) != NULL) - || (lookup_attribute ("naked", TYPE_ATTRIBUTES (type)) != NULL); -} - - -int -c4x_interrupt_function_p () -{ - if (lookup_attribute ("interrupt", - TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)))) - return 1; - - /* Look for TI style c_intnn. */ - return current_function_name[0] == 'c' - && current_function_name[1] == '_' - && current_function_name[2] == 'i' - && current_function_name[3] == 'n' - && current_function_name[4] == 't' - && ISDIGIT (current_function_name[5]) - && ISDIGIT (current_function_name[6]); -} - -void -c4x_expand_prologue () -{ - unsigned int regno; - int size = get_frame_size (); - rtx insn; - - /* In functions where ar3 is not used but frame pointers are still - specified, frame pointers are not adjusted (if >= -O2) and this - is used so it won't needlessly push the frame pointer. */ - int dont_push_ar3; - - /* For __assembler__ function don't build a prologue. */ - if (c4x_assembler_function_p ()) - { - return; - } - - /* For __interrupt__ function build specific prologue. */ - if (c4x_interrupt_function_p ()) - { - c4x_leaf_function = c4x_leaf_function_p (); - - insn = emit_insn (gen_push_st ()); - RTX_FRAME_RELATED_P (insn) = 1; - if (size) - { - insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, AR3_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, AR3_REGNO), - gen_rtx_REG (QImode, SP_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - /* We require that an ISR uses fewer than 32768 words of - local variables, otherwise we have to go to lots of - effort to save a register, load it with the desired size, - adjust the stack pointer, and then restore the modified - register. Frankly, I think it is a poor ISR that - requires more than 32767 words of local temporary - storage! */ - if (size > 32767) - error ("ISR %s requires %d words of local vars, max is 32767", - current_function_name, size); - - insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - GEN_INT (size))); - RTX_FRAME_RELATED_P (insn) = 1; - } - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - if (c4x_isr_reg_used_p (regno)) - { - if (regno == DP_REGNO) - { - insn = emit_insn (gen_push_dp ()); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - insn = emit_insn (gen_pushqi (gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - if (IS_EXT_REGNO (regno)) - { - insn = emit_insn (gen_pushqf - (gen_rtx_REG (QFmode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - } - /* We need to clear the repeat mode flag if the ISR is - going to use a RPTB instruction or uses the RC, RS, or RE - registers. */ - if (regs_ever_live[RC_REGNO] - || regs_ever_live[RS_REGNO] - || regs_ever_live[RE_REGNO]) - { - insn = emit_insn (gen_andn_st (GEN_INT(~0x100))); - RTX_FRAME_RELATED_P (insn) = 1; - } - - /* Reload DP reg if we are paranoid about some turkey - violating small memory model rules. */ - if (TARGET_SMALL && TARGET_PARANOID) - { - insn = emit_insn (gen_set_ldp_prologue - (gen_rtx_REG (QImode, DP_REGNO), - gen_rtx_SYMBOL_REF (QImode, "data_sec"))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - if (frame_pointer_needed) - { - if ((size != 0) - || (current_function_args_size != 0) - || (optimize < 2)) - { - insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, AR3_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, AR3_REGNO), - gen_rtx_REG (QImode, SP_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - dont_push_ar3 = 1; - } - else - { - /* Since ar3 is not used, we don't need to push it. */ - dont_push_ar3 = 1; - } - } - else - { - /* If we use ar3, we need to push it. */ - dont_push_ar3 = 0; - if ((size != 0) || (current_function_args_size != 0)) - { - /* If we are omitting the frame pointer, we still have - to make space for it so the offsets are correct - unless we don't use anything on the stack at all. */ - size += 1; - } - } - - if (size > 32767) - { - /* Local vars are too big, it will take multiple operations - to increment SP. */ - if (TARGET_C3X) - { - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R1_REGNO), - GEN_INT(size >> 16))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_lshrqi3 (gen_rtx_REG (QImode, R1_REGNO), - gen_rtx_REG (QImode, R1_REGNO), - GEN_INT(-16))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R1_REGNO), - GEN_INT(size & ~0xffff))); - RTX_FRAME_RELATED_P (insn) = 1; - } - insn = emit_insn (gen_iorqi3 (gen_rtx_REG (QImode, R1_REGNO), - gen_rtx_REG (QImode, R1_REGNO), - GEN_INT(size & 0xffff))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, R1_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else if (size != 0) - { - /* Local vars take up less than 32767 words, so we can directly - add the number. */ - insn = emit_insn (gen_addqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - GEN_INT (size))); - RTX_FRAME_RELATED_P (insn) = 1; - } - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - if (IS_FLOAT_CALL_SAVED_REGNO (regno)) - { - if (TARGET_PRESERVE_FLOAT) - { - insn = emit_insn (gen_pushqi - (gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - insn = emit_insn (gen_pushqf (gen_rtx_REG (QFmode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else if ((! dont_push_ar3) || (regno != AR3_REGNO)) - { - insn = emit_insn (gen_pushqi ( gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - } -} - - -void -c4x_expand_epilogue() -{ - int regno; - int jump = 0; - int dont_pop_ar3; - rtx insn; - int size = get_frame_size (); - - /* For __assembler__ function build no epilogue. */ - if (c4x_assembler_function_p ()) - { - insn = emit_jump_insn (gen_return_from_epilogue ()); - RTX_FRAME_RELATED_P (insn) = 1; - return; - } - - /* For __interrupt__ function build specific epilogue. */ - if (c4x_interrupt_function_p ()) - { - for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno) - { - if (! c4x_isr_reg_used_p (regno)) - continue; - if (regno == DP_REGNO) - { - insn = emit_insn (gen_pop_dp ()); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - /* We have to use unspec because the compiler will delete insns - that are not call-saved. */ - if (IS_EXT_REGNO (regno)) - { - insn = emit_insn (gen_popqf_unspec - (gen_rtx_REG (QFmode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - insn = emit_insn (gen_popqi_unspec (gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - if (size) - { - insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - GEN_INT(size))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_popqi - (gen_rtx_REG (QImode, AR3_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - } - insn = emit_insn (gen_pop_st ()); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_jump_insn (gen_return_from_interrupt_epilogue ()); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - if (frame_pointer_needed) - { - if ((size != 0) - || (current_function_args_size != 0) - || (optimize < 2)) - { - insn = emit_insn - (gen_movqi (gen_rtx_REG (QImode, R2_REGNO), - gen_rtx_MEM (QImode, - gen_rtx_PLUS - (QImode, gen_rtx_REG (QImode, - AR3_REGNO), - GEN_INT(-1))))); - RTX_FRAME_RELATED_P (insn) = 1; - - /* We already have the return value and the fp, - so we need to add those to the stack. */ - size += 2; - jump = 1; - dont_pop_ar3 = 1; - } - else - { - /* Since ar3 is not used for anything, we don't need to - pop it. */ - dont_pop_ar3 = 1; - } - } - else - { - dont_pop_ar3 = 0; /* If we use ar3, we need to pop it. */ - if (size || current_function_args_size) - { - /* If we are ommitting the frame pointer, we still have - to make space for it so the offsets are correct - unless we don't use anything on the stack at all. */ - size += 1; - } - } - - /* Now restore the saved registers, putting in the delayed branch - where required. */ - for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) - { - if (regs_ever_live[regno] && ! call_used_regs[regno]) - { - if (regno == AR3_REGNO && dont_pop_ar3) - continue; - - if (IS_FLOAT_CALL_SAVED_REGNO (regno)) - { - insn = emit_insn (gen_popqf_unspec - (gen_rtx_REG (QFmode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - if (TARGET_PRESERVE_FLOAT) - { - insn = emit_insn (gen_popqi_unspec - (gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - else - { - insn = emit_insn (gen_popqi (gen_rtx_REG (QImode, regno))); - RTX_FRAME_RELATED_P (insn) = 1; - } - } - } - - if (frame_pointer_needed) - { - if ((size != 0) - || (current_function_args_size != 0) - || (optimize < 2)) - { - /* Restore the old FP. */ - insn = emit_insn - (gen_movqi - (gen_rtx_REG (QImode, AR3_REGNO), - gen_rtx_MEM (QImode, gen_rtx_REG (QImode, AR3_REGNO)))); - - RTX_FRAME_RELATED_P (insn) = 1; - } - } - - if (size > 32767) - { - /* Local vars are too big, it will take multiple operations - to decrement SP. */ - if (TARGET_C3X) - { - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R3_REGNO), - GEN_INT(size >> 16))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_lshrqi3 (gen_rtx_REG (QImode, R3_REGNO), - gen_rtx_REG (QImode, R3_REGNO), - GEN_INT(-16))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - insn = emit_insn (gen_movqi (gen_rtx_REG (QImode, R3_REGNO), - GEN_INT(size & ~0xffff))); - RTX_FRAME_RELATED_P (insn) = 1; - } - insn = emit_insn (gen_iorqi3 (gen_rtx_REG (QImode, R3_REGNO), - gen_rtx_REG (QImode, R3_REGNO), - GEN_INT(size & 0xffff))); - RTX_FRAME_RELATED_P (insn) = 1; - insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, R3_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else if (size != 0) - { - /* Local vars take up less than 32768 words, so we can directly - subtract the number. */ - insn = emit_insn (gen_subqi3 (gen_rtx_REG (QImode, SP_REGNO), - gen_rtx_REG (QImode, SP_REGNO), - GEN_INT(size))); - RTX_FRAME_RELATED_P (insn) = 1; - } - - if (jump) - { - insn = emit_jump_insn (gen_return_indirect_internal - (gen_rtx_REG (QImode, R2_REGNO))); - RTX_FRAME_RELATED_P (insn) = 1; - } - else - { - insn = emit_jump_insn (gen_return_from_epilogue ()); - RTX_FRAME_RELATED_P (insn) = 1; - } - } -} - - -int -c4x_null_epilogue_p () -{ - int regno; - - if (reload_completed - && ! c4x_assembler_function_p () - && ! c4x_interrupt_function_p () - && ! current_function_calls_alloca - && ! current_function_args_size - && ! (optimize < 2) - && ! get_frame_size ()) - { - for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno--) - if (regs_ever_live[regno] && ! call_used_regs[regno] - && (regno != AR3_REGNO)) - return 0; - return 1; - } - return 0; -} - - -int -c4x_emit_move_sequence (operands, mode) - rtx *operands; - enum machine_mode mode; -{ - rtx op0 = operands[0]; - rtx op1 = operands[1]; - - if (! reload_in_progress - && ! REG_P (op0) - && ! REG_P (op1) - && ! (stik_const_operand (op1, mode) && ! push_operand (op0, mode))) - op1 = force_reg (mode, op1); - - if (GET_CODE (op1) == LO_SUM - && GET_MODE (op1) == Pmode - && dp_reg_operand (XEXP (op1, 0), mode)) - { - /* expand_increment will sometimes create a LO_SUM immediate - address. */ - op1 = XEXP (op1, 1); - } - else if (symbolic_address_operand (op1, mode)) - { - if (TARGET_LOAD_ADDRESS) - { - /* Alias analysis seems to do a better job if we force - constant addresses to memory after reload. */ - emit_insn (gen_load_immed_address (op0, op1)); - return 1; - } - else - { - /* Stick symbol or label address into the constant pool. */ - op1 = force_const_mem (Pmode, op1); - } - } - else if (mode == HFmode && CONSTANT_P (op1) && ! LEGITIMATE_CONSTANT_P (op1)) - { - /* We could be a lot smarter about loading some of these - constants... */ - op1 = force_const_mem (mode, op1); - } - - /* Convert (MEM (SYMREF)) to a (MEM (LO_SUM (REG) (SYMREF))) - and emit associated (HIGH (SYMREF)) if large memory model. - c4x_legitimize_address could be used to do this, - perhaps by calling validize_address. */ - if (TARGET_EXPOSE_LDP - && ! (reload_in_progress || reload_completed) - && GET_CODE (op1) == MEM - && symbolic_address_operand (XEXP (op1, 0), Pmode)) - { - rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO); - if (! TARGET_SMALL) - emit_insn (gen_set_ldp (dp_reg, XEXP (op1, 0))); - op1 = change_address (op1, mode, - gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op1, 0))); - } - - if (TARGET_EXPOSE_LDP - && ! (reload_in_progress || reload_completed) - && GET_CODE (op0) == MEM - && symbolic_address_operand (XEXP (op0, 0), Pmode)) - { - rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO); - if (! TARGET_SMALL) - emit_insn (gen_set_ldp (dp_reg, XEXP (op0, 0))); - op0 = change_address (op0, mode, - gen_rtx_LO_SUM (Pmode, dp_reg, XEXP (op0, 0))); - } - - if (GET_CODE (op0) == SUBREG - && mixed_subreg_operand (op0, mode)) - { - /* We should only generate these mixed mode patterns - during RTL generation. If we need do it later on - then we'll have to emit patterns that won't clobber CC. */ - if (reload_in_progress || reload_completed) - abort (); - if (GET_MODE (SUBREG_REG (op0)) == QImode) - op0 = SUBREG_REG (op0); - else if (GET_MODE (SUBREG_REG (op0)) == HImode) - { - op0 = copy_rtx (op0); - PUT_MODE (op0, QImode); - } - else - abort (); - - if (mode == QFmode) - emit_insn (gen_storeqf_int_clobber (op0, op1)); - else - abort (); - return 1; - } - - if (GET_CODE (op1) == SUBREG - && mixed_subreg_operand (op1, mode)) - { - /* We should only generate these mixed mode patterns - during RTL generation. If we need do it later on - then we'll have to emit patterns that won't clobber CC. */ - if (reload_in_progress || reload_completed) - abort (); - if (GET_MODE (SUBREG_REG (op1)) == QImode) - op1 = SUBREG_REG (op1); - else if (GET_MODE (SUBREG_REG (op1)) == HImode) - { - op1 = copy_rtx (op1); - PUT_MODE (op1, QImode); - } - else - abort (); - - if (mode == QFmode) - emit_insn (gen_loadqf_int_clobber (op0, op1)); - else - abort (); - return 1; - } - - if (mode == QImode - && reg_operand (op0, mode) - && const_int_operand (op1, mode) - && ! IS_INT16_CONST (INTVAL (op1)) - && ! IS_HIGH_CONST (INTVAL (op1))) - { - emit_insn (gen_loadqi_big_constant (op0, op1)); - return 1; - } - - if (mode == HImode - && reg_operand (op0, mode) - && const_int_operand (op1, mode)) - { - emit_insn (gen_loadhi_big_constant (op0, op1)); - return 1; - } - - /* Adjust operands in case we have modified them. */ - operands[0] = op0; - operands[1] = op1; - - /* Emit normal pattern. */ - return 0; -} - - -void -c4x_emit_libcall (libcall, code, dmode, smode, noperands, operands) - rtx libcall; - enum rtx_code code; - enum machine_mode dmode; - enum machine_mode smode; - int noperands; - rtx *operands; -{ - rtx ret; - rtx insns; - rtx equiv; - - start_sequence (); - switch (noperands) - { - case 2: - ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 1, - operands[1], smode); - equiv = gen_rtx (code, dmode, operands[1]); - break; - - case 3: - ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 2, - operands[1], smode, operands[2], smode); - equiv = gen_rtx (code, dmode, operands[1], operands[2]); - break; - - default: - abort (); - } - - insns = get_insns (); - end_sequence (); - emit_libcall_block (insns, operands[0], ret, equiv); -} - - -void -c4x_emit_libcall3 (libcall, code, mode, operands) - rtx libcall; - enum rtx_code code; - enum machine_mode mode; - rtx *operands; -{ - c4x_emit_libcall (libcall, code, mode, mode, 3, operands); -} - - -void -c4x_emit_libcall_mulhi (libcall, code, mode, operands) - rtx libcall; - enum rtx_code code; - enum machine_mode mode; - rtx *operands; -{ - rtx ret; - rtx insns; - rtx equiv; - - start_sequence (); - ret = emit_library_call_value (libcall, NULL_RTX, 1, mode, 2, - operands[1], mode, operands[2], mode); - equiv = gen_rtx_TRUNCATE (mode, - gen_rtx_LSHIFTRT (HImode, - gen_rtx_MULT (HImode, - gen_rtx (code, HImode, operands[1]), - gen_rtx (code, HImode, operands[2])), - GEN_INT (32))); - insns = get_insns (); - end_sequence (); - emit_libcall_block (insns, operands[0], ret, equiv); -} - - -/* Set the SYMBOL_REF_FLAG for a function decl. However, wo do not - yet use this info. */ -void -c4x_encode_section_info (decl) - tree decl; -{ -#if 0 - if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE) - SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; -#else - if (TREE_CODE (decl) == FUNCTION_DECL) - SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1; -#endif -} - - -int -c4x_check_legit_addr (mode, addr, strict) - enum machine_mode mode; - rtx addr; - int strict; -{ - rtx base = NULL_RTX; /* Base register (AR0-AR7). */ - rtx indx = NULL_RTX; /* Index register (IR0,IR1). */ - rtx disp = NULL_RTX; /* Displacement. */ - enum rtx_code code; - - code = GET_CODE (addr); - switch (code) - { - /* Register indirect with auto increment/decrement. We don't - allow SP here---push_operand should recognise an operand - being pushed on the stack. */ - - case PRE_DEC: - case PRE_INC: - case POST_DEC: - if (mode != QImode && mode != QFmode) - return 0; - - case POST_INC: - base = XEXP (addr, 0); - if (! REG_P (base)) - return 0; - break; - - case PRE_MODIFY: - case POST_MODIFY: - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - - if (mode != QImode && mode != QFmode) - return 0; - - if (! REG_P (op0) - || (GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS)) - return 0; - base = XEXP (op1, 0); - if (base != op0) - return 0; - if (REG_P (XEXP (op1, 1))) - indx = XEXP (op1, 1); - else - disp = XEXP (op1, 1); - } - break; - - /* Register indirect. */ - case REG: - base = addr; - break; - - /* Register indirect with displacement or index. */ - case PLUS: - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - enum rtx_code code0 = GET_CODE (op0); - - switch (code0) - { - case REG: - if (REG_P (op1)) - { - base = op0; /* Base + index. */ - indx = op1; - if (IS_INDEX_REG (base) || IS_ADDR_REG (indx)) - { - base = op1; - indx = op0; - } - } - else - { - base = op0; /* Base + displacement. */ - disp = op1; - } - break; - - default: - return 0; - } - } - break; - - /* Direct addressing with DP register. */ - case LO_SUM: - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - - /* HImode and HFmode direct memory references aren't truly - offsettable (consider case at end of data page). We - probably get better code by loading a pointer and using an - indirect memory reference. */ - if (mode == HImode || mode == HFmode) - return 0; - - if (!REG_P (op0) || REGNO (op0) != DP_REGNO) - return 0; - - if ((GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF)) - return 1; - - if (GET_CODE (op1) == CONST) - return 1; - return 0; - } - break; - - /* Direct addressing with some work for the assembler... */ - case CONST: - /* Direct addressing. */ - case LABEL_REF: - case SYMBOL_REF: - if (! TARGET_EXPOSE_LDP && ! strict && mode != HFmode && mode != HImode) - return 1; - /* These need to be converted to a LO_SUM (...). - LEGITIMIZE_RELOAD_ADDRESS will do this during reload. */ - return 0; - - /* Do not allow direct memory access to absolute addresses. - This is more pain than it's worth, especially for the - small memory model where we can't guarantee that - this address is within the data page---we don't want - to modify the DP register in the small memory model, - even temporarily, since an interrupt can sneak in.... */ - case CONST_INT: - return 0; - - /* Indirect indirect addressing. */ - case MEM: - return 0; - - case CONST_DOUBLE: - fatal_insn ("using CONST_DOUBLE for address", addr); - - default: - return 0; - } - - /* Validate the base register. */ - if (base) - { - /* Check that the address is offsettable for HImode and HFmode. */ - if (indx && (mode == HImode || mode == HFmode)) - return 0; - - /* Handle DP based stuff. */ - if (REGNO (base) == DP_REGNO) - return 1; - if (strict && ! REGNO_OK_FOR_BASE_P (REGNO (base))) - return 0; - else if (! strict && ! IS_ADDR_OR_PSEUDO_REG (base)) - return 0; - } - - /* Now validate the index register. */ - if (indx) - { - if (GET_CODE (indx) != REG) - return 0; - if (strict && ! REGNO_OK_FOR_INDEX_P (REGNO (indx))) - return 0; - else if (! strict && ! IS_INDEX_OR_PSEUDO_REG (indx)) - return 0; - } - - /* Validate displacement. */ - if (disp) - { - if (GET_CODE (disp) != CONST_INT) - return 0; - if (mode == HImode || mode == HFmode) - { - /* The offset displacement must be legitimate. */ - if (! IS_DISP8_OFF_CONST (INTVAL (disp))) - return 0; - } - else - { - if (! IS_DISP8_CONST (INTVAL (disp))) - return 0; - } - /* Can't add an index with a disp. */ - if (indx) - return 0; - } - return 1; -} - - -rtx -c4x_legitimize_address (orig, mode) - rtx orig ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (GET_CODE (orig) == SYMBOL_REF - || GET_CODE (orig) == LABEL_REF) - { - if (mode == HImode || mode == HFmode) - { - /* We need to force the address into - a register so that it is offsettable. */ - rtx addr_reg = gen_reg_rtx (Pmode); - emit_move_insn (addr_reg, orig); - return addr_reg; - } - else - { - rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO); - - if (! TARGET_SMALL) - emit_insn (gen_set_ldp (dp_reg, orig)); - - return gen_rtx_LO_SUM (Pmode, dp_reg, orig); - } - } - - return NULL_RTX; -} - - -/* Provide the costs of an addressing mode that contains ADDR. - If ADDR is not a valid address, its cost is irrelevant. - This is used in cse and loop optimisation to determine - if it is worthwhile storing a common address into a register. - Unfortunately, the C4x address cost depends on other operands. */ - -int -c4x_address_cost (addr) - rtx addr; -{ - switch (GET_CODE (addr)) - { - case REG: - return 1; - - case POST_INC: - case POST_DEC: - case PRE_INC: - case PRE_DEC: - return 1; - - /* These shouldn't be directly generated. */ - case SYMBOL_REF: - case LABEL_REF: - case CONST: - return 10; - - case LO_SUM: - { - rtx op1 = XEXP (addr, 1); - - if (GET_CODE (op1) == LABEL_REF || GET_CODE (op1) == SYMBOL_REF) - return TARGET_SMALL ? 3 : 4; - - if (GET_CODE (op1) == CONST) - { - rtx offset = const0_rtx; - - op1 = eliminate_constant_term (op1, &offset); - - /* ??? These costs need rethinking... */ - if (GET_CODE (op1) == LABEL_REF) - return 3; - - if (GET_CODE (op1) != SYMBOL_REF) - return 4; - - if (INTVAL (offset) == 0) - return 3; - - return 4; - } - fatal_insn ("c4x_address_cost: Invalid addressing mode", addr); - } - break; - - case PLUS: - { - register rtx op0 = XEXP (addr, 0); - register rtx op1 = XEXP (addr, 1); - - if (GET_CODE (op0) != REG) - break; - - switch (GET_CODE (op1)) - { - default: - break; - - case REG: - /* This cost for REG+REG must be greater than the cost - for REG if we want autoincrement addressing modes. */ - return 2; - - case CONST_INT: - /* The following tries to improve GIV combination - in strength reduce but appears not to help. */ - if (TARGET_DEVEL && IS_UINT5_CONST (INTVAL (op1))) - return 1; - - if (IS_DISP1_CONST (INTVAL (op1))) - return 1; - - if (! TARGET_C3X && IS_UINT5_CONST (INTVAL (op1))) - return 2; - - return 3; - } - } - default: - break; - } - - return 4; -} - - -rtx -c4x_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; - - if (mode == CC_NOOVmode - && (code == LE || code == GE || code == LT || code == GT)) - return NULL_RTX; - - cc_reg = gen_rtx_REG (mode, ST_REGNO); - emit_insn (gen_rtx_SET (VOIDmode, cc_reg, - gen_rtx_COMPARE (mode, x, y))); - return cc_reg; -} - -char * -c4x_output_cbranch (form, seq) - const char *form; - rtx seq; -{ - int delayed = 0; - int annultrue = 0; - int annulfalse = 0; - rtx delay; - char *cp; - static char str[100]; - - if (final_sequence) - { - delay = XVECEXP (final_sequence, 0, 1); - delayed = ! INSN_ANNULLED_BRANCH_P (seq); - annultrue = INSN_ANNULLED_BRANCH_P (seq) && ! INSN_FROM_TARGET_P (delay); - annulfalse = INSN_ANNULLED_BRANCH_P (seq) && INSN_FROM_TARGET_P (delay); - } - strcpy (str, form); - cp = &str [strlen (str)]; - if (delayed) - { - *cp++ = '%'; - *cp++ = '#'; - } - if (annultrue) - { - *cp++ = 'a'; - *cp++ = 't'; - } - if (annulfalse) - { - *cp++ = 'a'; - *cp++ = 'f'; - } - *cp++ = '\t'; - *cp++ = '%'; - *cp++ = 'l'; - *cp++ = '1'; - *cp = 0; - return str; -} - -void -c4x_print_operand (file, op, letter) - FILE *file; /* File to write to. */ - rtx op; /* Operand to print. */ - int letter; /* % or 0. */ -{ - rtx op1; - enum rtx_code code; - - switch (letter) - { - case '#': /* Delayed. */ - if (final_sequence) - asm_fprintf (file, "d"); - return; - } - - code = GET_CODE (op); - switch (letter) - { - case 'A': /* Direct address. */ - if (code == CONST_INT || code == SYMBOL_REF || code == CONST) - asm_fprintf (file, "@"); - break; - - case 'H': /* Sethi. */ - output_addr_const (file, op); - return; - - case 'I': /* Reversed condition. */ - code = reverse_condition (code); - break; - - case 'L': /* Log 2 of constant. */ - if (code != CONST_INT) - fatal_insn ("c4x_print_operand: %%L inconsistency", op); - fprintf (file, "%d", exact_log2 (INTVAL (op))); - return; - - case 'N': /* Ones complement of small constant. */ - if (code != CONST_INT) - fatal_insn ("c4x_print_operand: %%N inconsistency", op); - fprintf (file, "%d", ~INTVAL (op)); - return; - - case 'K': /* Generate ldp(k) if direct address. */ - if (! TARGET_SMALL - && code == MEM - && GET_CODE (XEXP (op, 0)) == LO_SUM - && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG - && REGNO (XEXP (XEXP (op, 0), 0)) == DP_REGNO) - { - op1 = XEXP (XEXP (op, 0), 1); - if (GET_CODE(op1) == CONST_INT || GET_CODE(op1) == SYMBOL_REF) - { - asm_fprintf (file, "\t%s\t@", TARGET_C3X ? "ldp" : "ldpk"); - output_address (XEXP (adjust_address (op, VOIDmode, 1), 0)); - asm_fprintf (file, "\n"); - } - } - return; - - case 'M': /* Generate ldp(k) if direct address. */ - if (! TARGET_SMALL /* Only used in asm statements. */ - && code == MEM - && (GET_CODE (XEXP (op, 0)) == CONST - || GET_CODE (XEXP (op, 0)) == SYMBOL_REF)) - { - asm_fprintf (file, "%s\t@", TARGET_C3X ? "ldp" : "ldpk"); - output_address (XEXP (op, 0)); - asm_fprintf (file, "\n\t"); - } - return; - - case 'O': /* Offset address. */ - if (code == MEM && c4x_autoinc_operand (op, Pmode)) - break; - else if (code == MEM) - output_address (XEXP (adjust_address (op, VOIDmode, 1), 0)); - else if (code == REG) - fprintf (file, "%s", reg_names[REGNO (op) + 1]); - else - fatal_insn ("c4x_print_operand: %%O inconsistency", op); - return; - - case 'C': /* Call. */ - break; - - case 'U': /* Call/callu. */ - if (code != SYMBOL_REF) - asm_fprintf (file, "u"); - return; - - default: - break; - } - - switch (code) - { - case REG: - if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT - && ! TARGET_TI) - fprintf (file, "%s", float_reg_names[REGNO (op)]); - else - fprintf (file, "%s", reg_names[REGNO (op)]); - break; - - case MEM: - output_address (XEXP (op, 0)); - break; - - case CONST_DOUBLE: - { - char str[30]; - REAL_VALUE_TYPE r; - - REAL_VALUE_FROM_CONST_DOUBLE (r, op); - REAL_VALUE_TO_DECIMAL (r, "%20f", str); - fprintf (file, "%s", str); - } - break; - - case CONST_INT: - fprintf (file, "%d", INTVAL (op)); - break; - - case NE: - asm_fprintf (file, "ne"); - break; - - case EQ: - asm_fprintf (file, "eq"); - break; - - case GE: - asm_fprintf (file, "ge"); - break; - - case GT: - asm_fprintf (file, "gt"); - break; - - case LE: - asm_fprintf (file, "le"); - break; - - case LT: - asm_fprintf (file, "lt"); - break; - - case GEU: - asm_fprintf (file, "hs"); - break; - - case GTU: - asm_fprintf (file, "hi"); - break; - - case LEU: - asm_fprintf (file, "ls"); - break; - - case LTU: - asm_fprintf (file, "lo"); - break; - - case SYMBOL_REF: - output_addr_const (file, op); - break; - - case CONST: - output_addr_const (file, XEXP (op, 0)); - break; - - case CODE_LABEL: - break; - - default: - fatal_insn ("c4x_print_operand: Bad operand case", op); - break; - } -} - - -void -c4x_print_operand_address (file, addr) - FILE *file; - rtx addr; -{ - switch (GET_CODE (addr)) - { - case REG: - fprintf (file, "*%s", reg_names[REGNO (addr)]); - break; - - case PRE_DEC: - fprintf (file, "*--%s", reg_names[REGNO (XEXP (addr, 0))]); - break; - - case POST_INC: - fprintf (file, "*%s++", reg_names[REGNO (XEXP (addr, 0))]); - break; - - case POST_MODIFY: - { - rtx op0 = XEXP (XEXP (addr, 1), 0); - rtx op1 = XEXP (XEXP (addr, 1), 1); - - if (GET_CODE (XEXP (addr, 1)) == PLUS && REG_P (op1)) - fprintf (file, "*%s++(%s)", reg_names[REGNO (op0)], - reg_names[REGNO (op1)]); - else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) > 0) - fprintf (file, "*%s++(%d)", reg_names[REGNO (op0)], - INTVAL (op1)); - else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0) - fprintf (file, "*%s--(%d)", reg_names[REGNO (op0)], - -INTVAL (op1)); - else if (GET_CODE (XEXP (addr, 1)) == MINUS && REG_P (op1)) - fprintf (file, "*%s--(%s)", reg_names[REGNO (op0)], - reg_names[REGNO (op1)]); - else - fatal_insn ("c4x_print_operand_address: Bad post_modify", addr); - } - break; - - case PRE_MODIFY: - { - rtx op0 = XEXP (XEXP (addr, 1), 0); - rtx op1 = XEXP (XEXP (addr, 1), 1); - - if (GET_CODE (XEXP (addr, 1)) == PLUS && REG_P (op1)) - fprintf (file, "*++%s(%s)", reg_names[REGNO (op0)], - reg_names[REGNO (op1)]); - else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) > 0) - fprintf (file, "*++%s(%d)", reg_names[REGNO (op0)], - INTVAL (op1)); - else if (GET_CODE (XEXP (addr, 1)) == PLUS && INTVAL (op1) < 0) - fprintf (file, "*--%s(%d)", reg_names[REGNO (op0)], - -INTVAL (op1)); - else if (GET_CODE (XEXP (addr, 1)) == MINUS && REG_P (op1)) - fprintf (file, "*--%s(%s)", reg_names[REGNO (op0)], - reg_names[REGNO (op1)]); - else - fatal_insn ("c4x_print_operand_address: Bad pre_modify", addr); - } - break; - - case PRE_INC: - fprintf (file, "*++%s", reg_names[REGNO (XEXP (addr, 0))]); - break; - - case POST_DEC: - fprintf (file, "*%s--", reg_names[REGNO (XEXP (addr, 0))]); - break; - - case PLUS: /* Indirect with displacement. */ - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - - if (REG_P (op0)) - { - if (REG_P (op1)) - { - if (IS_INDEX_REG (op0)) - { - fprintf (file, "*+%s(%s)", - reg_names[REGNO (op1)], - reg_names[REGNO (op0)]); /* Index + base. */ - } - else - { - fprintf (file, "*+%s(%s)", - reg_names[REGNO (op0)], - reg_names[REGNO (op1)]); /* Base + index. */ - } - } - else if (INTVAL (op1) < 0) - { - fprintf (file, "*-%s(%d)", - reg_names[REGNO (op0)], - -INTVAL (op1)); /* Base - displacement. */ - } - else - { - fprintf (file, "*+%s(%d)", - reg_names[REGNO (op0)], - INTVAL (op1)); /* Base + displacement. */ - } - } - else - fatal_insn ("c4x_print_operand_address: Bad operand case", addr); - } - break; - - case LO_SUM: - { - rtx op0 = XEXP (addr, 0); - rtx op1 = XEXP (addr, 1); - - if (REG_P (op0) && REGNO (op0) == DP_REGNO) - c4x_print_operand_address (file, op1); - else - fatal_insn ("c4x_print_operand_address: Bad operand case", addr); - } - break; - - case CONST: - case SYMBOL_REF: - case LABEL_REF: - fprintf (file, "@"); - output_addr_const (file, addr); - break; - - /* We shouldn't access CONST_INT addresses. */ - case CONST_INT: - - default: - fatal_insn ("c4x_print_operand_address: Bad operand case", addr); - break; - } -} - - -/* Return nonzero if the floating point operand will fit - in the immediate field. */ - -static int -c4x_immed_float_p (op) - rtx op; -{ - long convval[2]; - int exponent; - REAL_VALUE_TYPE r; - - REAL_VALUE_FROM_CONST_DOUBLE (r, op); - if (GET_MODE (op) == HFmode) - REAL_VALUE_TO_TARGET_DOUBLE (r, convval); - else - { - REAL_VALUE_TO_TARGET_SINGLE (r, convval[0]); - convval[1] = 0; - } - - /* Sign extend exponent. */ - exponent = (((convval[0] >> 24) & 0xff) ^ 0x80) - 0x80; - if (exponent == -128) - return 1; /* 0.0 */ - if ((convval[0] & 0x00000fff) != 0 || convval[1] != 0) - return 0; /* Precision doesn't fit. */ - return (exponent <= 7) /* Positive exp. */ - && (exponent >= -7); /* Negative exp. */ -} - - -/* The last instruction in a repeat block cannot be a Bcond, DBcound, - CALL, CALLCond, TRAPcond, RETIcond, RETScond, IDLE, RPTB or RPTS. - - None of the last four instructions from the bottom of the block can - be a BcondD, BRD, DBcondD, RPTBD, LAJ, LAJcond, LATcond, BcondAF, - BcondAT or RETIcondD. - - This routine scans the four previous insns for a jump insn, and if - one is found, returns 1 so that we bung in a nop instruction. - This simple minded strategy will add a nop, when it may not - be required. Say when there is a JUMP_INSN near the end of the - block that doesn't get converted into a delayed branch. - - Note that we cannot have a call insn, since we don't generate - repeat loops with calls in them (although I suppose we could, but - there's no benefit.) - - !!! FIXME. The rptb_top insn may be sucked into a SEQUENCE. */ - -int -c4x_rptb_nop_p (insn) - rtx insn; -{ - rtx start_label; - int i; - - /* Extract the start label from the jump pattern (rptb_end). */ - start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0); - - /* If there is a label at the end of the loop we must insert - a NOP. */ - do { - insn = previous_insn (insn); - } while (GET_CODE (insn) == NOTE - || GET_CODE (insn) == USE - || GET_CODE (insn) == CLOBBER); - if (GET_CODE (insn) == CODE_LABEL) - return 1; - - for (i = 0; i < 4; i++) - { - /* Search back for prev non-note and non-label insn. */ - while (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL - || GET_CODE (insn) == USE || GET_CODE (insn) == CLOBBER) - { - if (insn == start_label) - return i == 0; - - insn = previous_insn (insn); - }; - - /* If we have a jump instruction we should insert a NOP. If we - hit repeat block top we should only insert a NOP if the loop - is empty. */ - if (GET_CODE (insn) == JUMP_INSN) - return 1; - insn = previous_insn (insn); - } - return 0; -} - - -/* The C4x looping instruction needs to be emitted at the top of the - loop. Emitting the true RTL for a looping instruction at the top of - the loop can cause problems with flow analysis. So instead, a dummy - doloop insn is emitted at the end of the loop. This routine checks - for the presence of this doloop insn and then searches back to the - top of the loop, where it inserts the true looping insn (provided - there are no instructions in the loop which would cause problems). - Any additional labels can be emitted at this point. In addition, if - the desired loop count register was not allocated, this routine does - nothing. - - Before we can create a repeat block looping instruction we have to - verify that there are no jumps outside the loop and no jumps outside - the loop go into this loop. This can happen in the basic blocks reorder - pass. The C4x cpu can not handle this. */ - -static int -c4x_label_ref_used_p (x, code_label) - rtx x, code_label; -{ - enum rtx_code code; - int i, j; - const char *fmt; - - if (x == 0) - return 0; - - code = GET_CODE (x); - if (code == LABEL_REF) - return INSN_UID (XEXP (x,0)) == INSN_UID (code_label); - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (c4x_label_ref_used_p (XEXP (x, i), code_label)) - return 1; - } - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (c4x_label_ref_used_p (XVECEXP (x, i, j), code_label)) - return 1; - } - return 0; -} - - -static int -c4x_rptb_valid_p (insn, start_label) - rtx insn, start_label; -{ - rtx end = insn; - rtx start; - rtx tmp; - - /* Find the start label. */ - for (; insn; insn = PREV_INSN (insn)) - if (insn == start_label) - break; - - /* Note found then we can not use a rptb or rpts. The label was - probably moved by the basic block reorder pass. */ - if (! insn) - return 0; - - start = insn; - /* If any jump jumps inside this block then we must fail. */ - for (insn = PREV_INSN (start); insn; insn = PREV_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - { - for (tmp = NEXT_INSN (start); tmp != end; tmp = NEXT_INSN(tmp)) - if (GET_CODE (tmp) == JUMP_INSN - && c4x_label_ref_used_p (tmp, insn)) - return 0; - } - } - for (insn = NEXT_INSN (end); insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - { - for (tmp = NEXT_INSN (start); tmp != end; tmp = NEXT_INSN(tmp)) - if (GET_CODE (tmp) == JUMP_INSN - && c4x_label_ref_used_p (tmp, insn)) - return 0; - } - } - /* If any jump jumps outside this block then we must fail. */ - for (insn = NEXT_INSN (start); insn != end; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == CODE_LABEL) - { - for (tmp = NEXT_INSN (end); tmp; tmp = NEXT_INSN(tmp)) - if (GET_CODE (tmp) == JUMP_INSN - && c4x_label_ref_used_p (tmp, insn)) - return 0; - for (tmp = PREV_INSN (start); tmp; tmp = PREV_INSN(tmp)) - if (GET_CODE (tmp) == JUMP_INSN - && c4x_label_ref_used_p (tmp, insn)) - return 0; - } - } - - /* All checks OK. */ - return 1; -} - - -void -c4x_rptb_insert (insn) - rtx insn; -{ - rtx end_label; - rtx start_label; - rtx new_start_label; - rtx count_reg; - - /* If the count register has not been allocated to RC, say if - there is a movstr pattern in the loop, then do not insert a - RPTB instruction. Instead we emit a decrement and branch - at the end of the loop. */ - count_reg = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 0), 0); - if (REGNO (count_reg) != RC_REGNO) - return; - - /* Extract the start label from the jump pattern (rptb_end). */ - start_label = XEXP (XEXP (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)), 1), 0); - - if (! c4x_rptb_valid_p (insn, start_label)) - { - /* We can not use the rptb insn. Replace it so reorg can use - the delay slots of the jump insn. */ - emit_insn_before (gen_addqi3 (count_reg, count_reg, GEN_INT (-1)), insn); - emit_insn_before (gen_cmpqi (count_reg, GEN_INT (0)), insn); - emit_insn_before (gen_bge (start_label), insn); - LABEL_NUSES (start_label)++; - delete_insn (insn); - return; - } - - end_label = gen_label_rtx (); - LABEL_NUSES (end_label)++; - emit_label_after (end_label, insn); - - new_start_label = gen_label_rtx (); - LABEL_NUSES (new_start_label)++; - - for (; insn; insn = PREV_INSN (insn)) - { - if (insn == start_label) - break; - if (GET_CODE (insn) == JUMP_INSN && - JUMP_LABEL (insn) == start_label) - redirect_jump (insn, new_start_label, 0); - } - if (! insn) - fatal_insn ("c4x_rptb_insert: Cannot find start label", start_label); - - emit_label_after (new_start_label, insn); - - if (TARGET_RPTS && c4x_rptb_rpts_p (PREV_INSN (insn), 0)) - emit_insn_after (gen_rpts_top (new_start_label, end_label), insn); - else - emit_insn_after (gen_rptb_top (new_start_label, end_label), insn); - if (LABEL_NUSES (start_label) == 0) - delete_insn (start_label); -} - - -/* This function is a C4x special called immediately before delayed - branch scheduling. We fix up RTPB style loops that didn't get RC - allocated as the loop counter. */ - -void -c4x_process_after_reload (first) - rtx first; -{ - rtx insn; - - for (insn = first; insn; insn = NEXT_INSN (insn)) - { - /* Look for insn. */ - if (INSN_P (insn)) - { - int insn_code_number; - rtx old; - - insn_code_number = recog_memoized (insn); - - if (insn_code_number < 0) - continue; - - /* Insert the RTX for RPTB at the top of the loop - and a label at the end of the loop. */ - if (insn_code_number == CODE_FOR_rptb_end) - c4x_rptb_insert(insn); - - /* We need to split the insn here. Otherwise the calls to - force_const_mem will not work for load_immed_address. */ - old = insn; - - /* Don't split the insn if it has been deleted. */ - if (! INSN_DELETED_P (old)) - insn = try_split (PATTERN(old), old, 1); - - /* When not optimizing, the old insn will be still left around - with only the 'deleted' bit set. Transform it into a note - to avoid confusion of subsequent processing. */ - if (INSN_DELETED_P (old)) - { - PUT_CODE (old, NOTE); - NOTE_LINE_NUMBER (old) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (old) = 0; - } - } - } -} - - -static int -c4x_a_register (op) - rtx op; -{ - return REG_P (op) && IS_ADDR_OR_PSEUDO_REG (op); -} - - -static int -c4x_x_register (op) - rtx op; -{ - return REG_P (op) && IS_INDEX_OR_PSEUDO_REG (op); -} - - -static int -c4x_immed_int_constant (op) - rtx op; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - - return GET_MODE (op) == VOIDmode - || GET_MODE_CLASS (op) == MODE_INT - || GET_MODE_CLASS (op) == MODE_PARTIAL_INT; -} - - -static int -c4x_immed_float_constant (op) - rtx op; -{ - if (GET_CODE (op) != CONST_DOUBLE) - return 0; - - /* Do not check if the CONST_DOUBLE is in memory. If there is a MEM - present this only means that a MEM rtx has been generated. It does - not mean the rtx is really in memory. */ - - return GET_MODE (op) == QFmode || GET_MODE (op) == HFmode; -} - - -int -c4x_shiftable_constant (op) - rtx op; -{ - int i; - int mask; - int val = INTVAL (op); - - for (i = 0; i < 16; i++) - { - if (val & (1 << i)) - break; - } - mask = ((0xffff >> i) << 16) | 0xffff; - if (IS_INT16_CONST (val & (1 << 31) ? (val >> i) | ~mask - : (val >> i) & mask)) - return i; - return -1; -} - - -int -c4x_H_constant (op) - rtx op; -{ - return c4x_immed_float_constant (op) && c4x_immed_float_p (op); -} - - -int -c4x_I_constant (op) - rtx op; -{ - return c4x_immed_int_constant (op) && IS_INT16_CONST (INTVAL (op)); -} - - -int -c4x_J_constant (op) - rtx op; -{ - if (TARGET_C3X) - return 0; - return c4x_immed_int_constant (op) && IS_INT8_CONST (INTVAL (op)); -} - - -static int -c4x_K_constant (op) - rtx op; -{ - if (TARGET_C3X || ! c4x_immed_int_constant (op)) - return 0; - return IS_INT5_CONST (INTVAL (op)); -} - - -int -c4x_L_constant (op) - rtx op; -{ - return c4x_immed_int_constant (op) && IS_UINT16_CONST (INTVAL (op)); -} - - -static int -c4x_N_constant (op) - rtx op; -{ - return c4x_immed_int_constant (op) && IS_NOT_UINT16_CONST (INTVAL (op)); -} - - -static int -c4x_O_constant (op) - rtx op; -{ - return c4x_immed_int_constant (op) && IS_HIGH_CONST (INTVAL (op)); -} - - -/* The constraints do not have to check the register class, - except when needed to discriminate between the constraints. - The operand has been checked by the predicates to be valid. */ - -/* ARx + 9-bit signed const or IRn - *ARx, *+ARx(n), *-ARx(n), *+ARx(IRn), *-Arx(IRn) for -256 < n < 256 - We don't include the pre/post inc/dec forms here since - they are handled by the <> constraints. */ - -int -c4x_Q_constraint (op) - rtx op; -{ - enum machine_mode mode = GET_MODE (op); - - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case REG: - return 1; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (! REG_P (op0)) - return 0; - - if (REG_P (op1)) - return 1; - - if (GET_CODE (op1) != CONST_INT) - return 0; - - /* HImode and HFmode must be offsettable. */ - if (mode == HImode || mode == HFmode) - return IS_DISP8_OFF_CONST (INTVAL (op1)); - - return IS_DISP8_CONST (INTVAL (op1)); - } - break; - - default: - break; - } - return 0; -} - - -/* ARx + 5-bit unsigned const - *ARx, *+ARx(n) for n < 32. */ - -int -c4x_R_constraint (op) - rtx op; -{ - enum machine_mode mode = GET_MODE (op); - - if (TARGET_C3X) - return 0; - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case REG: - return 1; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (! REG_P (op0)) - return 0; - - if (GET_CODE (op1) != CONST_INT) - return 0; - - /* HImode and HFmode must be offsettable. */ - if (mode == HImode || mode == HFmode) - return IS_UINT5_CONST (INTVAL (op1) + 1); - - return IS_UINT5_CONST (INTVAL (op1)); - } - break; - - default: - break; - } - return 0; -} - - -static int -c4x_R_indirect (op) - rtx op; -{ - enum machine_mode mode = GET_MODE (op); - - if (TARGET_C3X || GET_CODE (op) != MEM) - return 0; - - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case REG: - return IS_ADDR_OR_PSEUDO_REG (op); - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - /* HImode and HFmode must be offsettable. */ - if (mode == HImode || mode == HFmode) - return IS_ADDR_OR_PSEUDO_REG (op0) - && GET_CODE (op1) == CONST_INT - && IS_UINT5_CONST (INTVAL (op1) + 1); - - return REG_P (op0) - && IS_ADDR_OR_PSEUDO_REG (op0) - && GET_CODE (op1) == CONST_INT - && IS_UINT5_CONST (INTVAL (op1)); - } - break; - - default: - break; - } - return 0; -} - - -/* ARx + 1-bit unsigned const or IRn - *ARx, *+ARx(1), *-ARx(1), *+ARx(IRn), *-Arx(IRn) - We don't include the pre/post inc/dec forms here since - they are handled by the <> constraints. */ - -int -c4x_S_constraint (op) - rtx op; -{ - enum machine_mode mode = GET_MODE (op); - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case REG: - return 1; - - case PRE_MODIFY: - case POST_MODIFY: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if ((GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS) - || (op0 != XEXP (op1, 0))) - return 0; - - op0 = XEXP (op1, 0); - op1 = XEXP (op1, 1); - return REG_P (op0) && REG_P (op1); - /* Pre or post_modify with a displacement of 0 or 1 - should not be generated. */ - } - break; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (!REG_P (op0)) - return 0; - - if (REG_P (op1)) - return 1; - - if (GET_CODE (op1) != CONST_INT) - return 0; - - /* HImode and HFmode must be offsettable. */ - if (mode == HImode || mode == HFmode) - return IS_DISP1_OFF_CONST (INTVAL (op1)); - - return IS_DISP1_CONST (INTVAL (op1)); - } - break; - - default: - break; - } - return 0; -} - - -static int -c4x_S_indirect (op) - rtx op; -{ - enum machine_mode mode = GET_MODE (op); - if (GET_CODE (op) != MEM) - return 0; - - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case PRE_DEC: - case POST_DEC: - if (mode != QImode && mode != QFmode) - return 0; - case PRE_INC: - case POST_INC: - op = XEXP (op, 0); - - case REG: - return IS_ADDR_OR_PSEUDO_REG (op); - - case PRE_MODIFY: - case POST_MODIFY: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (mode != QImode && mode != QFmode) - return 0; - - if ((GET_CODE (op1) != PLUS && GET_CODE (op1) != MINUS) - || (op0 != XEXP (op1, 0))) - return 0; - - op0 = XEXP (op1, 0); - op1 = XEXP (op1, 1); - return REG_P (op0) && IS_ADDR_OR_PSEUDO_REG (op0) - && REG_P (op1) && IS_INDEX_OR_PSEUDO_REG (op1); - /* Pre or post_modify with a displacement of 0 or 1 - should not be generated. */ - } - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (REG_P (op0)) - { - /* HImode and HFmode must be offsettable. */ - if (mode == HImode || mode == HFmode) - return IS_ADDR_OR_PSEUDO_REG (op0) - && GET_CODE (op1) == CONST_INT - && IS_DISP1_OFF_CONST (INTVAL (op1)); - - if (REG_P (op1)) - return (IS_INDEX_OR_PSEUDO_REG (op1) - && IS_ADDR_OR_PSEUDO_REG (op0)) - || (IS_ADDR_OR_PSEUDO_REG (op1) - && IS_INDEX_OR_PSEUDO_REG (op0)); - - return IS_ADDR_OR_PSEUDO_REG (op0) - && GET_CODE (op1) == CONST_INT - && IS_DISP1_CONST (INTVAL (op1)); - } - } - break; - - default: - break; - } - return 0; -} - - -/* Direct memory operand. */ - -int -c4x_T_constraint (op) - rtx op; -{ - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - - if (GET_CODE (op) != LO_SUM) - { - /* Allow call operands. */ - return GET_CODE (op) == SYMBOL_REF - && GET_MODE (op) == Pmode - && SYMBOL_REF_FLAG (op); - } - - /* HImode and HFmode are not offsettable. */ - if (GET_MODE (op) == HImode || GET_CODE (op) == HFmode) - return 0; - - if ((GET_CODE (XEXP (op, 0)) == REG) - && (REGNO (XEXP (op, 0)) == DP_REGNO)) - return c4x_U_constraint (XEXP (op, 1)); - - return 0; -} - - -/* Symbolic operand. */ - -int -c4x_U_constraint (op) - rtx op; -{ - /* Don't allow direct addressing to an arbitrary constant. */ - return GET_CODE (op) == CONST - || GET_CODE (op) == SYMBOL_REF - || GET_CODE (op) == LABEL_REF; -} - - -int -c4x_autoinc_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (GET_CODE (op) == MEM) - { - enum rtx_code code = GET_CODE (XEXP (op, 0)); - - if (code == PRE_INC - || code == PRE_DEC - || code == POST_INC - || code == POST_DEC - || code == PRE_MODIFY - || code == POST_MODIFY - ) - return 1; - } - return 0; -} - - -/* Match any operand. */ - -int -any_operand (op, mode) - register rtx op ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return 1; -} - - -/* Nonzero if OP is a floating point value with value 0.0. */ - -int -fp_zero_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - REAL_VALUE_TYPE r; - - if (GET_CODE (op) != CONST_DOUBLE) - return 0; - REAL_VALUE_FROM_CONST_DOUBLE (r, op); - return REAL_VALUES_EQUAL (r, dconst0); -} - - -int -const_operand (op, mode) - register rtx op; - register enum machine_mode mode; -{ - switch (mode) - { - case QFmode: - case HFmode: - if (GET_CODE (op) != CONST_DOUBLE - || GET_MODE (op) != mode - || GET_MODE_CLASS (mode) != MODE_FLOAT) - return 0; - - return c4x_immed_float_p (op); - -#if Pmode != QImode - case Pmode: -#endif - case QImode: - if (GET_CODE (op) == CONSTANT_P_RTX) - return 1; - - if (GET_CODE (op) != CONST_INT - || (GET_MODE (op) != VOIDmode && GET_MODE (op) != mode) - || GET_MODE_CLASS (mode) != MODE_INT) - return 0; - - return IS_HIGH_CONST (INTVAL (op)) || IS_INT16_CONST (INTVAL (op)); - - case HImode: - return 0; - - default: - return 0; - } -} - - -int -stik_const_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return c4x_K_constant (op); -} - - -int -not_const_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return c4x_N_constant (op); -} - - -int -reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG - && GET_MODE (op) == QFmode) - return 0; - return register_operand (op, mode); -} - - -int -mixed_subreg_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - /* Allow (subreg:HF (reg:HI)) that be generated for a union of an - int and a long double. */ - if (GET_CODE (op) == SUBREG - && (GET_MODE (op) == QFmode) - && (GET_MODE (SUBREG_REG (op)) == QImode - || GET_MODE (SUBREG_REG (op)) == HImode)) - return 1; - return 0; -} - - -int -reg_imm_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (REG_P (op) || CONSTANT_P (op)) - return 1; - return 0; -} - - -int -not_modify_reg (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (REG_P (op) || CONSTANT_P (op)) - return 1; - if (GET_CODE (op) != MEM) - return 0; - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case REG: - return 1; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (! REG_P (op0)) - return 0; - - if (REG_P (op1) || GET_CODE (op1) == CONST_INT) - return 1; - } - - case LO_SUM: - { - rtx op0 = XEXP (op, 0); - - if (REG_P (op0) && REGNO (op0) == DP_REGNO) - return 1; - } - break; - - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return 1; - - default: - break; - } - return 0; -} - - -int -not_rc_reg (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - if (REG_P (op) && REGNO (op) == RC_REGNO) - return 0; - return 1; -} - - -/* Extended precision register R0-R1. */ - -int -r0r1_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && IS_R0R1_OR_PSEUDO_REG (op); -} - - -/* Extended precision register R2-R3. */ - -int -r2r3_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && IS_R2R3_OR_PSEUDO_REG (op); -} - - -/* Low extended precision register R0-R7. */ - -int -ext_low_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && IS_EXT_LOW_OR_PSEUDO_REG (op); -} - - -/* Extended precision register. */ - -int -ext_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - if (! REG_P (op)) - return 0; - return IS_EXT_OR_PSEUDO_REG (op); -} - - -/* Standard precision register. */ - -int -std_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && IS_STD_OR_PSEUDO_REG (op); -} - -/* Standard precision or normal register. */ - -int -std_or_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (reload_in_progress) - return std_reg_operand (op, mode); - return reg_operand (op, mode); -} - -/* Address register. */ - -int -addr_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - return c4x_a_register (op); -} - - -/* Index register. */ - -int -index_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (! reg_operand (op, mode)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return c4x_x_register (op); -} - - -/* DP register. */ - -int -dp_reg_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return REG_P (op) && IS_DP_OR_PSEUDO_REG (op); -} - - -/* SP register. */ - -int -sp_reg_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return REG_P (op) && IS_SP_OR_PSEUDO_REG (op); -} - - -/* ST register. */ - -int -st_reg_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return REG_P (op) && IS_ST_OR_PSEUDO_REG (op); -} - - -/* RC register. */ - -int -rc_reg_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return REG_P (op) && IS_RC_OR_PSEUDO_REG (op); -} - - -int -call_address_operand (op, mode) - rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - return (REG_P (op) || symbolic_address_operand (op, mode)); -} - - -/* Symbolic address operand. */ - -int -symbolic_address_operand (op, mode) - register rtx op; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - switch (GET_CODE (op)) - { - case CONST: - case SYMBOL_REF: - case LABEL_REF: - return 1; - default: - return 0; - } -} - - -/* Check dst operand of a move instruction. */ - -int -dst_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG - && mixed_subreg_operand (op, mode)) - return 0; - - if (REG_P (op)) - return reg_operand (op, mode); - - return nonimmediate_operand (op, mode); -} - - -/* Check src operand of two operand arithmetic instructions. */ - -int -src_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (GET_CODE (op) == SUBREG - && mixed_subreg_operand (op, mode)) - return 0; - - if (REG_P (op)) - return reg_operand (op, mode); - - if (mode == VOIDmode) - mode = GET_MODE (op); - - if (GET_CODE (op) == CONST_INT) - return (mode == QImode || mode == Pmode || mode == HImode) - && c4x_I_constant (op); - - /* We don't like CONST_DOUBLE integers. */ - if (GET_CODE (op) == CONST_DOUBLE) - return c4x_H_constant (op); - - /* Disallow symbolic addresses. Only the predicate - symbolic_address_operand will match these. */ - if (GET_CODE (op) == SYMBOL_REF - || GET_CODE (op) == LABEL_REF - || GET_CODE (op) == CONST) - return 0; - - /* If TARGET_LOAD_DIRECT_MEMS is non-zero, disallow direct memory - access to symbolic addresses. These operands will get forced - into a register and the movqi expander will generate a - HIGH/LO_SUM pair if TARGET_EXPOSE_LDP is non-zero. */ - if (GET_CODE (op) == MEM - && ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF - || GET_CODE (XEXP (op, 0)) == LABEL_REF - || GET_CODE (XEXP (op, 0)) == CONST))) - return ! TARGET_LOAD_DIRECT_MEMS && GET_MODE (op) == mode; - - return general_operand (op, mode); -} - - -int -src_hi_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (c4x_O_constant (op)) - return 1; - return src_operand (op, mode); -} - - -/* Check src operand of two operand logical instructions. */ - -int -lsrc_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode == VOIDmode) - mode = GET_MODE (op); - - if (mode != QImode && mode != Pmode) - fatal_insn ("mode not QImode", op); - - if (GET_CODE (op) == CONST_INT) - return c4x_L_constant (op) || c4x_J_constant (op); - - return src_operand (op, mode); -} - - -/* Check src operand of two operand tricky instructions. */ - -int -tsrc_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode == VOIDmode) - mode = GET_MODE (op); - - if (mode != QImode && mode != Pmode) - fatal_insn ("mode not QImode", op); - - if (GET_CODE (op) == CONST_INT) - return c4x_L_constant (op) || c4x_N_constant (op) || c4x_J_constant (op); - - return src_operand (op, mode); -} - - -int -reg_or_const_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return reg_operand (op, mode) || const_operand (op, mode); -} - - -/* Check for indirect operands allowable in parallel instruction. */ - -int -par_ind_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - return c4x_S_indirect (op); -} - - -/* Check for operands allowable in parallel instruction. */ - -int -parallel_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return ext_low_reg_operand (op, mode) || par_ind_operand (op, mode); -} - - -static void -c4x_S_address_parse (op, base, incdec, index, disp) - rtx op; - int *base; - int *incdec; - int *index; - int *disp; -{ - *base = 0; - *incdec = 0; - *index = 0; - *disp = 0; - - if (GET_CODE (op) != MEM) - fatal_insn ("invalid indirect memory address", op); - - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case PRE_DEC: - *base = REGNO (XEXP (op, 0)); - *incdec = 1; - *disp = -1; - return; - - case POST_DEC: - *base = REGNO (XEXP (op, 0)); - *incdec = 1; - *disp = 0; - return; - - case PRE_INC: - *base = REGNO (XEXP (op, 0)); - *incdec = 1; - *disp = 1; - return; - - case POST_INC: - *base = REGNO (XEXP (op, 0)); - *incdec = 1; - *disp = 0; - return; - - case POST_MODIFY: - *base = REGNO (XEXP (op, 0)); - if (REG_P (XEXP (XEXP (op, 1), 1))) - { - *index = REGNO (XEXP (XEXP (op, 1), 1)); - *disp = 0; /* ??? */ - } - else - *disp = INTVAL (XEXP (XEXP (op, 1), 1)); - *incdec = 1; - return; - - case PRE_MODIFY: - *base = REGNO (XEXP (op, 0)); - if (REG_P (XEXP (XEXP (op, 1), 1))) - { - *index = REGNO (XEXP (XEXP (op, 1), 1)); - *disp = 1; /* ??? */ - } - else - *disp = INTVAL (XEXP (XEXP (op, 1), 1)); - *incdec = 1; - - return; - - case REG: - *base = REGNO (op); - return; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if (c4x_a_register (op0)) - { - if (c4x_x_register (op1)) - { - *base = REGNO (op0); - *index = REGNO (op1); - return; - } - else if ((GET_CODE (op1) == CONST_INT - && IS_DISP1_CONST (INTVAL (op1)))) - { - *base = REGNO (op0); - *disp = INTVAL (op1); - return; - } - } - else if (c4x_x_register (op0) && c4x_a_register (op1)) - { - *base = REGNO (op1); - *index = REGNO (op0); - return; - } - } - /* Fallthrough. */ - - default: - fatal_insn ("invalid indirect (S) memory address", op); - } -} - - -int -c4x_address_conflict (op0, op1, store0, store1) - rtx op0; - rtx op1; - int store0; - int store1; -{ - int base0; - int base1; - int incdec0; - int incdec1; - int index0; - int index1; - int disp0; - int disp1; - - if (MEM_VOLATILE_P (op0) && MEM_VOLATILE_P (op1)) - return 1; - - c4x_S_address_parse (op0, &base0, &incdec0, &index0, &disp0); - c4x_S_address_parse (op1, &base1, &incdec1, &index1, &disp1); - - if (store0 && store1) - { - /* If we have two stores in parallel to the same address, then - the C4x only executes one of the stores. This is unlikely to - cause problems except when writing to a hardware device such - as a FIFO since the second write will be lost. The user - should flag the hardware location as being volatile so that - we don't do this optimisation. While it is unlikely that we - have an aliased address if both locations are not marked - volatile, it is probably safer to flag a potential conflict - if either location is volatile. */ - if (! flag_argument_noalias) - { - if (MEM_VOLATILE_P (op0) || MEM_VOLATILE_P (op1)) - return 1; - } - } - - /* If have a parallel load and a store to the same address, the load - is performed first, so there is no conflict. Similarly, there is - no conflict if have parallel loads from the same address. */ - - /* Cannot use auto increment or auto decrement twice for same - base register. */ - if (base0 == base1 && incdec0 && incdec0) - return 1; - - /* It might be too confusing for GCC if we have use a base register - with a side effect and a memory reference using the same register - in parallel. */ - if (! TARGET_DEVEL && base0 == base1 && (incdec0 || incdec1)) - return 1; - - /* We can not optimize the case where op1 and op2 refer to the same - address. */ - if (base0 == base1 && disp0 == disp1 && index0 == index1) - return 1; - - /* No conflict. */ - return 0; -} - - -/* Check for while loop inside a decrement and branch loop. */ - -int -c4x_label_conflict (insn, jump, db) - rtx insn; - rtx jump; - rtx db; -{ - while (insn) - { - if (GET_CODE (insn) == CODE_LABEL) - { - if (CODE_LABEL_NUMBER (jump) == CODE_LABEL_NUMBER (insn)) - return 1; - if (CODE_LABEL_NUMBER (db) == CODE_LABEL_NUMBER (insn)) - return 0; - } - insn = PREV_INSN (insn); - } - return 1; -} - - -/* Validate combination of operands for parallel load/store instructions. */ - -int -valid_parallel_load_store (operands, mode) - rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - rtx op0 = operands[0]; - rtx op1 = operands[1]; - rtx op2 = operands[2]; - rtx op3 = operands[3]; - - if (GET_CODE (op0) == SUBREG) - op0 = SUBREG_REG (op0); - if (GET_CODE (op1) == SUBREG) - op1 = SUBREG_REG (op1); - if (GET_CODE (op2) == SUBREG) - op2 = SUBREG_REG (op2); - if (GET_CODE (op3) == SUBREG) - op3 = SUBREG_REG (op3); - - /* The patterns should only allow ext_low_reg_operand() or - par_ind_operand() operands. Thus of the 4 operands, only 2 - should be REGs and the other 2 should be MEMs. */ - - /* This test prevents the multipack pass from using this pattern if - op0 is used as an index or base register in op2 or op3, since - this combination will require reloading. */ - if (GET_CODE (op0) == REG - && ((GET_CODE (op2) == MEM && reg_mentioned_p (op0, XEXP (op2, 0))) - || (GET_CODE (op3) == MEM && reg_mentioned_p (op0, XEXP (op3, 0))))) - return 0; - - /* LDI||LDI. */ - if (GET_CODE (op0) == REG && GET_CODE (op2) == REG) - return (REGNO (op0) != REGNO (op2)) - && GET_CODE (op1) == MEM && GET_CODE (op3) == MEM - && ! c4x_address_conflict (op1, op3, 0, 0); - - /* STI||STI. */ - if (GET_CODE (op1) == REG && GET_CODE (op3) == REG) - return GET_CODE (op0) == MEM && GET_CODE (op2) == MEM - && ! c4x_address_conflict (op0, op2, 1, 1); - - /* LDI||STI. */ - if (GET_CODE (op0) == REG && GET_CODE (op3) == REG) - return GET_CODE (op1) == MEM && GET_CODE (op2) == MEM - && ! c4x_address_conflict (op1, op2, 0, 1); - - /* STI||LDI. */ - if (GET_CODE (op1) == REG && GET_CODE (op2) == REG) - return GET_CODE (op0) == MEM && GET_CODE (op3) == MEM - && ! c4x_address_conflict (op0, op3, 1, 0); - - return 0; -} - - -int -valid_parallel_operands_4 (operands, mode) - rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - rtx op0 = operands[0]; - rtx op2 = operands[2]; - - if (GET_CODE (op0) == SUBREG) - op0 = SUBREG_REG (op0); - if (GET_CODE (op2) == SUBREG) - op2 = SUBREG_REG (op2); - - /* This test prevents the multipack pass from using this pattern if - op0 is used as an index or base register in op2, since this combination - will require reloading. */ - if (GET_CODE (op0) == REG - && GET_CODE (op2) == MEM - && reg_mentioned_p (op0, XEXP (op2, 0))) - return 0; - - return 1; -} - - -int -valid_parallel_operands_5 (operands, mode) - rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - int regs = 0; - rtx op0 = operands[0]; - rtx op1 = operands[1]; - rtx op2 = operands[2]; - rtx op3 = operands[3]; - - if (GET_CODE (op0) == SUBREG) - op0 = SUBREG_REG (op0); - if (GET_CODE (op1) == SUBREG) - op1 = SUBREG_REG (op1); - if (GET_CODE (op2) == SUBREG) - op2 = SUBREG_REG (op2); - - /* The patterns should only allow ext_low_reg_operand() or - par_ind_operand() operands. Operands 1 and 2 may be commutative - but only one of them can be a register. */ - if (GET_CODE (op1) == REG) - regs++; - if (GET_CODE (op2) == REG) - regs++; - - if (regs != 1) - return 0; - - /* This test prevents the multipack pass from using this pattern if - op0 is used as an index or base register in op3, since this combination - will require reloading. */ - if (GET_CODE (op0) == REG - && GET_CODE (op3) == MEM - && reg_mentioned_p (op0, XEXP (op3, 0))) - return 0; - - return 1; -} - - -int -valid_parallel_operands_6 (operands, mode) - rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; -{ - int regs = 0; - rtx op0 = operands[0]; - rtx op1 = operands[1]; - rtx op2 = operands[2]; - rtx op4 = operands[4]; - rtx op5 = operands[5]; - - if (GET_CODE (op1) == SUBREG) - op1 = SUBREG_REG (op1); - if (GET_CODE (op2) == SUBREG) - op2 = SUBREG_REG (op2); - if (GET_CODE (op4) == SUBREG) - op4 = SUBREG_REG (op4); - if (GET_CODE (op5) == SUBREG) - op5 = SUBREG_REG (op5); - - /* The patterns should only allow ext_low_reg_operand() or - par_ind_operand() operands. Thus of the 4 input operands, only 2 - should be REGs and the other 2 should be MEMs. */ - - if (GET_CODE (op1) == REG) - regs++; - if (GET_CODE (op2) == REG) - regs++; - if (GET_CODE (op4) == REG) - regs++; - if (GET_CODE (op5) == REG) - regs++; - - /* The new C30/C40 silicon dies allow 3 regs of the 4 input operands. - Perhaps we should count the MEMs as well? */ - if (regs != 2) - return 0; - - /* This test prevents the multipack pass from using this pattern if - op0 is used as an index or base register in op4 or op5, since - this combination will require reloading. */ - if (GET_CODE (op0) == REG - && ((GET_CODE (op4) == MEM && reg_mentioned_p (op0, XEXP (op4, 0))) - || (GET_CODE (op5) == MEM && reg_mentioned_p (op0, XEXP (op5, 0))))) - return 0; - - return 1; -} - - -/* Validate combination of src operands. Note that the operands have - been screened by the src_operand predicate. We just have to check - that the combination of operands is valid. If FORCE is set, ensure - that the destination regno is valid if we have a 2 operand insn. */ - -static int -c4x_valid_operands (code, operands, mode, force) - enum rtx_code code; - rtx *operands; - enum machine_mode mode ATTRIBUTE_UNUSED; - int force; -{ - rtx op1; - rtx op2; - enum rtx_code code1; - enum rtx_code code2; - - if (code == COMPARE) - { - op1 = operands[0]; - op2 = operands[1]; - } - else - { - op1 = operands[1]; - op2 = operands[2]; - } - - if (GET_CODE (op1) == SUBREG) - op1 = SUBREG_REG (op1); - if (GET_CODE (op2) == SUBREG) - op2 = SUBREG_REG (op2); - - code1 = GET_CODE (op1); - code2 = GET_CODE (op2); - - if (code1 == REG && code2 == REG) - return 1; - - if (code1 == MEM && code2 == MEM) - { - if (c4x_S_indirect (op1) && c4x_S_indirect (op2)) - return 1; - return c4x_R_indirect (op1) && c4x_R_indirect (op2); - } - - if (code1 == code2) - return 0; - - if (code1 == REG) - { - switch (code2) - { - case CONST_INT: - if (c4x_J_constant (op2) && c4x_R_indirect (op1)) - return 1; - break; - - case CONST_DOUBLE: - if (! c4x_H_constant (op2)) - return 0; - break; - - /* Any valid memory operand screened by src_operand is OK. */ - case MEM: - - /* After CSE, any remaining (ADDRESSOF:P reg) gets converted - into a stack slot memory address comprising a PLUS and a - constant. */ - case ADDRESSOF: - break; - - default: - fatal_insn ("c4x_valid_operands: Internal error", op2); - break; - } - - /* Check that we have a valid destination register for a two operand - instruction. */ - return ! force || code == COMPARE || REGNO (op1) == REGNO (operands[0]); - } - - /* We assume MINUS is commutative since the subtract patterns - also support the reverse subtract instructions. Since op1 - is not a register, and op2 is a register, op1 can only - be a restricted memory operand for a shift instruction. */ - if (code == ASHIFTRT || code == LSHIFTRT - || code == ASHIFT || code == COMPARE) - return code2 == REG - && (c4x_S_indirect (op1) || c4x_R_indirect (op1)); - - switch (code1) - { - case CONST_INT: - if (c4x_J_constant (op1) && c4x_R_indirect (op2)) - return 1; - break; - - case CONST_DOUBLE: - if (! c4x_H_constant (op1)) - return 0; - break; - - /* Any valid memory operand screened by src_operand is OK. */ - case MEM: -#if 0 - if (code2 != REG) - return 0; -#endif - break; - - /* After CSE, any remaining (ADDRESSOF:P reg) gets converted - into a stack slot memory address comprising a PLUS and a - constant. */ - case ADDRESSOF: - break; - - default: - abort (); - break; - } - - /* Check that we have a valid destination register for a two operand - instruction. */ - return ! force || REGNO (op1) == REGNO (operands[0]); -} - - -int valid_operands (code, operands, mode) - enum rtx_code code; - rtx *operands; - enum machine_mode mode; -{ - - /* If we are not optimizing then we have to let anything go and let - reload fix things up. instantiate_decl in function.c can produce - invalid insns by changing the offset of a memory operand from a - valid one into an invalid one, when the second operand is also a - memory operand. The alternative is not to allow two memory - operands for an insn when not optimizing. The problem only rarely - occurs, for example with the C-torture program DFcmp.c. */ - - return ! optimize || c4x_valid_operands (code, operands, mode, 0); -} - - -int -legitimize_operands (code, operands, mode) - enum rtx_code code; - rtx *operands; - enum machine_mode mode; -{ - /* Compare only has 2 operands. */ - if (code == COMPARE) - { - /* During RTL generation, force constants into pseudos so that - they can get hoisted out of loops. This will tie up an extra - register but can save an extra cycle. Only do this if loop - optimisation enabled. (We cannot pull this trick for add and - sub instructions since the flow pass won't find - autoincrements etc.) This allows us to generate compare - instructions like CMPI R0, *AR0++ where R0 = 42, say, instead - of LDI *AR0++, R0; CMPI 42, R0. - - Note that expand_binops will try to load an expensive constant - into a register if it is used within a loop. Unfortunately, - the cost mechanism doesn't allow us to look at the other - operand to decide whether the constant is expensive. */ - - if (! reload_in_progress - && TARGET_HOIST - && optimize > 0 - && GET_CODE (operands[1]) == CONST_INT - && preserve_subexpressions_p () - && rtx_cost (operands[1], code) > 1) - operands[1] = force_reg (mode, operands[1]); - - if (! reload_in_progress - && ! c4x_valid_operands (code, operands, mode, 0)) - operands[0] = force_reg (mode, operands[0]); - return 1; - } - - /* We cannot do this for ADDI/SUBI insns since we will - defeat the flow pass from finding autoincrement addressing - opportunities. */ - if (! reload_in_progress - && ! ((code == PLUS || code == MINUS) && mode == Pmode) - && TARGET_HOIST - && optimize > 1 - && GET_CODE (operands[2]) == CONST_INT - && preserve_subexpressions_p () - && rtx_cost (operands[2], code) > 1) - operands[2] = force_reg (mode, operands[2]); - - /* We can get better code on a C30 if we force constant shift counts - into a register. This way they can get hoisted out of loops, - tying up a register, but saving an instruction. The downside is - that they may get allocated to an address or index register, and - thus we will get a pipeline conflict if there is a nearby - indirect address using an address register. - - Note that expand_binops will not try to load an expensive constant - into a register if it is used within a loop for a shift insn. */ - - if (! reload_in_progress - && ! c4x_valid_operands (code, operands, mode, TARGET_FORCE)) - { - /* If the operand combination is invalid, we force operand1 into a - register, preventing reload from having doing to do this at a - later stage. */ - operands[1] = force_reg (mode, operands[1]); - if (TARGET_FORCE) - { - emit_move_insn (operands[0], operands[1]); - operands[1] = copy_rtx (operands[0]); - } - else - { - /* Just in case... */ - if (! c4x_valid_operands (code, operands, mode, 0)) - operands[2] = force_reg (mode, operands[2]); - } - } - - /* Right shifts require a negative shift count, but GCC expects - a positive count, so we emit a NEG. */ - if ((code == ASHIFTRT || code == LSHIFTRT) - && (GET_CODE (operands[2]) != CONST_INT)) - operands[2] = gen_rtx_NEG (mode, negate_rtx (mode, operands[2])); - - return 1; -} - - -/* The following predicates are used for instruction scheduling. */ - -int -group1_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && (! reload_completed || IS_GROUP1_REG (op)); -} - - -int -group1_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - if (GET_CODE (op) == MEM) - { - op = XEXP (op, 0); - if (GET_CODE (op) == PLUS) - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if ((REG_P (op0) && (! reload_completed || IS_GROUP1_REG (op0))) - || (REG_P (op1) && (! reload_completed || IS_GROUP1_REG (op1)))) - return 1; - } - else if ((REG_P (op)) && (! reload_completed || IS_GROUP1_REG (op))) - return 1; - } - - return 0; -} - - -/* Return true if any one of the address registers. */ - -int -arx_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && (! reload_completed || IS_ADDR_REG (op)); -} - - -static int -c4x_arn_reg_operand (op, mode, regno) - rtx op; - enum machine_mode mode; - unsigned int regno; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - return REG_P (op) && (! reload_completed || (REGNO (op) == regno)); -} - - -static int -c4x_arn_mem_operand (op, mode, regno) - rtx op; - enum machine_mode mode; - unsigned int regno; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - if (GET_CODE (op) == MEM) - { - op = XEXP (op, 0); - switch (GET_CODE (op)) - { - case PRE_DEC: - case POST_DEC: - case PRE_INC: - case POST_INC: - op = XEXP (op, 0); - - case REG: - return REG_P (op) && (! reload_completed || (REGNO (op) == regno)); - - case PRE_MODIFY: - case POST_MODIFY: - if (REG_P (XEXP (op, 0)) && (! reload_completed - || (REGNO (XEXP (op, 0)) == regno))) - return 1; - if (REG_P (XEXP (XEXP (op, 1), 1)) - && (! reload_completed - || (REGNO (XEXP (XEXP (op, 1), 1)) == regno))) - return 1; - break; - - case PLUS: - { - rtx op0 = XEXP (op, 0); - rtx op1 = XEXP (op, 1); - - if ((REG_P (op0) && (! reload_completed - || (REGNO (op0) == regno))) - || (REG_P (op1) && (! reload_completed - || (REGNO (op1) == regno)))) - return 1; - } - break; - - default: - break; - } - } - return 0; -} - - -int -ar0_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR0_REGNO); -} - - -int -ar0_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR0_REGNO); -} - - -int -ar1_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR1_REGNO); -} - - -int -ar1_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR1_REGNO); -} - - -int -ar2_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR2_REGNO); -} - - -int -ar2_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR2_REGNO); -} - - -int -ar3_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR3_REGNO); -} - - -int -ar3_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR3_REGNO); -} - - -int -ar4_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR4_REGNO); -} - - -int -ar4_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR4_REGNO); -} - - -int -ar5_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR5_REGNO); -} - - -int -ar5_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR5_REGNO); -} - - -int -ar6_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR6_REGNO); -} - - -int -ar6_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR6_REGNO); -} - - -int -ar7_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, AR7_REGNO); -} - - -int -ar7_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, AR7_REGNO); -} - - -int -ir0_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, IR0_REGNO); -} - - -int -ir0_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, IR0_REGNO); -} - - -int -ir1_reg_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_reg_operand (op, mode, IR1_REGNO); -} - - -int -ir1_mem_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return c4x_arn_mem_operand (op, mode, IR1_REGNO); -} - - -/* This is similar to operand_subword but allows autoincrement - addressing. */ - -rtx -c4x_operand_subword (op, i, validate_address, mode) - rtx op; - int i; - int validate_address; - enum machine_mode mode; -{ - if (mode != HImode && mode != HFmode) - fatal_insn ("c4x_operand_subword: invalid mode", op); - - if (mode == HFmode && REG_P (op)) - fatal_insn ("c4x_operand_subword: invalid operand", op); - - if (GET_CODE (op) == MEM) - { - enum rtx_code code = GET_CODE (XEXP (op, 0)); - enum machine_mode mode = GET_MODE (XEXP (op, 0)); - enum machine_mode submode; - - submode = mode; - if (mode == HImode) - submode = QImode; - else if (mode == HFmode) - submode = QFmode; - - switch (code) - { - case POST_INC: - case PRE_INC: - return gen_rtx_MEM (submode, XEXP (op, 0)); - - case POST_DEC: - case PRE_DEC: - case PRE_MODIFY: - case POST_MODIFY: - /* We could handle these with some difficulty. - e.g., *p-- => *(p-=2); *(p+1). */ - fatal_insn ("c4x_operand_subword: invalid autoincrement", op); - - case SYMBOL_REF: - case LABEL_REF: - case CONST: - case CONST_INT: - fatal_insn ("c4x_operand_subword: invalid address", op); - - /* Even though offsettable_address_p considers (MEM - (LO_SUM)) to be offsettable, it is not safe if the - address is at the end of the data page since we also have - to fix up the associated high PART. In this case where - we are trying to split a HImode or HFmode memory - reference, we would have to emit another insn to reload a - new HIGH value. It's easier to disable LO_SUM memory references - in HImode or HFmode and we probably get better code. */ - case LO_SUM: - fatal_insn ("c4x_operand_subword: address not offsettable", op); - - default: - break; - } - } - - return operand_subword (op, i, validate_address, mode); -} - -struct name_list -{ - struct name_list *next; - const char *name; -}; - -static struct name_list *global_head; -static struct name_list *extern_head; - - -/* Add NAME to list of global symbols and remove from external list if - present on external list. */ - -void -c4x_global_label (name) - const char *name; -{ - struct name_list *p, *last; - - /* Do not insert duplicate names, so linearly search through list of - existing names. */ - p = global_head; - while (p) - { - if (strcmp (p->name, name) == 0) - return; - p = p->next; - } - p = (struct name_list *) permalloc (sizeof *p); - p->next = global_head; - p->name = name; - global_head = p; - - /* Remove this name from ref list if present. */ - last = NULL; - p = extern_head; - while (p) - { - if (strcmp (p->name, name) == 0) - { - if (last) - last->next = p->next; - else - extern_head = p->next; - break; - } - last = p; - p = p->next; - } -} - - -/* Add NAME to list of external symbols. */ - -void -c4x_external_ref (name) - const char *name; -{ - struct name_list *p; - - /* Do not insert duplicate names. */ - p = extern_head; - while (p) - { - if (strcmp (p->name, name) == 0) - return; - p = p->next; - } - - /* Do not insert ref if global found. */ - p = global_head; - while (p) - { - if (strcmp (p->name, name) == 0) - return; - p = p->next; - } - p = (struct name_list *) permalloc (sizeof *p); - p->next = extern_head; - p->name = name; - extern_head = p; -} - - -void -c4x_file_end (fp) - FILE *fp; -{ - struct name_list *p; - - /* Output all external names that are not global. */ - p = extern_head; - while (p) - { - fprintf (fp, "\t.ref\t"); - assemble_name (fp, p->name); - fprintf (fp, "\n"); - p = p->next; - } - fprintf (fp, "\t.end\n"); -} - - -static void -c4x_check_attribute (attrib, list, decl, attributes) - const char *attrib; - tree list, decl, *attributes; -{ - while (list != NULL_TREE - && IDENTIFIER_POINTER (TREE_PURPOSE (list)) - != IDENTIFIER_POINTER (DECL_NAME (decl))) - list = TREE_CHAIN (list); - if (list) - *attributes = tree_cons (get_identifier (attrib), TREE_VALUE (list), - *attributes); -} - - -static void -c4x_insert_attributes (decl, attributes) - tree decl, *attributes; -{ - switch (TREE_CODE (decl)) - { - case FUNCTION_DECL: - c4x_check_attribute ("section", code_tree, decl, attributes); - c4x_check_attribute ("const", pure_tree, decl, attributes); - c4x_check_attribute ("noreturn", noreturn_tree, decl, attributes); - c4x_check_attribute ("interrupt", interrupt_tree, decl, attributes); - break; - - case VAR_DECL: - c4x_check_attribute ("section", data_tree, decl, attributes); - break; - - default: - break; - } -} - -/* Table of valid machine attributes. */ -const struct attribute_spec c4x_attribute_table[] = -{ - /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ - { "interrupt", 0, 0, false, true, true, c4x_handle_fntype_attribute }, - /* FIXME: code elsewhere in this file treats "naked" as a synonym of - "interrupt"; should it be accepted here? */ - { "assembler", 0, 0, false, true, true, c4x_handle_fntype_attribute }, - { "leaf_pretend", 0, 0, false, true, true, c4x_handle_fntype_attribute }, - { NULL, 0, 0, false, false, false, NULL } -}; - -/* Handle an attribute requiring a FUNCTION_TYPE; - arguments as in struct attribute_spec.handler. */ -static tree -c4x_handle_fntype_attribute (node, name, args, flags, no_add_attrs) - tree *node; - tree name; - tree args ATTRIBUTE_UNUSED; - int flags ATTRIBUTE_UNUSED; - bool *no_add_attrs; -{ - if (TREE_CODE (*node) != FUNCTION_TYPE) - { - warning ("`%s' attribute only applies to functions", - IDENTIFIER_POINTER (name)); - *no_add_attrs = true; - } - - return NULL_TREE; -} - - -/* !!! FIXME to emit RPTS correctly. */ - -int -c4x_rptb_rpts_p (insn, op) - rtx insn, op; -{ - /* The next insn should be our label marking where the - repeat block starts. */ - insn = NEXT_INSN (insn); - if (GET_CODE (insn) != CODE_LABEL) - { - /* Some insns may have been shifted between the RPTB insn - and the top label... They were probably destined to - be moved out of the loop. For now, let's leave them - where they are and print a warning. We should - probably move these insns before the repeat block insn. */ - if (TARGET_DEBUG) - fatal_insn("c4x_rptb_rpts_p: Repeat block top label moved\n", - insn); - return 0; - } - - /* Skip any notes. */ - insn = next_nonnote_insn (insn); - - /* This should be our first insn in the loop. */ - if (! INSN_P (insn)) - return 0; - - /* Skip any notes. */ - insn = next_nonnote_insn (insn); - - if (! INSN_P (insn)) - return 0; - - if (recog_memoized (insn) != CODE_FOR_rptb_end) - return 0; - - if (TARGET_RPTS) - return 1; - - return (GET_CODE (op) == CONST_INT) && TARGET_RPTS_CYCLES (INTVAL (op)); -} - - -/* Check if register r11 is used as the destination of an insn. */ - -static int -c4x_r11_set_p(x) - rtx x; -{ - rtx set; - int i, j; - const char *fmt; - - if (x == 0) - return 0; - - if (INSN_P (x) && GET_CODE (PATTERN (x)) == SEQUENCE) - x = XVECEXP (PATTERN (x), 0, XVECLEN (PATTERN (x), 0) - 1); - - if (INSN_P (x) && (set = single_set (x))) - x = SET_DEST (set); - - if (GET_CODE (x) == REG && REGNO (x) == R11_REGNO) - return 1; - - fmt = GET_RTX_FORMAT (GET_CODE (x)); - for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) - { - if (fmt[i] == 'e') - { - if (c4x_r11_set_p (XEXP (x, i))) - return 1; - } - else if (fmt[i] == 'E') - for (j = XVECLEN (x, i) - 1; j >= 0; j--) - if (c4x_r11_set_p (XVECEXP (x, i, j))) - return 1; - } - return 0; -} - - -/* The c4x sometimes has a problem when the insn before the laj insn - sets the r11 register. Check for this situation. */ - -int -c4x_check_laj_p (insn) - rtx insn; -{ - insn = prev_nonnote_insn (insn); - - /* If this is the start of the function no nop is needed. */ - if (insn == 0) - return 0; - - /* If the previous insn is a code label we have to insert a nop. This - could be a jump or table jump. We can find the normal jumps by - scanning the function but this will not find table jumps. */ - if (GET_CODE (insn) == CODE_LABEL) - return 1; - - /* If the previous insn sets register r11 we have to insert a nop. */ - if (c4x_r11_set_p (insn)) - return 1; - - /* No nop needed. */ - return 0; -} - - -/* Adjust the cost of a scheduling dependency. Return the new cost of - a dependency LINK or INSN on DEP_INSN. COST is the current cost. - A set of an address register followed by a use occurs a 2 cycle - stall (reduced to a single cycle on the c40 using LDA), while - a read of an address register followed by a use occurs a single cycle. */ - -#define SET_USE_COST 3 -#define SETLDA_USE_COST 2 -#define READ_USE_COST 2 - -static int -c4x_adjust_cost (insn, link, dep_insn, cost) - rtx insn; - rtx link; - rtx dep_insn; - int cost; -{ - /* Don't worry about this until we know what registers have been - assigned. */ - if (flag_schedule_insns == 0 && ! reload_completed) - return 0; - - /* How do we handle dependencies where a read followed by another - read causes a pipeline stall? For example, a read of ar0 followed - by the use of ar0 for a memory reference. It looks like we - need to extend the scheduler to handle this case. */ - - /* Reload sometimes generates a CLOBBER of a stack slot, e.g., - (clobber (mem:QI (plus:QI (reg:QI 11 ar3) (const_int 261)))), - so only deal with insns we know about. */ - if (recog_memoized (dep_insn) < 0) - return 0; - - if (REG_NOTE_KIND (link) == 0) - { - int max = 0; - - /* Data dependency; DEP_INSN writes a register that INSN reads some - cycles later. */ - if (TARGET_C3X) - { - if (get_attr_setgroup1 (dep_insn) && get_attr_usegroup1 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_readarx (dep_insn) && get_attr_usegroup1 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - } - else - { - /* This could be significantly optimized. We should look - to see if dep_insn sets ar0-ar7 or ir0-ir1 and if - insn uses ar0-ar7. We then test if the same register - is used. The tricky bit is that some operands will - use several registers... */ - if (get_attr_setar0 (dep_insn) && get_attr_usear0 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar0 (dep_insn) && get_attr_usear0 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar0 (dep_insn) && get_attr_usear0 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar1 (dep_insn) && get_attr_usear1 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar1 (dep_insn) && get_attr_usear1 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar1 (dep_insn) && get_attr_usear1 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar2 (dep_insn) && get_attr_usear2 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar2 (dep_insn) && get_attr_usear2 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar2 (dep_insn) && get_attr_usear2 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar3 (dep_insn) && get_attr_usear3 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar3 (dep_insn) && get_attr_usear3 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar3 (dep_insn) && get_attr_usear3 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar4 (dep_insn) && get_attr_usear4 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar4 (dep_insn) && get_attr_usear4 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar4 (dep_insn) && get_attr_usear4 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar5 (dep_insn) && get_attr_usear5 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar5 (dep_insn) && get_attr_usear5 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar5 (dep_insn) && get_attr_usear5 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar6 (dep_insn) && get_attr_usear6 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar6 (dep_insn) && get_attr_usear6 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar6 (dep_insn) && get_attr_usear6 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setar7 (dep_insn) && get_attr_usear7 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ar7 (dep_insn) && get_attr_usear7 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - if (get_attr_readar7 (dep_insn) && get_attr_usear7 (insn)) - max = READ_USE_COST > max ? READ_USE_COST : max; - - if (get_attr_setir0 (dep_insn) && get_attr_useir0 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ir0 (dep_insn) && get_attr_useir0 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - - if (get_attr_setir1 (dep_insn) && get_attr_useir1 (insn)) - max = SET_USE_COST > max ? SET_USE_COST : max; - if (get_attr_setlda_ir1 (dep_insn) && get_attr_useir1 (insn)) - max = SETLDA_USE_COST > max ? SETLDA_USE_COST : max; - } - - if (max) - cost = max; - - /* For other data dependencies, the default cost specified in the - md is correct. */ - return cost; - } - else if (REG_NOTE_KIND (link) == REG_DEP_ANTI) - { - /* Anti dependency; DEP_INSN reads a register that INSN writes some - cycles later. */ - - /* For c4x anti dependencies, the cost is 0. */ - return 0; - } - else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) - { - /* Output dependency; DEP_INSN writes a register that INSN writes some - cycles later. */ - - /* For c4x output dependencies, the cost is 0. */ - return 0; - } - else - abort (); -} - -void -c4x_init_builtins () -{ - tree endlink = void_list_node; - - builtin_function ("fast_ftoi", - build_function_type - (integer_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)), - C4X_BUILTIN_FIX, BUILT_IN_MD, NULL); - builtin_function ("ansi_ftoi", - build_function_type - (integer_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)), - C4X_BUILTIN_FIX_ANSI, BUILT_IN_MD, NULL); - if (TARGET_C3X) - builtin_function ("fast_imult", - build_function_type - (integer_type_node, - tree_cons (NULL_TREE, integer_type_node, - tree_cons (NULL_TREE, - integer_type_node, endlink))), - C4X_BUILTIN_MPYI, BUILT_IN_MD, NULL); - else - { - builtin_function ("toieee", - build_function_type - (double_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)), - C4X_BUILTIN_TOIEEE, BUILT_IN_MD, NULL); - builtin_function ("frieee", - build_function_type - (double_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)), - C4X_BUILTIN_FRIEEE, BUILT_IN_MD, NULL); - builtin_function ("fast_invf", - build_function_type - (double_type_node, - tree_cons (NULL_TREE, double_type_node, endlink)), - C4X_BUILTIN_RCPF, BUILT_IN_MD, NULL); - } -} - - -rtx -c4x_expand_builtin (exp, target, subtarget, mode, ignore) - tree exp; - rtx target; - rtx subtarget ATTRIBUTE_UNUSED; - enum machine_mode mode ATTRIBUTE_UNUSED; - int ignore ATTRIBUTE_UNUSED; -{ - tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); - unsigned int fcode = DECL_FUNCTION_CODE (fndecl); - tree arglist = TREE_OPERAND (exp, 1); - tree arg0, arg1; - rtx r0, r1; - - switch (fcode) - { - case C4X_BUILTIN_FIX: - arg0 = TREE_VALUE (arglist); - r0 = expand_expr (arg0, NULL_RTX, QFmode, 0); - r0 = protect_from_queue (r0, 0); - if (! target || ! register_operand (target, QImode)) - target = gen_reg_rtx (QImode); - emit_insn (gen_fixqfqi_clobber (target, r0)); - return target; - - case C4X_BUILTIN_FIX_ANSI: - arg0 = TREE_VALUE (arglist); - r0 = expand_expr (arg0, NULL_RTX, QFmode, 0); - r0 = protect_from_queue (r0, 0); - if (! target || ! register_operand (target, QImode)) - target = gen_reg_rtx (QImode); - emit_insn (gen_fix_truncqfqi2 (target, r0)); - return target; - - case C4X_BUILTIN_MPYI: - if (! TARGET_C3X) - break; - arg0 = TREE_VALUE (arglist); - arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - r0 = expand_expr (arg0, NULL_RTX, QImode, 0); - r1 = expand_expr (arg1, NULL_RTX, QImode, 0); - r0 = protect_from_queue (r0, 0); - r1 = protect_from_queue (r1, 0); - if (! target || ! register_operand (target, QImode)) - target = gen_reg_rtx (QImode); - emit_insn (gen_mulqi3_24_clobber (target, r0, r1)); - return target; - - case C4X_BUILTIN_TOIEEE: - if (TARGET_C3X) - break; - arg0 = TREE_VALUE (arglist); - r0 = expand_expr (arg0, NULL_RTX, QFmode, 0); - r0 = protect_from_queue (r0, 0); - if (! target || ! register_operand (target, QFmode)) - target = gen_reg_rtx (QFmode); - emit_insn (gen_toieee (target, r0)); - return target; - - case C4X_BUILTIN_FRIEEE: - if (TARGET_C3X) - break; - arg0 = TREE_VALUE (arglist); - if (TREE_CODE (arg0) == VAR_DECL || TREE_CODE (arg0) == PARM_DECL) - put_var_into_stack (arg0); - r0 = expand_expr (arg0, NULL_RTX, QFmode, 0); - r0 = protect_from_queue (r0, 0); - if (register_operand (r0, QFmode)) - { - r1 = assign_stack_local (QFmode, GET_MODE_SIZE (QFmode), 0); - emit_move_insn (r1, r0); - r0 = r1; - } - if (! target || ! register_operand (target, QFmode)) - target = gen_reg_rtx (QFmode); - emit_insn (gen_frieee (target, r0)); - return target; - - case C4X_BUILTIN_RCPF: - if (TARGET_C3X) - break; - arg0 = TREE_VALUE (arglist); - r0 = expand_expr (arg0, NULL_RTX, QFmode, 0); - r0 = protect_from_queue (r0, 0); - if (! target || ! register_operand (target, QFmode)) - target = gen_reg_rtx (QFmode); - emit_insn (gen_rcpfqf_clobber (target, r0)); - return target; - } - return NULL_RTX; -} - -static void -c4x_asm_named_section (name, flags) - const char *name; - unsigned int flags ATTRIBUTE_UNUSED; -{ - fprintf (asm_out_file, "\t.sect\t\"%s\"\n", name); -}