summaryrefslogtreecommitdiff
path: root/gcc/config/m68hc11/m68hc11.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/m68hc11/m68hc11.c')
-rw-r--r--gcc/config/m68hc11/m68hc11.c5588
1 files changed, 0 insertions, 5588 deletions
diff --git a/gcc/config/m68hc11/m68hc11.c b/gcc/config/m68hc11/m68hc11.c
deleted file mode 100644
index 1e414102c3f..00000000000
--- a/gcc/config/m68hc11/m68hc11.c
+++ /dev/null
@@ -1,5588 +0,0 @@
-/* Subroutines for code generation on Motorola 68HC11 and 68HC12.
- Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010 Free Software Foundation, Inc.
- Contributed by Stephane Carrez (stcarrez@nerim.fr)
-
-This file is part of GCC.
-
-GCC 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 3, 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 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 GCC; see the file COPYING3. If not see
-<http://www.gnu.org/licenses/>.
-
-Note:
- A first 68HC11 port was made by Otto Lind (otto@coactive.com)
- on gcc 2.6.3. I have used it as a starting point for this port.
- However, this new port is a complete re-write. Its internal
- design is completely different. The generated code is not
- compatible with the gcc 2.6.3 port.
-
- The gcc 2.6.3 port is available at:
-
- ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
-
-*/
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tm.h"
-#include "rtl.h"
-#include "tree.h"
-#include "expr.h"
-#include "tm_p.h"
-#include "regs.h"
-#include "hard-reg-set.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 "libfuncs.h"
-#include "diagnostic-core.h"
-#include "basic-block.h"
-#include "function.h"
-#include "ggc.h"
-#include "reload.h"
-#include "target.h"
-#include "target-def.h"
-#include "df.h"
-
-static void m68hc11_option_override (void);
-static void emit_move_after_reload (rtx, rtx, rtx);
-static rtx simplify_logical (enum machine_mode, int, rtx, rtx *);
-static void m68hc11_emit_logical (enum machine_mode, enum rtx_code, rtx *);
-static void m68hc11_reorg (void);
-static bool m68hc11_legitimate_address_p_1 (enum machine_mode, rtx, bool);
-static bool m68hc11_legitimate_address_p (enum machine_mode, rtx, bool);
-static rtx m68hc11_expand_compare (enum rtx_code, rtx, rtx);
-static int must_parenthesize (rtx);
-static int m68hc11_address_cost (rtx, bool);
-static int m68hc11_shift_cost (enum machine_mode, rtx, int);
-static int m68hc11_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
-static bool m68hc11_rtx_costs (rtx, int, int, int *, bool);
-static tree m68hc11_handle_fntype_attribute (tree *, tree, tree, int, bool *);
-static tree m68hc11_handle_page0_attribute (tree *, tree, tree, int, bool *);
-static bool m68hc11_class_likely_spilled_p (reg_class_t);
-
-void create_regs_rtx (void);
-
-static void asm_print_register (FILE *, int);
-static void m68hc11_print_operand (FILE *, rtx, int);
-static void m68hc11_print_operand_address (FILE *, rtx);
-static void m68hc11_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static void m68hc11_asm_out_constructor (rtx, int);
-static void m68hc11_asm_out_destructor (rtx, int);
-static void m68hc11_file_start (void);
-static void m68hc11_encode_section_info (tree, rtx, int);
-static const char *m68hc11_strip_name_encoding (const char* str);
-static unsigned int m68hc11_section_type_flags (tree, const char*, int);
-static int autoinc_mode (rtx);
-static int m68hc11_make_autoinc_notes (rtx *, void *);
-static void m68hc11_init_libfuncs (void);
-static rtx m68hc11_struct_value_rtx (tree, int);
-static bool m68hc11_return_in_memory (const_tree, const_tree);
-static bool m68hc11_can_eliminate (const int, const int);
-static void m68hc11_conditional_register_usage (void);
-static void m68hc11_trampoline_init (rtx, tree, rtx);
-
-static rtx m68hc11_function_arg (CUMULATIVE_ARGS*, enum machine_mode,
- const_tree, bool);
-static void m68hc11_function_arg_advance (CUMULATIVE_ARGS*, enum machine_mode,
- const_tree, bool);
-
-/* Must be set to 1 to produce debug messages. */
-int debug_m6811 = 0;
-
-extern FILE *asm_out_file;
-
-rtx ix_reg;
-rtx iy_reg;
-rtx d_reg;
-rtx m68hc11_soft_tmp_reg;
-static GTY(()) rtx stack_push_word;
-static GTY(()) rtx stack_pop_word;
-static GTY(()) rtx z_reg;
-static GTY(()) rtx z_reg_qi;
-static int regs_inited = 0;
-
-/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
-int current_function_interrupt;
-
-/* Set to 1 by expand_prologue() when the function is a trap handler. */
-int current_function_trap;
-
-/* Set to 1 when the current function is placed in 68HC12 banked
- memory and must return with rtc. */
-int current_function_far;
-
-/* Min offset that is valid for the indirect addressing mode. */
-HOST_WIDE_INT m68hc11_min_offset = 0;
-
-/* Max offset that is valid for the indirect addressing mode. */
-HOST_WIDE_INT m68hc11_max_offset = 256;
-
-/* The class value for base registers. */
-enum reg_class m68hc11_base_reg_class = A_REGS;
-
-/* The class value for index registers. This is NO_REGS for 68HC11. */
-enum reg_class m68hc11_index_reg_class = NO_REGS;
-
-enum reg_class m68hc11_tmp_regs_class = NO_REGS;
-
-/* Tables that tell whether a given hard register is valid for
- a base or an index register. It is filled at init time depending
- on the target processor. */
-unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
-unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
-
-/* A correction offset which is applied to the stack pointer.
- This is 1 for 68HC11 and 0 for 68HC12. */
-int m68hc11_sp_correction;
-
-int m68hc11_addr_mode;
-int m68hc11_mov_addr_mode;
-
-
-const struct processor_costs *m68hc11_cost;
-
-/* Costs for a 68HC11. */
-static const struct processor_costs m6811_cost = {
- /* add */
- COSTS_N_INSNS (2),
- /* logical */
- COSTS_N_INSNS (2),
- /* non-constant shift */
- COSTS_N_INSNS (20),
- /* shiftQI const */
- { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
- COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
- COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
-
- /* shiftHI const */
- { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
- COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
- COSTS_N_INSNS (4), COSTS_N_INSNS (2),
- COSTS_N_INSNS (2), COSTS_N_INSNS (4),
- COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (10),
- COSTS_N_INSNS (8), COSTS_N_INSNS (6), COSTS_N_INSNS (4)
- },
- /* mulQI */
- COSTS_N_INSNS (20),
- /* mulHI */
- COSTS_N_INSNS (20 * 4),
- /* mulSI */
- COSTS_N_INSNS (20 * 16),
- /* divQI */
- COSTS_N_INSNS (20),
- /* divHI */
- COSTS_N_INSNS (80),
- /* divSI */
- COSTS_N_INSNS (100)
-};
-
-/* Costs for a 68HC12. */
-static const struct processor_costs m6812_cost = {
- /* add */
- COSTS_N_INSNS (2),
- /* logical */
- COSTS_N_INSNS (2),
- /* non-constant shift */
- COSTS_N_INSNS (20),
- /* shiftQI const */
- { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (2),
- COSTS_N_INSNS (3), COSTS_N_INSNS (4), COSTS_N_INSNS (3),
- COSTS_N_INSNS (2), COSTS_N_INSNS (1) },
-
- /* shiftHI const */
- { COSTS_N_INSNS (0), COSTS_N_INSNS (1), COSTS_N_INSNS (4),
- COSTS_N_INSNS (6), COSTS_N_INSNS (8), COSTS_N_INSNS (6),
- COSTS_N_INSNS (4), COSTS_N_INSNS (2),
- COSTS_N_INSNS (2), COSTS_N_INSNS (4), COSTS_N_INSNS (6),
- COSTS_N_INSNS (8), COSTS_N_INSNS (10), COSTS_N_INSNS (8),
- COSTS_N_INSNS (6), COSTS_N_INSNS (4)
- },
- /* mulQI */
- COSTS_N_INSNS (3),
- /* mulHI */
- COSTS_N_INSNS (3),
- /* mulSI */
- COSTS_N_INSNS (3 * 4),
- /* divQI */
- COSTS_N_INSNS (12),
- /* divHI */
- COSTS_N_INSNS (12),
- /* divSI */
- COSTS_N_INSNS (100)
-};
-
-/* M68HC11 specific attributes. */
-
-static const struct attribute_spec m68hc11_attribute_table[] =
-{
- /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
- affects_type_identity } */
- { "interrupt", 0, 0, false, true, true, m68hc11_handle_fntype_attribute,
- false },
- { "trap", 0, 0, false, true, true, m68hc11_handle_fntype_attribute,
- false },
- { "far", 0, 0, false, true, true, m68hc11_handle_fntype_attribute,
- false },
- { "near", 0, 0, false, true, true, m68hc11_handle_fntype_attribute,
- false },
- { "page0", 0, 0, false, false, false, m68hc11_handle_page0_attribute,
- false },
- { NULL, 0, 0, false, false, false, NULL, false }
-};
-
-/* Initialize the GCC target structure. */
-#undef TARGET_ATTRIBUTE_TABLE
-#define TARGET_ATTRIBUTE_TABLE m68hc11_attribute_table
-
-#undef TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
-
-#undef TARGET_PRINT_OPERAND
-#define TARGET_PRINT_OPERAND m68hc11_print_operand
-#undef TARGET_PRINT_OPERAND_ADDRESS
-#define TARGET_PRINT_OPERAND_ADDRESS m68hc11_print_operand_address
-
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE m68hc11_output_function_epilogue
-
-#undef TARGET_ASM_FILE_START
-#define TARGET_ASM_FILE_START m68hc11_file_start
-#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
-#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
-
-#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
-
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO m68hc11_encode_section_info
-
-#undef TARGET_SECTION_TYPE_FLAGS
-#define TARGET_SECTION_TYPE_FLAGS m68hc11_section_type_flags
-
-#undef TARGET_RTX_COSTS
-#define TARGET_RTX_COSTS m68hc11_rtx_costs
-#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST m68hc11_address_cost
-
-#undef TARGET_MACHINE_DEPENDENT_REORG
-#define TARGET_MACHINE_DEPENDENT_REORG m68hc11_reorg
-
-#undef TARGET_INIT_LIBFUNCS
-#define TARGET_INIT_LIBFUNCS m68hc11_init_libfuncs
-
-#undef TARGET_FUNCTION_ARG
-#define TARGET_FUNCTION_ARG m68hc11_function_arg
-#undef TARGET_FUNCTION_ARG_ADVANCE
-#define TARGET_FUNCTION_ARG_ADVANCE m68hc11_function_arg_advance
-
-#undef TARGET_STRUCT_VALUE_RTX
-#define TARGET_STRUCT_VALUE_RTX m68hc11_struct_value_rtx
-#undef TARGET_RETURN_IN_MEMORY
-#define TARGET_RETURN_IN_MEMORY m68hc11_return_in_memory
-#undef TARGET_CALLEE_COPIES
-#define TARGET_CALLEE_COPIES hook_callee_copies_named
-
-#undef TARGET_STRIP_NAME_ENCODING
-#define TARGET_STRIP_NAME_ENCODING m68hc11_strip_name_encoding
-
-#undef TARGET_LEGITIMATE_ADDRESS_P
-#define TARGET_LEGITIMATE_ADDRESS_P m68hc11_legitimate_address_p
-
-#undef TARGET_CAN_ELIMINATE
-#define TARGET_CAN_ELIMINATE m68hc11_can_eliminate
-
-#undef TARGET_CONDITIONAL_REGISTER_USAGE
-#define TARGET_CONDITIONAL_REGISTER_USAGE m68hc11_conditional_register_usage
-
-#undef TARGET_CLASS_LIKELY_SPILLED_P
-#define TARGET_CLASS_LIKELY_SPILLED_P m68hc11_class_likely_spilled_p
-
-#undef TARGET_TRAMPOLINE_INIT
-#define TARGET_TRAMPOLINE_INIT m68hc11_trampoline_init
-
-#undef TARGET_OPTION_OVERRIDE
-#define TARGET_OPTION_OVERRIDE m68hc11_option_override
-
-struct gcc_target targetm = TARGET_INITIALIZER;
-
-static void
-m68hc11_option_override (void)
-{
- memset (m68hc11_reg_valid_for_index, 0,
- sizeof (m68hc11_reg_valid_for_index));
- memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
-
- /* Compilation with -fpic generates a wrong code. */
- if (flag_pic)
- {
- warning (0, "-f%s ignored for 68HC11/68HC12 (not supported)",
- (flag_pic > 1) ? "PIC" : "pic");
- flag_pic = 0;
- }
-
- /* Do not enable -fweb because it breaks the 32-bit shift patterns
- by breaking the match_dup of those patterns. The shift patterns
- will no longer be recognized after that. */
- flag_web = 0;
-
- /* Configure for a 68hc11 processor. */
- if (TARGET_M6811)
- {
- target_flags &= ~(TARGET_AUTO_INC_DEC | TARGET_MIN_MAX);
- m68hc11_cost = &m6811_cost;
- m68hc11_min_offset = 0;
- m68hc11_max_offset = 256;
- m68hc11_index_reg_class = NO_REGS;
- m68hc11_base_reg_class = A_REGS;
- m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
- m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
- m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
- m68hc11_sp_correction = 1;
- m68hc11_tmp_regs_class = D_REGS;
- m68hc11_addr_mode = ADDR_OFFSET;
- m68hc11_mov_addr_mode = 0;
- if (m68hc11_soft_reg_count < 0)
- m68hc11_soft_reg_count = 4;
- }
-
- /* Configure for a 68hc12 processor. */
- if (TARGET_M6812)
- {
- m68hc11_cost = &m6812_cost;
- m68hc11_min_offset = -65536;
- m68hc11_max_offset = 65536;
- m68hc11_index_reg_class = D_REGS;
- m68hc11_base_reg_class = A_OR_SP_REGS;
- m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
- m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
- m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
- m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
- m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
- m68hc11_sp_correction = 0;
- m68hc11_tmp_regs_class = TMP_REGS;
- m68hc11_addr_mode = ADDR_INDIRECT | ADDR_OFFSET | ADDR_CONST
- | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
- m68hc11_mov_addr_mode = ADDR_OFFSET | ADDR_CONST
- | (TARGET_AUTO_INC_DEC ? ADDR_INCDEC : 0);
- target_flags |= MASK_NO_DIRECT_MODE;
- if (m68hc11_soft_reg_count < 0)
- m68hc11_soft_reg_count = 0;
-
- if (TARGET_LONG_CALLS)
- current_function_far = 1;
- }
-}
-
-
-/* The soft-registers are disabled or enabled according to the
- -msoft-reg-count=<n> option. */
-
-static void
-m68hc11_conditional_register_usage (void)
-{
- int i;
-
- if (m68hc11_soft_reg_count > SOFT_REG_LAST - SOFT_REG_FIRST)
- m68hc11_soft_reg_count = SOFT_REG_LAST - SOFT_REG_FIRST;
-
- for (i = SOFT_REG_FIRST + m68hc11_soft_reg_count; i < SOFT_REG_LAST; i++)
- {
- fixed_regs[i] = 1;
- call_used_regs[i] = 1;
- }
-
- /* For 68HC12, the Z register emulation is not necessary when the
- frame pointer is not used. The frame pointer is eliminated and
- replaced by the stack register (which is a BASE_REG_CLASS). */
- if (TARGET_M6812 && flag_omit_frame_pointer && optimize)
- {
- fixed_regs[HARD_Z_REGNUM] = 1;
- }
-}
-
-
-/* Reload and register operations. */
-
-
-void
-create_regs_rtx (void)
-{
- /* regs_inited = 1; */
- ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
- iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
- d_reg = gen_rtx_REG (HImode, HARD_D_REGNUM);
- m68hc11_soft_tmp_reg = gen_rtx_REG (HImode, SOFT_TMP_REGNUM);
-
- stack_push_word = gen_rtx_MEM (HImode,
- gen_rtx_PRE_DEC (HImode,
- gen_rtx_REG (HImode, HARD_SP_REGNUM)));
- stack_pop_word = gen_rtx_MEM (HImode,
- gen_rtx_POST_INC (HImode,
- gen_rtx_REG (HImode, HARD_SP_REGNUM)));
-
-}
-
-/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
- - 8-bit values are stored anywhere (except the SP register).
- - 16-bit values can be stored in any register whose mode is 16
- - 32-bit values can be stored in D, X registers or in a soft register
- (except the last one because we need 2 soft registers)
- - Values whose size is > 32 bit are not stored in real hard
- registers. They may be stored in soft registers if there are
- enough of them. */
-int
-hard_regno_mode_ok (int regno, enum machine_mode mode)
-{
- switch (GET_MODE_SIZE (mode))
- {
- case 8:
- return S_REGNO_P (regno) && m68hc11_soft_reg_count >= 4;
-
- case 4:
- return (X_REGNO_P (regno)
- || (S_REGNO_P (regno) && m68hc11_soft_reg_count >= 2));
-
- case 2:
- return G_REGNO_P (regno);
-
- case 1:
- /* We have to accept a QImode in X or Y registers. Otherwise, the
- reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
- in the insns. Reload fails if the insn rejects the register class 'a'
- as well as if it accepts it. Patterns that failed were
- zero_extend_qihi2 and iorqi3. */
-
- return G_REGNO_P (regno) && !SP_REGNO_P (regno);
-
- default:
- return 0;
- }
-}
-
-int
-m68hc11_hard_regno_rename_ok (int reg1, int reg2)
-{
- /* Don't accept renaming to Z register. We will replace it to
- X,Y or D during machine reorg pass. */
- if (reg2 == HARD_Z_REGNUM)
- return 0;
-
- /* Don't accept renaming D,X to Y register as the code will be bigger. */
- if (TARGET_M6811 && reg2 == HARD_Y_REGNUM
- && (D_REGNO_P (reg1) || X_REGNO_P (reg1)))
- return 0;
-
- return 1;
-}
-
-enum reg_class
-preferred_reload_class (rtx operand, enum reg_class rclass)
-{
- enum machine_mode mode;
-
- mode = GET_MODE (operand);
-
- if (debug_m6811)
- {
- printf ("Preferred reload: (class=%s): ", reg_class_names[rclass]);
- }
-
- if (rclass == D_OR_A_OR_S_REGS && SP_REG_P (operand))
- return m68hc11_base_reg_class;
-
- if (rclass >= S_REGS && (GET_CODE (operand) == MEM
- || GET_CODE (operand) == CONST_INT))
- {
- /* S_REGS class must not be used. The movhi template does not
- work to move a memory to a soft register.
- Restrict to a hard reg. */
- switch (rclass)
- {
- default:
- case G_REGS:
- case D_OR_A_OR_S_REGS:
- rclass = A_OR_D_REGS;
- break;
- case A_OR_S_REGS:
- rclass = A_REGS;
- break;
- case D_OR_SP_OR_S_REGS:
- rclass = D_OR_SP_REGS;
- break;
- case D_OR_Y_OR_S_REGS:
- rclass = D_OR_Y_REGS;
- break;
- case D_OR_X_OR_S_REGS:
- rclass = D_OR_X_REGS;
- break;
- case SP_OR_S_REGS:
- rclass = SP_REGS;
- break;
- case Y_OR_S_REGS:
- rclass = Y_REGS;
- break;
- case X_OR_S_REGS:
- rclass = X_REGS;
- break;
- case D_OR_S_REGS:
- rclass = D_REGS;
- }
- }
- else if (rclass == Y_REGS && GET_CODE (operand) == MEM)
- {
- rclass = Y_REGS;
- }
- else if (rclass == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
- {
- rclass = D_OR_X_REGS;
- }
- else if (rclass >= S_REGS && S_REG_P (operand))
- {
- switch (rclass)
- {
- default:
- case G_REGS:
- case D_OR_A_OR_S_REGS:
- rclass = A_OR_D_REGS;
- break;
- case A_OR_S_REGS:
- rclass = A_REGS;
- break;
- case D_OR_SP_OR_S_REGS:
- rclass = D_OR_SP_REGS;
- break;
- case D_OR_Y_OR_S_REGS:
- rclass = D_OR_Y_REGS;
- break;
- case D_OR_X_OR_S_REGS:
- rclass = D_OR_X_REGS;
- break;
- case SP_OR_S_REGS:
- rclass = SP_REGS;
- break;
- case Y_OR_S_REGS:
- rclass = Y_REGS;
- break;
- case X_OR_S_REGS:
- rclass = X_REGS;
- break;
- case D_OR_S_REGS:
- rclass = D_REGS;
- }
- }
- else if (rclass >= S_REGS)
- {
- if (debug_m6811)
- {
- printf ("Class = %s for: ", reg_class_names[rclass]);
- fflush (stdout);
- debug_rtx (operand);
- }
- }
-
- if (debug_m6811)
- {
- printf (" => class=%s\n", reg_class_names[rclass]);
- fflush (stdout);
- debug_rtx (operand);
- }
-
- return rclass;
-}
-
-/* Implement TARGET_CLASS_LIKELY_SPILLED_P. */
-
-static bool
-m68hc11_class_likely_spilled_p (reg_class_t rclass)
-{
- switch (rclass)
- {
- case D_REGS:
- case X_REGS:
- case Y_REGS:
- case A_REGS:
- case SP_REGS:
- case D_OR_X_REGS:
- case D_OR_Y_REGS:
- case X_OR_SP_REGS:
- case Y_OR_SP_REGS:
- case D_OR_SP_REGS:
- return true;
-
- default:
- break;
- }
-
- return false;
-}
-
-/* Return 1 if the operand is a valid indexed addressing mode.
- For 68hc11: n,r with n in [0..255] and r in A_REGS class
- For 68hc12: n,r no constraint on the constant, r in A_REGS class. */
-int
-m68hc11_valid_addressing_p (rtx operand, enum machine_mode mode, int addr_mode)
-{
- rtx base, offset;
-
- switch (GET_CODE (operand))
- {
- case MEM:
- if ((addr_mode & ADDR_INDIRECT) && GET_MODE_SIZE (mode) <= 2)
- return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
- addr_mode & (ADDR_STRICT | ADDR_OFFSET));
- return 0;
-
- case POST_INC:
- case PRE_INC:
- case POST_DEC:
- case PRE_DEC:
- if (addr_mode & ADDR_INCDEC)
- return m68hc11_valid_addressing_p (XEXP (operand, 0), mode,
- addr_mode & ADDR_STRICT);
- return 0;
-
- case PLUS:
- base = XEXP (operand, 0);
- if (GET_CODE (base) == MEM)
- return 0;
-
- offset = XEXP (operand, 1);
- if (GET_CODE (offset) == MEM)
- return 0;
-
- /* Indexed addressing mode with 2 registers. */
- if (GET_CODE (base) == REG && GET_CODE (offset) == REG)
- {
- if (!(addr_mode & ADDR_INDEXED))
- return 0;
-
- addr_mode &= ADDR_STRICT;
- if (REGNO_OK_FOR_BASE_P2 (REGNO (base), addr_mode)
- && REGNO_OK_FOR_INDEX_P2 (REGNO (offset), addr_mode))
- return 1;
-
- if (REGNO_OK_FOR_BASE_P2 (REGNO (offset), addr_mode)
- && REGNO_OK_FOR_INDEX_P2 (REGNO (base), addr_mode))
- return 1;
-
- return 0;
- }
-
- if (!(addr_mode & ADDR_OFFSET))
- return 0;
-
- if (GET_CODE (base) == REG)
- {
- if (!VALID_CONSTANT_OFFSET_P (offset, mode))
- return 0;
-
- if (!(addr_mode & ADDR_STRICT))
- return 1;
-
- return REGNO_OK_FOR_BASE_P2 (REGNO (base), 1);
- }
-
- if (GET_CODE (offset) == REG)
- {
- if (!VALID_CONSTANT_OFFSET_P (base, mode))
- return 0;
-
- if (!(addr_mode & ADDR_STRICT))
- return 1;
-
- return REGNO_OK_FOR_BASE_P2 (REGNO (offset), 1);
- }
- return 0;
-
- case REG:
- return REGNO_OK_FOR_BASE_P2 (REGNO (operand), addr_mode & ADDR_STRICT);
-
- case CONST_INT:
- if (addr_mode & ADDR_CONST)
- return VALID_CONSTANT_OFFSET_P (operand, mode);
- return 0;
-
- default:
- return 0;
- }
-}
-
-/* Returns 1 if the operand fits in a 68HC11 indirect mode or in
- a 68HC12 1-byte index addressing mode. */
-int
-m68hc11_small_indexed_indirect_p (rtx operand, enum machine_mode mode)
-{
- rtx base, offset;
- int addr_mode;
-
- if (GET_CODE (operand) == REG && reload_in_progress
- && REGNO (operand) >= FIRST_PSEUDO_REGISTER
- && reg_equiv_memory_loc[REGNO (operand)])
- {
- operand = reg_equiv_memory_loc[REGNO (operand)];
- operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
- }
-
- if (GET_CODE (operand) != MEM)
- return 0;
-
- operand = XEXP (operand, 0);
- if (CONSTANT_ADDRESS_P (operand))
- return 1;
-
- if (PUSH_POP_ADDRESS_P (operand))
- return 1;
-
- addr_mode = m68hc11_mov_addr_mode | (reload_completed ? ADDR_STRICT : 0);
- if (!m68hc11_valid_addressing_p (operand, mode, addr_mode))
- return 0;
-
- if (TARGET_M6812 && GET_CODE (operand) == PLUS
- && (reload_completed | reload_in_progress))
- {
- base = XEXP (operand, 0);
- offset = XEXP (operand, 1);
-
- /* The offset can be a symbol address and this is too big
- for the operand constraint. */
- if (GET_CODE (base) != CONST_INT && GET_CODE (offset) != CONST_INT)
- return 0;
-
- if (GET_CODE (base) == CONST_INT)
- offset = base;
-
- switch (GET_MODE_SIZE (mode))
- {
- case 8:
- if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
- return 0;
- break;
-
- case 4:
- if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
- return 0;
- break;
-
- default:
- if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
- return 0;
- break;
- }
- }
- return 1;
-}
-
-int
-m68hc11_register_indirect_p (rtx operand, enum machine_mode mode)
-{
- int addr_mode;
-
- if (GET_CODE (operand) == REG && reload_in_progress
- && REGNO (operand) >= FIRST_PSEUDO_REGISTER
- && reg_equiv_memory_loc[REGNO (operand)])
- {
- operand = reg_equiv_memory_loc[REGNO (operand)];
- operand = eliminate_regs (operand, VOIDmode, NULL_RTX);
- }
- if (GET_CODE (operand) != MEM)
- return 0;
-
- operand = XEXP (operand, 0);
- addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
- return m68hc11_valid_addressing_p (operand, mode, addr_mode);
-}
-
-static bool
-m68hc11_legitimate_address_p_1 (enum machine_mode mode, rtx operand,
- bool strict)
-{
- int addr_mode;
-
- if (CONSTANT_ADDRESS_P (operand) && TARGET_M6812)
- {
- /* Reject the global variables if they are too wide. This forces
- a load of their address in a register and generates smaller code. */
- if (GET_MODE_SIZE (mode) == 8)
- return 0;
-
- return 1;
- }
- addr_mode = m68hc11_addr_mode | (strict ? ADDR_STRICT : 0);
- if (m68hc11_valid_addressing_p (operand, mode, addr_mode))
- {
- return 1;
- }
- if (PUSH_POP_ADDRESS_P (operand))
- {
- return 1;
- }
- if (symbolic_memory_operand (operand, mode))
- {
- return 1;
- }
- return 0;
-}
-
-bool
-m68hc11_legitimate_address_p (enum machine_mode mode, rtx operand,
- bool strict)
-{
- int result;
-
- if (debug_m6811)
- {
- printf ("Checking: ");
- fflush (stdout);
- debug_rtx (operand);
- }
-
- result = m68hc11_legitimate_address_p_1 (mode, operand, strict);
-
- if (debug_m6811)
- {
- printf (" -> %s\n", result == 0 ? "NO" : "YES");
- }
-
- if (result == 0)
- {
- if (debug_m6811)
- {
- printf ("go_if_legitimate%s, ret 0: %d:",
- (strict ? "_strict" : ""), mode);
- fflush (stdout);
- debug_rtx (operand);
- }
- }
- return result;
-}
-
-
-int
-m68hc11_reload_operands (rtx operands[])
-{
- enum machine_mode mode;
-
- if (regs_inited == 0)
- create_regs_rtx ();
-
- mode = GET_MODE (operands[1]);
-
- /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
- if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
- {
- rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
- rtx base = XEXP (XEXP (operands[1], 0), 0);
-
- if (GET_CODE (base) != REG)
- {
- rtx tmp = base;
- base = big_offset;
- big_offset = tmp;
- }
-
- /* If the offset is out of range, we have to compute the address
- with a separate add instruction. We try to do this with an 8-bit
- add on the A register. This is possible only if the lowest part
- of the offset (i.e., big_offset % 256) is a valid constant offset
- with respect to the mode. If it's not, we have to generate a
- 16-bit add on the D register. From:
-
- (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
-
- we generate:
-
- [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
- (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
- [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
- (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
-
- (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
- (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256))))
-
- */
- if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
- {
- int vh, vl;
- rtx reg = operands[0];
- rtx offset;
- int val = INTVAL (big_offset);
-
-
- /* We use the 'operands[0]' as a scratch register to compute the
- address. Make sure 'base' is in that register. */
- if (!rtx_equal_p (base, operands[0]))
- {
- emit_move_insn (reg, base);
- }
-
- if (val > 0)
- {
- vh = val >> 8;
- vl = val & 0x0FF;
- }
- else
- {
- vh = (val >> 8) & 0x0FF;
- vl = val & 0x0FF;
- }
-
- /* Create the lowest part offset that still remains to be added.
- If it's not a valid offset, do a 16-bit add. */
- offset = GEN_INT (vl);
- if (!VALID_CONSTANT_OFFSET_P (offset, mode))
- {
- emit_insn (gen_rtx_SET (VOIDmode, reg,
- gen_rtx_PLUS (HImode, reg, big_offset)));
- offset = const0_rtx;
- }
- else
- {
- emit_insn (gen_rtx_SET (VOIDmode, reg,
- gen_rtx_PLUS (HImode, reg,
- GEN_INT (vh << 8))));
- }
- emit_move_insn (operands[0],
- gen_rtx_MEM (GET_MODE (operands[1]),
- gen_rtx_PLUS (Pmode, reg, offset)));
- return 1;
- }
- }
-
- /* Use the normal gen_movhi pattern. */
- return 0;
-}
-
-void
-m68hc11_emit_libcall (const char *name, enum rtx_code code,
- enum machine_mode dmode, enum machine_mode smode,
- int noperands, rtx *operands)
-{
- rtx ret;
- rtx insns;
- rtx libcall;
- rtx equiv;
-
- start_sequence ();
- libcall = gen_rtx_SYMBOL_REF (Pmode, name);
- switch (noperands)
- {
- case 2:
- ret = emit_library_call_value (libcall, NULL_RTX, LCT_CONST,
- dmode, 1, operands[1], smode);
- equiv = gen_rtx_fmt_e (code, dmode, operands[1]);
- break;
-
- case 3:
- ret = emit_library_call_value (libcall, NULL_RTX,
- LCT_CONST, dmode, 2,
- operands[1], smode, operands[2],
- smode);
- equiv = gen_rtx_fmt_ee (code, dmode, operands[1], operands[2]);
- break;
-
- default:
- gcc_unreachable ();
- }
-
- insns = get_insns ();
- end_sequence ();
- emit_libcall_block (insns, operands[0], ret, equiv);
-}
-
-/* Returns true if X is a PRE/POST increment decrement
- (same as auto_inc_p() in rtlanal.c but do not take into
- account the stack). */
-int
-m68hc11_auto_inc_p (rtx x)
-{
- return GET_CODE (x) == PRE_DEC
- || GET_CODE (x) == POST_INC
- || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
-}
-
-
-/* Predicates for machine description. */
-
-int
-memory_reload_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (operand) == MEM
- && GET_CODE (XEXP (operand, 0)) == PLUS
- && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
- && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
- || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
- && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
-}
-
-int
-m68hc11_symbolic_p (rtx operand, enum machine_mode mode)
-{
- if (GET_CODE (operand) == MEM)
- {
- rtx op = XEXP (operand, 0);
-
- if (symbolic_memory_operand (op, mode))
- return 1;
- }
- return 0;
-}
-
-int
-m68hc11_indirect_p (rtx operand, enum machine_mode mode)
-{
- if (GET_CODE (operand) == MEM && GET_MODE (operand) == mode)
- {
- rtx op = XEXP (operand, 0);
- int addr_mode;
-
- if (m68hc11_page0_symbol_p (op))
- return 1;
-
- if (symbolic_memory_operand (op, mode))
- return TARGET_M6812;
-
- if (reload_in_progress)
- return 1;
-
- operand = XEXP (operand, 0);
- addr_mode = m68hc11_addr_mode | (reload_completed ? ADDR_STRICT : 0);
- return m68hc11_valid_addressing_p (operand, mode, addr_mode);
- }
- return 0;
-}
-
-int
-memory_indexed_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (operand) != MEM)
- return 0;
-
- operand = XEXP (operand, 0);
- if (GET_CODE (operand) == PLUS)
- {
- if (GET_CODE (XEXP (operand, 0)) == REG)
- operand = XEXP (operand, 0);
- else if (GET_CODE (XEXP (operand, 1)) == REG)
- operand = XEXP (operand, 1);
- }
- return GET_CODE (operand) == REG
- && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
- || A_REGNO_P (REGNO (operand)));
-}
-
-int
-push_pop_operand_p (rtx operand)
-{
- if (GET_CODE (operand) != MEM)
- {
- return 0;
- }
- operand = XEXP (operand, 0);
- return PUSH_POP_ADDRESS_P (operand);
-}
-
-/* Returns 1 if OP is either a symbol reference or a sum of a symbol
- reference and a constant. */
-
-int
-symbolic_memory_operand (rtx op, enum machine_mode mode)
-{
- switch (GET_CODE (op))
- {
- case SYMBOL_REF:
- case LABEL_REF:
- return 1;
-
- case CONST:
- op = XEXP (op, 0);
- return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
- || GET_CODE (XEXP (op, 0)) == LABEL_REF)
- && GET_CODE (XEXP (op, 1)) == CONST_INT);
-
- /* ??? This clause seems to be irrelevant. */
- case CONST_DOUBLE:
- return GET_MODE (op) == mode;
-
- case PLUS:
- return symbolic_memory_operand (XEXP (op, 0), mode)
- && symbolic_memory_operand (XEXP (op, 1), mode);
-
- default:
- return 0;
- }
-}
-
-/* Emit the code to build the trampoline used to call a nested function.
-
- 68HC11 68HC12
-
- ldy #&CXT movw #&CXT,*_.d1
- sty *_.d1 jmp FNADDR
- jmp FNADDR
-
-*/
-static void
-m68hc11_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
-{
- const char *static_chain_reg = reg_names[STATIC_CHAIN_REGNUM];
- rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
- rtx mem;
-
- /* Skip the '*'. */
- if (*static_chain_reg == '*')
- static_chain_reg++;
- if (TARGET_M6811)
- {
- mem = adjust_address (m_tramp, HImode, 0);
- emit_move_insn (mem, GEN_INT (0x18ce));
- mem = adjust_address (m_tramp, HImode, 2);
- emit_move_insn (mem, cxt);
- mem = adjust_address (m_tramp, HImode, 4);
- emit_move_insn (mem, GEN_INT (0x18df));
- mem = adjust_address (m_tramp, QImode, 6);
- emit_move_insn (mem,
- gen_rtx_CONST (QImode,
- gen_rtx_SYMBOL_REF (Pmode,
- static_chain_reg)));
- mem = adjust_address (m_tramp, QImode, 7);
- emit_move_insn (mem, GEN_INT (0x7e));
- mem = adjust_address (m_tramp, HImode, 8);
- emit_move_insn (mem, fnaddr);
- }
- else
- {
- mem = adjust_address (m_tramp, HImode, 0);
- emit_move_insn (mem, GEN_INT (0x1803));
- mem = adjust_address (m_tramp, HImode, 2);
- emit_move_insn (mem, cxt);
- mem = adjust_address (m_tramp, HImode, 4);
- emit_move_insn (mem,
- gen_rtx_CONST (HImode,
- gen_rtx_SYMBOL_REF (Pmode,
- static_chain_reg)));
- mem = adjust_address (m_tramp, QImode, 6);
- emit_move_insn (mem, GEN_INT (0x06));
- mem = adjust_address (m_tramp, HImode, 7);
- emit_move_insn (mem, fnaddr);
- }
-}
-
-/* Declaration of types. */
-
-/* Handle an "tiny_data" attribute; arguments as in
- struct attribute_spec.handler. */
-static tree
-m68hc11_handle_page0_attribute (tree *node, tree name,
- tree args ATTRIBUTE_UNUSED,
- int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
-{
- tree decl = *node;
-
- if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
- {
- DECL_SECTION_NAME (decl) = build_string (6, ".page0");
- }
- else
- {
- warning (OPT_Wattributes, "%qE attribute ignored",
- name);
- *no_add_attrs = true;
- }
-
- return NULL_TREE;
-}
-
-/* Keep track of the symbol which has a `trap' attribute and which uses
- the `swi' calling convention. Since there is only one trap, we only
- record one such symbol. If there are several, a warning is reported. */
-static rtx trap_handler_symbol = 0;
-
-/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
- arguments as in struct attribute_spec.handler. */
-static tree
-m68hc11_handle_fntype_attribute (tree *node, tree name,
- tree args ATTRIBUTE_UNUSED,
- int flags ATTRIBUTE_UNUSED,
- bool *no_add_attrs)
-{
- if (TREE_CODE (*node) != FUNCTION_TYPE
- && TREE_CODE (*node) != METHOD_TYPE
- && TREE_CODE (*node) != FIELD_DECL
- && TREE_CODE (*node) != TYPE_DECL)
- {
- warning (OPT_Wattributes, "%qE attribute only applies to functions",
- name);
- *no_add_attrs = true;
- }
-
- return NULL_TREE;
-}
-/* Undo the effects of the above. */
-
-static const char *
-m68hc11_strip_name_encoding (const char *str)
-{
- return str + (*str == '*' || *str == '@' || *str == '&');
-}
-
-static void
-m68hc11_encode_label (tree decl)
-{
- const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
- int len = strlen (str);
- char *newstr = XALLOCAVEC (char, len + 2);
-
- newstr[0] = '@';
- strcpy (&newstr[1], str);
-
- XSTR (XEXP (DECL_RTL (decl), 0), 0) = ggc_alloc_string (newstr, len + 1);
-}
-
-/* Return 1 if this is a symbol in page0 */
-int
-m68hc11_page0_symbol_p (rtx x)
-{
- switch (GET_CODE (x))
- {
- case SYMBOL_REF:
- return XSTR (x, 0) != 0 && XSTR (x, 0)[0] == '@';
-
- case CONST:
- return m68hc11_page0_symbol_p (XEXP (x, 0));
-
- case PLUS:
- if (!m68hc11_page0_symbol_p (XEXP (x, 0)))
- return 0;
-
- return GET_CODE (XEXP (x, 1)) == CONST_INT
- && INTVAL (XEXP (x, 1)) < 256
- && INTVAL (XEXP (x, 1)) >= 0;
-
- default:
- return 0;
- }
-}
-
-/* We want to recognize trap handlers so that we handle calls to traps
- in a special manner (by issuing the trap). This information is stored
- in SYMBOL_REF_FLAG. */
-
-static void
-m68hc11_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
-{
- tree func_attr;
- int trap_handler;
- int is_far = 0;
-
- if (TREE_CODE (decl) == VAR_DECL)
- {
- if (lookup_attribute ("page0", DECL_ATTRIBUTES (decl)) != 0)
- m68hc11_encode_label (decl);
- return;
- }
-
- if (TREE_CODE (decl) != FUNCTION_DECL)
- return;
-
- func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
-
-
- if (lookup_attribute ("far", func_attr) != NULL_TREE)
- is_far = 1;
- else if (lookup_attribute ("near", func_attr) == NULL_TREE)
- is_far = TARGET_LONG_CALLS != 0;
-
- trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
- if (trap_handler && is_far)
- {
- warning (OPT_Wattributes, "%<trap%> and %<far%> attributes are "
- "not compatible, ignoring %<far%>");
- trap_handler = 0;
- }
- if (trap_handler)
- {
- if (trap_handler_symbol != 0)
- warning (OPT_Wattributes, "%<trap%> attribute is already used");
- else
- trap_handler_symbol = XEXP (rtl, 0);
- }
- SYMBOL_REF_FLAG (XEXP (rtl, 0)) = is_far;
-}
-
-static unsigned int
-m68hc11_section_type_flags (tree decl, const char *name, int reloc)
-{
- unsigned int flags = default_section_type_flags (decl, name, reloc);
-
- if (strncmp (name, ".eeprom", 7) == 0)
- {
- flags |= SECTION_WRITE | SECTION_CODE | SECTION_OVERRIDE;
- }
-
- return flags;
-}
-
-int
-m68hc11_is_far_symbol (rtx sym)
-{
- if (GET_CODE (sym) == MEM)
- sym = XEXP (sym, 0);
-
- return SYMBOL_REF_FLAG (sym);
-}
-
-int
-m68hc11_is_trap_symbol (rtx sym)
-{
- if (GET_CODE (sym) == MEM)
- sym = XEXP (sym, 0);
-
- return trap_handler_symbol != 0 && rtx_equal_p (trap_handler_symbol, sym);
-}
-
-
-/* Argument support functions. */
-
-/* Given FROM and TO register numbers, say whether this elimination is
- allowed. Frame pointer elimination is automatically handled.
-
- All other eliminations are valid. */
-
-bool
-m68hc11_can_eliminate (const int from, const int to)
-{
- return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
- ? ! frame_pointer_needed
- : true);
-}
-
-/* Define the offset between two registers, one to be eliminated, and the
- other its replacement, at the start of a routine. */
-int
-m68hc11_initial_elimination_offset (int from, int to)
-{
- int trap_handler;
- tree func_attr;
- int size;
- int regno;
-
- /* For a trap handler, we must take into account the registers which
- are pushed on the stack during the trap (except the PC). */
- func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
- current_function_interrupt = lookup_attribute ("interrupt",
- func_attr) != NULL_TREE;
- trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
-
- if (lookup_attribute ("far", func_attr) != 0)
- current_function_far = 1;
- else if (lookup_attribute ("near", func_attr) != 0)
- current_function_far = 0;
- else
- current_function_far = (TARGET_LONG_CALLS != 0
- && !current_function_interrupt
- && !trap_handler);
-
- if (trap_handler && from == ARG_POINTER_REGNUM)
- size = 7;
-
- /* For a function using 'call/rtc' we must take into account the
- page register which is pushed in the call. */
- else if (current_function_far && from == ARG_POINTER_REGNUM)
- size = 1;
- else
- size = 0;
-
- if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
- {
- /* 2 is for the saved frame.
- 1 is for the 'sts' correction when creating the frame. */
- return get_frame_size () + 2 + m68hc11_sp_correction + size;
- }
-
- if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
- {
- return m68hc11_sp_correction;
- }
-
- /* Push any 2 byte pseudo hard registers that we need to save. */
- for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
- {
- if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
- {
- size += 2;
- }
- }
-
- if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
- {
- return get_frame_size () + size;
- }
-
- if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
- {
- return size;
- }
- return 0;
-}
-
-/* Initialize a variable CUM of type CUMULATIVE_ARGS
- for a call to a function whose data type is FNTYPE.
- For a library call, FNTYPE is 0. */
-
-void
-m68hc11_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname)
-{
- tree ret_type;
-
- z_replacement_completed = 0;
- cum->words = 0;
- cum->nregs = 0;
-
- /* For a library call, we must find out the type of the return value.
- When the return value is bigger than 4 bytes, it is returned in
- memory. In that case, the first argument of the library call is a
- pointer to the memory location. Because the first argument is passed in
- register D, we have to identify this, so that the first function
- parameter is not passed in D either. */
- if (fntype == 0)
- {
- const char *name;
- size_t len;
-
- if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
- return;
-
- /* If the library ends in 'di' or in 'df', we assume it's
- returning some DImode or some DFmode which are 64-bit wide. */
- name = XSTR (libname, 0);
- len = strlen (name);
- if (len > 3
- && ((name[len - 2] == 'd'
- && (name[len - 1] == 'f' || name[len - 1] == 'i'))
- || (name[len - 3] == 'd'
- && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
- {
- /* We are in. Mark the first parameter register as already used. */
- cum->words = 1;
- cum->nregs = 1;
- }
- return;
- }
-
- ret_type = TREE_TYPE (fntype);
-
- if (ret_type && aggregate_value_p (ret_type, fntype))
- {
- cum->words = 1;
- cum->nregs = 1;
- }
-}
-
-/* Update the data in CUM to advance over an argument
- of mode MODE and data type TYPE.
- (TYPE is null for libcalls where that information may not be available.) */
-
-static void
-m68hc11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- const_tree type, bool named ATTRIBUTE_UNUSED)
-{
- if (mode != BLKmode)
- {
- if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
- {
- cum->nregs = 2;
- cum->words = GET_MODE_SIZE (mode);
- }
- else
- {
- cum->words += GET_MODE_SIZE (mode);
- if (cum->words <= HARD_REG_SIZE)
- cum->nregs = 1;
- }
- }
- else
- {
- cum->words += int_size_in_bytes (type);
- }
- return;
-}
-
-/* Define where to put the arguments to a function.
- Value is zero to push the argument on the stack,
- or a hard register in which to store the argument.
-
- MODE is the argument's machine mode.
- TYPE is the data type of the argument (as a tree).
- This is null for libcalls where that information may
- not be available.
- CUM is a variable of type CUMULATIVE_ARGS which gives info about
- the preceding args and about the function being called.
- NAMED is nonzero if this argument is a named parameter
- (otherwise it is an extra parameter matching an ellipsis). */
-
-static rtx
-m68hc11_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- const_tree type ATTRIBUTE_UNUSED,
- bool named ATTRIBUTE_UNUSED)
-{
- if (cum->words != 0)
- {
- return NULL_RTX;
- }
-
- if (mode != BLKmode)
- {
- if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
- return gen_rtx_REG (mode, HARD_X_REGNUM);
-
- if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
- {
- return NULL_RTX;
- }
- return gen_rtx_REG (mode, HARD_D_REGNUM);
- }
- return NULL_RTX;
-}
-
-/* If defined, a C expression which determines whether, and in which direction,
- to pad out an argument with extra space. The value should be of type
- `enum direction': either `upward' to pad above the argument,
- `downward' to pad below, or `none' to inhibit padding.
-
- Structures are stored left shifted in their argument slot. */
-enum direction
-m68hc11_function_arg_padding (enum machine_mode mode, const_tree type)
-{
- if (type != 0 && AGGREGATE_TYPE_P (type))
- return upward;
-
- /* Fall back to the default. */
- return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
-}
-
-
-/* Function prologue and epilogue. */
-
-/* Emit a move after the reload pass has completed. This is used to
- emit the prologue and epilogue. */
-static void
-emit_move_after_reload (rtx to, rtx from, rtx scratch)
-{
- rtx insn;
-
- if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
- {
- insn = emit_move_insn (to, from);
- }
- else
- {
- emit_move_insn (scratch, from);
- insn = emit_move_insn (to, scratch);
- }
-
- /* Put a REG_INC note to tell the flow analysis that the instruction
- is necessary. */
- if (IS_STACK_PUSH (to))
- add_reg_note (insn, REG_INC, XEXP (XEXP (to, 0), 0));
- else if (IS_STACK_POP (from))
- add_reg_note (insn, REG_INC, XEXP (XEXP (from, 0), 0));
-
- /* For 68HC11, put a REG_INC note on `sts _.frame' to prevent the cse-reg
- to think that sp == _.frame and later replace a x = sp with x = _.frame.
- The problem is that we are lying to gcc and use `txs' for x = sp
- (which is not really true because txs is really x = sp + 1). */
- else if (TARGET_M6811 && SP_REG_P (from))
- add_reg_note (insn, REG_INC, from);
-}
-
-int
-m68hc11_total_frame_size (void)
-{
- int size;
- int regno;
-
- size = get_frame_size ();
- if (current_function_interrupt)
- {
- size += 3 * HARD_REG_SIZE;
- }
- if (frame_pointer_needed)
- size += HARD_REG_SIZE;
-
- for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
- if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
- size += HARD_REG_SIZE;
-
- return size;
-}
-
-static void
-m68hc11_output_function_epilogue (FILE *out ATTRIBUTE_UNUSED,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
-{
- /* We catch the function epilogue generation to have a chance
- to clear the z_replacement_completed flag. */
- z_replacement_completed = 0;
-}
-
-void
-expand_prologue (void)
-{
- tree func_attr;
- int size;
- int regno;
- rtx scratch;
-
- gcc_assert (reload_completed == 1);
-
- size = get_frame_size ();
-
- create_regs_rtx ();
-
- /* Generate specific prologue for interrupt handlers. */
- func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
- current_function_interrupt = lookup_attribute ("interrupt",
- func_attr) != NULL_TREE;
- current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
- if (lookup_attribute ("far", func_attr) != NULL_TREE)
- current_function_far = 1;
- else if (lookup_attribute ("near", func_attr) != NULL_TREE)
- current_function_far = 0;
- else
- current_function_far = (TARGET_LONG_CALLS != 0
- && !current_function_interrupt
- && !current_function_trap);
-
- /* Get the scratch register to build the frame and push registers.
- If the first argument is a 32-bit quantity, the D+X registers
- are used. Use Y to compute the frame. Otherwise, X is cheaper.
- For 68HC12, this scratch register is not used. */
- if (crtl->args.info.nregs == 2)
- scratch = iy_reg;
- else
- scratch = ix_reg;
-
- /* Save current stack frame. */
- if (frame_pointer_needed)
- emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
-
- /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
- Other soft registers in page0 need not to be saved because they
- will be restored by C functions. For a trap handler, we don't
- need to preserve these registers because this is a synchronous call. */
- if (current_function_interrupt)
- {
- emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
- emit_move_after_reload (stack_push_word,
- gen_rtx_REG (HImode, SOFT_Z_REGNUM), scratch);
- emit_move_after_reload (stack_push_word,
- gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
- scratch);
- }
-
- /* Allocate local variables. */
- if (TARGET_M6812 && (size > 4 || size == 3))
- {
- emit_insn (gen_addhi3 (stack_pointer_rtx,
- stack_pointer_rtx, GEN_INT (-size)));
- }
- else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
- {
- rtx insn;
-
- insn = gen_rtx_PARALLEL
- (VOIDmode,
- gen_rtvec (2,
- gen_rtx_SET (VOIDmode,
- stack_pointer_rtx,
- gen_rtx_PLUS (HImode,
- stack_pointer_rtx,
- GEN_INT (-size))),
- gen_rtx_CLOBBER (VOIDmode, scratch)));
- emit_insn (insn);
- }
- else
- {
- int i;
-
- /* Allocate by pushing scratch values. */
- for (i = 2; i <= size; i += 2)
- emit_move_after_reload (stack_push_word, ix_reg, 0);
-
- if (size & 1)
- emit_insn (gen_addhi3 (stack_pointer_rtx,
- stack_pointer_rtx, constm1_rtx));
- }
-
- /* Create the frame pointer. */
- if (frame_pointer_needed)
- emit_move_after_reload (hard_frame_pointer_rtx,
- stack_pointer_rtx, scratch);
-
- /* Push any 2 byte pseudo hard registers that we need to save. */
- for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
- {
- if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
- {
- emit_move_after_reload (stack_push_word,
- gen_rtx_REG (HImode, regno), scratch);
- }
- }
-}
-
-void
-expand_epilogue (void)
-{
- int size;
- register int regno;
- int return_size;
- rtx scratch;
-
- gcc_assert (reload_completed == 1);
-
- size = get_frame_size ();
-
- /* If we are returning a value in two registers, we have to preserve the
- X register and use the Y register to restore the stack and the saved
- registers. Otherwise, use X because it's faster (and smaller). */
- if (crtl->return_rtx == 0)
- return_size = 0;
- else if (GET_CODE (crtl->return_rtx) == MEM)
- return_size = HARD_REG_SIZE;
- else
- return_size = GET_MODE_SIZE (GET_MODE (crtl->return_rtx));
-
- if (return_size > HARD_REG_SIZE && return_size <= 2 * HARD_REG_SIZE)
- scratch = iy_reg;
- else
- scratch = ix_reg;
-
- /* Pop any 2 byte pseudo hard registers that we saved. */
- for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
- {
- if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
- {
- emit_move_after_reload (gen_rtx_REG (HImode, regno),
- stack_pop_word, scratch);
- }
- }
-
- /* de-allocate auto variables */
- if (TARGET_M6812 && (size > 4 || size == 3))
- {
- emit_insn (gen_addhi3 (stack_pointer_rtx,
- stack_pointer_rtx, GEN_INT (size)));
- }
- else if ((!optimize_size && size > 8) || (optimize_size && size > 10))
- {
- rtx insn;
-
- insn = gen_rtx_PARALLEL
- (VOIDmode,
- gen_rtvec (2,
- gen_rtx_SET (VOIDmode,
- stack_pointer_rtx,
- gen_rtx_PLUS (HImode,
- stack_pointer_rtx,
- GEN_INT (size))),
- gen_rtx_CLOBBER (VOIDmode, scratch)));
- emit_insn (insn);
- }
- else
- {
- int i;
-
- for (i = 2; i <= size; i += 2)
- emit_move_after_reload (scratch, stack_pop_word, scratch);
- if (size & 1)
- emit_insn (gen_addhi3 (stack_pointer_rtx,
- stack_pointer_rtx, const1_rtx));
- }
-
- /* For an interrupt handler, restore ZTMP, ZREG and XYREG. */
- if (current_function_interrupt)
- {
- emit_move_after_reload (gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM),
- stack_pop_word, scratch);
- emit_move_after_reload (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
- stack_pop_word, scratch);
- emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
- }
-
- /* Restore previous frame pointer. */
- if (frame_pointer_needed)
- emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
-
- /* If the trap handler returns some value, copy the value
- in D, X onto the stack so that the rti will pop the return value
- correctly. */
- else if (current_function_trap && return_size != 0)
- {
- rtx addr_reg = stack_pointer_rtx;
-
- if (!TARGET_M6812)
- {
- emit_move_after_reload (scratch, stack_pointer_rtx, 0);
- addr_reg = scratch;
- }
- emit_move_after_reload (gen_rtx_MEM (HImode,
- gen_rtx_PLUS (HImode, addr_reg,
- const1_rtx)), d_reg, 0);
- if (return_size > HARD_REG_SIZE)
- emit_move_after_reload (gen_rtx_MEM (HImode,
- gen_rtx_PLUS (HImode, addr_reg,
- GEN_INT (3))), ix_reg, 0);
- }
-
- emit_jump_insn (gen_return ());
-}
-
-
-/* Low and High part extraction for 68HC11. These routines are
- similar to gen_lowpart and gen_highpart but they have been
- fixed to work for constants and 68HC11 specific registers. */
-
-rtx
-m68hc11_gen_lowpart (enum machine_mode mode, rtx x)
-{
- /* We assume that the low part of an auto-inc mode is the same with
- the mode changed and that the caller split the larger mode in the
- correct order. */
- if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
- {
- return gen_rtx_MEM (mode, XEXP (x, 0));
- }
-
- /* Note that a CONST_DOUBLE rtx could represent either an integer or a
- floating-point constant. A CONST_DOUBLE is used whenever the
- constant requires more than one word in order to be adequately
- represented. */
- if (GET_CODE (x) == CONST_DOUBLE)
- {
- long l[2];
-
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
- {
- REAL_VALUE_TYPE r;
-
- if (GET_MODE (x) == SFmode)
- {
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
- }
- else
- {
- rtx first, second;
-
- split_double (x, &first, &second);
- return second;
- }
- if (mode == SImode)
- return GEN_INT (l[0]);
-
- return gen_int_mode (l[0], HImode);
- }
- else
- {
- l[0] = CONST_DOUBLE_LOW (x);
- }
- switch (mode)
- {
- case SImode:
- return GEN_INT (l[0]);
- case HImode:
- gcc_assert (GET_MODE (x) == SFmode);
- return gen_int_mode (l[0], HImode);
- default:
- gcc_unreachable ();
- }
- }
-
- if (mode == QImode && D_REG_P (x))
- return gen_rtx_REG (mode, HARD_B_REGNUM);
-
- /* gen_lowpart crashes when it is called with a SUBREG. */
- if (GET_CODE (x) == SUBREG && SUBREG_BYTE (x) != 0)
- {
- switch (mode)
- {
- case SImode:
- return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 4);
- case HImode:
- return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_BYTE (x) + 2);
- default:
- gcc_unreachable ();
- }
- }
- x = gen_lowpart (mode, x);
-
- /* Return a different rtx to avoid to share it in several insns
- (when used by a split pattern). Sharing addresses within
- a MEM breaks the Z register replacement (and reloading). */
- if (GET_CODE (x) == MEM)
- x = copy_rtx (x);
- return x;
-}
-
-rtx
-m68hc11_gen_highpart (enum machine_mode mode, rtx x)
-{
- /* We assume that the high part of an auto-inc mode is the same with
- the mode changed and that the caller split the larger mode in the
- correct order. */
- if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
- {
- return gen_rtx_MEM (mode, XEXP (x, 0));
- }
-
- /* Note that a CONST_DOUBLE rtx could represent either an integer or a
- floating-point constant. A CONST_DOUBLE is used whenever the
- constant requires more than one word in order to be adequately
- represented. */
- if (GET_CODE (x) == CONST_DOUBLE)
- {
- long l[2];
-
- if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
- {
- REAL_VALUE_TYPE r;
-
- if (GET_MODE (x) == SFmode)
- {
- REAL_VALUE_FROM_CONST_DOUBLE (r, x);
- REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
- }
- else
- {
- rtx first, second;
-
- split_double (x, &first, &second);
- return first;
- }
- if (mode == SImode)
- return GEN_INT (l[1]);
-
- return gen_int_mode ((l[1] >> 16), HImode);
- }
- else
- {
- l[1] = CONST_DOUBLE_HIGH (x);
- }
-
- switch (mode)
- {
- case SImode:
- return GEN_INT (l[1]);
- case HImode:
- gcc_assert (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
- return gen_int_mode ((l[0] >> 16), HImode);
- default:
- gcc_unreachable ();
- }
- }
- if (GET_CODE (x) == CONST_INT)
- {
- HOST_WIDE_INT val = INTVAL (x);
-
- if (mode == QImode)
- {
- return gen_int_mode (val >> 8, QImode);
- }
- else if (mode == HImode)
- {
- return gen_int_mode (val >> 16, HImode);
- }
- else if (mode == SImode)
- {
- return gen_int_mode ((val >> 16) >> 16, SImode);
- }
- }
- if (mode == QImode && D_REG_P (x))
- return gen_rtx_REG (mode, HARD_A_REGNUM);
-
- /* There is no way in GCC to represent the upper part of a word register.
- To obtain the 8-bit upper part of a soft register, we change the
- reg into a mem rtx. This is possible because they are physically
- located in memory. There is no offset because we are big-endian. */
- if (mode == QImode && S_REG_P (x))
- {
- int pos;
-
- /* Avoid the '*' for direct addressing mode when this
- addressing mode is disabled. */
- pos = TARGET_NO_DIRECT_MODE ? 1 : 0;
- return gen_rtx_MEM (QImode,
- gen_rtx_SYMBOL_REF (Pmode,
- &reg_names[REGNO (x)][pos]));
- }
-
- /* gen_highpart crashes when it is called with a SUBREG. */
- switch (GET_CODE (x))
- {
- case SUBREG:
- return gen_rtx_SUBREG (mode, XEXP (x, 0), XINT (x, 1));
- case REG:
- if (REGNO (x) < FIRST_PSEUDO_REGISTER)
- return gen_rtx_REG (mode, REGNO (x));
- else
- return gen_rtx_SUBREG (mode, x, 0);
- case MEM:
- x = change_address (x, mode, 0);
-
- /* Return a different rtx to avoid to share it in several insns
- (when used by a split pattern). Sharing addresses within
- a MEM breaks the Z register replacement (and reloading). */
- if (GET_CODE (x) == MEM)
- x = copy_rtx (x);
- return x;
-
- default:
- gcc_unreachable ();
- }
-}
-
-
-/* Obscure register manipulation. */
-
-/* Finds backward in the instructions to see if register 'reg' is
- dead. This is used when generating code to see if we can use 'reg'
- as a scratch register. This allows us to choose a better generation
- of code when we know that some register dies or can be clobbered. */
-
-int
-dead_register_here (rtx x, rtx reg)
-{
- rtx x_reg;
- rtx p;
-
- if (D_REG_P (reg))
- x_reg = gen_rtx_REG (SImode, HARD_X_REGNUM);
- else
- x_reg = 0;
-
- for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
- if (INSN_P (p))
- {
- rtx body;
-
- body = PATTERN (p);
-
- if (GET_CODE (body) == CALL_INSN)
- break;
- if (GET_CODE (body) == JUMP_INSN)
- break;
-
- if (GET_CODE (body) == SET)
- {
- rtx dst = XEXP (body, 0);
-
- if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
- break;
- if (x_reg && rtx_equal_p (dst, x_reg))
- break;
-
- if (find_regno_note (p, REG_DEAD, REGNO (reg)))
- return 1;
- }
- else if (reg_mentioned_p (reg, p)
- || (x_reg && reg_mentioned_p (x_reg, p)))
- break;
- }
-
- /* Scan forward to see if the register is set in some insns and never
- used since then. */
- for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
- {
- rtx body;
-
- if (GET_CODE (p) == CODE_LABEL
- || GET_CODE (p) == JUMP_INSN
- || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
- break;
-
- if (GET_CODE (p) != INSN)
- continue;
-
- body = PATTERN (p);
- if (GET_CODE (body) == SET)
- {
- rtx src = XEXP (body, 1);
- rtx dst = XEXP (body, 0);
-
- if (GET_CODE (dst) == REG
- && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
- return 1;
- }
-
- /* Register is used (may be in source or in dest). */
- if (reg_mentioned_p (reg, p)
- || (x_reg != 0 && GET_MODE (p) == SImode
- && reg_mentioned_p (x_reg, p)))
- break;
- }
- return p == 0 ? 1 : 0;
-}
-
-
-/* Code generation operations called from machine description file. */
-
-/* Print the name of register 'regno' in the assembly file. */
-static void
-asm_print_register (FILE *file, int regno)
-{
- const char *name = reg_names[regno];
-
- if (TARGET_NO_DIRECT_MODE && name[0] == '*')
- name++;
-
- fprintf (file, "%s", name);
-}
-
-/* A C compound statement to output to stdio stream STREAM the
- assembler syntax for an instruction operand X. X is an RTL
- expression.
-
- CODE is a value that can be used to specify one of several ways
- of printing the operand. It is used when identical operands
- must be printed differently depending on the context. CODE
- comes from the `%' specification that was used to request
- printing of the operand. If the specification was just `%DIGIT'
- then CODE is 0; if the specification was `%LTR DIGIT' then CODE
- is the ASCII code for LTR.
-
- If X is a register, this macro should print the register's name.
- The names can be found in an array `reg_names' whose type is
- `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
-
- When the machine description has a specification `%PUNCT' (a `%'
- followed by a punctuation character), this macro is called with
- a null pointer for X and the punctuation character for CODE.
-
- The M68HC11 specific codes are:
-
- 'b' for the low part of the operand.
- 'h' for the high part of the operand
- The 'b' or 'h' modifiers have no effect if the operand has
- the QImode and is not a S_REG_P (soft register). If the
- operand is a hard register, these two modifiers have no effect.
- 't' generate the temporary scratch register. The operand is
- ignored.
- 'T' generate the low-part temporary scratch register. The operand is
- ignored. */
-
-static void
-m68hc11_print_operand (FILE *file, rtx op, int letter)
-{
- if (letter == 't')
- {
- asm_print_register (file, SOFT_TMP_REGNUM);
- return;
- }
- else if (letter == 'T')
- {
- asm_print_register (file, SOFT_TMP_REGNUM);
- fprintf (file, "+1");
- return;
- }
- else if (letter == '#')
- {
- asm_fprintf (file, "%I");
- }
-
- if (GET_CODE (op) == REG)
- {
- if (letter == 'b' && S_REG_P (op))
- {
- asm_print_register (file, REGNO (op));
- fprintf (file, "+1");
- }
- else if (letter == 'b' && D_REG_P (op))
- {
- asm_print_register (file, HARD_B_REGNUM);
- }
- else
- {
- asm_print_register (file, REGNO (op));
- }
- return;
- }
-
- if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
- {
- if (letter == 'b')
- asm_fprintf (file, "%I%%lo(");
- else
- asm_fprintf (file, "%I%%hi(");
-
- output_addr_const (file, op);
- fprintf (file, ")");
- return;
- }
-
- /* Get the low or high part of the operand when 'b' or 'h' modifiers
- are specified. If we already have a QImode, there is nothing to do. */
- if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
- {
- if (letter == 'b')
- {
- op = m68hc11_gen_lowpart (QImode, op);
- }
- else if (letter == 'h')
- {
- op = m68hc11_gen_highpart (QImode, op);
- }
- }
-
- if (GET_CODE (op) == MEM)
- {
- rtx base = XEXP (op, 0);
- switch (GET_CODE (base))
- {
- case PRE_DEC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- break;
-
- case POST_DEC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- fprintf (file, "-");
- break;
-
- case POST_INC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- fprintf (file, "+");
- break;
-
- case PRE_INC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- break;
-
- case MEM:
- gcc_assert (TARGET_M6812);
- fprintf (file, "[");
- m68hc11_print_operand_address (file, XEXP (base, 0));
- fprintf (file, "]");
- break;
-
- default:
- if (m68hc11_page0_symbol_p (base))
- fprintf (file, "*");
-
- output_address (base);
- break;
- }
- }
- else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
- {
- REAL_VALUE_TYPE r;
- long l;
-
- REAL_VALUE_FROM_CONST_DOUBLE (r, op);
- REAL_VALUE_TO_TARGET_SINGLE (r, l);
- asm_fprintf (file, "%I0x%lx", l);
- }
- else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
- {
- char dstr[30];
-
- real_to_decimal (dstr, CONST_DOUBLE_REAL_VALUE (op),
- sizeof (dstr), 0, 1);
- asm_fprintf (file, "%I0r%s", dstr);
- }
- else
- {
- int need_parenthesize = 0;
-
- if (letter != 'i')
- asm_fprintf (file, "%I");
- else
- need_parenthesize = must_parenthesize (op);
-
- if (need_parenthesize)
- fprintf (file, "(");
-
- output_addr_const (file, op);
- if (need_parenthesize)
- fprintf (file, ")");
- }
-}
-
-/* Returns true if the operand 'op' must be printed with parenthesis
- around it. This must be done only if there is a symbol whose name
- is a processor register. */
-static int
-must_parenthesize (rtx op)
-{
- const char *name;
-
- switch (GET_CODE (op))
- {
- case SYMBOL_REF:
- name = XSTR (op, 0);
- /* Avoid a conflict between symbol name and a possible
- register. */
- return (strcasecmp (name, "a") == 0
- || strcasecmp (name, "b") == 0
- || strcasecmp (name, "d") == 0
- || strcasecmp (name, "x") == 0
- || strcasecmp (name, "y") == 0
- || strcasecmp (name, "ix") == 0
- || strcasecmp (name, "iy") == 0
- || strcasecmp (name, "pc") == 0
- || strcasecmp (name, "sp") == 0
- || strcasecmp (name, "ccr") == 0) ? 1 : 0;
-
- case PLUS:
- case MINUS:
- return must_parenthesize (XEXP (op, 0))
- || must_parenthesize (XEXP (op, 1));
-
- case MEM:
- case CONST:
- case ZERO_EXTEND:
- case SIGN_EXTEND:
- return must_parenthesize (XEXP (op, 0));
-
- case CONST_DOUBLE:
- case CONST_INT:
- case LABEL_REF:
- case CODE_LABEL:
- default:
- return 0;
- }
-}
-
-/* A C compound statement to output to stdio stream STREAM the
- assembler syntax for an instruction operand that is a memory
- reference whose address is ADDR. ADDR is an RTL expression. */
-
-static void
-m68hc11_print_operand_address (FILE *file, rtx addr)
-{
- rtx base;
- rtx offset;
- int need_parenthesis = 0;
-
- switch (GET_CODE (addr))
- {
- case REG:
- gcc_assert (REG_P (addr) && REG_OK_FOR_BASE_STRICT_P (addr));
-
- fprintf (file, "0,");
- asm_print_register (file, REGNO (addr));
- break;
-
- case MEM:
- base = XEXP (addr, 0);
- switch (GET_CODE (base))
- {
- case PRE_DEC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- break;
-
- case POST_DEC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- fprintf (file, "-");
- break;
-
- case POST_INC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- fprintf (file, "+");
- break;
-
- case PRE_INC:
- gcc_assert (TARGET_M6812);
- fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
- asm_print_register (file, REGNO (XEXP (base, 0)));
- break;
-
- default:
- need_parenthesis = must_parenthesize (base);
- if (need_parenthesis)
- fprintf (file, "(");
-
- output_addr_const (file, base);
- if (need_parenthesis)
- fprintf (file, ")");
- break;
- }
- break;
-
- case PLUS:
- base = XEXP (addr, 0);
- offset = XEXP (addr, 1);
- if (!G_REG_P (base) && G_REG_P (offset))
- {
- base = XEXP (addr, 1);
- offset = XEXP (addr, 0);
- }
- if (CONSTANT_ADDRESS_P (base))
- {
- need_parenthesis = must_parenthesize (addr);
-
- gcc_assert (CONSTANT_ADDRESS_P (offset));
- if (need_parenthesis)
- fprintf (file, "(");
-
- output_addr_const (file, base);
- fprintf (file, "+");
- output_addr_const (file, offset);
- if (need_parenthesis)
- fprintf (file, ")");
- }
- else
- {
- gcc_assert (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base));
- if (REG_P (offset))
- {
- gcc_assert (TARGET_M6812);
- asm_print_register (file, REGNO (offset));
- fprintf (file, ",");
- asm_print_register (file, REGNO (base));
- }
- else
- {
- need_parenthesis = must_parenthesize (offset);
- if (need_parenthesis)
- fprintf (file, "(");
-
- output_addr_const (file, offset);
- if (need_parenthesis)
- fprintf (file, ")");
- fprintf (file, ",");
- asm_print_register (file, REGNO (base));
- }
- }
- break;
-
- default:
- if (GET_CODE (addr) == CONST_INT
- && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
- {
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (addr));
- }
- else
- {
- need_parenthesis = must_parenthesize (addr);
- if (need_parenthesis)
- fprintf (file, "(");
-
- output_addr_const (file, addr);
- if (need_parenthesis)
- fprintf (file, ")");
- }
- break;
- }
-}
-
-
-/* Splitting of some instructions. */
-
-static rtx
-m68hc11_expand_compare (enum rtx_code code, rtx op0, rtx op1)
-{
- rtx ret = 0;
-
- gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) != MODE_FLOAT);
- emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
- gen_rtx_COMPARE (VOIDmode, op0, op1)));
- ret = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
-
- return ret;
-}
-
-rtx
-m68hc11_expand_compare_and_branch (enum rtx_code code, rtx op0, rtx op1,
- rtx label)
-{
- rtx tmp;
-
- switch (GET_MODE (op0))
- {
- case QImode:
- case HImode:
- tmp = m68hc11_expand_compare (code, op0, op1);
- tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
- gen_rtx_LABEL_REF (VOIDmode, label),
- pc_rtx);
- emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
- return 0;
-#if 0
-
- /* SCz: from i386.c */
- case SFmode:
- case DFmode:
- /* Don't expand the comparison early, so that we get better code
- when jump or whoever decides to reverse the comparison. */
- {
- rtvec vec;
- int use_fcomi;
-
- code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
- &m68hc11_compare_op1);
-
- tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
- m68hc11_compare_op0, m68hc11_compare_op1);
- tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
- gen_rtx_LABEL_REF (VOIDmode, label),
- pc_rtx);
- tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
-
- use_fcomi = ix86_use_fcomi_compare (code);
- vec = rtvec_alloc (3 + !use_fcomi);
- RTVEC_ELT (vec, 0) = tmp;
- RTVEC_ELT (vec, 1)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
- RTVEC_ELT (vec, 2)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
- if (!use_fcomi)
- RTVEC_ELT (vec, 3)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
-
- emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
- return;
- }
-#endif
-
- case SImode:
- /* Expand SImode branch into multiple compare+branch. */
- {
- rtx lo[2], hi[2], label2;
- enum rtx_code code1, code2, code3;
-
- if (CONSTANT_P (op0) && !CONSTANT_P (op1))
- {
- tmp = op0;
- op0 = op1;
- op1 = tmp;
- code = swap_condition (code);
- }
- lo[0] = m68hc11_gen_lowpart (HImode, op0);
- lo[1] = m68hc11_gen_lowpart (HImode, op1);
- hi[0] = m68hc11_gen_highpart (HImode, op0);
- hi[1] = m68hc11_gen_highpart (HImode, op1);
-
- /* Otherwise, if we are doing less-than, op1 is a constant and the
- low word is zero, then we can just examine the high word. */
-
- if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
- && (code == LT || code == LTU))
- {
- return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
- label);
- }
-
- /* Otherwise, we need two or three jumps. */
-
- label2 = gen_label_rtx ();
-
- code1 = code;
- code2 = swap_condition (code);
- code3 = unsigned_condition (code);
-
- switch (code)
- {
- case LT:
- case GT:
- case LTU:
- case GTU:
- break;
-
- case LE:
- code1 = LT;
- code2 = GT;
- break;
- case GE:
- code1 = GT;
- code2 = LT;
- break;
- case LEU:
- code1 = LTU;
- code2 = GTU;
- break;
- case GEU:
- code1 = GTU;
- code2 = LTU;
- break;
-
- case EQ:
- code1 = UNKNOWN;
- code2 = NE;
- break;
- case NE:
- code2 = UNKNOWN;
- break;
-
- default:
- gcc_unreachable ();
- }
-
- /*
- * a < b =>
- * if (hi(a) < hi(b)) goto true;
- * if (hi(a) > hi(b)) goto false;
- * if (lo(a) < lo(b)) goto true;
- * false:
- */
- if (code1 != UNKNOWN)
- m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
- if (code2 != UNKNOWN)
- m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
-
- m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
-
- if (code2 != UNKNOWN)
- emit_label (label2);
- return 0;
- }
-
- default:
- gcc_unreachable ();
- }
- return 0;
-}
-
-/* Return the increment/decrement mode of a MEM if it is such.
- Return CONST if it is anything else. */
-static int
-autoinc_mode (rtx x)
-{
- if (GET_CODE (x) != MEM)
- return CONST;
-
- x = XEXP (x, 0);
- if (GET_CODE (x) == PRE_INC
- || GET_CODE (x) == PRE_DEC
- || GET_CODE (x) == POST_INC
- || GET_CODE (x) == POST_DEC)
- return GET_CODE (x);
-
- return CONST;
-}
-
-static int
-m68hc11_make_autoinc_notes (rtx *x, void *data)
-{
- rtx insn;
-
- switch (GET_CODE (*x))
- {
- case PRE_DEC:
- case PRE_INC:
- case POST_DEC:
- case POST_INC:
- insn = (rtx) data;
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, XEXP (*x, 0),
- REG_NOTES (insn));
- return -1;
-
- default:
- return 0;
- }
-}
-
-/* Split a DI, SI or HI move into several smaller move operations.
- The scratch register 'scratch' is used as a temporary to load
- store intermediate values. It must be a hard register. */
-void
-m68hc11_split_move (rtx to, rtx from, rtx scratch)
-{
- rtx low_to, low_from;
- rtx high_to, high_from;
- rtx insn;
- enum machine_mode mode;
- int offset = 0;
- int autoinc_from = autoinc_mode (from);
- int autoinc_to = autoinc_mode (to);
-
- mode = GET_MODE (to);
-
- /* If the TO and FROM contain autoinc modes that are not compatible
- together (one pop and the other a push), we must change one to
- an offsetable operand and generate an appropriate add at the end. */
- if (TARGET_M6812 && GET_MODE_SIZE (mode) > 2)
- {
- rtx reg;
- int code;
-
- /* The source uses an autoinc mode which is not compatible with
- a split (this would result in a word swap). */
- if (autoinc_from == PRE_INC || autoinc_from == POST_DEC)
- {
- code = GET_CODE (XEXP (from, 0));
- reg = XEXP (XEXP (from, 0), 0);
- offset = GET_MODE_SIZE (GET_MODE (from));
- if (code == POST_DEC)
- offset = -offset;
-
- if (code == PRE_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
-
- m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
- if (code == POST_DEC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
- return;
- }
-
- /* Likewise for destination. */
- if (autoinc_to == PRE_INC || autoinc_to == POST_DEC)
- {
- code = GET_CODE (XEXP (to, 0));
- reg = XEXP (XEXP (to, 0), 0);
- offset = GET_MODE_SIZE (GET_MODE (to));
- if (code == POST_DEC)
- offset = -offset;
-
- if (code == PRE_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
-
- m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
- if (code == POST_DEC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
- return;
- }
-
- /* The source and destination auto increment modes must be compatible
- with each other: same direction. */
- if ((autoinc_to != autoinc_from
- && autoinc_to != CONST && autoinc_from != CONST)
- /* The destination address register must not be used within
- the source operand because the source address would change
- while doing the copy. */
- || (autoinc_to != CONST
- && reg_mentioned_p (XEXP (XEXP (to, 0), 0), from)
- && !IS_STACK_PUSH (to)))
- {
- /* Must change the destination. */
- code = GET_CODE (XEXP (to, 0));
- reg = XEXP (XEXP (to, 0), 0);
- offset = GET_MODE_SIZE (GET_MODE (to));
- if (code == PRE_DEC || code == POST_DEC)
- offset = -offset;
-
- if (code == PRE_DEC || code == PRE_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
- m68hc11_split_move (gen_rtx_MEM (GET_MODE (to), reg), from, scratch);
- if (code == POST_DEC || code == POST_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
-
- return;
- }
-
- /* Likewise, the source address register must not be used within
- the destination operand. */
- if (autoinc_from != CONST
- && reg_mentioned_p (XEXP (XEXP (from, 0), 0), to)
- && !IS_STACK_PUSH (to))
- {
- /* Must change the source. */
- code = GET_CODE (XEXP (from, 0));
- reg = XEXP (XEXP (from, 0), 0);
- offset = GET_MODE_SIZE (GET_MODE (from));
- if (code == PRE_DEC || code == POST_DEC)
- offset = -offset;
-
- if (code == PRE_DEC || code == PRE_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
- m68hc11_split_move (to, gen_rtx_MEM (GET_MODE (from), reg), scratch);
- if (code == POST_DEC || code == POST_INC)
- emit_insn (gen_addhi3 (reg, reg, GEN_INT (offset)));
-
- return;
- }
- }
-
- if (GET_MODE_SIZE (mode) == 8)
- mode = SImode;
- else if (GET_MODE_SIZE (mode) == 4)
- mode = HImode;
- else
- mode = QImode;
-
- if (TARGET_M6812
- && IS_STACK_PUSH (to)
- && reg_mentioned_p (gen_rtx_REG (HImode, HARD_SP_REGNUM), from))
- {
- if (mode == SImode)
- {
- offset = 4;
- }
- else if (mode == HImode)
- {
- offset = 2;
- }
- else
- offset = 0;
- }
-
- low_to = m68hc11_gen_lowpart (mode, to);
- high_to = m68hc11_gen_highpart (mode, to);
-
- low_from = m68hc11_gen_lowpart (mode, from);
- high_from = m68hc11_gen_highpart (mode, from);
-
- if (offset)
- {
- high_from = adjust_address (high_from, mode, offset);
- low_from = high_from;
- }
-
- /* When copying with a POST_INC mode, we must copy the
- high part and then the low part to guarantee a correct
- 32/64-bit copy. */
- if (TARGET_M6812
- && GET_MODE_SIZE (mode) >= 2
- && autoinc_from != autoinc_to
- && (autoinc_from == POST_INC || autoinc_to == POST_INC))
- {
- rtx swap;
-
- swap = low_to;
- low_to = high_to;
- high_to = swap;
-
- swap = low_from;
- low_from = high_from;
- high_from = swap;
- }
- if (mode == SImode)
- {
- m68hc11_split_move (low_to, low_from, scratch);
- m68hc11_split_move (high_to, high_from, scratch);
- }
- else if (H_REG_P (to) || H_REG_P (from)
- || (low_from == const0_rtx
- && high_from == const0_rtx
- && ! push_operand (to, GET_MODE (to))
- && ! H_REG_P (scratch))
- || (TARGET_M6812
- && (!m68hc11_register_indirect_p (from, GET_MODE (from))
- || m68hc11_small_indexed_indirect_p (from,
- GET_MODE (from)))
- && (!m68hc11_register_indirect_p (to, GET_MODE (to))
- || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
- {
- insn = emit_move_insn (low_to, low_from);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
-
- insn = emit_move_insn (high_to, high_from);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
- }
- else
- {
- insn = emit_move_insn (scratch, low_from);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
- insn = emit_move_insn (low_to, scratch);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
-
- insn = emit_move_insn (scratch, high_from);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
- insn = emit_move_insn (high_to, scratch);
- for_each_rtx (&PATTERN (insn), m68hc11_make_autoinc_notes, insn);
- }
-}
-
-static rtx
-simplify_logical (enum machine_mode mode, int code, rtx operand, rtx *result)
-{
- int val;
- int mask;
-
- *result = 0;
- if (GET_CODE (operand) != CONST_INT)
- return operand;
-
- if (mode == HImode)
- mask = 0x0ffff;
- else
- mask = 0x0ff;
-
- val = INTVAL (operand);
- switch (code)
- {
- case IOR:
- if ((val & mask) == 0)
- return 0;
- if ((val & mask) == mask)
- *result = constm1_rtx;
- break;
-
- case AND:
- if ((val & mask) == 0)
- *result = const0_rtx;
- if ((val & mask) == mask)
- return 0;
- break;
-
- case XOR:
- if ((val & mask) == 0)
- return 0;
- break;
- }
- return operand;
-}
-
-static void
-m68hc11_emit_logical (enum machine_mode mode, enum rtx_code code, rtx *operands)
-{
- rtx result;
- int need_copy;
-
- need_copy = (rtx_equal_p (operands[0], operands[1])
- || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
-
- operands[1] = simplify_logical (mode, code, operands[1], &result);
- operands[2] = simplify_logical (mode, code, operands[2], &result);
-
- if (result && GET_CODE (result) == CONST_INT)
- {
- if (!H_REG_P (operands[0]) && operands[3]
- && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
- {
- emit_move_insn (operands[3], result);
- emit_move_insn (operands[0], operands[3]);
- }
- else
- {
- emit_move_insn (operands[0], result);
- }
- }
- else if (operands[1] != 0 && operands[2] != 0)
- {
- if (!H_REG_P (operands[0]) && operands[3])
- {
- emit_move_insn (operands[3], operands[1]);
- emit_insn (gen_rtx_SET (mode,
- operands[3],
- gen_rtx_fmt_ee (code, mode,
- operands[3], operands[2])));
- emit_move_insn (operands[0], operands[3]);
- }
- else
- {
- emit_insn (gen_rtx_SET (mode, operands[0],
- gen_rtx_fmt_ee (code, mode,
- operands[0], operands[2])));
- }
- }
-
- /* The logical operation is similar to a copy. */
- else if (need_copy)
- {
- rtx src;
-
- if (GET_CODE (operands[1]) == CONST_INT)
- src = operands[2];
- else
- src = operands[1];
-
- if (!H_REG_P (operands[0]) && !H_REG_P (src))
- {
- emit_move_insn (operands[3], src);
- emit_move_insn (operands[0], operands[3]);
- }
- else
- {
- emit_move_insn (operands[0], src);
- }
- }
-}
-
-void
-m68hc11_split_logical (enum machine_mode mode, enum rtx_code code,
- rtx *operands)
-{
- rtx low[4];
- rtx high[4];
-
- low[0] = m68hc11_gen_lowpart (mode, operands[0]);
- low[1] = m68hc11_gen_lowpart (mode, operands[1]);
- low[2] = m68hc11_gen_lowpart (mode, operands[2]);
-
- high[0] = m68hc11_gen_highpart (mode, operands[0]);
- high[1] = m68hc11_gen_highpart (mode, operands[1]);
- high[2] = m68hc11_gen_highpart (mode, operands[2]);
-
- low[3] = operands[3];
- high[3] = operands[3];
- if (mode == SImode)
- {
- m68hc11_split_logical (HImode, code, low);
- m68hc11_split_logical (HImode, code, high);
- return;
- }
-
- m68hc11_emit_logical (mode, code, low);
- m68hc11_emit_logical (mode, code, high);
-}
-
-
-/* Code generation. */
-
-void
-m68hc11_output_swap (rtx insn ATTRIBUTE_UNUSED, rtx operands[])
-{
- /* We have to be careful with the cc_status. An address register swap
- is generated for some comparison. The comparison is made with D
- but the branch really uses the address register. See the split
- pattern for compare. The xgdx/xgdy preserve the flags but after
- the exchange, the flags will reflect to the value of X and not D.
- Tell this by setting the cc_status according to the cc_prev_status. */
- if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
- {
- if (cc_prev_status.value1 != 0
- && (D_REG_P (cc_prev_status.value1)
- || X_REG_P (cc_prev_status.value1)))
- {
- cc_status = cc_prev_status;
- if (D_REG_P (cc_status.value1))
- cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
- HARD_X_REGNUM);
- else
- cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
- HARD_D_REGNUM);
- }
- else
- CC_STATUS_INIT;
-
- output_asm_insn ("xgdx", operands);
- }
- else
- {
- if (cc_prev_status.value1 != 0
- && (D_REG_P (cc_prev_status.value1)
- || Y_REG_P (cc_prev_status.value1)))
- {
- cc_status = cc_prev_status;
- if (D_REG_P (cc_status.value1))
- cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
- HARD_Y_REGNUM);
- else
- cc_status.value1 = gen_rtx_REG (GET_MODE (cc_status.value1),
- HARD_D_REGNUM);
- }
- else
- CC_STATUS_INIT;
-
- output_asm_insn ("xgdy", operands);
- }
-}
-
-/* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
- This is used to decide whether a move that set flags should be used
- instead. */
-int
-next_insn_test_reg (rtx insn, rtx reg)
-{
- rtx body;
-
- insn = next_nonnote_insn (insn);
- if (GET_CODE (insn) != INSN)
- return 0;
-
- body = PATTERN (insn);
- if (sets_cc0_p (body) != 1)
- return 0;
-
- if (rtx_equal_p (XEXP (body, 1), reg) == 0)
- return 0;
-
- return 1;
-}
-
-/* Generate the code to move a 16-bit operand into another one. */
-
-void
-m68hc11_gen_movhi (rtx insn, rtx *operands)
-{
- int reg;
-
- /* Move a register or memory to the same location.
- This is possible because such insn can appear
- in a non-optimizing mode. */
- if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
- {
- cc_status = cc_prev_status;
- return;
- }
-
- if (TARGET_M6812)
- {
- rtx from = operands[1];
- rtx to = operands[0];
-
- if (IS_STACK_PUSH (to) && H_REG_P (from))
- {
- cc_status = cc_prev_status;
- switch (REGNO (from))
- {
- case HARD_X_REGNUM:
- case HARD_Y_REGNUM:
- case HARD_D_REGNUM:
- output_asm_insn ("psh%1", operands);
- break;
- case HARD_SP_REGNUM:
- output_asm_insn ("sts\t2,-sp", operands);
- break;
- default:
- gcc_unreachable ();
- }
- return;
- }
- if (IS_STACK_POP (from) && H_REG_P (to))
- {
- cc_status = cc_prev_status;
- switch (REGNO (to))
- {
- case HARD_X_REGNUM:
- case HARD_Y_REGNUM:
- case HARD_D_REGNUM:
- output_asm_insn ("pul%0", operands);
- break;
- default:
- gcc_unreachable ();
- }
- return;
- }
- if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("tfr\t%1,%0", operands);
- }
- else if (H_REG_P (operands[0]))
- {
- if (SP_REG_P (operands[0]))
- output_asm_insn ("lds\t%1", operands);
- else
- output_asm_insn ("ld%0\t%1", operands);
- }
- else if (H_REG_P (operands[1]))
- {
- if (SP_REG_P (operands[1]))
- output_asm_insn ("sts\t%0", operands);
- else
- output_asm_insn ("st%1\t%0", operands);
- }
-
- /* The 68hc12 does not support (MEM:HI (MEM:HI)) with the movw
- instruction. We have to use a scratch register as temporary location.
- Trying to use a specific pattern or constrain failed. */
- else if (GET_CODE (to) == MEM && GET_CODE (XEXP (to, 0)) == MEM)
- {
- rtx ops[4];
-
- ops[0] = to;
- ops[2] = from;
- ops[3] = 0;
- if (dead_register_here (insn, d_reg))
- ops[1] = d_reg;
- else if (dead_register_here (insn, ix_reg))
- ops[1] = ix_reg;
- else if (dead_register_here (insn, iy_reg))
- ops[1] = iy_reg;
- else
- {
- ops[1] = d_reg;
- ops[3] = d_reg;
- output_asm_insn ("psh%3", ops);
- }
-
- ops[0] = to;
- ops[2] = from;
- output_asm_insn ("ld%1\t%2", ops);
- output_asm_insn ("st%1\t%0", ops);
- if (ops[3])
- output_asm_insn ("pul%3", ops);
- }
-
- /* Use movw for non-null constants or when we are clearing
- a volatile memory reference. However, this is possible
- only if the memory reference has a small offset or is an
- absolute address. */
- else if (GET_CODE (from) == CONST_INT
- && INTVAL (from) == 0
- && (MEM_VOLATILE_P (to) == 0
- || m68hc11_small_indexed_indirect_p (to, HImode) == 0))
- {
- output_asm_insn ("clr\t%h0", operands);
- output_asm_insn ("clr\t%b0", operands);
- }
- else
- {
- if ((m68hc11_register_indirect_p (from, GET_MODE (from))
- && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
- || (m68hc11_register_indirect_p (to, GET_MODE (to))
- && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
- {
- rtx ops[3];
-
- if (operands[2])
- {
- ops[0] = operands[2];
- ops[1] = from;
- ops[2] = 0;
- m68hc11_gen_movhi (insn, ops);
- ops[0] = to;
- ops[1] = operands[2];
- m68hc11_gen_movhi (insn, ops);
- return;
- }
- else
- {
- /* !!!! SCz wrong here. */
- fatal_insn ("move insn not handled", insn);
- }
- }
- else
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("movw\t%1,%0", operands);
- }
- }
- return;
- }
-
- if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
- {
- cc_status = cc_prev_status;
- switch (REGNO (operands[0]))
- {
- case HARD_X_REGNUM:
- case HARD_Y_REGNUM:
- output_asm_insn ("pul%0", operands);
- break;
- case HARD_D_REGNUM:
- output_asm_insn ("pula", operands);
- output_asm_insn ("pulb", operands);
- break;
- default:
- gcc_unreachable ();
- }
- return;
- }
- /* Some moves to a hard register are special. Not all of them
- are really supported and we have to use a temporary
- location to provide them (either the stack of a temp var). */
- if (H_REG_P (operands[0]))
- {
- switch (REGNO (operands[0]))
- {
- case HARD_D_REGNUM:
- if (X_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else if (next_insn_test_reg (insn, operands[0]))
- {
- output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
- }
- else
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
- }
- }
- else if (Y_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else
- {
- /* %t means *ZTMP scratch register. */
- output_asm_insn ("sty\t%t1", operands);
- output_asm_insn ("ldd\t%t1", operands);
- }
- }
- else if (SP_REG_P (operands[1]))
- {
- CC_STATUS_INIT;
- if (ix_reg == 0)
- create_regs_rtx ();
- if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
- output_asm_insn ("xgdx", operands);
- output_asm_insn ("tsx", operands);
- output_asm_insn ("xgdx", operands);
- }
- else if (IS_STACK_POP (operands[1]))
- {
- output_asm_insn ("pula\n\tpulb", operands);
- }
- else if (GET_CODE (operands[1]) == CONST_INT
- && INTVAL (operands[1]) == 0)
- {
- output_asm_insn ("clra\n\tclrb", operands);
- }
- else
- {
- output_asm_insn ("ldd\t%1", operands);
- }
- break;
-
- case HARD_X_REGNUM:
- if (D_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else if (next_insn_test_reg (insn, operands[0]))
- {
- output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
- }
- else
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("pshb", operands);
- output_asm_insn ("psha", operands);
- output_asm_insn ("pulx", operands);
- }
- }
- else if (Y_REG_P (operands[1]))
- {
- /* When both D and Y are dead, use the sequence xgdy, xgdx
- to move Y into X. The D and Y registers are modified. */
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM)
- && dead_register_here (insn, d_reg))
- {
- output_asm_insn ("xgdy", operands);
- output_asm_insn ("xgdx", operands);
- CC_STATUS_INIT;
- }
- else if (!optimize_size)
- {
- output_asm_insn ("sty\t%t1", operands);
- output_asm_insn ("ldx\t%t1", operands);
- }
- else
- {
- CC_STATUS_INIT;
- output_asm_insn ("pshy", operands);
- output_asm_insn ("pulx", operands);
- }
- }
- else if (SP_REG_P (operands[1]))
- {
- /* tsx, tsy preserve the flags */
- cc_status = cc_prev_status;
- output_asm_insn ("tsx", operands);
- }
- else
- {
- output_asm_insn ("ldx\t%1", operands);
- }
- break;
-
- case HARD_Y_REGNUM:
- if (D_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else
- {
- output_asm_insn ("std\t%t1", operands);
- output_asm_insn ("ldy\t%t1", operands);
- }
- }
- else if (X_REG_P (operands[1]))
- {
- /* When both D and X are dead, use the sequence xgdx, xgdy
- to move X into Y. The D and X registers are modified. */
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM)
- && dead_register_here (insn, d_reg))
- {
- output_asm_insn ("xgdx", operands);
- output_asm_insn ("xgdy", operands);
- CC_STATUS_INIT;
- }
- else if (!optimize_size)
- {
- output_asm_insn ("stx\t%t1", operands);
- output_asm_insn ("ldy\t%t1", operands);
- }
- else
- {
- CC_STATUS_INIT;
- output_asm_insn ("pshx", operands);
- output_asm_insn ("puly", operands);
- }
- }
- else if (SP_REG_P (operands[1]))
- {
- /* tsx, tsy preserve the flags */
- cc_status = cc_prev_status;
- output_asm_insn ("tsy", operands);
- }
- else
- {
- output_asm_insn ("ldy\t%1", operands);
- }
- break;
-
- case HARD_SP_REGNUM:
- if (D_REG_P (operands[1]))
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("xgdx", operands);
- output_asm_insn ("txs", operands);
- output_asm_insn ("xgdx", operands);
- }
- else if (X_REG_P (operands[1]))
- {
- /* tys, txs preserve the flags */
- cc_status = cc_prev_status;
- output_asm_insn ("txs", operands);
- }
- else if (Y_REG_P (operands[1]))
- {
- /* tys, txs preserve the flags */
- cc_status = cc_prev_status;
- output_asm_insn ("tys", operands);
- }
- else
- {
- /* lds sets the flags but the des does not. */
- CC_STATUS_INIT;
- output_asm_insn ("lds\t%1", operands);
- output_asm_insn ("des", operands);
- }
- break;
-
- default:
- fatal_insn ("invalid register in the move instruction", insn);
- break;
- }
- return;
- }
- if (SP_REG_P (operands[1]) && REG_P (operands[0])
- && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
- {
- output_asm_insn ("sts\t%0", operands);
- return;
- }
-
- if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
- {
- cc_status = cc_prev_status;
- switch (REGNO (operands[1]))
- {
- case HARD_X_REGNUM:
- case HARD_Y_REGNUM:
- output_asm_insn ("psh%1", operands);
- break;
- case HARD_D_REGNUM:
- output_asm_insn ("pshb", operands);
- output_asm_insn ("psha", operands);
- break;
- default:
- gcc_unreachable ();
- }
- return;
- }
-
- /* Operand 1 must be a hard register. */
- if (!H_REG_P (operands[1]))
- {
- fatal_insn ("invalid operand in the instruction", insn);
- }
-
- reg = REGNO (operands[1]);
- switch (reg)
- {
- case HARD_D_REGNUM:
- output_asm_insn ("std\t%0", operands);
- break;
-
- case HARD_X_REGNUM:
- output_asm_insn ("stx\t%0", operands);
- break;
-
- case HARD_Y_REGNUM:
- output_asm_insn ("sty\t%0", operands);
- break;
-
- case HARD_SP_REGNUM:
- if (ix_reg == 0)
- create_regs_rtx ();
-
- if (REG_P (operands[0]) && REGNO (operands[0]) == SOFT_TMP_REGNUM)
- {
- output_asm_insn ("pshx", operands);
- output_asm_insn ("tsx", operands);
- output_asm_insn ("inx", operands);
- output_asm_insn ("inx", operands);
- output_asm_insn ("stx\t%0", operands);
- output_asm_insn ("pulx", operands);
- }
-
- else if (reg_mentioned_p (ix_reg, operands[0]))
- {
- output_asm_insn ("sty\t%t0", operands);
- output_asm_insn ("tsy", operands);
- output_asm_insn ("sty\t%0", operands);
- output_asm_insn ("ldy\t%t0", operands);
- }
- else
- {
- output_asm_insn ("stx\t%t0", operands);
- output_asm_insn ("tsx", operands);
- output_asm_insn ("stx\t%0", operands);
- output_asm_insn ("ldx\t%t0", operands);
- }
- CC_STATUS_INIT;
- break;
-
- default:
- fatal_insn ("invalid register in the move instruction", insn);
- break;
- }
-}
-
-void
-m68hc11_gen_movqi (rtx insn, rtx *operands)
-{
- /* Move a register or memory to the same location.
- This is possible because such insn can appear
- in a non-optimizing mode. */
- if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
- {
- cc_status = cc_prev_status;
- return;
- }
-
- if (TARGET_M6812)
- {
-
- if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("tfr\t%1,%0", operands);
- }
- else if (H_REG_P (operands[0]))
- {
- if (IS_STACK_POP (operands[1]))
- output_asm_insn ("pul%b0", operands);
- else if (Q_REG_P (operands[0]))
- output_asm_insn ("lda%0\t%b1", operands);
- else if (D_REG_P (operands[0]))
- output_asm_insn ("ldab\t%b1", operands);
- else
- goto m6811_move;
- }
- else if (H_REG_P (operands[1]))
- {
- if (Q_REG_P (operands[1]))
- output_asm_insn ("sta%1\t%b0", operands);
- else if (D_REG_P (operands[1]))
- output_asm_insn ("stab\t%b0", operands);
- else
- goto m6811_move;
- }
- else
- {
- rtx from = operands[1];
- rtx to = operands[0];
-
- if ((m68hc11_register_indirect_p (from, GET_MODE (from))
- && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
- || (m68hc11_register_indirect_p (to, GET_MODE (to))
- && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
- {
- rtx ops[3];
-
- if (operands[2])
- {
- ops[0] = operands[2];
- ops[1] = from;
- ops[2] = 0;
- m68hc11_gen_movqi (insn, ops);
- ops[0] = to;
- ops[1] = operands[2];
- m68hc11_gen_movqi (insn, ops);
- }
- else
- {
- /* !!!! SCz wrong here. */
- fatal_insn ("move insn not handled", insn);
- }
- }
- else
- {
- if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
- {
- output_asm_insn ("clr\t%b0", operands);
- }
- else
- {
- m68hc11_notice_keep_cc (operands[0]);
- output_asm_insn ("movb\t%b1,%b0", operands);
- }
- }
- }
- return;
- }
-
- m6811_move:
- if (H_REG_P (operands[0]))
- {
- switch (REGNO (operands[0]))
- {
- case HARD_B_REGNUM:
- case HARD_D_REGNUM:
- if (X_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else
- {
- output_asm_insn ("stx\t%t1", operands);
- output_asm_insn ("ldab\t%T0", operands);
- }
- }
- else if (Y_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else
- {
- output_asm_insn ("sty\t%t1", operands);
- output_asm_insn ("ldab\t%T0", operands);
- }
- }
- else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
- && !DA_REG_P (operands[1]))
- {
- output_asm_insn ("ldab\t%b1", operands);
- }
- else if (DA_REG_P (operands[1]))
- {
- output_asm_insn ("tab", operands);
- }
- else
- {
- cc_status = cc_prev_status;
- return;
- }
- break;
-
- case HARD_A_REGNUM:
- if (X_REG_P (operands[1]))
- {
- output_asm_insn ("stx\t%t1", operands);
- output_asm_insn ("ldaa\t%T0", operands);
- }
- else if (Y_REG_P (operands[1]))
- {
- output_asm_insn ("sty\t%t1", operands);
- output_asm_insn ("ldaa\t%T0", operands);
- }
- else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
- && !DA_REG_P (operands[1]))
- {
- output_asm_insn ("ldaa\t%b1", operands);
- }
- else if (!DA_REG_P (operands[1]))
- {
- output_asm_insn ("tba", operands);
- }
- else
- {
- cc_status = cc_prev_status;
- }
- break;
-
- case HARD_X_REGNUM:
- if (D_REG_P (operands[1]))
- {
- if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
- {
- m68hc11_output_swap (insn, operands);
- }
- else
- {
- output_asm_insn ("stab\t%T1", operands);
- output_asm_insn ("ldx\t%t1", operands);
- }
- CC_STATUS_INIT;
- }
- else if (Y_REG_P (operands[1]))
- {
- output_asm_insn ("sty\t%t0", operands);
- output_asm_insn ("ldx\t%t0", operands);
- }
- else if (GET_CODE (operands[1]) == CONST_INT)
- {
- output_asm_insn ("ldx\t%1", operands);
- }
- else if (dead_register_here (insn, d_reg))
- {
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("xgdx", operands);
- }
- else if (!reg_mentioned_p (operands[0], operands[1]))
- {
- output_asm_insn ("xgdx", operands);
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("xgdx", operands);
- }
- else
- {
- output_asm_insn ("pshb", operands);
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("stab\t%T1", operands);
- output_asm_insn ("ldx\t%t1", operands);
- output_asm_insn ("pulb", operands);
- CC_STATUS_INIT;
- }
- break;
-
- case HARD_Y_REGNUM:
- if (D_REG_P (operands[1]))
- {
- output_asm_insn ("stab\t%T1", operands);
- output_asm_insn ("ldy\t%t1", operands);
- CC_STATUS_INIT;
- }
- else if (X_REG_P (operands[1]))
- {
- output_asm_insn ("stx\t%t1", operands);
- output_asm_insn ("ldy\t%t1", operands);
- CC_STATUS_INIT;
- }
- else if (GET_CODE (operands[1]) == CONST_INT)
- {
- output_asm_insn ("ldy\t%1", operands);
- }
- else if (dead_register_here (insn, d_reg))
- {
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("xgdy", operands);
- }
- else if (!reg_mentioned_p (operands[0], operands[1]))
- {
- output_asm_insn ("xgdy", operands);
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("xgdy", operands);
- }
- else
- {
- output_asm_insn ("pshb", operands);
- output_asm_insn ("ldab\t%b1", operands);
- output_asm_insn ("stab\t%T1", operands);
- output_asm_insn ("ldy\t%t1", operands);
- output_asm_insn ("pulb", operands);
- CC_STATUS_INIT;
- }
- break;
-
- default:
- fatal_insn ("invalid register in the instruction", insn);
- break;
- }
- }
- else if (H_REG_P (operands[1]))
- {
- switch (REGNO (operands[1]))
- {
- case HARD_D_REGNUM:
- case HARD_B_REGNUM:
- output_asm_insn ("stab\t%b0", operands);
- break;
-
- case HARD_A_REGNUM:
- output_asm_insn ("staa\t%b0", operands);
- break;
-
- case HARD_X_REGNUM:
- output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
- break;
-
- case HARD_Y_REGNUM:
- output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
- break;
-
- default:
- fatal_insn ("invalid register in the move instruction", insn);
- break;
- }
- return;
- }
- else
- {
- fatal_insn ("operand 1 must be a hard register", insn);
- }
-}
-
-/* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
- The source and destination must be D or A and the shift must
- be a constant. */
-void
-m68hc11_gen_rotate (enum rtx_code code, rtx insn, rtx operands[])
-{
- int val;
-
- if (GET_CODE (operands[2]) != CONST_INT
- || (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
- fatal_insn ("invalid rotate insn", insn);
-
- val = INTVAL (operands[2]);
- if (code == ROTATERT)
- val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
-
- if (GET_MODE (operands[0]) != QImode)
- CC_STATUS_INIT;
-
- /* Rotate by 8-bits if the shift is within [5..11]. */
- if (val >= 5 && val <= 11)
- {
- if (TARGET_M6812)
- output_asm_insn ("exg\ta,b", operands);
- else
- {
- output_asm_insn ("psha", operands);
- output_asm_insn ("tba", operands);
- output_asm_insn ("pulb", operands);
- }
- val -= 8;
- }
-
- /* If the shift is big, invert the rotation. */
- else if (val >= 12)
- {
- val = val - 16;
- }
-
- if (val > 0)
- {
- while (--val >= 0)
- {
- /* Set the carry to bit-15, but don't change D yet. */
- if (GET_MODE (operands[0]) != QImode)
- {
- output_asm_insn ("asra", operands);
- output_asm_insn ("rola", operands);
- }
-
- /* Rotate B first to move the carry to bit-0. */
- if (D_REG_P (operands[0]))
- output_asm_insn ("rolb", operands);
-
- if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
- output_asm_insn ("rola", operands);
- }
- }
- else
- {
- while (++val <= 0)
- {
- /* Set the carry to bit-8 of D. */
- if (GET_MODE (operands[0]) != QImode)
- output_asm_insn ("tap", operands);
-
- /* Rotate B first to move the carry to bit-7. */
- if (D_REG_P (operands[0]))
- output_asm_insn ("rorb", operands);
-
- if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
- output_asm_insn ("rora", operands);
- }
- }
-}
-
-
-
-/* Store in cc_status the expressions that the condition codes will
- describe after execution of an instruction whose pattern is EXP.
- Do not alter them if the instruction would not alter the cc's. */
-
-void
-m68hc11_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
-{
- /* recognize SET insn's. */
- if (GET_CODE (exp) == SET)
- {
- /* Jumps do not alter the cc's. */
- if (SET_DEST (exp) == pc_rtx)
- ;
-
- /* NOTE: most instructions don't affect the carry bit, but the
- bhi/bls/bhs/blo instructions use it. This isn't mentioned in
- the conditions.h header. */
-
- /* Function calls clobber the cc's. */
- else if (GET_CODE (SET_SRC (exp)) == CALL)
- {
- CC_STATUS_INIT;
- }
-
- /* Tests and compares set the cc's in predictable ways. */
- else if (SET_DEST (exp) == cc0_rtx)
- {
- cc_status.flags = 0;
- cc_status.value1 = XEXP (exp, 0);
- if (GET_CODE (XEXP (exp, 1)) == COMPARE
- && XEXP (XEXP (exp, 1), 1) == CONST0_RTX (GET_MODE (XEXP (XEXP (exp, 1), 0))))
- cc_status.value2 = XEXP (XEXP (exp, 1), 0);
- else
- cc_status.value2 = XEXP (exp, 1);
- }
- else
- {
- /* All other instructions affect the condition codes. */
- cc_status.flags = 0;
- cc_status.value1 = XEXP (exp, 0);
- cc_status.value2 = XEXP (exp, 1);
- }
- }
- else
- {
- /* Default action if we haven't recognized something
- and returned earlier. */
- CC_STATUS_INIT;
- }
-
- if (cc_status.value2 != 0)
- switch (GET_CODE (cc_status.value2))
- {
- /* These logical operations can generate several insns.
- The flags are setup according to what is generated. */
- case IOR:
- case XOR:
- case AND:
- break;
-
- /* The (not ...) generates several 'com' instructions for
- non QImode. We have to invalidate the flags. */
- case NOT:
- if (GET_MODE (cc_status.value2) != QImode)
- CC_STATUS_INIT;
- break;
-
- case PLUS:
- case MINUS:
- case MULT:
- case DIV:
- case UDIV:
- case MOD:
- case UMOD:
- case NEG:
- if (GET_MODE (cc_status.value2) != VOIDmode)
- cc_status.flags |= CC_NO_OVERFLOW;
- break;
-
- /* The asl sets the overflow bit in such a way that this
- makes the flags unusable for a next compare insn. */
- case ASHIFT:
- case ROTATE:
- case ROTATERT:
- if (GET_MODE (cc_status.value2) != VOIDmode)
- cc_status.flags |= CC_NO_OVERFLOW;
- break;
-
- /* A load/store instruction does not affect the carry. */
- case MEM:
- case SYMBOL_REF:
- case REG:
- case CONST_INT:
- cc_status.flags |= CC_NO_OVERFLOW;
- break;
-
- default:
- break;
- }
- if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
- && cc_status.value2
- && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
- cc_status.value2 = 0;
-
- else if (cc_status.value1 && side_effects_p (cc_status.value1))
- cc_status.value1 = 0;
-
- else if (cc_status.value2 && side_effects_p (cc_status.value2))
- cc_status.value2 = 0;
-}
-
-/* The current instruction does not affect the flags but changes
- the register 'reg'. See if the previous flags can be kept for the
- next instruction to avoid a comparison. */
-void
-m68hc11_notice_keep_cc (rtx reg)
-{
- if (reg == 0
- || cc_prev_status.value1 == 0
- || rtx_equal_p (reg, cc_prev_status.value1)
- || (cc_prev_status.value2
- && reg_mentioned_p (reg, cc_prev_status.value2)))
- CC_STATUS_INIT;
- else
- cc_status = cc_prev_status;
-}
-
-
-
-/* Machine Specific Reorg. */
-
-/* Z register replacement:
-
- GCC treats the Z register as an index base address register like
- X or Y. In general, it uses it during reload to compute the address
- of some operand. This helps the reload pass to avoid to fall into the
- register spill failure.
-
- The Z register is in the A_REGS class. In the machine description,
- the 'A' constraint matches it. The 'x' or 'y' constraints do not.
-
- It can appear everywhere an X or Y register can appear, except for
- some templates in the clobber section (when a clobber of X or Y is asked).
- For a given instruction, the template must ensure that no more than
- 2 'A' registers are used. Otherwise, the register replacement is not
- possible.
-
- To replace the Z register, the algorithm is not terrific:
- 1. Insns that do not use the Z register are not changed
- 2. When a Z register is used, we scan forward the insns to see
- a potential register to use: either X or Y and sometimes D.
- We stop when a call, a label or a branch is seen, or when we
- detect that both X and Y are used (probably at different times, but it does
- not matter).
- 3. The register that will be used for the replacement of Z is saved
- in a .page0 register or on the stack. If the first instruction that
- used Z, uses Z as an input, the value is loaded from another .page0
- register. The replacement register is pushed on the stack in the
- rare cases where a compare insn uses Z and we couldn't find if X/Y
- are dead.
- 4. The Z register is replaced in all instructions until we reach
- the end of the Z-block, as detected by step 2.
- 5. If we detect that Z is still alive, its value is saved.
- If the replacement register is alive, its old value is loaded.
-
- The Z register can be disabled with -ffixed-z.
-*/
-
-struct replace_info
-{
- rtx first;
- rtx replace_reg;
- int need_save_z;
- int must_load_z;
- int must_save_reg;
- int must_restore_reg;
- rtx last;
- int regno;
- int x_used;
- int y_used;
- int can_use_d;
- int found_call;
- int z_died;
- int z_set_count;
- rtx z_value;
- int must_push_reg;
- int save_before_last;
- int z_loaded_with_sp;
-};
-
-static int m68hc11_check_z_replacement (rtx, struct replace_info *);
-static void m68hc11_find_z_replacement (rtx, struct replace_info *);
-static void m68hc11_z_replacement (rtx);
-static void m68hc11_reassign_regs (rtx);
-
-int z_replacement_completed = 0;
-
-/* Analyze the insn to find out which replacement register to use and
- the boundaries of the replacement.
- Returns 0 if we reached the last insn to be replaced, 1 if we can
- continue replacement in next insns. */
-
-static int
-m68hc11_check_z_replacement (rtx insn, struct replace_info *info)
-{
- int this_insn_uses_ix;
- int this_insn_uses_iy;
- int this_insn_uses_z;
- int this_insn_uses_z_in_dst;
- int this_insn_uses_d;
- rtx body;
- int z_dies_here;
-
- /* A call is said to clobber the Z register, we don't need
- to save the value of Z. We also don't need to restore
- the replacement register (unless it is used by the call). */
- if (GET_CODE (insn) == CALL_INSN)
- {
- body = PATTERN (insn);
-
- info->can_use_d = 0;
-
- /* If the call is an indirect call with Z, we have to use the
- Y register because X can be used as an input (D+X).
- We also must not save Z nor restore Y. */
- if (reg_mentioned_p (z_reg, body))
- {
- insn = NEXT_INSN (insn);
- info->x_used = 1;
- info->y_used = 0;
- info->found_call = 1;
- info->must_restore_reg = 0;
- info->last = NEXT_INSN (insn);
- }
- info->need_save_z = 0;
- return 0;
- }
- if (GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
- return 0;
-
- if (GET_CODE (insn) == JUMP_INSN)
- {
- if (reg_mentioned_p (z_reg, insn) == 0)
- return 0;
-
- info->can_use_d = 0;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- info->need_save_z = 0;
- info->last = NEXT_INSN (insn);
- return 0;
- }
- if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
- {
- return 1;
- }
-
- /* Z register dies here. */
- z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == SET)
- {
- rtx src = XEXP (body, 1);
- rtx dst = XEXP (body, 0);
-
- /* Condition code is set here. We have to restore the X/Y and
- save into Z before any test/compare insn because once we save/restore
- we can change the condition codes. When the compare insn uses Z and
- we can't use X/Y, the comparison is made with the *ZREG soft register
- (this is supported by cmphi, cmpqi, tsthi, tstqi patterns). */
- if (dst == cc0_rtx)
- {
- if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
- || (GET_CODE (src) == COMPARE &&
- ((rtx_equal_p (XEXP (src, 0), z_reg)
- && H_REG_P (XEXP (src, 1)))
- || (rtx_equal_p (XEXP (src, 1), z_reg)
- && H_REG_P (XEXP (src, 0))))))
- {
- if (insn == info->first)
- {
- info->must_load_z = 0;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- info->need_save_z = 0;
- info->found_call = 1;
- info->regno = SOFT_Z_REGNUM;
- info->last = NEXT_INSN (insn);
- }
- return 0;
- }
- if (reg_mentioned_p (z_reg, src) == 0)
- {
- info->can_use_d = 0;
- return 0;
- }
-
- if (insn != info->first)
- return 0;
-
- /* Compare insn which uses Z. We have to save/restore the X/Y
- register without modifying the condition codes. For this
- we have to use a push/pop insn. */
- info->must_push_reg = 1;
- info->last = insn;
- }
-
- /* Z reg is set to something new. We don't need to load it. */
- if (Z_REG_P (dst))
- {
- if (!reg_mentioned_p (z_reg, src))
- {
- /* Z reg is used before being set. Treat this as
- a new sequence of Z register replacement. */
- if (insn != info->first)
- {
- return 0;
- }
- info->must_load_z = 0;
- }
- info->z_set_count++;
- info->z_value = src;
- if (SP_REG_P (src))
- info->z_loaded_with_sp = 1;
- }
- else if (reg_mentioned_p (z_reg, dst))
- info->can_use_d = 0;
-
- this_insn_uses_d = reg_mentioned_p (d_reg, src)
- | reg_mentioned_p (d_reg, dst);
- this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
- | reg_mentioned_p (ix_reg, dst);
- this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
- | reg_mentioned_p (iy_reg, dst);
- this_insn_uses_z = reg_mentioned_p (z_reg, src);
-
- /* If z is used as an address operand (like (MEM (reg z))),
- we can't replace it with d. */
- if (this_insn_uses_z && !Z_REG_P (src)
- && !(m68hc11_arith_operator (src, GET_MODE (src))
- && Z_REG_P (XEXP (src, 0))
- && !reg_mentioned_p (z_reg, XEXP (src, 1))
- && insn == info->first
- && dead_register_here (insn, d_reg)))
- info->can_use_d = 0;
-
- this_insn_uses_z_in_dst = reg_mentioned_p (z_reg, dst);
- if (TARGET_M6812 && !z_dies_here
- && ((this_insn_uses_z && side_effects_p (src))
- || (this_insn_uses_z_in_dst && side_effects_p (dst))))
- {
- info->need_save_z = 1;
- info->z_set_count++;
- }
- this_insn_uses_z |= this_insn_uses_z_in_dst;
-
- if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
- {
- fatal_insn ("registers IX, IY and Z used in the same INSN", insn);
- }
-
- if (this_insn_uses_d)
- info->can_use_d = 0;
-
- /* IX and IY are used at the same time, we have to restore
- the value of the scratch register before this insn. */
- if (this_insn_uses_ix && this_insn_uses_iy)
- {
- return 0;
- }
-
- if (this_insn_uses_ix && X_REG_P (dst) && GET_MODE (dst) == SImode)
- info->can_use_d = 0;
-
- if (info->x_used == 0 && this_insn_uses_ix)
- {
- if (info->y_used)
- {
- /* We have a (set (REG:HI X) (REG:HI Z)).
- Since we use Z as the replacement register, this insn
- is no longer necessary. We turn it into a note. We must
- not reload the old value of X. */
- if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
- {
- if (z_dies_here)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- }
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- info->found_call = 1;
- info->can_use_d = 0;
- SET_INSN_DELETED (insn);
- info->last = NEXT_INSN (insn);
- return 0;
- }
-
- if (X_REG_P (dst)
- && (rtx_equal_p (src, z_reg)
- || (z_dies_here && !reg_mentioned_p (ix_reg, src))))
- {
- if (z_dies_here)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- }
- info->last = NEXT_INSN (insn);
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
- && !reg_mentioned_p (ix_reg, src))
- {
- if (z_dies_here)
- {
- info->z_died = 1;
- info->need_save_z = 0;
- }
- else if (TARGET_M6812 && side_effects_p (src))
- {
- info->last = 0;
- info->must_restore_reg = 0;
- return 0;
- }
- else
- {
- info->save_before_last = 1;
- }
- info->must_restore_reg = 0;
- info->last = NEXT_INSN (insn);
- }
- else if (info->can_use_d)
- {
- info->last = NEXT_INSN (insn);
- info->x_used = 1;
- }
- return 0;
- }
- info->x_used = 1;
- if (z_dies_here && !reg_mentioned_p (ix_reg, src)
- && GET_CODE (dst) == REG && REGNO (dst) == HARD_X_REGNUM)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- info->last = NEXT_INSN (insn);
- info->regno = HARD_X_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- return 0;
- }
- if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, ix_reg))
- {
- info->regno = HARD_X_REGNUM;
- info->must_restore_reg = 0;
- info->must_save_reg = 0;
- return 0;
- }
- }
- if (info->y_used == 0 && this_insn_uses_iy)
- {
- if (info->x_used)
- {
- if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
- {
- if (z_dies_here)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- }
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- info->found_call = 1;
- info->can_use_d = 0;
- SET_INSN_DELETED (insn);
- info->last = NEXT_INSN (insn);
- return 0;
- }
-
- if (Y_REG_P (dst)
- && (rtx_equal_p (src, z_reg)
- || (z_dies_here && !reg_mentioned_p (iy_reg, src))))
- {
- if (z_dies_here)
- {
- info->z_died = 1;
- info->need_save_z = 0;
- }
- info->last = NEXT_INSN (insn);
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
- && !reg_mentioned_p (iy_reg, src))
- {
- if (z_dies_here)
- {
- info->z_died = 1;
- info->need_save_z = 0;
- }
- else if (TARGET_M6812 && side_effects_p (src))
- {
- info->last = 0;
- info->must_restore_reg = 0;
- return 0;
- }
- else
- {
- info->save_before_last = 1;
- }
- info->must_restore_reg = 0;
- info->last = NEXT_INSN (insn);
- }
- else if (info->can_use_d)
- {
- info->last = NEXT_INSN (insn);
- info->y_used = 1;
- }
-
- return 0;
- }
- info->y_used = 1;
- if (z_dies_here && !reg_mentioned_p (iy_reg, src)
- && GET_CODE (dst) == REG && REGNO (dst) == HARD_Y_REGNUM)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- info->last = NEXT_INSN (insn);
- info->regno = HARD_Y_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- return 0;
- }
- if (rtx_equal_p (src, z_reg) && rtx_equal_p (dst, iy_reg))
- {
- info->regno = HARD_Y_REGNUM;
- info->must_restore_reg = 0;
- info->must_save_reg = 0;
- return 0;
- }
- }
- if (z_dies_here)
- {
- info->need_save_z = 0;
- info->z_died = 1;
- if (info->last == 0)
- info->last = NEXT_INSN (insn);
- return 0;
- }
- return info->last != NULL_RTX ? 0 : 1;
- }
- if (GET_CODE (body) == PARALLEL)
- {
- int i;
- char ix_clobber = 0;
- char iy_clobber = 0;
- char z_clobber = 0;
- this_insn_uses_iy = 0;
- this_insn_uses_ix = 0;
- this_insn_uses_z = 0;
-
- for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
- {
- rtx x;
- int uses_ix, uses_iy, uses_z;
-
- x = XVECEXP (body, 0, i);
-
- if (info->can_use_d && reg_mentioned_p (d_reg, x))
- info->can_use_d = 0;
-
- uses_ix = reg_mentioned_p (ix_reg, x);
- uses_iy = reg_mentioned_p (iy_reg, x);
- uses_z = reg_mentioned_p (z_reg, x);
- if (GET_CODE (x) == CLOBBER)
- {
- ix_clobber |= uses_ix;
- iy_clobber |= uses_iy;
- z_clobber |= uses_z;
- }
- else
- {
- this_insn_uses_ix |= uses_ix;
- this_insn_uses_iy |= uses_iy;
- this_insn_uses_z |= uses_z;
- }
- if (uses_z && GET_CODE (x) == SET)
- {
- rtx dst = XEXP (x, 0);
-
- if (Z_REG_P (dst))
- info->z_set_count++;
- }
- if (TARGET_M6812 && uses_z && side_effects_p (x))
- info->need_save_z = 1;
-
- if (z_clobber)
- info->need_save_z = 0;
- }
- if (debug_m6811)
- {
- printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
- this_insn_uses_ix, this_insn_uses_iy,
- this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
- debug_rtx (insn);
- }
- if (this_insn_uses_z)
- info->can_use_d = 0;
-
- if (z_clobber && info->first != insn)
- {
- info->need_save_z = 0;
- info->last = insn;
- return 0;
- }
- if (z_clobber && info->x_used == 0 && info->y_used == 0)
- {
- if (this_insn_uses_z == 0 && insn == info->first)
- {
- info->must_load_z = 0;
- }
- if (dead_register_here (insn, d_reg))
- {
- info->regno = HARD_D_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- else if (dead_register_here (insn, ix_reg))
- {
- info->regno = HARD_X_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- else if (dead_register_here (insn, iy_reg))
- {
- info->regno = HARD_Y_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- if (info->regno >= 0)
- {
- info->last = NEXT_INSN (insn);
- return 0;
- }
- if (this_insn_uses_ix == 0)
- {
- info->regno = HARD_X_REGNUM;
- info->must_save_reg = 1;
- info->must_restore_reg = 1;
- }
- else if (this_insn_uses_iy == 0)
- {
- info->regno = HARD_Y_REGNUM;
- info->must_save_reg = 1;
- info->must_restore_reg = 1;
- }
- else
- {
- info->regno = HARD_D_REGNUM;
- info->must_save_reg = 1;
- info->must_restore_reg = 1;
- }
- info->last = NEXT_INSN (insn);
- return 0;
- }
-
- if (((info->x_used || this_insn_uses_ix) && iy_clobber)
- || ((info->y_used || this_insn_uses_iy) && ix_clobber))
- {
- if (this_insn_uses_z)
- {
- if (info->y_used == 0 && iy_clobber)
- {
- info->regno = HARD_Y_REGNUM;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- if (info->first != insn
- && ((info->y_used && ix_clobber)
- || (info->x_used && iy_clobber)))
- info->last = insn;
- else
- info->last = NEXT_INSN (insn);
- info->save_before_last = 1;
- }
- return 0;
- }
- if (this_insn_uses_ix && this_insn_uses_iy)
- {
- if (this_insn_uses_z)
- {
- fatal_insn ("cannot do z-register replacement", insn);
- }
- return 0;
- }
- if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
- {
- if (info->y_used)
- {
- return 0;
- }
- info->x_used = 1;
- if (iy_clobber || z_clobber)
- {
- info->last = NEXT_INSN (insn);
- info->save_before_last = 1;
- return 0;
- }
- }
-
- if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
- {
- if (info->x_used)
- {
- return 0;
- }
- info->y_used = 1;
- if (ix_clobber || z_clobber)
- {
- info->last = NEXT_INSN (insn);
- info->save_before_last = 1;
- return 0;
- }
- }
- if (z_dies_here)
- {
- info->z_died = 1;
- info->need_save_z = 0;
- }
- return 1;
- }
- if (GET_CODE (body) == CLOBBER)
- {
- rtx dst = XEXP (body, 0);
-
- this_insn_uses_ix = reg_mentioned_p (ix_reg, dst);
- this_insn_uses_iy = reg_mentioned_p (iy_reg, dst);
-
- /* IX and IY are used at the same time, we have to restore
- the value of the scratch register before this insn. */
- if (this_insn_uses_ix && this_insn_uses_iy)
- {
- return 0;
- }
- if (info->x_used == 0 && this_insn_uses_ix)
- {
- if (info->y_used)
- {
- return 0;
- }
- info->x_used = 1;
- }
- if (info->y_used == 0 && this_insn_uses_iy)
- {
- if (info->x_used)
- {
- return 0;
- }
- info->y_used = 1;
- }
- return 1;
- }
- return 1;
-}
-
-static void
-m68hc11_find_z_replacement (rtx insn, struct replace_info *info)
-{
- int reg;
-
- info->replace_reg = NULL_RTX;
- info->must_load_z = 1;
- info->need_save_z = 1;
- info->must_save_reg = 1;
- info->must_restore_reg = 1;
- info->first = insn;
- info->x_used = 0;
- info->y_used = 0;
- info->can_use_d = TARGET_M6811 ? 1 : 0;
- info->found_call = 0;
- info->z_died = 0;
- info->last = 0;
- info->regno = -1;
- info->z_set_count = 0;
- info->z_value = NULL_RTX;
- info->must_push_reg = 0;
- info->save_before_last = 0;
- info->z_loaded_with_sp = 0;
-
- /* Scan the insn forward to find an address register that is not used.
- Stop when:
- - the flow of the program changes,
- - when we detect that both X and Y are necessary,
- - when the Z register dies,
- - when the condition codes are set. */
-
- for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
- {
- if (m68hc11_check_z_replacement (insn, info) == 0)
- break;
- }
-
- /* May be we can use Y or X if they contain the same value as Z.
- This happens very often after the reload. */
- if (info->z_set_count == 1)
- {
- rtx p = info->first;
- rtx v = 0;
-
- if (info->x_used)
- {
- v = find_last_value (iy_reg, &p, insn, 1);
- }
- else if (info->y_used)
- {
- v = find_last_value (ix_reg, &p, insn, 1);
- }
- if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
- {
- if (info->x_used)
- info->regno = HARD_Y_REGNUM;
- else
- info->regno = HARD_X_REGNUM;
- info->must_load_z = 0;
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- info->found_call = 1;
- }
- }
- if (info->z_set_count == 0)
- info->need_save_z = 0;
-
- if (insn == 0)
- info->need_save_z = 0;
-
- if (info->last == 0)
- info->last = insn;
-
- if (info->regno >= 0)
- {
- reg = info->regno;
- info->replace_reg = gen_rtx_REG (HImode, reg);
- }
- else if (info->can_use_d)
- {
- reg = HARD_D_REGNUM;
- info->replace_reg = d_reg;
- }
- else if (info->x_used)
- {
- reg = HARD_Y_REGNUM;
- info->replace_reg = iy_reg;
- }
- else
- {
- reg = HARD_X_REGNUM;
- info->replace_reg = ix_reg;
- }
- info->regno = reg;
-
- if (info->must_save_reg && info->must_restore_reg)
- {
- if (insn && dead_register_here (insn, info->replace_reg))
- {
- info->must_save_reg = 0;
- info->must_restore_reg = 0;
- }
- }
-}
-
-/* The insn uses the Z register. Find a replacement register for it
- (either X or Y) and replace it in the insn and the next ones until
- the flow changes or the replacement register is used. Instructions
- are emitted before and after the Z-block to preserve the value of
- Z and of the replacement register. */
-
-static void
-m68hc11_z_replacement (rtx insn)
-{
- rtx replace_reg_qi;
- rtx replace_reg;
- struct replace_info info;
-
- /* Find trivial case where we only need to replace z with the
- equivalent soft register. */
- if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
- {
- rtx body = PATTERN (insn);
- rtx src = XEXP (body, 1);
- rtx dst = XEXP (body, 0);
-
- if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
- {
- XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
- return;
- }
- else if (Z_REG_P (src)
- && ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
- {
- XEXP (body, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
- return;
- }
- else if (D_REG_P (dst)
- && m68hc11_arith_operator (src, GET_MODE (src))
- && D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
- {
- XEXP (src, 1) = gen_rtx_REG (GET_MODE (src), SOFT_Z_REGNUM);
- return;
- }
- else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
- && INTVAL (src) == 0)
- {
- XEXP (body, 0) = gen_rtx_REG (GET_MODE (dst), SOFT_Z_REGNUM);
- /* Force it to be re-recognized. */
- INSN_CODE (insn) = -1;
- return;
- }
- }
-
- m68hc11_find_z_replacement (insn, &info);
-
- replace_reg = info.replace_reg;
- replace_reg_qi = NULL_RTX;
-
- /* Save the X register in a .page0 location. */
- if (info.must_save_reg && !info.must_push_reg)
- {
- rtx dst;
-
- if (info.must_push_reg && 0)
- dst = gen_rtx_MEM (HImode,
- gen_rtx_PRE_DEC (HImode,
- gen_rtx_REG (HImode, HARD_SP_REGNUM)));
- else
- dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
-
- emit_insn_before (gen_movhi (dst,
- gen_rtx_REG (HImode, info.regno)), insn);
- }
- if (info.must_load_z && !info.must_push_reg)
- {
- emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
- gen_rtx_REG (HImode, SOFT_Z_REGNUM)),
- insn);
- }
-
-
- /* Replace all occurrence of Z by replace_reg.
- Stop when the last instruction to replace is reached.
- Also stop when we detect a change in the flow (but it's not
- necessary; just safeguard). */
-
- for (; insn && insn != info.last; insn = NEXT_INSN (insn))
- {
- rtx body;
-
- if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
- break;
-
- if (GET_CODE (insn) != INSN
- && GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
- continue;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
- || GET_CODE (body) == ASM_OPERANDS
- || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
- {
- rtx note;
-
- if (debug_m6811 && reg_mentioned_p (replace_reg, body))
- {
- printf ("Reg mentioned here...:\n");
- fflush (stdout);
- debug_rtx (insn);
- }
-
- /* Stack pointer was decremented by 2 due to the push.
- Correct that by adding 2 to the destination. */
- if (info.must_push_reg
- && info.z_loaded_with_sp && GET_CODE (body) == SET)
- {
- rtx src, dst;
-
- src = SET_SRC (body);
- dst = SET_DEST (body);
- if (SP_REG_P (src) && Z_REG_P (dst))
- emit_insn_after (gen_addhi3 (dst, dst, const2_rtx), insn);
- }
-
- /* Replace any (REG:HI Z) occurrence by either X or Y. */
- if (!validate_replace_rtx (z_reg, replace_reg, insn))
- {
- INSN_CODE (insn) = -1;
- if (!validate_replace_rtx (z_reg, replace_reg, insn))
- fatal_insn ("cannot do z-register replacement", insn);
- }
-
- /* Likewise for (REG:QI Z). */
- if (reg_mentioned_p (z_reg, insn))
- {
- if (replace_reg_qi == NULL_RTX)
- replace_reg_qi = gen_rtx_REG (QImode, REGNO (replace_reg));
- validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
- }
-
- /* If there is a REG_INC note on Z, replace it with a
- REG_INC note on the replacement register. This is necessary
- to make sure that the flow pass will identify the change
- and it will not remove a possible insn that saves Z. */
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- {
- if (REG_NOTE_KIND (note) == REG_INC
- && GET_CODE (XEXP (note, 0)) == REG
- && REGNO (XEXP (note, 0)) == REGNO (z_reg))
- {
- XEXP (note, 0) = replace_reg;
- }
- }
- }
- if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
- break;
- }
-
- /* Save Z before restoring the old value. */
- if (insn && info.need_save_z && !info.must_push_reg)
- {
- rtx save_pos_insn = insn;
-
- /* If Z is clobber by the last insn, we have to save its value
- before the last instruction. */
- if (info.save_before_last)
- save_pos_insn = PREV_INSN (save_pos_insn);
-
- emit_insn_before (gen_movhi (gen_rtx_REG (HImode, SOFT_Z_REGNUM),
- gen_rtx_REG (HImode, info.regno)),
- save_pos_insn);
- }
-
- if (info.must_push_reg && info.last)
- {
- rtx new_body, body;
-
- body = PATTERN (info.last);
- new_body = gen_rtx_PARALLEL (VOIDmode,
- gen_rtvec (3, body,
- gen_rtx_USE (VOIDmode,
- replace_reg),
- gen_rtx_USE (VOIDmode,
- gen_rtx_REG (HImode,
- SOFT_Z_REGNUM))));
- PATTERN (info.last) = new_body;
-
- /* Force recognition on insn since we changed it. */
- INSN_CODE (insn) = -1;
-
- if (!validate_replace_rtx (z_reg, replace_reg, info.last))
- {
- fatal_insn ("invalid Z register replacement for insn", insn);
- }
- insn = NEXT_INSN (info.last);
- }
-
- /* Restore replacement register unless it was died. */
- if (insn && info.must_restore_reg && !info.must_push_reg)
- {
- rtx dst;
-
- if (info.must_push_reg && 0)
- dst = gen_rtx_MEM (HImode,
- gen_rtx_POST_INC (HImode,
- gen_rtx_REG (HImode, HARD_SP_REGNUM)));
- else
- dst = gen_rtx_REG (HImode, SOFT_SAVED_XY_REGNUM);
-
- emit_insn_before (gen_movhi (gen_rtx_REG (HImode, info.regno),
- dst), insn);
- }
-
-}
-
-
-/* Scan all the insn and re-affects some registers
- - The Z register (if it was used), is affected to X or Y depending
- on the instruction. */
-
-static void
-m68hc11_reassign_regs (rtx first)
-{
- rtx insn;
-
- ix_reg = gen_rtx_REG (HImode, HARD_X_REGNUM);
- iy_reg = gen_rtx_REG (HImode, HARD_Y_REGNUM);
- z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
- z_reg_qi = gen_rtx_REG (QImode, HARD_Z_REGNUM);
-
- /* Scan all insns to replace Z by X or Y preserving the old value
- of X/Y and restoring it afterward. */
-
- for (insn = first; insn; insn = NEXT_INSN (insn))
- {
- rtx body;
-
- if (GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
- continue;
-
- if (!INSN_P (insn))
- continue;
-
- body = PATTERN (insn);
- if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
- continue;
-
- if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
- || GET_CODE (body) == ASM_OPERANDS
- || GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
- continue;
-
- if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
- || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
- {
-
- /* If Z appears in this insn, replace it in the current insn
- and the next ones until the flow changes or we have to
- restore back the replacement register. */
-
- if (reg_mentioned_p (z_reg, body))
- {
- m68hc11_z_replacement (insn);
- }
- }
- else
- {
- printf ("insn not handled by Z replacement:\n");
- fflush (stdout);
- debug_rtx (insn);
- }
- }
-}
-
-
-/* Machine-dependent reorg pass.
- Specific optimizations are defined here:
- - this pass changes the Z register into either X or Y
- (it preserves X/Y previous values in a memory slot in page0).
-
- When this pass is finished, the global variable
- 'z_replacement_completed' is set to 2. */
-
-static void
-m68hc11_reorg (void)
-{
- int split_done = 0;
- rtx first;
-
- z_replacement_completed = 0;
- z_reg = gen_rtx_REG (HImode, HARD_Z_REGNUM);
- first = get_insns ();
-
- /* Some RTX are shared at this point. This breaks the Z register
- replacement, unshare everything. */
- unshare_all_rtl_again (first);
-
- /* Force a split of all splittable insn. This is necessary for the
- Z register replacement mechanism because we end up with basic insns. */
- split_all_insns_noflow ();
- split_done = 1;
-
- z_replacement_completed = 1;
- m68hc11_reassign_regs (first);
-
- if (optimize)
- compute_bb_for_insn ();
-
- /* After some splitting, there are some opportunities for CSE pass.
- This happens quite often when 32-bit or above patterns are split. */
- if (optimize > 0 && split_done)
- {
- reload_cse_regs (first);
- }
-
- /* Re-create the REG_DEAD notes. These notes are used in the machine
- description to use the best assembly directives. */
- if (optimize)
- {
- df_note_add_problem ();
- df_analyze ();
- df_remove_problem (df_note);
- }
-
- z_replacement_completed = 2;
-
- /* If optimizing, then go ahead and split insns that must be
- split after Z register replacement. This gives more opportunities
- for peephole (in particular for consecutives xgdx/xgdy). */
- if (optimize > 0)
- split_all_insns_noflow ();
-
- /* Once insns are split after the z_replacement_completed == 2,
- we must not re-run the life_analysis. The xgdx/xgdy patterns
- are not recognized and the life_analysis pass removes some
- insns because it thinks some (SETs) are noops or made to dead
- stores (which is false due to the swap).
-
- Do a simple pass to eliminate the noop set that the final
- split could generate (because it was easier for split definition). */
- {
- rtx insn;
-
- for (insn = first; insn; insn = NEXT_INSN (insn))
- {
- rtx body;
-
- if (INSN_DELETED_P (insn))
- continue;
- if (!INSN_P (insn))
- continue;
-
- /* Remove the (set (R) (R)) insns generated by some splits. */
- body = PATTERN (insn);
- if (GET_CODE (body) == SET
- && rtx_equal_p (SET_SRC (body), SET_DEST (body)))
- {
- SET_INSN_DELETED (insn);
- continue;
- }
- }
- }
-}
-
-/* Override memcpy */
-
-static void
-m68hc11_init_libfuncs (void)
-{
- memcpy_libfunc = init_one_libfunc ("__memcpy");
- memcmp_libfunc = init_one_libfunc ("__memcmp");
- memset_libfunc = init_one_libfunc ("__memset");
-}
-
-
-
-/* Cost functions. */
-
-/* Cost of moving memory. */
-int
-m68hc11_memory_move_cost (enum machine_mode mode, enum reg_class rclass,
- int in ATTRIBUTE_UNUSED)
-{
- if (rclass <= H_REGS && rclass > NO_REGS)
- {
- if (GET_MODE_SIZE (mode) <= 2)
- return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
- else
- return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
- }
- else
- {
- if (GET_MODE_SIZE (mode) <= 2)
- return COSTS_N_INSNS (3);
- else
- return COSTS_N_INSNS (4);
- }
-}
-
-
-/* Cost of moving data from a register of class 'from' to on in class 'to'.
- Reload does not check the constraint of set insns when the two registers
- have a move cost of 2. Setting a higher cost will force reload to check
- the constraints. */
-int
-m68hc11_register_move_cost (enum machine_mode mode, enum reg_class from,
- enum reg_class to)
-{
- /* All costs are symmetric, so reduce cases by putting the
- lower number class as the destination. */
- if (from < to)
- {
- enum reg_class tmp = to;
- to = from, from = tmp;
- }
- if (to >= S_REGS)
- return m68hc11_memory_move_cost (mode, S_REGS, 0);
- else if (from <= S_REGS)
- return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
- else
- return COSTS_N_INSNS (2);
-}
-
-
-/* Provide the costs of an addressing mode that contains ADDR.
- If ADDR is not a valid address, its cost is irrelevant. */
-
-static int
-m68hc11_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
-{
- int cost = 4;
-
- switch (GET_CODE (addr))
- {
- case REG:
- /* Make the cost of hard registers and specially SP, FP small. */
- if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
- cost = 0;
- else
- cost = 1;
- break;
-
- case SYMBOL_REF:
- cost = 8;
- break;
-
- case LABEL_REF:
- case CONST:
- cost = 0;
- break;
-
- case PLUS:
- {
- register rtx plus0 = XEXP (addr, 0);
- register rtx plus1 = XEXP (addr, 1);
-
- if (GET_CODE (plus0) != REG)
- break;
-
- switch (GET_CODE (plus1))
- {
- case CONST_INT:
- if (INTVAL (plus1) >= 2 * m68hc11_max_offset
- || INTVAL (plus1) < m68hc11_min_offset)
- cost = 3;
- else if (INTVAL (plus1) >= m68hc11_max_offset)
- cost = 2;
- else
- cost = 1;
- if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
- cost += 0;
- else
- cost += 1;
- break;
-
- case SYMBOL_REF:
- cost = 8;
- break;
-
- case CONST:
- case LABEL_REF:
- cost = 0;
- break;
-
- default:
- break;
- }
- break;
- }
- case PRE_DEC:
- case PRE_INC:
- if (SP_REG_P (XEXP (addr, 0)))
- cost = 1;
- break;
-
- default:
- break;
- }
- if (debug_m6811)
- {
- printf ("Address cost: %d for :", cost);
- fflush (stdout);
- debug_rtx (addr);
- }
-
- return cost;
-}
-
-static int
-m68hc11_shift_cost (enum machine_mode mode, rtx x, int shift)
-{
- int total;
-
- total = rtx_cost (x, SET, !optimize_size);
- if (mode == QImode)
- total += m68hc11_cost->shiftQI_const[shift % 8];
- else if (mode == HImode)
- total += m68hc11_cost->shiftHI_const[shift % 16];
- else if (shift == 8 || shift == 16 || shift == 32)
- total += m68hc11_cost->shiftHI_const[8];
- else if (shift != 0 && shift != 16 && shift != 32)
- {
- total += m68hc11_cost->shiftHI_const[1] * shift;
- }
-
- /* For SI and others, the cost is higher. */
- if (GET_MODE_SIZE (mode) > 2 && (shift % 16) != 0)
- total *= GET_MODE_SIZE (mode) / 2;
-
- /* When optimizing for size, make shift more costly so that
- multiplications are preferred. */
- if (optimize_size && (shift % 8) != 0)
- total *= 2;
-
- return total;
-}
-
-static int
-m68hc11_rtx_costs_1 (rtx x, enum rtx_code code,
- enum rtx_code outer_code ATTRIBUTE_UNUSED)
-{
- enum machine_mode mode = GET_MODE (x);
- int extra_cost = 0;
- int total;
-
- switch (code)
- {
- case ROTATE:
- case ROTATERT:
- case ASHIFT:
- case LSHIFTRT:
- case ASHIFTRT:
- if (GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- return m68hc11_shift_cost (mode, XEXP (x, 0), INTVAL (XEXP (x, 1)));
- }
-
- total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
- total += m68hc11_cost->shift_var;
- return total;
-
- case AND:
- case XOR:
- case IOR:
- total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
- total += m68hc11_cost->logical;
-
- /* Logical instructions are byte instructions only. */
- total *= GET_MODE_SIZE (mode);
- return total;
-
- case MINUS:
- case PLUS:
- total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
- total += m68hc11_cost->add;
- if (GET_MODE_SIZE (mode) > 2)
- {
- total *= GET_MODE_SIZE (mode) / 2;
- }
- return total;
-
- case UDIV:
- case DIV:
- case MOD:
- total = rtx_cost (XEXP (x, 0), code, !optimize_size) + rtx_cost (XEXP (x, 1), code, !optimize_size);
- switch (mode)
- {
- case QImode:
- total += m68hc11_cost->divQI;
- break;
-
- case HImode:
- total += m68hc11_cost->divHI;
- break;
-
- case SImode:
- default:
- total += m68hc11_cost->divSI;
- break;
- }
- return total;
-
- case MULT:
- /* mul instruction produces 16-bit result. */
- if (mode == HImode && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
- && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
- return m68hc11_cost->multQI
- + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
- + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
-
- /* emul instruction produces 32-bit result for 68HC12. */
- if (TARGET_M6812 && mode == SImode
- && GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
- && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND)
- return m68hc11_cost->multHI
- + rtx_cost (XEXP (XEXP (x, 0), 0), code, !optimize_size)
- + rtx_cost (XEXP (XEXP (x, 1), 0), code, !optimize_size);
-
- total = rtx_cost (XEXP (x, 0), code, !optimize_size)
- + rtx_cost (XEXP (x, 1), code, !optimize_size);
- switch (mode)
- {
- case QImode:
- total += m68hc11_cost->multQI;
- break;
-
- case HImode:
- total += m68hc11_cost->multHI;
- break;
-
- case SImode:
- default:
- total += m68hc11_cost->multSI;
- break;
- }
- return total;
-
- case NEG:
- case SIGN_EXTEND:
- extra_cost = COSTS_N_INSNS (2);
-
- /* Fall through */
- case NOT:
- case COMPARE:
- case ABS:
- case ZERO_EXTEND:
- case ZERO_EXTRACT:
- total = extra_cost + rtx_cost (XEXP (x, 0), code, !optimize_size);
- if (mode == QImode)
- {
- return total + COSTS_N_INSNS (1);
- }
- if (mode == HImode)
- {
- return total + COSTS_N_INSNS (2);
- }
- if (mode == SImode)
- {
- return total + COSTS_N_INSNS (4);
- }
- return total + COSTS_N_INSNS (8);
-
- case IF_THEN_ELSE:
- if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
- return COSTS_N_INSNS (1);
-
- return COSTS_N_INSNS (1);
-
- default:
- return COSTS_N_INSNS (4);
- }
-}
-
-static bool
-m68hc11_rtx_costs (rtx x, int codearg, int outer_code_arg, int *total,
- bool speed ATTRIBUTE_UNUSED)
-{
- enum rtx_code code = (enum rtx_code) codearg;
- enum rtx_code outer_code = (enum rtx_code) outer_code_arg;
-
- switch (code)
- {
- /* Constants are cheap. Moving them in registers must be avoided
- because most instructions do not handle two register operands. */
- case CONST_INT:
- case CONST:
- case LABEL_REF:
- case SYMBOL_REF:
- case CONST_DOUBLE:
- /* Logical and arithmetic operations with a constant operand are
- better because they are not supported with two registers. */
- /* 'clr' is slow */
- if (outer_code == SET && x == const0_rtx)
- /* After reload, the reload_cse pass checks the cost to change
- a SET into a PLUS. Make const0 cheap then. */
- *total = 1 - reload_completed;
- else
- *total = 0;
- return true;
-
- case ZERO_EXTRACT:
- if (outer_code != COMPARE)
- return false;
-
- case ROTATE:
- case ROTATERT:
- case ASHIFT:
- case LSHIFTRT:
- case ASHIFTRT:
- case MINUS:
- case PLUS:
- case AND:
- case XOR:
- case IOR:
- case UDIV:
- case DIV:
- case MOD:
- case MULT:
- case NEG:
- case SIGN_EXTEND:
- case NOT:
- case COMPARE:
- case ZERO_EXTEND:
- case IF_THEN_ELSE:
- *total = m68hc11_rtx_costs_1 (x, code, outer_code);
- return true;
-
- default:
- return false;
- }
-}
-
-
-/* Worker function for TARGET_ASM_FILE_START. */
-
-static void
-m68hc11_file_start (void)
-{
- default_file_start ();
-
- fprintf (asm_out_file, "\t.mode %s\n", TARGET_SHORT ? "mshort" : "mlong");
-}
-
-
-/* Worker function for TARGET_ASM_CONSTRUCTOR. */
-
-static void
-m68hc11_asm_out_constructor (rtx symbol, int priority)
-{
- default_ctor_section_asm_out_constructor (symbol, priority);
- fprintf (asm_out_file, "\t.globl\t__do_global_ctors\n");
-}
-
-/* Worker function for TARGET_ASM_DESTRUCTOR. */
-
-static void
-m68hc11_asm_out_destructor (rtx symbol, int priority)
-{
- default_dtor_section_asm_out_destructor (symbol, priority);
- fprintf (asm_out_file, "\t.globl\t__do_global_dtors\n");
-}
-
-/* Worker function for TARGET_STRUCT_VALUE_RTX. */
-
-static rtx
-m68hc11_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
- int incoming ATTRIBUTE_UNUSED)
-{
- return gen_rtx_REG (Pmode, HARD_D_REGNUM);
-}
-
-/* Return true if type TYPE should be returned in memory.
- Blocks and data types largers than 4 bytes cannot be returned
- in the register (D + X = 4). */
-
-static bool
-m68hc11_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
-{
- if (TYPE_MODE (type) == BLKmode)
- {
- HOST_WIDE_INT size = int_size_in_bytes (type);
- return (size == -1 || size > 4);
- }
- else
- return GET_MODE_SIZE (TYPE_MODE (type)) > 4;
-}
-
-#include "gt-m68hc11.h"