OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gcc-4.2.2/] [gcc/] [config/] [m68hc11/] [m68hc11.c] - Diff between revs 154 and 816

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.