📄 sh.c
字号:
/* Output routines for GCC for Hitachi / SuperH SH. Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com). Improved by Jim Wilson (wilson@cygnus.com). 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 "insn-config.h"#include "rtl.h"#include "tree.h"#include "flags.h"#include "expr.h"#include "optabs.h"#include "function.h"#include "regs.h"#include "hard-reg-set.h"#include "output.h"#include "insn-attr.h"#include "toplev.h"#include "recog.h"#include "c-pragma.h"#include "integrate.h"#include "tm_p.h"#include "target.h"#include "target-def.h"int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)/* These are some macros to abstract register modes. */#define CONST_OK_FOR_ADD(size) \ (TARGET_SHMEDIA ? CONST_OK_FOR_P (size) : CONST_OK_FOR_I (size))#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))/* Set to 1 by expand_prologue() when the function is an interrupt handler. */int current_function_interrupt;/* ??? The pragma interrupt support will not work for SH3. *//* This is set by #pragma interrupt and #pragma trapa, and causes gcc to output code for the next function appropriate for an interrupt handler. */int pragma_interrupt;/* This is set by the trap_exit attribute for functions. It specifies a trap number to be used in a trapa instruction at function exit (instead of an rte instruction). */int trap_exit;/* This is used by the sp_switch attribute for functions. It specifies a variable holding the address of the stack the interrupt function should switch to/from at entry/exit. */rtx sp_switch;/* This is set by #pragma trapa, and is similar to the above, except that the compiler doesn't emit code to preserve all registers. */static int pragma_trapa;/* This is set by #pragma nosave_low_regs. This is useful on the SH3, which has a separate set of low regs for User and Supervisor modes. This should only be used for the lowest level of interrupts. Higher levels of interrupts must save the registers in case they themselves are interrupted. */int pragma_nosave_low_regs;/* This is used for communication between SETUP_INCOMING_VARARGS and sh_expand_prologue. */int current_function_anonymous_args;/* Global variables for machine-dependent things. *//* Which cpu are we scheduling for. */enum processor_type sh_cpu;/* Saved operands from the last compare to use when we generate an scc or bcc insn. */rtx sh_compare_op0;rtx sh_compare_op1;/* Provides the class number of the smallest class containing reg number. */int regno_reg_class[FIRST_PSEUDO_REGISTER] ={ R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, FP0_REGS,FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS, DF_REGS, DF_REGS, DF_REGS, DF_REGS, DF_REGS, DF_REGS, DF_REGS, DF_REGS, NO_REGS, GENERAL_REGS, PR_REGS, T_REGS, MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS, GENERAL_REGS,};char sh_register_names[FIRST_PSEUDO_REGISTER] \ [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;char sh_additional_register_names[ADDREGNAMES_SIZE] \ [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1] = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;/* Provide reg_class from a letter such as appears in the machine description. */const enum reg_class reg_class_from_letter[] ={ /* a */ ALL_REGS, /* b */ TARGET_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS, /* e */ NO_REGS, /* f */ FP_REGS, /* g */ NO_REGS, /* h */ NO_REGS, /* i */ NO_REGS, /* j */ NO_REGS, /* k */ SIBCALL_REGS, /* l */ PR_REGS, /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS, /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS, /* u */ NO_REGS, /* v */ NO_REGS, /* w */ FP0_REGS, /* x */ MAC_REGS, /* y */ FPUL_REGS, /* z */ R0_REGS};int assembler_dialect;static void split_branches PARAMS ((rtx));static int branch_dest PARAMS ((rtx));static void force_into PARAMS ((rtx, rtx));static void print_slot PARAMS ((rtx));static rtx add_constant PARAMS ((rtx, enum machine_mode, rtx));static void dump_table PARAMS ((rtx));static int hi_const PARAMS ((rtx));static int broken_move PARAMS ((rtx));static int mova_p PARAMS ((rtx));static rtx find_barrier PARAMS ((int, rtx, rtx));static int noncall_uses_reg PARAMS ((rtx, rtx, rtx *));static rtx gen_block_redirect PARAMS ((rtx, int, int));static void output_stack_adjust PARAMS ((int, rtx, int));static void push PARAMS ((int));static void pop PARAMS ((int));static void push_regs PARAMS ((HOST_WIDE_INT *));static void calc_live_regs PARAMS ((int *, HOST_WIDE_INT *));static void mark_use PARAMS ((rtx, rtx *));static HOST_WIDE_INT rounded_frame_size PARAMS ((int));static rtx mark_constant_pool_use PARAMS ((rtx));const struct attribute_spec sh_attribute_table[];static tree sh_handle_interrupt_handler_attribute PARAMS ((tree *, tree, tree, int, bool *));static tree sh_handle_sp_switch_attribute PARAMS ((tree *, tree, tree, int, bool *));static tree sh_handle_trap_exit_attribute PARAMS ((tree *, tree, tree, int, bool *));static void sh_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));static void sh_insert_attributes PARAMS ((tree, tree *));#ifndef OBJECT_FORMAT_ELFstatic void sh_asm_named_section PARAMS ((const char *, unsigned int));#endifstatic int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int));static bool sh_cannot_modify_jumps_p PARAMS ((void));static bool sh_ms_bitfield_layout_p PARAMS ((tree));/* Initialize the GCC target structure. */#undef TARGET_ATTRIBUTE_TABLE#define TARGET_ATTRIBUTE_TABLE sh_attribute_table/* The next two are used for debug info when compiling with -gdwarf. */#undef TARGET_ASM_UNALIGNED_HI_OP#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"#undef TARGET_ASM_UNALIGNED_SI_OP#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"/* These are NULLed out on non-SH5 in OVERRIDE_OPTIONS. */#undef TARGET_ASM_UNALIGNED_DI_OP#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"#undef TARGET_ASM_ALIGNED_DI_OP#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue#undef TARGET_INSERT_ATTRIBUTES#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes#undef TARGET_SCHED_ADJUST_COST#define TARGET_SCHED_ADJUST_COST sh_adjust_cost#undef TARGET_CANNOT_MODIFY_JUMPS_P#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p#undef TARGET_MS_BITFIELD_LAYOUT_P#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_pstruct gcc_target targetm = TARGET_INITIALIZER;/* Print the operand address in x to the stream. */voidprint_operand_address (stream, x) FILE *stream; rtx x;{ switch (GET_CODE (x)) { case REG: case SUBREG: fprintf (stream, "@%s", reg_names[true_regnum (x)]); break; case PLUS: { rtx base = XEXP (x, 0); rtx index = XEXP (x, 1); switch (GET_CODE (index)) { case CONST_INT: fprintf (stream, "@(%d,%s)", (int) INTVAL (index), reg_names[true_regnum (base)]); break; case REG: case SUBREG: { int base_num = true_regnum (base); int index_num = true_regnum (index); fprintf (stream, "@(r0,%s)", reg_names[MAX (base_num, index_num)]); break; } default: debug_rtx (x); abort (); } } break; case PRE_DEC: fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]); break; case POST_INC: fprintf (stream, "@%s+", reg_names[true_regnum (XEXP (x, 0))]); break; default: x = mark_constant_pool_use (x); output_addr_const (stream, x); break; }}/* Print operand x (an rtx) in assembler syntax to file stream according to modifier code. '.' print a .s if insn needs delay slot ',' print LOCAL_LABEL_PREFIX '@' print trap, rte or rts depending upon pragma interruptness '#' output a nop if there is nothing to put in the delay slot 'O' print a constant without the # 'R' print the LSW of a dp value - changes if in little endian 'S' print the MSW of a dp value - changes if in little endian 'T' print the next word of a dp value - same as 'R' in big endian mode. 'M' print an `x' if `m' will print `base,index'. 'm' print a pair `base,offset' or `base,index', for LD and ST. 'u' prints the lowest 16 bits of CONST_INT, as an unsigned value. 'o' output an operator. */voidprint_operand (stream, x, code) FILE *stream; rtx x; int code;{ switch (code) { case '.': if (final_sequence && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))) fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s"); break; case ',': fprintf (stream, "%s", LOCAL_LABEL_PREFIX); break; case '@': { int interrupt_handler; if ((lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (current_function_decl))) != NULL_TREE) interrupt_handler = 1; else interrupt_handler = 0; if (trap_exit) fprintf (stream, "trapa #%d", trap_exit); else if (interrupt_handler) fprintf (stream, "rte"); else fprintf (stream, "rts"); break; } case '#': /* Output a nop if there's nothing in the delay slot. */ if (dbr_sequence_length () == 0) fprintf (stream, "\n\tnop"); break; case 'O': x = mark_constant_pool_use (x); output_addr_const (stream, x); break; case 'R': fputs (reg_names[REGNO (x) + LSW], (stream)); break; case 'S': fputs (reg_names[REGNO (x) + MSW], (stream)); break; case 'T': /* Next word of a double. */ switch (GET_CODE (x)) { case REG: fputs (reg_names[REGNO (x) + 1], (stream)); break; case MEM: if (GET_CODE (XEXP (x, 0)) != PRE_DEC && GET_CODE (XEXP (x, 0)) != POST_INC) x = adjust_address (x, SImode, 4); print_operand_address (stream, XEXP (x, 0)); break; default: break; } break; case 'o': switch (GET_CODE (x)) { case PLUS: fputs ("add", stream); break; case MINUS: fputs ("sub", stream); break; case MULT: fputs ("mul", stream); break; case DIV: fputs ("div", stream); break; default: break; } break; case 'M': if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PLUS && (GET_CODE (XEXP (XEXP (x, 0), 1)) == REG || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG)) fputc ('x', stream); break; case 'm': if (GET_CODE (x) != MEM) abort (); x = XEXP (x, 0); switch (GET_CODE (x)) { case REG: case SUBREG: print_operand (stream, x, 0); fputs (", 0", stream); break; case PLUS: print_operand (stream, XEXP (x, 0), 0); fputs (", ", stream); print_operand (stream, XEXP (x, 1), 0); break; default: abort (); } break; case 'u': if (GET_CODE (x) == CONST_INT) { fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1)); break; } /* Fall through. */ default: switch (GET_CODE (x)) { /* FIXME: We need this on SHmedia32 because reload generates some sign-extended HI or QI loads into DImode registers but, because Pmode is SImode, the address ends up with a subreg:SI of the DImode register. Maybe reload should be fixed so as to apply alter_subreg to such loads? */ case SUBREG: if (SUBREG_BYTE (x) != 0 || GET_CODE (SUBREG_REG (x)) != REG) abort (); x = SUBREG_REG (x); /* Fall through. */ case REG: if (FP_REGISTER_P (REGNO (x)) && GET_MODE (x) == V16SFmode) fprintf ((stream), "mtrx%s", reg_names[REGNO (x)] + 2); else if (FP_REGISTER_P (REGNO (x)) && GET_MODE (x) == V4SFmode) fprintf ((stream), "fv%s", reg_names[REGNO (x)] + 2); else if (GET_CODE (x) == REG && GET_MODE (x) == V2SFmode) fprintf ((stream), "fp%s", reg_names[REGNO (x)] + 2); else if (FP_REGISTER_P (REGNO (x)) && GET_MODE_SIZE (GET_MODE (x)) > 4) fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1); else fputs (reg_names[REGNO (x)], (stream)); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -