📄 avr.c
字号:
/* Subroutines for insn-output.c for ATMEL AVR micro controllers Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Denis Chertykov (denisc@overta.ru) 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. */#include "config.h"#include "system.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "insn-attr.h"#include "flags.h"#include "reload.h"#include "tree.h"#include "output.h"#include "expr.h"#include "toplev.h"#include "obstack.h"#include "function.h"#include "recog.h"#include "tm_p.h"#include "target.h"#include "target-def.h"/* Maximal allowed offset for an address in the LD command */#define MAX_LD_OFFSET(MODE) (64 - (signed)GET_MODE_SIZE (MODE))static int avr_naked_function_p PARAMS ((tree));static int interrupt_function_p PARAMS ((tree));static int signal_function_p PARAMS ((tree));static int avr_regs_to_save PARAMS ((HARD_REG_SET *));static int sequent_regs_live PARAMS ((void));static const char * ptrreg_to_str PARAMS ((int));static const char * cond_string PARAMS ((enum rtx_code));static int avr_num_arg_regs PARAMS ((enum machine_mode, tree));static int out_adj_frame_ptr PARAMS ((FILE *, int));static int out_set_stack_ptr PARAMS ((FILE *, int, int));static RTX_CODE compare_condition PARAMS ((rtx insn));static int compare_sign_p PARAMS ((rtx insn));static int reg_was_0 PARAMS ((rtx insn, rtx op));static tree avr_handle_progmem_attribute PARAMS ((tree *, tree, tree, int, bool *));static tree avr_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *));const struct attribute_spec avr_attribute_table[];static bool avr_assemble_integer PARAMS ((rtx, unsigned int, int));static void avr_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));static void avr_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));static void avr_unique_section PARAMS ((tree, int));static void avr_encode_section_info PARAMS ((tree, int));static unsigned int avr_section_type_flags PARAMS ((tree, const char *, int));static void avr_asm_out_ctor PARAMS ((rtx, int));static void avr_asm_out_dtor PARAMS ((rtx, int));/* Allocate registers from r25 to r8 for parameters for function calls */#define FIRST_CUM_REG 26/* Temporary register RTX (gen_rtx (REG,QImode,TMP_REGNO)) */rtx tmp_reg_rtx;/* Zeroed register RTX (gen_rtx (REG,QImode,ZERO_REGNO)) */rtx zero_reg_rtx;/* RTX for register which will be used for loading immediate values to r0-r15 registers. */rtx ldi_reg_rtx;/* AVR register names {"r0", "r1", ..., "r31"} */static const char *const avr_regnames[] = REGISTER_NAMES;/* This holds the last insn address. */static int last_insn_address = 0;/* Commands count in the compiled file */static int commands_in_file;/* Commands in the functions prologues in the compiled file */static int commands_in_prologues;/* Commands in the functions epilogues in the compiled file */static int commands_in_epilogues;/* Prologue/Epilogue size in words */static int prologue_size;static int epilogue_size;/* Size of all jump tables in the current function, in words. */static int jump_tables_size;/* Initial stack value specified by the `-minit-stack=' option */const char *avr_init_stack = "__stack";/* Default MCU name */const char *avr_mcu_name = "avr2";/* Preprocessor macros to define depending on MCU type. */const char *avr_base_arch_macro;const char *avr_extra_arch_macro;/* More than 8K of program memory: use "call" and "jmp". */int avr_mega_p = 0;/* Enhanced core: use "movw", "mul", ... */int avr_enhanced_p = 0;/* Assembler only. */int avr_asm_only_p = 0;struct base_arch_s { int asm_only; int enhanced; int mega; const char *const macro;};static const struct base_arch_s avr_arch_types[] = { { 1, 0, 0, NULL }, /* unknown device specified */ { 1, 0, 0, "__AVR_ARCH__=1" }, { 0, 0, 0, "__AVR_ARCH__=2" }, { 0, 0, 1, "__AVR_ARCH__=3" }, { 0, 1, 0, "__AVR_ARCH__=4" }, { 0, 1, 1, "__AVR_ARCH__=5" }};struct mcu_type_s { const char *const name; int arch; /* index in avr_arch_types[] */ /* Must lie outside user's namespace. NULL == no macro. */ const char *const macro;};/* List of all known AVR MCU types - if updated, it has to be kept in sync in several places (FIXME: is there a better way?): - here - avr.h (CPP_SPEC, LINK_SPEC, CRT_BINUTILS_SPECS) - t-avr (MULTILIB_MATCHES) - gas/config/tc-avr.c - avr-libc */static const struct mcu_type_s avr_mcu_types[] = { /* Classic, <= 8K. */ { "avr2", 2, NULL }, { "at90s2313", 2, "__AVR_AT90S2313__" }, { "at90s2323", 2, "__AVR_AT90S2323__" }, { "at90s2333", 2, "__AVR_AT90S2333__" }, { "at90s2343", 2, "__AVR_AT90S2343__" }, { "attiny22", 2, "__AVR_ATtiny22__" }, { "attiny26", 2, "__AVR_ATtiny26__" }, { "at90s4414", 2, "__AVR_AT90S4414__" }, { "at90s4433", 2, "__AVR_AT90S4433__" }, { "at90s4434", 2, "__AVR_AT90S4434__" }, { "at90s8515", 2, "__AVR_AT90S8515__" }, { "at90c8534", 2, "__AVR_AT90C8534__" }, { "at90s8535", 2, "__AVR_AT90S8535__" }, { "at86rf401", 2, "__AVR_AT86RF401__" }, /* Classic, > 8K. */ { "avr3", 3, NULL }, { "atmega103", 3, "__AVR_ATmega103__" }, { "atmega603", 3, "__AVR_ATmega603__" }, { "at43usb320", 3, "__AVR_AT43USB320__" }, { "at43usb355", 3, "__AVR_AT43USB355__" }, { "at76c711", 3, "__AVR_AT76C711__" }, /* Enhanced, <= 8K. */ { "avr4", 4, NULL }, { "atmega8", 4, "__AVR_ATmega8__" }, { "atmega8515", 4, "__AVR_ATmega8515__" }, { "atmega8535", 4, "__AVR_ATmega8535__" }, /* Enhanced, > 8K. */ { "avr5", 5, NULL }, { "atmega16", 5, "__AVR_ATmega16__" }, { "atmega161", 5, "__AVR_ATmega161__" }, { "atmega162", 5, "__AVR_ATmega162__" }, { "atmega163", 5, "__AVR_ATmega163__" }, { "atmega169", 5, "__AVR_ATmega169__" }, { "atmega32", 5, "__AVR_ATmega32__" }, { "atmega323", 5, "__AVR_ATmega323__" }, { "atmega64", 5, "__AVR_ATmega64__" }, { "atmega128", 5, "__AVR_ATmega128__" }, { "at94k", 5, "__AVR_AT94K__" }, /* Assembler only. */ { "avr1", 1, NULL }, { "at90s1200", 1, "__AVR_AT90S1200__" }, { "attiny11", 1, "__AVR_ATtiny11__" }, { "attiny12", 1, "__AVR_ATtiny12__" }, { "attiny15", 1, "__AVR_ATtiny15__" }, { "attiny28", 1, "__AVR_ATtiny28__" }, { NULL, 0, NULL }};int avr_case_values_threshold = 30000;/* Initialize the GCC target structure. */#undef TARGET_ASM_ALIGNED_HI_OP#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"#undef TARGET_ASM_INTEGER#define TARGET_ASM_INTEGER avr_assemble_integer#undef TARGET_ASM_FUNCTION_PROLOGUE#define TARGET_ASM_FUNCTION_PROLOGUE avr_output_function_prologue#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE avr_output_function_epilogue#undef TARGET_ATTRIBUTE_TABLE#define TARGET_ATTRIBUTE_TABLE avr_attribute_table#undef TARGET_ASM_UNIQUE_SECTION#define TARGET_ASM_UNIQUE_SECTION avr_unique_section#undef TARGET_ENCODE_SECTION_INFO#define TARGET_ENCODE_SECTION_INFO avr_encode_section_info#undef TARGET_SECTION_TYPE_FLAGS#define TARGET_SECTION_TYPE_FLAGS avr_section_type_flagsstruct gcc_target targetm = TARGET_INITIALIZER;voidavr_override_options (){ const struct mcu_type_s *t; const struct base_arch_s *base; for (t = avr_mcu_types; t->name; t++) if (strcmp (t->name, avr_mcu_name) == 0) break; if (!t->name) { fprintf (stderr, "unknown MCU `%s' specified\nKnown MCU names:\n", avr_mcu_name); for (t = avr_mcu_types; t->name; t++) fprintf (stderr," %s\n", t->name); } base = &avr_arch_types[t->arch]; avr_asm_only_p = base->asm_only; avr_enhanced_p = base->enhanced; avr_mega_p = base->mega; avr_base_arch_macro = base->macro; avr_extra_arch_macro = t->macro; if (optimize && !TARGET_NO_TABLEJUMP) avr_case_values_threshold = (!AVR_MEGA || TARGET_CALL_PROLOGUES) ? 8 : 17;}/* Initialize TMP_REG_RTX and ZERO_REG_RTX */voidavr_init_once (){ tmp_reg_rtx = xmalloc (sizeof (struct rtx_def) + 1 * sizeof (rtunion)); memset (tmp_reg_rtx, 0, sizeof (struct rtx_def) + 1 * sizeof (rtunion)); PUT_CODE (tmp_reg_rtx, REG); PUT_MODE (tmp_reg_rtx, QImode); XINT (tmp_reg_rtx, 0) = TMP_REGNO; zero_reg_rtx = xmalloc (sizeof (struct rtx_def) + 1 * sizeof (rtunion)); memset (zero_reg_rtx, 0, sizeof (struct rtx_def) + 1 * sizeof (rtunion)); PUT_CODE (zero_reg_rtx, REG); PUT_MODE (zero_reg_rtx, QImode); XINT (zero_reg_rtx, 0) = ZERO_REGNO; ldi_reg_rtx = xmalloc (sizeof (struct rtx_def) + 1 * sizeof (rtunion)); memset (ldi_reg_rtx, 0, sizeof (struct rtx_def) + 1 * sizeof (rtunion)); PUT_CODE (ldi_reg_rtx, REG); PUT_MODE (ldi_reg_rtx, QImode); XINT (ldi_reg_rtx, 0) = LDI_REG_REGNO;}/* return register class from register number */static const int reg_class_tab[]={ 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, /* r0 - r15 */ LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS,LD_REGS, LD_REGS, /* r16 - 23 */ ADDW_REGS,ADDW_REGS, /* r24,r25 */ POINTER_X_REGS,POINTER_X_REGS, /* r26,27 */ POINTER_Y_REGS,POINTER_Y_REGS, /* r28,r29 */ POINTER_Z_REGS,POINTER_Z_REGS, /* r30,r31 */ STACK_REG,STACK_REG /* SPL,SPH */};/* Return register class for register R */enum reg_classavr_regno_reg_class (r) int r;{ if (r <= 33) return reg_class_tab[r]; return ALL_REGS;}/* A C expression which defines the machine-dependent operand constraint letters for register classes. If C is such a letter, the value should be the register class corresponding to it. Otherwise, the value should be `NO_REGS'. The register letter `r', corresponding to class `GENERAL_REGS', will not be passed to this macro; you do not need to handle it. */enum reg_classavr_reg_class_from_letter (c) int c;{ switch (c) { case 't' : return R0_REG; case 'b' : return BASE_POINTER_REGS; case 'e' : return POINTER_REGS; case 'w' : return ADDW_REGS; case 'd' : return LD_REGS; case 'l' : return NO_LD_REGS; case 'a' : return SIMPLE_LD_REGS; case 'x' : return POINTER_X_REGS; case 'y' : return POINTER_Y_REGS; case 'z' : return POINTER_Z_REGS; case 'q' : return STACK_REG; default: break; } return NO_REGS;}/* Return nonzero if FUNC is a naked function. */static intavr_naked_function_p (func) tree func;{ tree a; if (TREE_CODE (func) != FUNCTION_DECL) abort (); a = lookup_attribute ("naked", DECL_ATTRIBUTES (func)); return a != NULL_TREE;}/* Return nonzero if FUNC is an interrupt function as specified by the "interrupt" attribute. */static intinterrupt_function_p (func) tree func;{ tree a; if (TREE_CODE (func) != FUNCTION_DECL) return 0; a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func)); return a != NULL_TREE;}/* Return nonzero if FUNC is a signal function as specified by the "signal" attribute. */static intsignal_function_p (func) tree func;{ tree a; if (TREE_CODE (func) != FUNCTION_DECL) return 0; a = lookup_attribute ("signal", DECL_ATTRIBUTES (func)); return a != NULL_TREE;}/* Return the number of hard registers to push/pop in the prologue/epilogue of the current function, and optionally store these registers in SET. */static intavr_regs_to_save (set) HARD_REG_SET *set;{ int reg, count; int int_or_sig_p = (interrupt_function_p (current_function_decl) || signal_function_p (current_function_decl)); int leaf_func_p = leaf_function_p (); if (set) CLEAR_HARD_REG_SET (*set); count = 0; /* No need to save any registers if the function never returns. */ if (TREE_THIS_VOLATILE (current_function_decl)) return 0; for (reg = 0; reg < 32; reg++) { /* Do not push/pop __tmp_reg__, __zero_reg__, as well as any global register variables. */ if (fixed_regs[reg]) continue; if ((int_or_sig_p && !leaf_func_p && call_used_regs[reg]) || (regs_ever_live[reg] && (int_or_sig_p || !call_used_regs[reg]) && !(frame_pointer_needed && (reg == REG_Y || reg == (REG_Y+1))))) { if (set) SET_HARD_REG_BIT (*set, reg); count++; } } return count;}/* Compute offset between arg_pointer and frame_pointer */intinitial_elimination_offset (from, to) int from; int to;{ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return 0; else { int offset = frame_pointer_needed ? 2 : 0; offset += avr_regs_to_save (NULL); return get_frame_size () + 2 + 1 + offset; }}/* Return 1 if the function epilogue is just a single "ret". */intavr_simple_epilogue (){ return (! frame_pointer_needed && get_frame_size () == 0 && avr_regs_to_save (NULL) == 0 && ! interrupt_function_p (current_function_decl) && ! signal_function_p (current_function_decl) && ! avr_naked_function_p (current_function_decl) && ! MAIN_NAME_P (DECL_NAME (current_function_decl)) && ! TREE_THIS_VOLATILE (current_function_decl));}/* This function checks sequence of live registers */static intsequent_regs_live (){ int reg; int live_seq=0; int cur_seq=0; for (reg = 0; reg < 18; ++reg) { if (!call_used_regs[reg]) { if (regs_ever_live[reg]) { ++live_seq; ++cur_seq; } else cur_seq = 0; } } if (!frame_pointer_needed) { if (regs_ever_live[REG_Y]) { ++live_seq; ++cur_seq; } else cur_seq = 0; if (regs_ever_live[REG_Y+1]) { ++live_seq; ++cur_seq; } else cur_seq = 0; } else { cur_seq += 2; live_seq += 2; } return (cur_seq == live_seq) ? live_seq : 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -