/* 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,
|
®_names[REGNO (x)][pos]));
|
®_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 = ®_NOTES (insn);
|
pnote = ®_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"
|
|
|