📄 mcore.c
字号:
/* Output routines for Motorola MCore processor Copyright (C) 1993, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "config.h"#include "system.h"#include "rtl.h"#include "tree.h"#include "tm_p.h"#include "assert.h"#include "mcore.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "output.h"#include "insn-attr.h"#include "flags.h"#include "obstack.h"#include "expr.h"#include "reload.h"#include "recog.h"#include "function.h"#include "ggc.h"#include "toplev.h"#include "target.h"#include "target-def.h"/* Maximum size we are allowed to grow the stack in a single operation. If we want more, we must do it in increments of at most this size. If this value is 0, we don't check at all. */const char * mcore_stack_increment_string = 0;int mcore_stack_increment = STACK_UNITS_MAXSTEP;/* For dumping information about frame sizes. */char * mcore_current_function_name = 0;long mcore_current_compilation_timestamp = 0;/* Global variables for machine-dependent things. *//* Saved operands from the last compare to use when we generate an scc or bcc insn. */rtx arch_compare_op0;rtx arch_compare_op1;/* Provides the class number of the smallest class containing reg number. */const int regno_reg_class[FIRST_PSEUDO_REGISTER] ={ GENERAL_REGS, ONLYR1_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, LRW_REGS, GENERAL_REGS, GENERAL_REGS, C_REGS, NO_REGS, NO_REGS,};/* Provide reg_class from a letter such as appears in the machine description. */const enum reg_class reg_class_from_letter[] ={ /* a */ LRW_REGS, /* b */ ONLYR1_REGS, /* c */ C_REGS, /* d */ NO_REGS, /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS, /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ NO_REGS, /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS, /* q */ NO_REGS, /* r */ GENERAL_REGS, /* s */ NO_REGS, /* t */ NO_REGS, /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ ALL_REGS, /* y */ NO_REGS, /* z */ NO_REGS};struct mcore_frame{ int arg_size; /* stdarg spills (bytes) */ int reg_size; /* non-volatile reg saves (bytes) */ int reg_mask; /* non-volatile reg saves */ int local_size; /* locals */ int outbound_size; /* arg overflow on calls out */ int pad_outbound; int pad_local; int pad_reg; /* Describe the steps we'll use to grow it. */#define MAX_STACK_GROWS 4 /* gives us some spare space */ int growth[MAX_STACK_GROWS]; int arg_offset; int reg_offset; int reg_growth; int local_growth;};typedef enum{ COND_NO, COND_MOV_INSN, COND_CLR_INSN, COND_INC_INSN, COND_DEC_INSN, COND_BRANCH_INSN}cond_type;static void output_stack_adjust PARAMS ((int, int));static int calc_live_regs PARAMS ((int *));static int const_ok_for_mcore PARAMS ((int));static int try_constant_tricks PARAMS ((long, int *, int *));static const char * output_inline_const PARAMS ((enum machine_mode, rtx *));static void block_move_sequence PARAMS ((rtx, rtx, rtx, rtx, int, int, int));static void layout_mcore_frame PARAMS ((struct mcore_frame *));static cond_type is_cond_candidate PARAMS ((rtx));static rtx emit_new_cond_insn PARAMS ((rtx, int));static rtx conditionalize_block PARAMS ((rtx));static void conditionalize_optimization PARAMS ((rtx));static rtx handle_structs_in_regs PARAMS ((enum machine_mode, tree, int));static void mcore_mark_dllexport PARAMS ((tree));static void mcore_mark_dllimport PARAMS ((tree));static int mcore_dllexport_p PARAMS ((tree));static int mcore_dllimport_p PARAMS ((tree));const struct attribute_spec mcore_attribute_table[];static tree mcore_handle_naked_attribute PARAMS ((tree *, tree, tree, int, bool *));#ifdef OBJECT_FORMAT_ELFstatic void mcore_asm_named_section PARAMS ((const char *, unsigned int));#endifstatic void mcore_unique_section PARAMS ((tree, int));static void mcore_encode_section_info PARAMS ((tree, int));static const char *mcore_strip_name_encoding PARAMS ((const char *));/* Initialize the GCC target structure. */#ifdef TARGET_DLLIMPORT_DECL_ATTRIBUTES#undef TARGET_MERGE_DECL_ATTRIBUTES#define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes#endif#ifdef OBJECT_FORMAT_ELF#undef TARGET_ASM_UNALIGNED_HI_OP#define TARGET_ASM_UNALIGNED_HI_OP "\t.short\t"#undef TARGET_ASM_UNALIGNED_SI_OP#define TARGET_ASM_UNALIGNED_SI_OP "\t.long\t"#endif#undef TARGET_ATTRIBUTE_TABLE#define TARGET_ATTRIBUTE_TABLE mcore_attribute_table#undef TARGET_ASM_UNIQUE_SECTION#define TARGET_ASM_UNIQUE_SECTION mcore_unique_section#undef TARGET_ENCODE_SECTION_INFO#define TARGET_ENCODE_SECTION_INFO mcore_encode_section_info#undef TARGET_STRIP_NAME_ENCODING#define TARGET_STRIP_NAME_ENCODING mcore_strip_name_encodingstruct gcc_target targetm = TARGET_INITIALIZER;/* Adjust the stack and return the number of bytes taken to do it. */static voidoutput_stack_adjust (direction, size) int direction; int size;{ /* If extending stack a lot, we do it incrementally. */ if (direction < 0 && size > mcore_stack_increment && mcore_stack_increment > 0) { rtx tmp = gen_rtx (REG, SImode, 1); rtx memref; emit_insn (gen_movsi (tmp, GEN_INT (mcore_stack_increment))); do { emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); memref = gen_rtx (MEM, SImode, stack_pointer_rtx); MEM_VOLATILE_P (memref) = 1; emit_insn (gen_movsi (memref, stack_pointer_rtx)); size -= mcore_stack_increment; } while (size > mcore_stack_increment); /* SIZE is now the residual for the last adjustment, which doesn't require a probe. */ } if (size) { rtx insn; rtx val = GEN_INT (size); if (size > 32) { rtx nval = gen_rtx (REG, SImode, 1); emit_insn (gen_movsi (nval, val)); val = nval; } if (direction > 0) insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val); else insn = gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, val); emit_insn (insn); }}/* Work out the registers which need to be saved, both as a mask and a count. */static intcalc_live_regs (count) int * count;{ int reg; int live_regs_mask = 0; * count = 0; for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) { if (regs_ever_live[reg] && !call_used_regs[reg]) { (*count)++; live_regs_mask |= (1 << reg); } } return live_regs_mask;}/* Print the operand address in x to the stream. */voidmcore_print_operand_address (stream, x) FILE * stream; rtx x;{ switch (GET_CODE (x)) { case REG: fprintf (stream, "(%s)", reg_names[REGNO (x)]); break; case PLUS: { rtx base = XEXP (x, 0); rtx index = XEXP (x, 1); if (GET_CODE (base) != REG) { /* Ensure that BASE is a register (one of them must be). */ rtx temp = base; base = index; index = temp; } switch (GET_CODE (index)) { case CONST_INT: fprintf (stream, "(%s,%d)", reg_names[REGNO(base)], INTVAL (index)); break; default: debug_rtx (x); abort (); } } break; default: output_addr_const (stream, x); break; }}/* Print operand x (an rtx) in assembler syntax to file stream according to modifier code. 'R' print the next register or memory location along, ie the lsw in a double word value 'O' print a constant without the # 'M' print a constant as its negative 'P' print log2 of a power of two 'Q' print log2 of an inverse of a power of two 'U' print register for ldm/stm instruction 'X' print byte number for xtrbN instruction. */voidmcore_print_operand (stream, x, code) FILE * stream; rtx x; int code;{ switch (code) { case 'N': if (INTVAL(x) == -1) fprintf (asm_out_file, "32"); else fprintf (asm_out_file, "%d", exact_log2 (INTVAL (x) + 1)); break; case 'P': fprintf (asm_out_file, "%d", exact_log2 (INTVAL (x))); break; case 'Q': fprintf (asm_out_file, "%d", exact_log2 (~INTVAL (x))); break; case 'O': fprintf (asm_out_file, "%d", INTVAL (x)); break; case 'M': fprintf (asm_out_file, "%d", - INTVAL (x)); break; case 'R': /* Next location along in memory or register. */ switch (GET_CODE (x)) { case REG: fputs (reg_names[REGNO (x) + 1], (stream)); break; case MEM: mcore_print_operand_address (stream, XEXP (adjust_address (x, SImode, 4), 0)); break; default: abort (); } break; case 'U': fprintf (asm_out_file, "%s-%s", reg_names[REGNO (x)], reg_names[REGNO (x) + 3]); break; case 'x': fprintf (asm_out_file, "0x%x", INTVAL (x)); break; case 'X': fprintf (asm_out_file, "%d", 3 - INTVAL (x) / 8); break; default: switch (GET_CODE (x)) { case REG: fputs (reg_names[REGNO (x)], (stream)); break; case MEM: output_address (XEXP (x, 0)); break; default: output_addr_const (stream, x); break; } break; }}/* What does a constant cost ? */intmcore_const_costs (exp, code) rtx exp; enum rtx_code code;{ int val = INTVAL (exp); /* Easy constants. */ if ( CONST_OK_FOR_I (val) || CONST_OK_FOR_M (val) || CONST_OK_FOR_N (val) || (code == PLUS && CONST_OK_FOR_L (val))) return 1; else if (code == AND && ( CONST_OK_FOR_M (~val) || CONST_OK_FOR_N (~val))) return 2; else if (code == PLUS && ( CONST_OK_FOR_I (-val) || CONST_OK_FOR_M (-val) || CONST_OK_FOR_N (-val))) return 2; return 5; }/* What does an and instruction cost - we do this b/c immediates may have been relaxed. We want to ensure that cse will cse relaxed immeds out. Otherwise we'll get bad code (multiple reloads of the same const). */intmcore_and_cost (x) rtx x;{ int val; if (GET_CODE (XEXP (x, 1)) != CONST_INT) return 2; val = INTVAL (XEXP (x, 1)); /* Do it directly. */ if (CONST_OK_FOR_K (val) || CONST_OK_FOR_M (~val)) return 2; /* Takes one instruction to load. */ else if (const_ok_for_mcore (val)) return 3; /* Takes two instructions to load. */ else if (TARGET_HARDLIT && mcore_const_ok_for_inline (val)) return 4; /* Takes a lrw to load. */ return 5;}/* What does an or cost - see and_cost(). */intmcore_ior_cost (x) rtx x;{ int val; if (GET_CODE (XEXP (x, 1)) != CONST_INT) return 2; val = INTVAL (XEXP (x, 1)); /* Do it directly with bclri. */ if (CONST_OK_FOR_M (val)) return 2; /* Takes one instruction to load. */ else if (const_ok_for_mcore (val)) return 3; /* Takes two instructions to load. */ else if (TARGET_HARDLIT && mcore_const_ok_for_inline (val)) return 4; /* Takes a lrw to load. */ return 5;}/* Check to see if a comparison against a constant can be made more efficient by incrementing/decrementing the constant to get one that is more efficient to load. */intmcore_modify_comparison (code) enum rtx_code code;{ rtx op1 = arch_compare_op1; if (GET_CODE (op1) == CONST_INT) { int val = INTVAL (op1); switch (code) { case LE: if (CONST_OK_FOR_J (val + 1)) { arch_compare_op1 = GEN_INT (val + 1); return 1; } break; default: break; } } return 0;}/* Prepare the operands for a comparison. */rtxmcore_gen_compare_reg (code) enum rtx_code code;{ rtx op0 = arch_compare_op0; rtx op1 = arch_compare_op1; rtx cc_reg = gen_rtx (REG, CCmode, CC_REG); if (CONSTANT_P (op1) && GET_CODE (op1) != CONST_INT) op1 = force_reg (SImode, op1); /* cmpnei: 0-31 (K immediate) cmplti: 1-32 (J immediate, 0 using btsti x,31). */ switch (code) { case EQ: /* Use inverted condition, cmpne. */ code = NE; /* drop through */ case NE: /* Use normal condition, cmpne. */ if (GET_CODE (op1) == CONST_INT && ! CONST_OK_FOR_K (INTVAL (op1))) op1 = force_reg (SImode, op1); break; case LE: /* Use inverted condition, reversed cmplt. */ code = GT; /* drop through */ case GT: /* Use normal condition, reversed cmplt. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -