📄 c4x.c
字号:
/* Subroutines for assembler code output on the TMS320C[34]x Copyright (C) 1994-98, 1999 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 "toplev.h"#include "rtl.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 "insn-codes.h"#include "conditions.h"#include "insn-flags.h"#include "output.h"#include "tree.h"#include "expr.h"#include "flags.h"#include "loop.h"#include "recog.h"#include "c-tree.h"static int c4x_leaf_function;static char *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;char *c4x_rpts_cycles_string;int c4x_rpts_cycles = 0; /* Max. cycles for RPTS */char *c4x_cpu_version_string;int c4x_cpu_version = 40; /* CPU version C30/31/32/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;/* Override command line options. Called once after all options have been parsed. Mostly we process the processor type and sometimes adjust other TARGET_ options. */voidc4x_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_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) c4x_cpu_version = atoi (c4x_cpu_version_string); target_flags &= ~(C30_FLAG | C31_FLAG | C32_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 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_flags |= C3X_FLAG; else target_flags &= ~C3X_FLAG; /* Convert foo / 8.0 into foo * 0.125, etc. */ flag_fast_math = 1; /* 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;}/* This is called before c4x_override_options. */voidc4x_optimization_options (level, size) int level; 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; /* When optimizing, enable use of RPTB instruction. */ if (level >= 1) flag_branch_on_count_reg = 1;}/* Write an ASCII string. */#define C4X_ASCII_LIMIT 40voidc4x_output_ascii (stream, ptr, len) FILE *stream; unsigned char *ptr; int len;{ char sbuf[C4X_ASCII_LIMIT + 1]; int s, first, onlys; if (len) { fprintf (stream, "\t.byte\t"); first = 1; } for (s = 0; len > 0; --len, ++ptr) { onlys = 0; /* Escape " and \ with a \". */ if (*ptr == '\"' || *ptr == '\\') sbuf[s++] = '\\'; /* If printable - add to buff. */ if (*ptr >= 0x20 && *ptr < 0x7f) { sbuf[s++] = *ptr; if (s < C4X_ASCII_LIMIT - 1) continue; onlys = 1; } if (s) { if (first) first = 0; else fputc (',', stream); sbuf[s] = 0; fprintf (stream, "\"%s\"", sbuf); s = 0; } if (onlys) continue; if (first) first = 0; else fputc (',', stream); fprintf (stream, "%d", *ptr); } if (s) { if (! first) fputc (',', stream); sbuf[s] = 0; fprintf (stream, "\"%s\"", sbuf); s = 0; } fputc ('\n', stream);}intc4x_hard_regno_mode_ok (regno, mode) 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_REG (regno); case QFmode: /* Float, Double (32 bits) */ case HFmode: /* Long Double (40 bits) */ return IS_EXT_REG (regno); case CCmode: /* Condition Codes */ case CC_NOOVmode: /* Condition Codes */ return IS_ST_REG (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_REG (regno) && IS_INT_REG (regno + 1) && (regno & 1) == 0; default: return 0; /* We don't support these modes */ } return 0;}/* 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 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. */voidc4x_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.) */voidc4x_function_arg_advance (cum, mode, type, named) CUMULATIVE_ARGS *cum; /* current arg information */ enum machine_mode mode; /* current arg mode */ tree type; /* type of the argument or 0 if lib support */ int named; /* whether or not the argument was named */{ if (TARGET_DEBUG) 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 argument 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -