📄 m68k.c
字号:
/* Subroutines for insn-output.c for Motorola 68000 family. Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.This file is part of GCC.GCC 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.GCC 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 GCC; 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 "coretypes.h"#include "tm.h"#include "tree.h"#include "rtl.h"#include "function.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 "recog.h"#include "toplev.h"#include "expr.h"#include "reload.h"#include "tm_p.h"#include "target.h"#include "target-def.h"#include "debug.h"#include "flags.h"enum reg_class regno_reg_class[] ={ DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, DATA_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, ADDR_REGS};/* The ASM_DOT macro allows easy string pasting to handle the differences between MOTOROLA and MIT syntaxes in asm_fprintf(), which doesn't support the %. option. */#if MOTOROLA# define ASM_DOT "."# define ASM_DOTW ".w"# define ASM_DOTL ".l"#else# define ASM_DOT ""# define ASM_DOTW ""# define ASM_DOTL ""#endif/* Structure describing stack frame layout. */struct m68k_frame{ /* Stack pointer to frame pointer offset. */ HOST_WIDE_INT offset; /* Offset of FPU registers. */ HOST_WIDE_INT foffset; /* Frame size in bytes (rounded up). */ HOST_WIDE_INT size; /* Data and address register. */ int reg_no; unsigned int reg_mask; unsigned int reg_rev_mask; /* FPU registers. */ int fpu_no; unsigned int fpu_mask; unsigned int fpu_rev_mask; /* Offsets relative to ARG_POINTER. */ HOST_WIDE_INT frame_pointer_offset; HOST_WIDE_INT stack_pointer_offset; /* Function which the above information refers to. */ int funcdef_no;};/* Current frame information calculated by m68k_compute_frame_layout(). */static struct m68k_frame current_frame;static rtx find_addr_reg (rtx);static const char *singlemove_string (rtx *);static void m68k_output_function_prologue (FILE *, HOST_WIDE_INT);static void m68k_output_function_epilogue (FILE *, HOST_WIDE_INT);#ifdef M68K_TARGET_COFFstatic void m68k_coff_asm_named_section (const char *, unsigned int, tree);#endif /* M68K_TARGET_COFF */static void m68k_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);static rtx m68k_struct_value_rtx (tree, int);static bool m68k_interrupt_function_p (tree func);static tree m68k_handle_fndecl_attribute (tree *node, tree name, tree args, int flags, bool *no_add_attrs);static void m68k_compute_frame_layout (void);static bool m68k_save_reg (unsigned int regno, bool interrupt_handler);static int const_int_cost (rtx);static bool m68k_rtx_costs (rtx, int, int, int *);/* Specify the identification number of the library being built */const char *m68k_library_id_string;/* Nonzero if the last compare/test insn had FP operands. The sCC expanders peek at this to determine what to do for the 68060, which has no fsCC instructions. */int m68k_last_compare_had_fp_operands;/* Initialize the GCC target structure. */#if INT_OP_GROUP == INT_OP_DOT_WORD#undef TARGET_ASM_ALIGNED_HI_OP#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"#endif#if INT_OP_GROUP == INT_OP_NO_DOT#undef TARGET_ASM_BYTE_OP#define TARGET_ASM_BYTE_OP "\tbyte\t"#undef TARGET_ASM_ALIGNED_HI_OP#define TARGET_ASM_ALIGNED_HI_OP "\tshort\t"#undef TARGET_ASM_ALIGNED_SI_OP#define TARGET_ASM_ALIGNED_SI_OP "\tlong\t"#endif#if INT_OP_GROUP == INT_OP_DC#undef TARGET_ASM_BYTE_OP#define TARGET_ASM_BYTE_OP "\tdc.b\t"#undef TARGET_ASM_ALIGNED_HI_OP#define TARGET_ASM_ALIGNED_HI_OP "\tdc.w\t"#undef TARGET_ASM_ALIGNED_SI_OP#define TARGET_ASM_ALIGNED_SI_OP "\tdc.l\t"#endif#undef TARGET_ASM_UNALIGNED_HI_OP#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP#undef TARGET_ASM_UNALIGNED_SI_OP#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP#undef TARGET_ASM_FUNCTION_PROLOGUE#define TARGET_ASM_FUNCTION_PROLOGUE m68k_output_function_prologue#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE m68k_output_function_epilogue#undef TARGET_ASM_OUTPUT_MI_THUNK#define TARGET_ASM_OUTPUT_MI_THUNK m68k_output_mi_thunk#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall#undef TARGET_ASM_FILE_START_APP_OFF#define TARGET_ASM_FILE_START_APP_OFF true#undef TARGET_RTX_COSTS#define TARGET_RTX_COSTS m68k_rtx_costs#undef TARGET_ATTRIBUTE_TABLE#define TARGET_ATTRIBUTE_TABLE m68k_attribute_table#undef TARGET_PROMOTE_PROTOTYPES#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true#undef TARGET_STRUCT_VALUE_RTX#define TARGET_STRUCT_VALUE_RTX m68k_struct_value_rtxstatic const struct attribute_spec m68k_attribute_table[] ={ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ { "interrupt_handler", 0, 0, true, false, false, m68k_handle_fndecl_attribute }, { NULL, 0, 0, false, false, false, NULL }};struct gcc_target targetm = TARGET_INITIALIZER;/* Sometimes certain combinations of command options do not make sense on a particular target machine. You can define a macro `OVERRIDE_OPTIONS' to take account of this. This macro, if defined, is executed once just after all the command options have been parsed. Don't use this macro to turn on various extra optimizations for `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */voidoverride_options (void){ /* Library identification */ if (m68k_library_id_string) { int id; if (! TARGET_ID_SHARED_LIBRARY) error ("-mshared-library-id= specified without -mid-shared-library"); id = atoi (m68k_library_id_string); if (id < 0 || id > MAX_LIBRARY_ID) error ("-mshared-library-id=%d is not between 0 and %d", id, MAX_LIBRARY_ID); /* From now on, m68k_library_id_string will contain the library offset. */ asprintf ((char **)&m68k_library_id_string, "%d", (id * -4) - 4); } else /* If TARGET_ID_SHARED_LIBRARY is enabled, this will point to the current library. */ m68k_library_id_string = "_current_shared_library_a5_offset_"; /* Sanity check to ensure that msep-data and mid-sahred-library are not * both specified together. Doing so simply doesn't make sense. */ if (TARGET_SEP_DATA && TARGET_ID_SHARED_LIBRARY) error ("cannot specify both -msep-data and -mid-shared-library"); /* If we're generating code for a separate A5 relative data segment, * we've got to enable -fPIC as well. This might be relaxable to * -fpic but it hasn't been tested properly. */ if (TARGET_SEP_DATA || TARGET_ID_SHARED_LIBRARY) flag_pic = 2; /* -fPIC uses 32-bit pc-relative displacements, which don't exist until the 68020. */ if (!TARGET_68020 && !TARGET_COLDFIRE && (flag_pic == 2)) error("-fPIC is not currently supported on the 68000 or 68010\n"); /* ??? A historic way of turning on pic, or is this intended to be an embedded thing that doesn't have the same name binding significance that it does on hosted ELF systems? */ if (TARGET_PCREL && flag_pic == 0) flag_pic = 1; /* Turn off function cse if we are doing PIC. We always want function call to be done as `bsr foo@PLTPC', so it will force the assembler to create the PLT entry for `foo'. Doing function cse will cause the address of `foo' to be loaded into a register, which is exactly what we want to avoid when we are doing PIC on svr4 m68k. */ if (flag_pic) flag_no_function_cse = 1; SUBTARGET_OVERRIDE_OPTIONS;}/* Return nonzero if FUNC is an interrupt function as specified by the "interrupt_handler" attribute. */static boolm68k_interrupt_function_p(tree func){ tree a; if (TREE_CODE (func) != FUNCTION_DECL) return false; a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func)); return (a != NULL_TREE);}/* Handle an attribute requiring a FUNCTION_DECL; arguments as in struct attribute_spec.handler. */static treem68k_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, bool *no_add_attrs){ if (TREE_CODE (*node) != FUNCTION_DECL) { warning ("%qs attribute only applies to functions", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE;}static voidm68k_compute_frame_layout (void){ int regno, saved; unsigned int mask, rmask; bool interrupt_handler = m68k_interrupt_function_p (current_function_decl); /* Only compute the frame once per function. Don't cache information until reload has been completed. */ if (current_frame.funcdef_no == current_function_funcdef_no && reload_completed) return; current_frame.size = (get_frame_size () + 3) & -4; mask = rmask = saved = 0; for (regno = 0; regno < 16; regno++) if (m68k_save_reg (regno, interrupt_handler)) { mask |= 1 << regno; rmask |= 1 << (15 - regno); saved++; } current_frame.offset = saved * 4; current_frame.reg_no = saved; current_frame.reg_mask = mask; current_frame.reg_rev_mask = rmask; current_frame.foffset = 0; mask = rmask = saved = 0; if (TARGET_68881 /* || TARGET_CFV4E */) { for (regno = 16; regno < 24; regno++) if (m68k_save_reg (regno, interrupt_handler)) { mask |= 1 << (regno - 16); rmask |= 1 << (23 - regno); saved++; } current_frame.foffset = saved * 12 /* (TARGET_CFV4E ? 8 : 12) */; current_frame.offset += current_frame.foffset; } current_frame.fpu_no = saved; current_frame.fpu_mask = mask; current_frame.fpu_rev_mask = rmask; /* Remember what function this frame refers to. */ current_frame.funcdef_no = current_function_funcdef_no;}HOST_WIDE_INTm68k_initial_elimination_offset (int from, int to){ /* FIXME: The correct offset to compute here would appear to be (frame_pointer_needed ? -UNITS_PER_WORD * 2 : -UNITS_PER_WORD); but for some obscure reason, this must be 0 to get correct code. */ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) return 0; m68k_compute_frame_layout (); if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return current_frame.offset + current_frame.size + (frame_pointer_needed ? -UNITS_PER_WORD * 2 : -UNITS_PER_WORD); else if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return current_frame.offset + current_frame.size; abort();}/* Refer to the array `regs_ever_live' to determine which registers to save; `regs_ever_live[I]' is nonzero if register number I is ever used in the function. This function is responsible for knowing which registers should not be saved even if used. Return true if we need to save REGNO. */static boolm68k_save_reg (unsigned int regno, bool interrupt_handler){ if (flag_pic && regno == PIC_OFFSET_TABLE_REGNUM) { if (current_function_uses_pic_offset_table) return true; if (!current_function_is_leaf && TARGET_ID_SHARED_LIBRARY) return true; } if (current_function_calls_eh_return) { unsigned int i; for (i = 0; ; i++) { unsigned int test = EH_RETURN_DATA_REGNO (i); if (test == INVALID_REGNUM) break; if (test == regno) return true; } } /* Fixed regs we never touch. */ if (fixed_regs[regno]) return false; /* The frame pointer (if it is such) is handled specially. */ if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) return false; /* Interrupt handlers must also save call_used_regs if they are live or when calling nested functions. */ if (interrupt_handler) { if (regs_ever_live[regno]) return true; if (!current_function_is_leaf && call_used_regs[regno]) return true; } /* Never need to save registers that aren't touched. */ if (!regs_ever_live[regno]) return false; /* Otherwise save everything that isn't call-clobbered. */ return !call_used_regs[regno];}/* This function generates the assembly code for function entry. STREAM is a stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. */static voidm68k_output_function_prologue (FILE *stream, HOST_WIDE_INT size ATTRIBUTE_UNUSED){ HOST_WIDE_INT fsize_with_regs; HOST_WIDE_INT cfa_offset = INCOMING_FRAME_SP_OFFSET; m68k_compute_frame_layout(); /* If the stack limit is a symbol, we can check it here, before actually allocating the space. */ if (current_function_limit_stack && GET_CODE (stack_limit_rtx) == SYMBOL_REF) asm_fprintf (stream, "\tcmp" ASM_DOT "l %I%s+%wd,%Rsp\n\ttrapcs\n", XSTR (stack_limit_rtx, 0), current_frame.size + 4);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -