📄 m32c.c
字号:
/* Target Code for R8C/M16C/M32C Copyright (C) 2005 Free Software Foundation, Inc. Contributed by Red Hat. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */#include "config.h"#include "system.h"#include "coretypes.h"#include "tm.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "insn-flags.h"#include "output.h"#include "insn-attr.h"#include "flags.h"#include "recog.h"#include "reload.h"#include "toplev.h"#include "obstack.h"#include "tree.h"#include "expr.h"#include "optabs.h"#include "except.h"#include "function.h"#include "ggc.h"#include "target.h"#include "target-def.h"#include "tm_p.h"#include "langhooks.h"#include "tree-gimple.h"/* Prototypes *//* Used by m32c_pushm_popm. */typedef enum{ PP_pushm, PP_popm, PP_justcount} Push_Pop_Type;static tree interrupt_handler (tree *, tree, tree, int, bool *);static int interrupt_p (tree node);static bool m32c_asm_integer (rtx, unsigned int, int);static int m32c_comp_type_attributes (tree, tree);static bool m32c_fixed_condition_code_regs (unsigned int *, unsigned int *);static struct machine_function *m32c_init_machine_status (void);static void m32c_insert_attributes (tree, tree *);static bool m32c_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, tree, bool);static bool m32c_promote_prototypes (tree);static int m32c_pushm_popm (Push_Pop_Type);static bool m32c_strict_argument_naming (CUMULATIVE_ARGS *);static rtx m32c_struct_value_rtx (tree, int);static rtx m32c_subreg (enum machine_mode, rtx, enum machine_mode, int);static int need_to_save (int);#define streq(a,b) (strcmp ((a), (b)) == 0)/* Internal support routines *//* Debugging statements are tagged with DEBUG0 only so that they can be easily enabled individually, by replacing the '0' with '1' as needed. */#define DEBUG0 0#define DEBUG1 1#if DEBUG0/* This is needed by some of the commented-out debug statements below. */static char const *class_names[LIM_REG_CLASSES] = REG_CLASS_NAMES;#endifstatic int class_contents[LIM_REG_CLASSES][1] = REG_CLASS_CONTENTS;/* These are all to support encode_pattern(). */static char pattern[30], *patternp;static GTY(()) rtx patternr[30];#define RTX_IS(x) (streq (pattern, x))/* Some macros to simplify the logic throughout this file. */#define IS_MEM_REGNO(regno) ((regno) >= MEM0_REGNO && (regno) <= MEM7_REGNO)#define IS_MEM_REG(rtx) (GET_CODE (rtx) == REG && IS_MEM_REGNO (REGNO (rtx)))#define IS_CR_REGNO(regno) ((regno) >= SB_REGNO && (regno) <= PC_REGNO)#define IS_CR_REG(rtx) (GET_CODE (rtx) == REG && IS_CR_REGNO (REGNO (rtx)))/* We do most RTX matching by converting the RTX into a string, and using string compares. This vastly simplifies the logic in many of the functions in this file. On exit, pattern[] has the encoded string (use RTX_IS("...") to compare it) and patternr[] has pointers to the nodes in the RTX corresponding to each character in the encoded string. The latter is mostly used by print_operand(). Unrecognized patterns have '?' in them; this shows up when the assembler complains about syntax errors.*/static voidencode_pattern_1 (rtx x){ int i; if (patternp == pattern + sizeof (pattern) - 2) { patternp[-1] = '?'; return; } patternr[patternp - pattern] = x; switch (GET_CODE (x)) { case REG: *patternp++ = 'r'; break; case SUBREG: if (GET_MODE_SIZE (GET_MODE (x)) != GET_MODE_SIZE (GET_MODE (XEXP (x, 0)))) *patternp++ = 'S'; encode_pattern_1 (XEXP (x, 0)); break; case MEM: *patternp++ = 'm'; case CONST: encode_pattern_1 (XEXP (x, 0)); break; case PLUS: *patternp++ = '+'; encode_pattern_1 (XEXP (x, 0)); encode_pattern_1 (XEXP (x, 1)); break; case PRE_DEC: *patternp++ = '>'; encode_pattern_1 (XEXP (x, 0)); break; case POST_INC: *patternp++ = '<'; encode_pattern_1 (XEXP (x, 0)); break; case LO_SUM: *patternp++ = 'L'; encode_pattern_1 (XEXP (x, 0)); encode_pattern_1 (XEXP (x, 1)); break; case HIGH: *patternp++ = 'H'; encode_pattern_1 (XEXP (x, 0)); break; case SYMBOL_REF: *patternp++ = 's'; break; case LABEL_REF: *patternp++ = 'l'; break; case CODE_LABEL: *patternp++ = 'c'; break; case CONST_INT: case CONST_DOUBLE: *patternp++ = 'i'; break; case UNSPEC: *patternp++ = 'u'; *patternp++ = '0' + XCINT (x, 1, UNSPEC); for (i = 0; i < XVECLEN (x, 0); i++) encode_pattern_1 (XVECEXP (x, 0, i)); break; case USE: *patternp++ = 'U'; break; case PARALLEL: *patternp++ = '|'; for (i = 0; i < XVECLEN (x, 0); i++) encode_pattern_1 (XVECEXP (x, 0, i)); break; case EXPR_LIST: *patternp++ = 'E'; encode_pattern_1 (XEXP (x, 0)); if (XEXP (x, 1)) encode_pattern_1 (XEXP (x, 1)); break; default: *patternp++ = '?';#if DEBUG0 fprintf (stderr, "can't encode pattern %s\n", GET_RTX_NAME (GET_CODE (x))); debug_rtx (x); gcc_unreachable ();#endif break; }}static voidencode_pattern (rtx x){ patternp = pattern; encode_pattern_1 (x); *patternp = 0;}/* Since register names indicate the mode they're used in, we need a way to determine which name to refer to the register with. Called by print_operand(). */static const char *reg_name_with_mode (int regno, enum machine_mode mode){ int mlen = GET_MODE_SIZE (mode); if (regno == R0_REGNO && mlen == 1) return "r0l"; if (regno == R0_REGNO && (mlen == 3 || mlen == 4)) return "r2r0"; if (regno == R0_REGNO && mlen == 6) return "r2r1r0"; if (regno == R0_REGNO && mlen == 8) return "r3r1r2r0"; if (regno == R1_REGNO && mlen == 1) return "r1l"; if (regno == R1_REGNO && (mlen == 3 || mlen == 4)) return "r3r1"; if (regno == A0_REGNO && TARGET_A16 && (mlen == 3 || mlen == 4)) return "a1a0"; return reg_names[regno];}/* How many bytes a register uses on stack when it's pushed. We need to know this because the push opcode needs to explicitly indicate the size of the register, even though the name of the register already tells it that. Used by m32c_output_reg_{push,pop}, which is only used through calls to ASM_OUTPUT_REG_{PUSH,POP}. */static intreg_push_size (int regno){ switch (regno) { case R0_REGNO: case R1_REGNO: return 2; case R2_REGNO: case R3_REGNO: case FLG_REGNO: return 2; case A0_REGNO: case A1_REGNO: case SB_REGNO: case FB_REGNO: case SP_REGNO: if (TARGET_A16) return 2; else return 3; default: gcc_unreachable (); }}static int *class_sizes = 0;/* Given two register classes, find the largest intersection between them. If there is no intersection, return RETURNED_IF_EMPTY instead. */static intreduce_class (int original_class, int limiting_class, int returned_if_empty){ int cc = class_contents[original_class][0]; int i, best = NO_REGS; int best_size = 0; if (original_class == limiting_class) return original_class; if (!class_sizes) { int r; class_sizes = (int *) xmalloc (LIM_REG_CLASSES * sizeof (int)); for (i = 0; i < LIM_REG_CLASSES; i++) { class_sizes[i] = 0; for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) if (class_contents[i][0] & (1 << r)) class_sizes[i]++; } } cc &= class_contents[limiting_class][0]; for (i = 0; i < LIM_REG_CLASSES; i++) { int ic = class_contents[i][0]; if ((~cc & ic) == 0) if (best_size < class_sizes[i]) { best = i; best_size = class_sizes[i]; } } if (best == NO_REGS) return returned_if_empty; return best;}/* Returns TRUE If there are any registers that exist in both register classes. */static intclasses_intersect (int class1, int class2){ return class_contents[class1][0] & class_contents[class2][0];}/* Used by m32c_register_move_cost to determine if a move is impossibly expensive. */static intclass_can_hold_mode (int class, enum machine_mode mode){ /* Cache the results: 0=untested 1=no 2=yes */ static char results[LIM_REG_CLASSES][MAX_MACHINE_MODE]; if (results[class][mode] == 0) { int r, n, i; results[class][mode] = 1; for (r = 0; r < FIRST_PSEUDO_REGISTER; r++) if (class_contents[class][0] & (1 << r) && HARD_REGNO_MODE_OK (r, mode)) { int ok = 1; n = HARD_REGNO_NREGS (r, mode); for (i = 1; i < n; i++) if (!(class_contents[class][0] & (1 << (r + i)))) ok = 0; if (ok) { results[class][mode] = 2; break; } } }#if DEBUG0 fprintf (stderr, "class %s can hold %s? %s\n", class_names[class], mode_name[mode], (results[class][mode] == 2) ? "yes" : "no");#endif return results[class][mode] == 2;}/* Run-time Target Specification. *//* Memregs are memory locations that gcc treats like general registers, as there are a limited number of true registers and the m32c families can use memory in most places that registers can be used. However, since memory accesses are more expensive than registers, we allow the user to limit the number of memregs available, in order to try to persuade gcc to try harder to use real registers. Memregs are provided by m32c-lib1.S.*/int target_memregs = 16;static bool target_memregs_set = FALSE;int ok_to_change_target_memregs = TRUE;#undef TARGET_HANDLE_OPTION#define TARGET_HANDLE_OPTION m32c_handle_optionstatic boolm32c_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED, int value ATTRIBUTE_UNUSED){ if (code == OPT_memregs_) { target_memregs_set = TRUE; target_memregs = atoi (arg); } return TRUE;}/* Implements OVERRIDE_OPTIONS. We limit memregs to 0..16, and provide a default. */voidm32c_override_options (void){ if (target_memregs_set) { if (target_memregs < 0 || target_memregs > 16) error ("invalid target memregs value '%d'", target_memregs); } else target_memregs = "16";}/* Defining data structures for per-function information *//* The usual; we set up our machine_function data. */static struct machine_function *m32c_init_machine_status (void){ struct machine_function *machine; machine = (machine_function *) ggc_alloc_cleared (sizeof (machine_function)); return machine;}/* Implements INIT_EXPANDERS. We just set up to call the above function. */voidm32c_init_expanders (void){ init_machine_status = m32c_init_machine_status;}/* Storage Layout */#undef TARGET_PROMOTE_FUNCTION_RETURN#define TARGET_PROMOTE_FUNCTION_RETURN m32c_promote_function_returnboolm32c_promote_function_return (tree fntype ATTRIBUTE_UNUSED){ return false;}/* Register Basics *//* Basic Characteristics of Registers *//* Whether a mode fits in a register is complex enough to warrant a table. */static struct{ char qi_regs; char hi_regs; char pi_regs; char si_regs; char di_regs;} nregs_table[FIRST_PSEUDO_REGISTER] ={ { 1, 1, 2, 2, 4 }, /* r0 */ { 0, 1, 0, 0, 0 }, /* r2 */ { 1, 1, 2, 2, 0 }, /* r1 */ { 0, 1, 0, 0, 0 }, /* r3 */ { 0, 1, 1, 0, 0 }, /* a0 */ { 0, 1, 1, 0, 0 }, /* a1 */ { 0, 1, 1, 0, 0 }, /* sb */ { 0, 1, 1, 0, 0 }, /* fb */ { 0, 1, 1, 0, 0 }, /* sp */ { 1, 1, 1, 0, 0 }, /* pc */ { 0, 0, 0, 0, 0 }, /* fl */ { 1, 1, 1, 0, 0 }, /* ap */ { 1, 1, 2, 2, 4 }, /* mem0 */ { 1, 1, 2, 2, 4 }, /* mem1 */ { 1, 1, 2, 2, 4 }, /* mem2 */ { 1, 1, 2, 2, 4 }, /* mem3 */ { 1, 1, 2, 2, 4 }, /* mem4 */ { 1, 1, 2, 2, 0 }, /* mem5 */ { 1, 1, 2, 2, 0 }, /* mem6 */ { 1, 1, 0, 0, 0 }, /* mem7 */};/* Implements CONDITIONAL_REGISTER_USAGE. We adjust the number of available memregs, and select which registers need to be preserved across calls based on the chip family. */voidm32c_conditional_register_usage (void){ int memregs; int i; if (0 <= target_memregs && target_memregs <= 16) { /* The command line option is bytes, but our "registers" are 16-bit words. */ for (i = target_memregs/2; i < 8; i++) { fixed_regs[MEM0_REGNO + i] = 1; CLEAR_HARD_REG_BIT (reg_class_contents[MEM_REGS], MEM0_REGNO + i); } } /* M32CM and M32C preserve more registers across function calls. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -