📄 h8300.c
字号:
/* Subroutines for insn-output.c for Renesas H8/300. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Steve Chamberlain (sac@cygnus.com), Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).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 "rtl.h"#include "tree.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 "recog.h"#include "expr.h"#include "function.h"#include "optabs.h"#include "toplev.h"#include "c-pragma.h"#include "tm_p.h"#include "ggc.h"#include "target.h"#include "target-def.h"/* Classifies a h8300_src_operand or h8300_dst_operand. H8OP_IMMEDIATE A constant operand of some sort. H8OP_REGISTER An ordinary register. H8OP_MEM_ABSOLUTE A memory reference with a constant address. H8OP_MEM_BASE A memory reference with a register as its address. H8OP_MEM_COMPLEX Some other kind of memory reference. */enum h8300_operand_class{ H8OP_IMMEDIATE, H8OP_REGISTER, H8OP_MEM_ABSOLUTE, H8OP_MEM_BASE, H8OP_MEM_COMPLEX, NUM_H8OPS};/* Classifies an h8sx shift operation. H8SX_SHIFT_NONE The shift cannot be done in a single instruction. H8SX_SHIFT_UNARY The shift is effectively a unary operation. The instruction will allow any sort of destination operand and have a format similar to neg and not. This is true of certain power-of-2 shifts. H8SX_SHIFT_BINARY The shift is a binary operation. The destination must be a register and the source can be a register or a constant. */enum h8sx_shift_type{ H8SX_SHIFT_NONE, H8SX_SHIFT_UNARY, H8SX_SHIFT_BINARY};/* For a general two-operand instruction, element [X][Y] gives the length of the opcode fields when the first operand has class (X + 1) and the second has class Y. */typedef unsigned char h8300_length_table[NUM_H8OPS - 1][NUM_H8OPS];/* Forward declarations. */static const char *byte_reg (rtx, int);static int h8300_interrupt_function_p (tree);static int h8300_saveall_function_p (tree);static int h8300_monitor_function_p (tree);static int h8300_os_task_function_p (tree);static void h8300_emit_stack_adjustment (int, unsigned int);static int round_frame_size (int);static unsigned int compute_saved_regs (void);static void push (int);static void pop (int);static const char *cond_string (enum rtx_code);static unsigned int h8300_asm_insn_count (const char *);static tree h8300_handle_fndecl_attribute (tree *, tree, tree, int, bool *);static tree h8300_handle_eightbit_data_attribute (tree *, tree, tree, int, bool *);static tree h8300_handle_tiny_data_attribute (tree *, tree, tree, int, bool *);#ifndef OBJECT_FORMAT_ELFstatic void h8300_asm_named_section (const char *, unsigned int, tree);#endifstatic int h8300_and_costs (rtx);static int h8300_shift_costs (rtx);static void h8300_push_pop (int, int, int, int);static int h8300_stack_offset_p (rtx, int);static int h8300_ldm_stm_regno (rtx, int, int, int);static int h8300_ldm_stm_parallel (rtvec, int, int);static void h8300_reorg (void);static unsigned int h8300_constant_length (rtx);static unsigned int h8300_displacement_length (rtx, int);static unsigned int h8300_classify_operand (rtx, int, enum h8300_operand_class *);static unsigned int h8300_length_from_table (rtx, rtx, const h8300_length_table *);static unsigned int h8300_unary_length (rtx);static unsigned int h8300_short_immediate_length (rtx);static unsigned int h8300_bitfield_length (rtx, rtx);static unsigned int h8300_binary_length (rtx, const h8300_length_table *);static bool h8300_short_move_mem_p (rtx, enum rtx_code);static unsigned int h8300_move_length (rtx *, const h8300_length_table *);enum h8sx_shift_type h8sx_classify_shift (enum machine_mode, enum rtx_code, rtx);/* CPU_TYPE, says what cpu we're compiling for. */int cpu_type;/* True if a #pragma interrupt has been seen for the current function. */static int pragma_interrupt;/* True if a #pragma saveall has been seen for the current function. */static int pragma_saveall;static const char *const names_big[] ={ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" };static const char *const names_extended[] ={ "er0", "er1", "er2", "er3", "er4", "er5", "er6", "er7" };static const char *const names_upper_extended[] ={ "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7" };/* Points to one of the above. *//* ??? The above could be put in an array indexed by CPU_TYPE. */const char * const *h8_reg_names;/* Various operations needed by the following, indexed by CPU_TYPE. */const char *h8_push_op, *h8_pop_op, *h8_mov_op;/* Value of MOVE_RATIO. */int h8300_move_ratio;/* Machine-specific symbol_ref flags. */#define SYMBOL_FLAG_FUNCVEC_FUNCTION (SYMBOL_FLAG_MACH_DEP << 0)#define SYMBOL_FLAG_EIGHTBIT_DATA (SYMBOL_FLAG_MACH_DEP << 1)#define SYMBOL_FLAG_TINY_DATA (SYMBOL_FLAG_MACH_DEP << 2)/* See below where shifts are handled for explanation of this enum. */enum shift_alg{ SHIFT_INLINE, SHIFT_ROT_AND, SHIFT_SPECIAL, SHIFT_LOOP};/* Symbols of the various shifts which can be used as indices. */enum shift_type{ SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT};/* Macros to keep the shift algorithm tables small. */#define INL SHIFT_INLINE#define ROT SHIFT_ROT_AND#define LOP SHIFT_LOOP#define SPC SHIFT_SPECIAL/* The shift algorithms for each machine, mode, shift type, and shift count are defined below. The three tables below correspond to QImode, HImode, and SImode, respectively. Each table is organized by, in the order of indices, machine, shift type, and shift count. */static enum shift_alg shift_alg_qi[3][3][8] = { { /* TARGET_H8300 */ /* 0 1 2 3 4 5 6 7 */ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300H */ /* 0 1 2 3 4 5 6 7 */ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, LOP, LOP, SPC } /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300S */ /* 0 1 2 3 4 5 6 7 */ { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, INL, INL, SPC } /* SHIFT_ASHIFTRT */ }};static enum shift_alg shift_alg_hi[3][3][16] = { { /* TARGET_H8300 */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ { INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, LOP, LOP, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, LOP, LOP, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300H */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ { INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300S */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ { INL, INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, INL, INL, INL, SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */ }};static enum shift_alg shift_alg_si[3][3][32] = { { /* TARGET_H8300 */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ /* 16 17 18 19 20 21 22 23 */ /* 24 25 26 27 28 29 30 31 */ { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT */ { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, LOP, LOP, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300H */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ /* 16 17 18 19 20 21 22 23 */ /* 24 25 26 27 28 29 30 31 */ { INL, INL, INL, INL, INL, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC, LOP, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP, SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ }, { /* TARGET_H8300S */ /* 0 1 2 3 4 5 6 7 */ /* 8 9 10 11 12 13 14 15 */ /* 16 17 18 19 20 21 22 23 */ /* 24 25 26 27 28 29 30 31 */ { INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT */ { INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC, SPC, LOP, LOP, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */ { INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, INL, LOP, LOP, LOP, LOP, LOP, SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */ }};#undef INL#undef ROT#undef LOP#undef SPCenum h8_cpu{ H8_300, H8_300H, H8_S};/* Initialize various cpu specific globals at start up. */voidh8300_init_once (void){ static const char *const h8_push_ops[2] = { "push" , "push.l" }; static const char *const h8_pop_ops[2] = { "pop" , "pop.l" }; static const char *const h8_mov_ops[2] = { "mov.w", "mov.l" }; if (TARGET_H8300) { cpu_type = (int) CPU_H8300; h8_reg_names = names_big; } else { /* For this we treat the H8/300H and H8S the same. */ cpu_type = (int) CPU_H8300H; h8_reg_names = names_extended; } h8_push_op = h8_push_ops[cpu_type]; h8_pop_op = h8_pop_ops[cpu_type]; h8_mov_op = h8_mov_ops[cpu_type]; if (!TARGET_H8300S && TARGET_MAC) { error ("-ms2600 is used without -ms"); target_flags |= MASK_H8300S; } if (TARGET_H8300 && TARGET_NORMAL_MODE) { error ("-mn is used without -mh or -ms"); target_flags ^= MASK_NORMAL_MODE; } /* Some of the shifts are optimized for speed by default. See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html If optimizing for size, change shift_alg for those shift to SHIFT_LOOP. */ if (optimize_size) { /* H8/300 */ shift_alg_hi[H8_300][SHIFT_ASHIFT][5] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_ASHIFT][6] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_ASHIFT][13] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_ASHIFT][14] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_LSHIFTRT][13] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_LSHIFTRT][14] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_ASHIFTRT][13] = SHIFT_LOOP; shift_alg_hi[H8_300][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; /* H8/300H */ shift_alg_hi[H8_300H][SHIFT_ASHIFT][5] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_ASHIFT][6] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][5] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][6] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][5] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][6] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][13] = SHIFT_LOOP; shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; /* H8S */ shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP; } /* Work out a value for MOVE_RATIO. */ if (!TARGET_H8300SX) { /* Memory-memory moves are quite expensive without the h8sx instructions. */ h8300_move_ratio = 3; } else if (flag_omit_frame_pointer) { /* movmd sequences are fairly cheap when er6 isn't fixed. They can sometimes be as short as two individual memory-to-memory moves, but since they use all the call-saved registers, it seems better to allow up to three moves here. */ h8300_move_ratio = 4; } else if (optimize_size) { /* In this case we don't use movmd sequences since they tend to be longer than calls to memcpy(). Memory-to-memory moves are cheaper than for !TARGET_H8300SX, so it makes sense to have a slightly higher threshold. */ h8300_move_ratio = 4; } else { /* We use movmd sequences for some moves since it can be quicker than calling memcpy(). The sequences will need to save and restore er6 though, so bump up the cost. */ h8300_move_ratio = 6; }}/* Implement REG_CLASS_FROM_LETTER. Some patterns need to use er6 as a scratch register. This is difficult to arrange since er6 is the frame pointer and usually can't be spilled. Such patterns should define two alternatives, one which allows only er6 and one which allows any general register. The former alternative should have a 'd' constraint while the latter should be disparaged and use 'D'. Normally, 'd' maps to DESTINATION_REGS and 'D' maps to GENERAL_REGS. However, there are cases where they should be NO_REGS: - 'd' should be NO_REGS when reloading a function that uses the frame pointer. In this case, DESTINATION_REGS won't contain any spillable registers, so the first alternative can't be used. - -fno-omit-frame-pointer means that the frame pointer will always be in use. It's therefore better to map 'd' to NO_REGS before reload so that register allocator will pick the second alternative. - we would like 'D' to be be NO_REGS when the frame pointer isn't live, but we the frame pointer may turn out to be needed after we start reload, and then we may have already decided we don't have a choice, so we can't do that. Forcing the register allocator to use er6 if possible might produce better code for small functions: it's more efficient to save and restore er6 in the prologue & epilogue than to do it in a define_split. Hopefully disparaging 'D' will have a similar effect, without forcing a reload failure if the frame pointer is found to be needed too late. */enum reg_classh8300_reg_class_from_letter (int c){ switch (c) { case 'a': return MAC_REGS; case 'c': return COUNTER_REGS; case 'd': if (!flag_omit_frame_pointer && !reload_completed) return NO_REGS; if (frame_pointer_needed && reload_in_progress) return NO_REGS; return DESTINATION_REGS; case 'D': /* The meaning of a constraint shouldn't change dynamically, so we can't make this NO_REGS. */ return GENERAL_REGS; case 'f': return SOURCE_REGS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -