/* Subroutines for insn-output.c for NEC V850 series
|
/* Subroutines for insn-output.c for NEC V850 series
|
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
Contributed by Jeff Law (law@cygnus.com).
|
Contributed by Jeff Law (law@cygnus.com).
|
|
|
This file is part of GCC.
|
This file is part of GCC.
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
GCC is free software; you can redistribute it and/or modify it
|
under the terms of the GNU General Public License as published by
|
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, but WITHOUT
|
GCC is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
for more details.
|
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/>. */
|
|
|
#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 "tree.h"
|
#include "tree.h"
|
#include "rtl.h"
|
#include "rtl.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 "function.h"
|
#include "function.h"
|
#include "toplev.h"
|
#include "toplev.h"
|
#include "ggc.h"
|
#include "ggc.h"
|
#include "integrate.h"
|
#include "integrate.h"
|
#include "tm_p.h"
|
#include "tm_p.h"
|
#include "target.h"
|
#include "target.h"
|
#include "target-def.h"
|
#include "target-def.h"
|
#include "df.h"
|
#include "df.h"
|
|
|
#ifndef streq
|
#ifndef streq
|
#define streq(a,b) (strcmp (a, b) == 0)
|
#define streq(a,b) (strcmp (a, b) == 0)
|
#endif
|
#endif
|
|
|
/* Function prototypes for stupid compilers: */
|
/* Function prototypes for stupid compilers: */
|
static bool v850_handle_option (size_t, const char *, int);
|
static bool v850_handle_option (size_t, const char *, int);
|
static void const_double_split (rtx, HOST_WIDE_INT *, HOST_WIDE_INT *);
|
static void const_double_split (rtx, HOST_WIDE_INT *, HOST_WIDE_INT *);
|
static int const_costs_int (HOST_WIDE_INT, int);
|
static int const_costs_int (HOST_WIDE_INT, int);
|
static int const_costs (rtx, enum rtx_code);
|
static int const_costs (rtx, enum rtx_code);
|
static bool v850_rtx_costs (rtx, int, int, int *, bool);
|
static bool v850_rtx_costs (rtx, int, int, int *, bool);
|
static void substitute_ep_register (rtx, rtx, int, int, rtx *, rtx *);
|
static void substitute_ep_register (rtx, rtx, int, int, rtx *, rtx *);
|
static void v850_reorg (void);
|
static void v850_reorg (void);
|
static int ep_memory_offset (enum machine_mode, int);
|
static int ep_memory_offset (enum machine_mode, int);
|
static void v850_set_data_area (tree, v850_data_area);
|
static void v850_set_data_area (tree, v850_data_area);
|
static tree v850_handle_interrupt_attribute (tree *, tree, tree, int, bool *);
|
static tree v850_handle_interrupt_attribute (tree *, tree, tree, int, bool *);
|
static tree v850_handle_data_area_attribute (tree *, tree, tree, int, bool *);
|
static tree v850_handle_data_area_attribute (tree *, tree, tree, int, bool *);
|
static void v850_insert_attributes (tree, tree *);
|
static void v850_insert_attributes (tree, tree *);
|
static void v850_asm_init_sections (void);
|
static void v850_asm_init_sections (void);
|
static section *v850_select_section (tree, int, unsigned HOST_WIDE_INT);
|
static section *v850_select_section (tree, int, unsigned HOST_WIDE_INT);
|
static void v850_encode_data_area (tree, rtx);
|
static void v850_encode_data_area (tree, rtx);
|
static void v850_encode_section_info (tree, rtx, int);
|
static void v850_encode_section_info (tree, rtx, int);
|
static bool v850_return_in_memory (const_tree, const_tree);
|
static bool v850_return_in_memory (const_tree, const_tree);
|
static rtx v850_function_value (const_tree, const_tree, bool);
|
static rtx v850_function_value (const_tree, const_tree, bool);
|
static void v850_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
|
static void v850_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
|
tree, int *, int);
|
tree, int *, int);
|
static bool v850_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
|
static bool v850_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
|
const_tree, bool);
|
const_tree, bool);
|
static int v850_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
|
static int v850_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
|
tree, bool);
|
tree, bool);
|
static bool v850_can_eliminate (const int, const int);
|
static bool v850_can_eliminate (const int, const int);
|
static void v850_asm_trampoline_template (FILE *);
|
static void v850_asm_trampoline_template (FILE *);
|
static void v850_trampoline_init (rtx, tree, rtx);
|
static void v850_trampoline_init (rtx, tree, rtx);
|
|
|
/* Information about the various small memory areas. */
|
/* Information about the various small memory areas. */
|
struct small_memory_info small_memory[ (int)SMALL_MEMORY_max ] =
|
struct small_memory_info small_memory[ (int)SMALL_MEMORY_max ] =
|
{
|
{
|
/* name max physical max */
|
/* name max physical max */
|
{ "tda", 0, 256 },
|
{ "tda", 0, 256 },
|
{ "sda", 0, 65536 },
|
{ "sda", 0, 65536 },
|
{ "zda", 0, 32768 },
|
{ "zda", 0, 32768 },
|
};
|
};
|
|
|
/* Names of the various data areas used on the v850. */
|
/* Names of the various data areas used on the v850. */
|
tree GHS_default_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
|
tree GHS_default_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
|
tree GHS_current_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
|
tree GHS_current_section_names [(int) COUNT_OF_GHS_SECTION_KINDS];
|
|
|
/* Track the current data area set by the data area pragma (which
|
/* Track the current data area set by the data area pragma (which
|
can be nested). Tested by check_default_data_area. */
|
can be nested). Tested by check_default_data_area. */
|
data_area_stack_element * data_area_stack = NULL;
|
data_area_stack_element * data_area_stack = NULL;
|
|
|
/* True if we don't need to check any more if the current
|
/* True if we don't need to check any more if the current
|
function is an interrupt handler. */
|
function is an interrupt handler. */
|
static int v850_interrupt_cache_p = FALSE;
|
static int v850_interrupt_cache_p = FALSE;
|
|
|
/* Whether current function is an interrupt handler. */
|
/* Whether current function is an interrupt handler. */
|
static int v850_interrupt_p = FALSE;
|
static int v850_interrupt_p = FALSE;
|
|
|
static GTY(()) section *rosdata_section;
|
static GTY(()) section *rosdata_section;
|
static GTY(()) section *rozdata_section;
|
static GTY(()) section *rozdata_section;
|
static GTY(()) section *tdata_section;
|
static GTY(()) section *tdata_section;
|
static GTY(()) section *zdata_section;
|
static GTY(()) section *zdata_section;
|
static GTY(()) section *zbss_section;
|
static GTY(()) section *zbss_section;
|
|
|
/* V850 specific attributes. */
|
/* V850 specific attributes. */
|
|
|
static const struct attribute_spec v850_attribute_table[] =
|
static const struct attribute_spec v850_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_handler", 0, 0, true, false, false, v850_handle_interrupt_attribute },
|
{ "interrupt_handler", 0, 0, true, false, false, v850_handle_interrupt_attribute },
|
{ "interrupt", 0, 0, true, false, false, v850_handle_interrupt_attribute },
|
{ "interrupt", 0, 0, true, false, false, v850_handle_interrupt_attribute },
|
{ "sda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ "sda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ "tda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ "tda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ "zda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ "zda", 0, 0, true, false, false, v850_handle_data_area_attribute },
|
{ NULL, 0, 0, false, false, false, NULL }
|
{ NULL, 0, 0, false, false, false, NULL }
|
};
|
};
|
|
|
|
|
/* Initialize the GCC target structure. */
|
/* Initialize the GCC target structure. */
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
|
|
|
#undef TARGET_ATTRIBUTE_TABLE
|
#undef TARGET_ATTRIBUTE_TABLE
|
#define TARGET_ATTRIBUTE_TABLE v850_attribute_table
|
#define TARGET_ATTRIBUTE_TABLE v850_attribute_table
|
|
|
#undef TARGET_INSERT_ATTRIBUTES
|
#undef TARGET_INSERT_ATTRIBUTES
|
#define TARGET_INSERT_ATTRIBUTES v850_insert_attributes
|
#define TARGET_INSERT_ATTRIBUTES v850_insert_attributes
|
|
|
#undef TARGET_ASM_SELECT_SECTION
|
#undef TARGET_ASM_SELECT_SECTION
|
#define TARGET_ASM_SELECT_SECTION v850_select_section
|
#define TARGET_ASM_SELECT_SECTION v850_select_section
|
|
|
/* The assembler supports switchable .bss sections, but
|
/* The assembler supports switchable .bss sections, but
|
v850_select_section doesn't yet make use of them. */
|
v850_select_section doesn't yet make use of them. */
|
#undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
|
#undef TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
|
#define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
|
#define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
|
|
|
#undef TARGET_ENCODE_SECTION_INFO
|
#undef TARGET_ENCODE_SECTION_INFO
|
#define TARGET_ENCODE_SECTION_INFO v850_encode_section_info
|
#define TARGET_ENCODE_SECTION_INFO v850_encode_section_info
|
|
|
#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 (MASK_DEFAULT | MASK_APP_REGS)
|
#define TARGET_DEFAULT_TARGET_FLAGS (MASK_DEFAULT | MASK_APP_REGS)
|
#undef TARGET_HANDLE_OPTION
|
#undef TARGET_HANDLE_OPTION
|
#define TARGET_HANDLE_OPTION v850_handle_option
|
#define TARGET_HANDLE_OPTION v850_handle_option
|
|
|
#undef TARGET_RTX_COSTS
|
#undef TARGET_RTX_COSTS
|
#define TARGET_RTX_COSTS v850_rtx_costs
|
#define TARGET_RTX_COSTS v850_rtx_costs
|
|
|
#undef TARGET_ADDRESS_COST
|
#undef TARGET_ADDRESS_COST
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
|
|
#undef TARGET_MACHINE_DEPENDENT_REORG
|
#undef TARGET_MACHINE_DEPENDENT_REORG
|
#define TARGET_MACHINE_DEPENDENT_REORG v850_reorg
|
#define TARGET_MACHINE_DEPENDENT_REORG v850_reorg
|
|
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
|
|
|
#undef TARGET_RETURN_IN_MEMORY
|
#undef TARGET_RETURN_IN_MEMORY
|
#define TARGET_RETURN_IN_MEMORY v850_return_in_memory
|
#define TARGET_RETURN_IN_MEMORY v850_return_in_memory
|
|
|
#undef TARGET_FUNCTION_VALUE
|
#undef TARGET_FUNCTION_VALUE
|
#define TARGET_FUNCTION_VALUE v850_function_value
|
#define TARGET_FUNCTION_VALUE v850_function_value
|
|
|
#undef TARGET_PASS_BY_REFERENCE
|
#undef TARGET_PASS_BY_REFERENCE
|
#define TARGET_PASS_BY_REFERENCE v850_pass_by_reference
|
#define TARGET_PASS_BY_REFERENCE v850_pass_by_reference
|
|
|
#undef TARGET_CALLEE_COPIES
|
#undef TARGET_CALLEE_COPIES
|
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
|
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
|
|
|
#undef TARGET_SETUP_INCOMING_VARARGS
|
#undef TARGET_SETUP_INCOMING_VARARGS
|
#define TARGET_SETUP_INCOMING_VARARGS v850_setup_incoming_varargs
|
#define TARGET_SETUP_INCOMING_VARARGS v850_setup_incoming_varargs
|
|
|
#undef TARGET_ARG_PARTIAL_BYTES
|
#undef TARGET_ARG_PARTIAL_BYTES
|
#define TARGET_ARG_PARTIAL_BYTES v850_arg_partial_bytes
|
#define TARGET_ARG_PARTIAL_BYTES v850_arg_partial_bytes
|
|
|
#undef TARGET_CAN_ELIMINATE
|
#undef TARGET_CAN_ELIMINATE
|
#define TARGET_CAN_ELIMINATE v850_can_eliminate
|
#define TARGET_CAN_ELIMINATE v850_can_eliminate
|
|
|
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
|
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
|
#define TARGET_ASM_TRAMPOLINE_TEMPLATE v850_asm_trampoline_template
|
#define TARGET_ASM_TRAMPOLINE_TEMPLATE v850_asm_trampoline_template
|
#undef TARGET_TRAMPOLINE_INIT
|
#undef TARGET_TRAMPOLINE_INIT
|
#define TARGET_TRAMPOLINE_INIT v850_trampoline_init
|
#define TARGET_TRAMPOLINE_INIT v850_trampoline_init
|
|
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
/* Set the maximum size of small memory area TYPE to the value given
|
/* Set the maximum size of small memory area TYPE to the value given
|
by VALUE. Return true if VALUE was syntactically correct. VALUE
|
by VALUE. Return true if VALUE was syntactically correct. VALUE
|
starts with the argument separator: either "-" or "=". */
|
starts with the argument separator: either "-" or "=". */
|
|
|
static bool
|
static bool
|
v850_handle_memory_option (enum small_memory_type type, const char *value)
|
v850_handle_memory_option (enum small_memory_type type, const char *value)
|
{
|
{
|
int i, size;
|
int i, size;
|
|
|
if (*value != '-' && *value != '=')
|
if (*value != '-' && *value != '=')
|
return false;
|
return false;
|
|
|
value++;
|
value++;
|
for (i = 0; value[i]; i++)
|
for (i = 0; value[i]; i++)
|
if (!ISDIGIT (value[i]))
|
if (!ISDIGIT (value[i]))
|
return false;
|
return false;
|
|
|
size = atoi (value);
|
size = atoi (value);
|
if (size > small_memory[type].physical_max)
|
if (size > small_memory[type].physical_max)
|
error ("value passed to %<-m%s%> is too large", small_memory[type].name);
|
error ("value passed to %<-m%s%> is too large", small_memory[type].name);
|
else
|
else
|
small_memory[type].max = size;
|
small_memory[type].max = size;
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Implement TARGET_HANDLE_OPTION. */
|
/* Implement TARGET_HANDLE_OPTION. */
|
|
|
static bool
|
static bool
|
v850_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
v850_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case OPT_mspace:
|
case OPT_mspace:
|
target_flags |= MASK_EP | MASK_PROLOG_FUNCTION;
|
target_flags |= MASK_EP | MASK_PROLOG_FUNCTION;
|
return true;
|
return true;
|
|
|
case OPT_mv850:
|
case OPT_mv850:
|
target_flags &= ~(MASK_CPU ^ MASK_V850);
|
target_flags &= ~(MASK_CPU ^ MASK_V850);
|
return true;
|
return true;
|
|
|
case OPT_mv850e:
|
case OPT_mv850e:
|
case OPT_mv850e1:
|
case OPT_mv850e1:
|
target_flags &= ~(MASK_CPU ^ MASK_V850E);
|
target_flags &= ~(MASK_CPU ^ MASK_V850E);
|
return true;
|
return true;
|
|
|
case OPT_mtda:
|
case OPT_mtda:
|
return v850_handle_memory_option (SMALL_MEMORY_TDA, arg);
|
return v850_handle_memory_option (SMALL_MEMORY_TDA, arg);
|
|
|
case OPT_msda:
|
case OPT_msda:
|
return v850_handle_memory_option (SMALL_MEMORY_SDA, arg);
|
return v850_handle_memory_option (SMALL_MEMORY_SDA, arg);
|
|
|
case OPT_mzda:
|
case OPT_mzda:
|
return v850_handle_memory_option (SMALL_MEMORY_ZDA, arg);
|
return v850_handle_memory_option (SMALL_MEMORY_ZDA, arg);
|
|
|
default:
|
default:
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
static bool
|
static bool
|
v850_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
|
v850_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
|
enum machine_mode mode, const_tree type,
|
enum machine_mode mode, const_tree type,
|
bool named ATTRIBUTE_UNUSED)
|
bool named ATTRIBUTE_UNUSED)
|
{
|
{
|
unsigned HOST_WIDE_INT size;
|
unsigned HOST_WIDE_INT size;
|
|
|
if (type)
|
if (type)
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
else
|
else
|
size = GET_MODE_SIZE (mode);
|
size = GET_MODE_SIZE (mode);
|
|
|
return size > 8;
|
return size > 8;
|
}
|
}
|
|
|
/* Return an RTX to represent where an argument with mode MODE
|
/* Return an RTX to represent where an argument with mode MODE
|
and type TYPE will be passed to a function. If the result
|
and type TYPE will be passed to a function. If the result
|
is NULL_RTX, the argument will be pushed. */
|
is NULL_RTX, the argument will be pushed. */
|
|
|
rtx
|
rtx
|
function_arg (CUMULATIVE_ARGS * cum,
|
function_arg (CUMULATIVE_ARGS * cum,
|
enum machine_mode mode,
|
enum machine_mode mode,
|
tree type,
|
tree type,
|
int named)
|
int named)
|
{
|
{
|
rtx result = NULL_RTX;
|
rtx result = NULL_RTX;
|
int size, align;
|
int size, align;
|
|
|
if (TARGET_GHS && !named)
|
if (TARGET_GHS && !named)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
else
|
else
|
size = GET_MODE_SIZE (mode);
|
size = GET_MODE_SIZE (mode);
|
|
|
if (size < 1)
|
if (size < 1)
|
{
|
{
|
/* Once we have stopped using argument registers, do not start up again. */
|
/* Once we have stopped using argument registers, do not start up again. */
|
cum->nbytes = 4 * UNITS_PER_WORD;
|
cum->nbytes = 4 * UNITS_PER_WORD;
|
return NULL_RTX;
|
return NULL_RTX;
|
}
|
}
|
|
|
if (type)
|
if (type)
|
align = TYPE_ALIGN (type) / BITS_PER_UNIT;
|
align = TYPE_ALIGN (type) / BITS_PER_UNIT;
|
else
|
else
|
align = size;
|
align = size;
|
|
|
cum->nbytes = (cum->nbytes + align - 1) &~(align - 1);
|
cum->nbytes = (cum->nbytes + align - 1) &~(align - 1);
|
|
|
if (cum->nbytes > 4 * UNITS_PER_WORD)
|
if (cum->nbytes > 4 * UNITS_PER_WORD)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
if (type == NULL_TREE
|
if (type == NULL_TREE
|
&& cum->nbytes + size > 4 * UNITS_PER_WORD)
|
&& cum->nbytes + size > 4 * UNITS_PER_WORD)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
switch (cum->nbytes / UNITS_PER_WORD)
|
switch (cum->nbytes / UNITS_PER_WORD)
|
{
|
{
|
case 0:
|
case 0:
|
result = gen_rtx_REG (mode, 6);
|
result = gen_rtx_REG (mode, 6);
|
break;
|
break;
|
case 1:
|
case 1:
|
result = gen_rtx_REG (mode, 7);
|
result = gen_rtx_REG (mode, 7);
|
break;
|
break;
|
case 2:
|
case 2:
|
result = gen_rtx_REG (mode, 8);
|
result = gen_rtx_REG (mode, 8);
|
break;
|
break;
|
case 3:
|
case 3:
|
result = gen_rtx_REG (mode, 9);
|
result = gen_rtx_REG (mode, 9);
|
break;
|
break;
|
default:
|
default:
|
result = NULL_RTX;
|
result = NULL_RTX;
|
}
|
}
|
|
|
return result;
|
return result;
|
}
|
}
|
|
|
|
|
/* Return the number of bytes which must be put into registers
|
/* Return the number of bytes which must be put into registers
|
for values which are part in registers and part in memory. */
|
for values which are part in registers and part in memory. */
|
|
|
static int
|
static int
|
v850_arg_partial_bytes (CUMULATIVE_ARGS * cum, enum machine_mode mode,
|
v850_arg_partial_bytes (CUMULATIVE_ARGS * cum, enum machine_mode mode,
|
tree type, bool named)
|
tree type, bool named)
|
{
|
{
|
int size, align;
|
int size, align;
|
|
|
if (TARGET_GHS && !named)
|
if (TARGET_GHS && !named)
|
return 0;
|
return 0;
|
|
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
else
|
else
|
size = GET_MODE_SIZE (mode);
|
size = GET_MODE_SIZE (mode);
|
|
|
if (size < 1)
|
if (size < 1)
|
size = 1;
|
size = 1;
|
|
|
if (type)
|
if (type)
|
align = TYPE_ALIGN (type) / BITS_PER_UNIT;
|
align = TYPE_ALIGN (type) / BITS_PER_UNIT;
|
else
|
else
|
align = size;
|
align = size;
|
|
|
cum->nbytes = (cum->nbytes + align - 1) & ~ (align - 1);
|
cum->nbytes = (cum->nbytes + align - 1) & ~ (align - 1);
|
|
|
if (cum->nbytes > 4 * UNITS_PER_WORD)
|
if (cum->nbytes > 4 * UNITS_PER_WORD)
|
return 0;
|
return 0;
|
|
|
if (cum->nbytes + size <= 4 * UNITS_PER_WORD)
|
if (cum->nbytes + size <= 4 * UNITS_PER_WORD)
|
return 0;
|
return 0;
|
|
|
if (type == NULL_TREE
|
if (type == NULL_TREE
|
&& cum->nbytes + size > 4 * UNITS_PER_WORD)
|
&& cum->nbytes + size > 4 * UNITS_PER_WORD)
|
return 0;
|
return 0;
|
|
|
return 4 * UNITS_PER_WORD - cum->nbytes;
|
return 4 * UNITS_PER_WORD - cum->nbytes;
|
}
|
}
|
|
|
|
|
/* Return the high and low words of a CONST_DOUBLE */
|
/* Return the high and low words of a CONST_DOUBLE */
|
|
|
static void
|
static void
|
const_double_split (rtx x, HOST_WIDE_INT * p_high, HOST_WIDE_INT * p_low)
|
const_double_split (rtx x, HOST_WIDE_INT * p_high, HOST_WIDE_INT * p_low)
|
{
|
{
|
if (GET_CODE (x) == CONST_DOUBLE)
|
if (GET_CODE (x) == CONST_DOUBLE)
|
{
|
{
|
long t[2];
|
long t[2];
|
REAL_VALUE_TYPE rv;
|
REAL_VALUE_TYPE rv;
|
|
|
switch (GET_MODE (x))
|
switch (GET_MODE (x))
|
{
|
{
|
case DFmode:
|
case DFmode:
|
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
|
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
|
REAL_VALUE_TO_TARGET_DOUBLE (rv, t);
|
REAL_VALUE_TO_TARGET_DOUBLE (rv, t);
|
*p_high = t[1]; /* since v850 is little endian */
|
*p_high = t[1]; /* since v850 is little endian */
|
*p_low = t[0]; /* high is second word */
|
*p_low = t[0]; /* high is second word */
|
return;
|
return;
|
|
|
case SFmode:
|
case SFmode:
|
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
|
REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
|
REAL_VALUE_TO_TARGET_SINGLE (rv, *p_high);
|
REAL_VALUE_TO_TARGET_SINGLE (rv, *p_high);
|
*p_low = 0;
|
*p_low = 0;
|
return;
|
return;
|
|
|
case VOIDmode:
|
case VOIDmode:
|
case DImode:
|
case DImode:
|
*p_high = CONST_DOUBLE_HIGH (x);
|
*p_high = CONST_DOUBLE_HIGH (x);
|
*p_low = CONST_DOUBLE_LOW (x);
|
*p_low = CONST_DOUBLE_LOW (x);
|
return;
|
return;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
fatal_insn ("const_double_split got a bad insn:", x);
|
fatal_insn ("const_double_split got a bad insn:", x);
|
}
|
}
|
|
|
|
|
/* Return the cost of the rtx R with code CODE. */
|
/* Return the cost of the rtx R with code CODE. */
|
|
|
static int
|
static int
|
const_costs_int (HOST_WIDE_INT value, int zero_cost)
|
const_costs_int (HOST_WIDE_INT value, int zero_cost)
|
{
|
{
|
if (CONST_OK_FOR_I (value))
|
if (CONST_OK_FOR_I (value))
|
return zero_cost;
|
return zero_cost;
|
else if (CONST_OK_FOR_J (value))
|
else if (CONST_OK_FOR_J (value))
|
return 1;
|
return 1;
|
else if (CONST_OK_FOR_K (value))
|
else if (CONST_OK_FOR_K (value))
|
return 2;
|
return 2;
|
else
|
else
|
return 4;
|
return 4;
|
}
|
}
|
|
|
static int
|
static int
|
const_costs (rtx r, enum rtx_code c)
|
const_costs (rtx r, enum rtx_code c)
|
{
|
{
|
HOST_WIDE_INT high, low;
|
HOST_WIDE_INT high, low;
|
|
|
switch (c)
|
switch (c)
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
return const_costs_int (INTVAL (r), 0);
|
return const_costs_int (INTVAL (r), 0);
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
const_double_split (r, &high, &low);
|
const_double_split (r, &high, &low);
|
if (GET_MODE (r) == SFmode)
|
if (GET_MODE (r) == SFmode)
|
return const_costs_int (high, 1);
|
return const_costs_int (high, 1);
|
else
|
else
|
return const_costs_int (high, 1) + const_costs_int (low, 1);
|
return const_costs_int (high, 1) + const_costs_int (low, 1);
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case LABEL_REF:
|
case LABEL_REF:
|
case CONST:
|
case CONST:
|
return 2;
|
return 2;
|
|
|
case HIGH:
|
case HIGH:
|
return 1;
|
return 1;
|
|
|
default:
|
default:
|
return 4;
|
return 4;
|
}
|
}
|
}
|
}
|
|
|
static bool
|
static bool
|
v850_rtx_costs (rtx x,
|
v850_rtx_costs (rtx x,
|
int codearg,
|
int codearg,
|
int outer_code ATTRIBUTE_UNUSED,
|
int outer_code ATTRIBUTE_UNUSED,
|
int * total, bool speed)
|
int * total, bool speed)
|
{
|
{
|
enum rtx_code code = (enum rtx_code) codearg;
|
enum rtx_code code = (enum rtx_code) codearg;
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
case CONST:
|
case CONST:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case LABEL_REF:
|
case LABEL_REF:
|
*total = COSTS_N_INSNS (const_costs (x, code));
|
*total = COSTS_N_INSNS (const_costs (x, code));
|
return true;
|
return true;
|
|
|
case MOD:
|
case MOD:
|
case DIV:
|
case DIV:
|
case UMOD:
|
case UMOD:
|
case UDIV:
|
case UDIV:
|
if (TARGET_V850E && !speed)
|
if (TARGET_V850E && !speed)
|
*total = 6;
|
*total = 6;
|
else
|
else
|
*total = 60;
|
*total = 60;
|
return true;
|
return true;
|
|
|
case MULT:
|
case MULT:
|
if (TARGET_V850E
|
if (TARGET_V850E
|
&& ( GET_MODE (x) == SImode
|
&& ( GET_MODE (x) == SImode
|
|| GET_MODE (x) == HImode
|
|| GET_MODE (x) == HImode
|
|| GET_MODE (x) == QImode))
|
|| GET_MODE (x) == QImode))
|
{
|
{
|
if (GET_CODE (XEXP (x, 1)) == REG)
|
if (GET_CODE (XEXP (x, 1)) == REG)
|
*total = 4;
|
*total = 4;
|
else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
else if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
{
|
{
|
if (CONST_OK_FOR_O (INTVAL (XEXP (x, 1))))
|
if (CONST_OK_FOR_O (INTVAL (XEXP (x, 1))))
|
*total = 6;
|
*total = 6;
|
else if (CONST_OK_FOR_K (INTVAL (XEXP (x, 1))))
|
else if (CONST_OK_FOR_K (INTVAL (XEXP (x, 1))))
|
*total = 10;
|
*total = 10;
|
}
|
}
|
}
|
}
|
else
|
else
|
*total = 20;
|
*total = 20;
|
return true;
|
return true;
|
|
|
case ZERO_EXTRACT:
|
case ZERO_EXTRACT:
|
if (outer_code == COMPARE)
|
if (outer_code == COMPARE)
|
*total = 0;
|
*total = 0;
|
return false;
|
return false;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
/* Print operand X using operand code CODE to assembly language output file
|
/* Print operand X using operand code CODE to assembly language output file
|
FILE. */
|
FILE. */
|
|
|
void
|
void
|
print_operand (FILE * file, rtx x, int code)
|
print_operand (FILE * file, rtx x, int code)
|
{
|
{
|
HOST_WIDE_INT high, low;
|
HOST_WIDE_INT high, low;
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case 'c':
|
case 'c':
|
/* We use 'c' operands with symbols for .vtinherit */
|
/* We use 'c' operands with symbols for .vtinherit */
|
if (GET_CODE (x) == SYMBOL_REF)
|
if (GET_CODE (x) == SYMBOL_REF)
|
{
|
{
|
output_addr_const(file, x);
|
output_addr_const(file, x);
|
break;
|
break;
|
}
|
}
|
/* fall through */
|
/* fall through */
|
case 'b':
|
case 'b':
|
case 'B':
|
case 'B':
|
case 'C':
|
case 'C':
|
switch ((code == 'B' || code == 'C')
|
switch ((code == 'B' || code == 'C')
|
? reverse_condition (GET_CODE (x)) : GET_CODE (x))
|
? reverse_condition (GET_CODE (x)) : GET_CODE (x))
|
{
|
{
|
case NE:
|
case NE:
|
if (code == 'c' || code == 'C')
|
if (code == 'c' || code == 'C')
|
fprintf (file, "nz");
|
fprintf (file, "nz");
|
else
|
else
|
fprintf (file, "ne");
|
fprintf (file, "ne");
|
break;
|
break;
|
case EQ:
|
case EQ:
|
if (code == 'c' || code == 'C')
|
if (code == 'c' || code == 'C')
|
fprintf (file, "z");
|
fprintf (file, "z");
|
else
|
else
|
fprintf (file, "e");
|
fprintf (file, "e");
|
break;
|
break;
|
case GE:
|
case GE:
|
fprintf (file, "ge");
|
fprintf (file, "ge");
|
break;
|
break;
|
case GT:
|
case GT:
|
fprintf (file, "gt");
|
fprintf (file, "gt");
|
break;
|
break;
|
case LE:
|
case LE:
|
fprintf (file, "le");
|
fprintf (file, "le");
|
break;
|
break;
|
case LT:
|
case LT:
|
fprintf (file, "lt");
|
fprintf (file, "lt");
|
break;
|
break;
|
case GEU:
|
case GEU:
|
fprintf (file, "nl");
|
fprintf (file, "nl");
|
break;
|
break;
|
case GTU:
|
case GTU:
|
fprintf (file, "h");
|
fprintf (file, "h");
|
break;
|
break;
|
case LEU:
|
case LEU:
|
fprintf (file, "nh");
|
fprintf (file, "nh");
|
break;
|
break;
|
case LTU:
|
case LTU:
|
fprintf (file, "l");
|
fprintf (file, "l");
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
case 'F': /* high word of CONST_DOUBLE */
|
case 'F': /* high word of CONST_DOUBLE */
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
fprintf (file, "%d", (INTVAL (x) >= 0) ? 0 : -1);
|
fprintf (file, "%d", (INTVAL (x) >= 0) ? 0 : -1);
|
break;
|
break;
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
const_double_split (x, &high, &low);
|
const_double_split (x, &high, &low);
|
fprintf (file, "%ld", (long) high);
|
fprintf (file, "%ld", (long) high);
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
case 'G': /* low word of CONST_DOUBLE */
|
case 'G': /* low word of CONST_DOUBLE */
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
fprintf (file, "%ld", (long) INTVAL (x));
|
fprintf (file, "%ld", (long) INTVAL (x));
|
break;
|
break;
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
const_double_split (x, &high, &low);
|
const_double_split (x, &high, &low);
|
fprintf (file, "%ld", (long) low);
|
fprintf (file, "%ld", (long) low);
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
case 'L':
|
case 'L':
|
fprintf (file, "%d\n", (int)(INTVAL (x) & 0xffff));
|
fprintf (file, "%d\n", (int)(INTVAL (x) & 0xffff));
|
break;
|
break;
|
case 'M':
|
case 'M':
|
fprintf (file, "%d", exact_log2 (INTVAL (x)));
|
fprintf (file, "%d", exact_log2 (INTVAL (x)));
|
break;
|
break;
|
case 'O':
|
case 'O':
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
|
|
if (GET_CODE (x) == CONST)
|
if (GET_CODE (x) == CONST)
|
x = XEXP (XEXP (x, 0), 0);
|
x = XEXP (XEXP (x, 0), 0);
|
else
|
else
|
gcc_assert (GET_CODE (x) == SYMBOL_REF);
|
gcc_assert (GET_CODE (x) == SYMBOL_REF);
|
|
|
if (SYMBOL_REF_ZDA_P (x))
|
if (SYMBOL_REF_ZDA_P (x))
|
fprintf (file, "zdaoff");
|
fprintf (file, "zdaoff");
|
else if (SYMBOL_REF_SDA_P (x))
|
else if (SYMBOL_REF_SDA_P (x))
|
fprintf (file, "sdaoff");
|
fprintf (file, "sdaoff");
|
else if (SYMBOL_REF_TDA_P (x))
|
else if (SYMBOL_REF_TDA_P (x))
|
fprintf (file, "tdaoff");
|
fprintf (file, "tdaoff");
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
break;
|
break;
|
case 'P':
|
case 'P':
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
break;
|
break;
|
case 'Q':
|
case 'Q':
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
gcc_assert (special_symbolref_operand (x, VOIDmode));
|
|
|
if (GET_CODE (x) == CONST)
|
if (GET_CODE (x) == CONST)
|
x = XEXP (XEXP (x, 0), 0);
|
x = XEXP (XEXP (x, 0), 0);
|
else
|
else
|
gcc_assert (GET_CODE (x) == SYMBOL_REF);
|
gcc_assert (GET_CODE (x) == SYMBOL_REF);
|
|
|
if (SYMBOL_REF_ZDA_P (x))
|
if (SYMBOL_REF_ZDA_P (x))
|
fprintf (file, "r0");
|
fprintf (file, "r0");
|
else if (SYMBOL_REF_SDA_P (x))
|
else if (SYMBOL_REF_SDA_P (x))
|
fprintf (file, "gp");
|
fprintf (file, "gp");
|
else if (SYMBOL_REF_TDA_P (x))
|
else if (SYMBOL_REF_TDA_P (x))
|
fprintf (file, "ep");
|
fprintf (file, "ep");
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
break;
|
break;
|
case 'R': /* 2nd word of a double. */
|
case 'R': /* 2nd word of a double. */
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case REG:
|
case REG:
|
fprintf (file, reg_names[REGNO (x) + 1]);
|
fprintf (file, reg_names[REGNO (x) + 1]);
|
break;
|
break;
|
case MEM:
|
case MEM:
|
x = XEXP (adjust_address (x, SImode, 4), 0);
|
x = XEXP (adjust_address (x, SImode, 4), 0);
|
print_operand_address (file, x);
|
print_operand_address (file, x);
|
if (GET_CODE (x) == CONST_INT)
|
if (GET_CODE (x) == CONST_INT)
|
fprintf (file, "[r0]");
|
fprintf (file, "[r0]");
|
break;
|
break;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
case 'S':
|
case 'S':
|
{
|
{
|
/* If it's a reference to a TDA variable, use sst/sld vs. st/ld. */
|
/* If it's a reference to a TDA variable, use sst/sld vs. st/ld. */
|
if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), FALSE))
|
if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), FALSE))
|
fputs ("s", file);
|
fputs ("s", file);
|
|
|
break;
|
break;
|
}
|
}
|
case 'T':
|
case 'T':
|
{
|
{
|
/* Like an 'S' operand above, but for unsigned loads only. */
|
/* Like an 'S' operand above, but for unsigned loads only. */
|
if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), TRUE))
|
if (GET_CODE (x) == MEM && ep_memory_operand (x, GET_MODE (x), TRUE))
|
fputs ("s", file);
|
fputs ("s", file);
|
|
|
break;
|
break;
|
}
|
}
|
case 'W': /* print the instruction suffix */
|
case 'W': /* print the instruction suffix */
|
switch (GET_MODE (x))
|
switch (GET_MODE (x))
|
{
|
{
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
|
|
case QImode: fputs (".b", file); break;
|
case QImode: fputs (".b", file); break;
|
case HImode: fputs (".h", file); break;
|
case HImode: fputs (".h", file); break;
|
case SImode: fputs (".w", file); break;
|
case SImode: fputs (".w", file); break;
|
case SFmode: fputs (".w", file); break;
|
case SFmode: fputs (".w", file); break;
|
}
|
}
|
break;
|
break;
|
case '.': /* register r0 */
|
case '.': /* register r0 */
|
fputs (reg_names[0], file);
|
fputs (reg_names[0], file);
|
break;
|
break;
|
case 'z': /* reg or zero */
|
case 'z': /* reg or zero */
|
if (GET_CODE (x) == REG)
|
if (GET_CODE (x) == REG)
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
else
|
else
|
{
|
{
|
gcc_assert (x == const0_rtx);
|
gcc_assert (x == const0_rtx);
|
fputs (reg_names[0], file);
|
fputs (reg_names[0], file);
|
}
|
}
|
break;
|
break;
|
default:
|
default:
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case MEM:
|
case MEM:
|
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
|
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
|
output_address (gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, 0),
|
output_address (gen_rtx_PLUS (SImode, gen_rtx_REG (SImode, 0),
|
XEXP (x, 0)));
|
XEXP (x, 0)));
|
else
|
else
|
output_address (XEXP (x, 0));
|
output_address (XEXP (x, 0));
|
break;
|
break;
|
|
|
case REG:
|
case REG:
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
break;
|
break;
|
case SUBREG:
|
case SUBREG:
|
fputs (reg_names[subreg_regno (x)], file);
|
fputs (reg_names[subreg_regno (x)], file);
|
break;
|
break;
|
case CONST_INT:
|
case CONST_INT:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
case CONST:
|
case CONST:
|
case LABEL_REF:
|
case LABEL_REF:
|
case CODE_LABEL:
|
case CODE_LABEL:
|
print_operand_address (file, x);
|
print_operand_address (file, x);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Output assembly language output for the address ADDR to FILE. */
|
/* Output assembly language output for the address ADDR to FILE. */
|
|
|
void
|
void
|
print_operand_address (FILE * file, rtx addr)
|
print_operand_address (FILE * file, rtx addr)
|
{
|
{
|
switch (GET_CODE (addr))
|
switch (GET_CODE (addr))
|
{
|
{
|
case REG:
|
case REG:
|
fprintf (file, "0[");
|
fprintf (file, "0[");
|
print_operand (file, addr, 0);
|
print_operand (file, addr, 0);
|
fprintf (file, "]");
|
fprintf (file, "]");
|
break;
|
break;
|
case LO_SUM:
|
case LO_SUM:
|
if (GET_CODE (XEXP (addr, 0)) == REG)
|
if (GET_CODE (XEXP (addr, 0)) == REG)
|
{
|
{
|
/* reg,foo */
|
/* reg,foo */
|
fprintf (file, "lo(");
|
fprintf (file, "lo(");
|
print_operand (file, XEXP (addr, 1), 0);
|
print_operand (file, XEXP (addr, 1), 0);
|
fprintf (file, ")[");
|
fprintf (file, ")[");
|
print_operand (file, XEXP (addr, 0), 0);
|
print_operand (file, XEXP (addr, 0), 0);
|
fprintf (file, "]");
|
fprintf (file, "]");
|
}
|
}
|
break;
|
break;
|
case PLUS:
|
case PLUS:
|
if (GET_CODE (XEXP (addr, 0)) == REG
|
if (GET_CODE (XEXP (addr, 0)) == REG
|
|| GET_CODE (XEXP (addr, 0)) == SUBREG)
|
|| GET_CODE (XEXP (addr, 0)) == SUBREG)
|
{
|
{
|
/* reg,foo */
|
/* reg,foo */
|
print_operand (file, XEXP (addr, 1), 0);
|
print_operand (file, XEXP (addr, 1), 0);
|
fprintf (file, "[");
|
fprintf (file, "[");
|
print_operand (file, XEXP (addr, 0), 0);
|
print_operand (file, XEXP (addr, 0), 0);
|
fprintf (file, "]");
|
fprintf (file, "]");
|
}
|
}
|
else
|
else
|
{
|
{
|
print_operand (file, XEXP (addr, 0), 0);
|
print_operand (file, XEXP (addr, 0), 0);
|
fprintf (file, "+");
|
fprintf (file, "+");
|
print_operand (file, XEXP (addr, 1), 0);
|
print_operand (file, XEXP (addr, 1), 0);
|
}
|
}
|
break;
|
break;
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
{
|
{
|
const char *off_name = NULL;
|
const char *off_name = NULL;
|
const char *reg_name = NULL;
|
const char *reg_name = NULL;
|
|
|
if (SYMBOL_REF_ZDA_P (addr))
|
if (SYMBOL_REF_ZDA_P (addr))
|
{
|
{
|
off_name = "zdaoff";
|
off_name = "zdaoff";
|
reg_name = "r0";
|
reg_name = "r0";
|
}
|
}
|
else if (SYMBOL_REF_SDA_P (addr))
|
else if (SYMBOL_REF_SDA_P (addr))
|
{
|
{
|
off_name = "sdaoff";
|
off_name = "sdaoff";
|
reg_name = "gp";
|
reg_name = "gp";
|
}
|
}
|
else if (SYMBOL_REF_TDA_P (addr))
|
else if (SYMBOL_REF_TDA_P (addr))
|
{
|
{
|
off_name = "tdaoff";
|
off_name = "tdaoff";
|
reg_name = "ep";
|
reg_name = "ep";
|
}
|
}
|
|
|
if (off_name)
|
if (off_name)
|
fprintf (file, "%s(", off_name);
|
fprintf (file, "%s(", off_name);
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
if (reg_name)
|
if (reg_name)
|
fprintf (file, ")[%s]", reg_name);
|
fprintf (file, ")[%s]", reg_name);
|
}
|
}
|
break;
|
break;
|
case CONST:
|
case CONST:
|
if (special_symbolref_operand (addr, VOIDmode))
|
if (special_symbolref_operand (addr, VOIDmode))
|
{
|
{
|
rtx x = XEXP (XEXP (addr, 0), 0);
|
rtx x = XEXP (XEXP (addr, 0), 0);
|
const char *off_name;
|
const char *off_name;
|
const char *reg_name;
|
const char *reg_name;
|
|
|
if (SYMBOL_REF_ZDA_P (x))
|
if (SYMBOL_REF_ZDA_P (x))
|
{
|
{
|
off_name = "zdaoff";
|
off_name = "zdaoff";
|
reg_name = "r0";
|
reg_name = "r0";
|
}
|
}
|
else if (SYMBOL_REF_SDA_P (x))
|
else if (SYMBOL_REF_SDA_P (x))
|
{
|
{
|
off_name = "sdaoff";
|
off_name = "sdaoff";
|
reg_name = "gp";
|
reg_name = "gp";
|
}
|
}
|
else if (SYMBOL_REF_TDA_P (x))
|
else if (SYMBOL_REF_TDA_P (x))
|
{
|
{
|
off_name = "tdaoff";
|
off_name = "tdaoff";
|
reg_name = "ep";
|
reg_name = "ep";
|
}
|
}
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
|
|
fprintf (file, "%s(", off_name);
|
fprintf (file, "%s(", off_name);
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
fprintf (file, ")[%s]", reg_name);
|
fprintf (file, ")[%s]", reg_name);
|
}
|
}
|
else
|
else
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
break;
|
break;
|
default:
|
default:
|
output_addr_const (file, addr);
|
output_addr_const (file, addr);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* When assemble_integer is used to emit the offsets for a switch
|
/* When assemble_integer is used to emit the offsets for a switch
|
table it can encounter (TRUNCATE:HI (MINUS:SI (LABEL_REF:SI) (LABEL_REF:SI))).
|
table it can encounter (TRUNCATE:HI (MINUS:SI (LABEL_REF:SI) (LABEL_REF:SI))).
|
output_addr_const will normally barf at this, but it is OK to omit
|
output_addr_const will normally barf at this, but it is OK to omit
|
the truncate and just emit the difference of the two labels. The
|
the truncate and just emit the difference of the two labels. The
|
.hword directive will automatically handle the truncation for us.
|
.hword directive will automatically handle the truncation for us.
|
|
|
Returns 1 if rtx was handled, 0 otherwise. */
|
Returns 1 if rtx was handled, 0 otherwise. */
|
|
|
int
|
int
|
v850_output_addr_const_extra (FILE * file, rtx x)
|
v850_output_addr_const_extra (FILE * file, rtx x)
|
{
|
{
|
if (GET_CODE (x) != TRUNCATE)
|
if (GET_CODE (x) != TRUNCATE)
|
return 0;
|
return 0;
|
|
|
x = XEXP (x, 0);
|
x = XEXP (x, 0);
|
|
|
/* We must also handle the case where the switch table was passed a
|
/* We must also handle the case where the switch table was passed a
|
constant value and so has been collapsed. In this case the first
|
constant value and so has been collapsed. In this case the first
|
label will have been deleted. In such a case it is OK to emit
|
label will have been deleted. In such a case it is OK to emit
|
nothing, since the table will not be used.
|
nothing, since the table will not be used.
|
(cf gcc.c-torture/compile/990801-1.c). */
|
(cf gcc.c-torture/compile/990801-1.c). */
|
if (GET_CODE (x) == MINUS
|
if (GET_CODE (x) == MINUS
|
&& GET_CODE (XEXP (x, 0)) == LABEL_REF
|
&& GET_CODE (XEXP (x, 0)) == LABEL_REF
|
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == CODE_LABEL
|
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == CODE_LABEL
|
&& INSN_DELETED_P (XEXP (XEXP (x, 0), 0)))
|
&& INSN_DELETED_P (XEXP (XEXP (x, 0), 0)))
|
return 1;
|
return 1;
|
|
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return appropriate code to load up a 1, 2, or 4 integer/floating
|
/* Return appropriate code to load up a 1, 2, or 4 integer/floating
|
point value. */
|
point value. */
|
|
|
const char *
|
const char *
|
output_move_single (rtx * operands)
|
output_move_single (rtx * operands)
|
{
|
{
|
rtx dst = operands[0];
|
rtx dst = operands[0];
|
rtx src = operands[1];
|
rtx src = operands[1];
|
|
|
if (REG_P (dst))
|
if (REG_P (dst))
|
{
|
{
|
if (REG_P (src))
|
if (REG_P (src))
|
return "mov %1,%0";
|
return "mov %1,%0";
|
|
|
else if (GET_CODE (src) == CONST_INT)
|
else if (GET_CODE (src) == CONST_INT)
|
{
|
{
|
HOST_WIDE_INT value = INTVAL (src);
|
HOST_WIDE_INT value = INTVAL (src);
|
|
|
if (CONST_OK_FOR_J (value)) /* Signed 5-bit immediate. */
|
if (CONST_OK_FOR_J (value)) /* Signed 5-bit immediate. */
|
return "mov %1,%0";
|
return "mov %1,%0";
|
|
|
else if (CONST_OK_FOR_K (value)) /* Signed 16-bit immediate. */
|
else if (CONST_OK_FOR_K (value)) /* Signed 16-bit immediate. */
|
return "movea lo(%1),%.,%0";
|
return "movea lo(%1),%.,%0";
|
|
|
else if (CONST_OK_FOR_L (value)) /* Upper 16 bits were set. */
|
else if (CONST_OK_FOR_L (value)) /* Upper 16 bits were set. */
|
return "movhi hi(%1),%.,%0";
|
return "movhi hi(%1),%.,%0";
|
|
|
/* A random constant. */
|
/* A random constant. */
|
else if (TARGET_V850E)
|
else if (TARGET_V850E)
|
return "mov %1,%0";
|
return "mov %1,%0";
|
else
|
else
|
return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
|
return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
|
}
|
}
|
|
|
else if (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode)
|
else if (GET_CODE (src) == CONST_DOUBLE && GET_MODE (src) == SFmode)
|
{
|
{
|
HOST_WIDE_INT high, low;
|
HOST_WIDE_INT high, low;
|
|
|
const_double_split (src, &high, &low);
|
const_double_split (src, &high, &low);
|
|
|
if (CONST_OK_FOR_J (high)) /* Signed 5-bit immediate. */
|
if (CONST_OK_FOR_J (high)) /* Signed 5-bit immediate. */
|
return "mov %F1,%0";
|
return "mov %F1,%0";
|
|
|
else if (CONST_OK_FOR_K (high)) /* Signed 16-bit immediate. */
|
else if (CONST_OK_FOR_K (high)) /* Signed 16-bit immediate. */
|
return "movea lo(%F1),%.,%0";
|
return "movea lo(%F1),%.,%0";
|
|
|
else if (CONST_OK_FOR_L (high)) /* Upper 16 bits were set. */
|
else if (CONST_OK_FOR_L (high)) /* Upper 16 bits were set. */
|
return "movhi hi(%F1),%.,%0";
|
return "movhi hi(%F1),%.,%0";
|
|
|
/* A random constant. */
|
/* A random constant. */
|
else if (TARGET_V850E)
|
else if (TARGET_V850E)
|
return "mov %F1,%0";
|
return "mov %F1,%0";
|
|
|
else
|
else
|
return "movhi hi(%F1),%.,%0\n\tmovea lo(%F1),%0,%0";
|
return "movhi hi(%F1),%.,%0\n\tmovea lo(%F1),%0,%0";
|
}
|
}
|
|
|
else if (GET_CODE (src) == MEM)
|
else if (GET_CODE (src) == MEM)
|
return "%S1ld%W1 %1,%0";
|
return "%S1ld%W1 %1,%0";
|
|
|
else if (special_symbolref_operand (src, VOIDmode))
|
else if (special_symbolref_operand (src, VOIDmode))
|
return "movea %O1(%P1),%Q1,%0";
|
return "movea %O1(%P1),%Q1,%0";
|
|
|
else if (GET_CODE (src) == LABEL_REF
|
else if (GET_CODE (src) == LABEL_REF
|
|| GET_CODE (src) == SYMBOL_REF
|
|| GET_CODE (src) == SYMBOL_REF
|
|| GET_CODE (src) == CONST)
|
|| GET_CODE (src) == CONST)
|
{
|
{
|
if (TARGET_V850E)
|
if (TARGET_V850E)
|
return "mov hilo(%1),%0";
|
return "mov hilo(%1),%0";
|
else
|
else
|
return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
|
return "movhi hi(%1),%.,%0\n\tmovea lo(%1),%0,%0";
|
}
|
}
|
|
|
else if (GET_CODE (src) == HIGH)
|
else if (GET_CODE (src) == HIGH)
|
return "movhi hi(%1),%.,%0";
|
return "movhi hi(%1),%.,%0";
|
|
|
else if (GET_CODE (src) == LO_SUM)
|
else if (GET_CODE (src) == LO_SUM)
|
{
|
{
|
operands[2] = XEXP (src, 0);
|
operands[2] = XEXP (src, 0);
|
operands[3] = XEXP (src, 1);
|
operands[3] = XEXP (src, 1);
|
return "movea lo(%3),%2,%0";
|
return "movea lo(%3),%2,%0";
|
}
|
}
|
}
|
}
|
|
|
else if (GET_CODE (dst) == MEM)
|
else if (GET_CODE (dst) == MEM)
|
{
|
{
|
if (REG_P (src))
|
if (REG_P (src))
|
return "%S0st%W0 %1,%0";
|
return "%S0st%W0 %1,%0";
|
|
|
else if (GET_CODE (src) == CONST_INT && INTVAL (src) == 0)
|
else if (GET_CODE (src) == CONST_INT && INTVAL (src) == 0)
|
return "%S0st%W0 %.,%0";
|
return "%S0st%W0 %.,%0";
|
|
|
else if (GET_CODE (src) == CONST_DOUBLE
|
else if (GET_CODE (src) == CONST_DOUBLE
|
&& CONST0_RTX (GET_MODE (dst)) == src)
|
&& CONST0_RTX (GET_MODE (dst)) == src)
|
return "%S0st%W0 %.,%0";
|
return "%S0st%W0 %.,%0";
|
}
|
}
|
|
|
fatal_insn ("output_move_single:", gen_rtx_SET (VOIDmode, dst, src));
|
fatal_insn ("output_move_single:", gen_rtx_SET (VOIDmode, dst, src));
|
return "";
|
return "";
|
}
|
}
|
|
|
|
|
/* Return maximum offset supported for a short EP memory reference of mode
|
/* Return maximum offset supported for a short EP memory reference of mode
|
MODE and signedness UNSIGNEDP. */
|
MODE and signedness UNSIGNEDP. */
|
|
|
static int
|
static int
|
ep_memory_offset (enum machine_mode mode, int unsignedp ATTRIBUTE_UNUSED)
|
ep_memory_offset (enum machine_mode mode, int unsignedp ATTRIBUTE_UNUSED)
|
{
|
{
|
int max_offset = 0;
|
int max_offset = 0;
|
|
|
switch (mode)
|
switch (mode)
|
{
|
{
|
case QImode:
|
case QImode:
|
if (TARGET_SMALL_SLD)
|
if (TARGET_SMALL_SLD)
|
max_offset = (1 << 4);
|
max_offset = (1 << 4);
|
else if (TARGET_V850E
|
else if (TARGET_V850E
|
&& ( ( unsignedp && ! TARGET_US_BIT_SET)
|
&& ( ( unsignedp && ! TARGET_US_BIT_SET)
|
|| (! unsignedp && TARGET_US_BIT_SET)))
|
|| (! unsignedp && TARGET_US_BIT_SET)))
|
max_offset = (1 << 4);
|
max_offset = (1 << 4);
|
else
|
else
|
max_offset = (1 << 7);
|
max_offset = (1 << 7);
|
break;
|
break;
|
|
|
case HImode:
|
case HImode:
|
if (TARGET_SMALL_SLD)
|
if (TARGET_SMALL_SLD)
|
max_offset = (1 << 5);
|
max_offset = (1 << 5);
|
else if (TARGET_V850E
|
else if (TARGET_V850E
|
&& ( ( unsignedp && ! TARGET_US_BIT_SET)
|
&& ( ( unsignedp && ! TARGET_US_BIT_SET)
|
|| (! unsignedp && TARGET_US_BIT_SET)))
|
|| (! unsignedp && TARGET_US_BIT_SET)))
|
max_offset = (1 << 5);
|
max_offset = (1 << 5);
|
else
|
else
|
max_offset = (1 << 8);
|
max_offset = (1 << 8);
|
break;
|
break;
|
|
|
case SImode:
|
case SImode:
|
case SFmode:
|
case SFmode:
|
max_offset = (1 << 8);
|
max_offset = (1 << 8);
|
break;
|
break;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return max_offset;
|
return max_offset;
|
}
|
}
|
|
|
/* Return true if OP is a valid short EP memory reference */
|
/* Return true if OP is a valid short EP memory reference */
|
|
|
int
|
int
|
ep_memory_operand (rtx op, enum machine_mode mode, int unsigned_load)
|
ep_memory_operand (rtx op, enum machine_mode mode, int unsigned_load)
|
{
|
{
|
rtx addr, op0, op1;
|
rtx addr, op0, op1;
|
int max_offset;
|
int max_offset;
|
int mask;
|
int mask;
|
|
|
/* If we are not using the EP register on a per-function basis
|
/* If we are not using the EP register on a per-function basis
|
then do not allow this optimization at all. This is to
|
then do not allow this optimization at all. This is to
|
prevent the use of the SLD/SST instructions which cannot be
|
prevent the use of the SLD/SST instructions which cannot be
|
guaranteed to work properly due to a hardware bug. */
|
guaranteed to work properly due to a hardware bug. */
|
if (!TARGET_EP)
|
if (!TARGET_EP)
|
return FALSE;
|
return FALSE;
|
|
|
if (GET_CODE (op) != MEM)
|
if (GET_CODE (op) != MEM)
|
return FALSE;
|
return FALSE;
|
|
|
max_offset = ep_memory_offset (mode, unsigned_load);
|
max_offset = ep_memory_offset (mode, unsigned_load);
|
|
|
mask = GET_MODE_SIZE (mode) - 1;
|
mask = GET_MODE_SIZE (mode) - 1;
|
|
|
addr = XEXP (op, 0);
|
addr = XEXP (op, 0);
|
if (GET_CODE (addr) == CONST)
|
if (GET_CODE (addr) == CONST)
|
addr = XEXP (addr, 0);
|
addr = XEXP (addr, 0);
|
|
|
switch (GET_CODE (addr))
|
switch (GET_CODE (addr))
|
{
|
{
|
default:
|
default:
|
break;
|
break;
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
return SYMBOL_REF_TDA_P (addr);
|
return SYMBOL_REF_TDA_P (addr);
|
|
|
case REG:
|
case REG:
|
return REGNO (addr) == EP_REGNUM;
|
return REGNO (addr) == EP_REGNUM;
|
|
|
case PLUS:
|
case PLUS:
|
op0 = XEXP (addr, 0);
|
op0 = XEXP (addr, 0);
|
op1 = XEXP (addr, 1);
|
op1 = XEXP (addr, 1);
|
if (GET_CODE (op1) == CONST_INT
|
if (GET_CODE (op1) == CONST_INT
|
&& INTVAL (op1) < max_offset
|
&& INTVAL (op1) < max_offset
|
&& INTVAL (op1) >= 0
|
&& INTVAL (op1) >= 0
|
&& (INTVAL (op1) & mask) == 0)
|
&& (INTVAL (op1) & mask) == 0)
|
{
|
{
|
if (GET_CODE (op0) == REG && REGNO (op0) == EP_REGNUM)
|
if (GET_CODE (op0) == REG && REGNO (op0) == EP_REGNUM)
|
return TRUE;
|
return TRUE;
|
|
|
if (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_TDA_P (op0))
|
if (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_TDA_P (op0))
|
return TRUE;
|
return TRUE;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
return FALSE;
|
return FALSE;
|
}
|
}
|
|
|
/* Substitute memory references involving a pointer, to use the ep pointer,
|
/* Substitute memory references involving a pointer, to use the ep pointer,
|
taking care to save and preserve the ep. */
|
taking care to save and preserve the ep. */
|
|
|
static void
|
static void
|
substitute_ep_register (rtx first_insn,
|
substitute_ep_register (rtx first_insn,
|
rtx last_insn,
|
rtx last_insn,
|
int uses,
|
int uses,
|
int regno,
|
int regno,
|
rtx * p_r1,
|
rtx * p_r1,
|
rtx * p_ep)
|
rtx * p_ep)
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, regno);
|
rtx reg = gen_rtx_REG (Pmode, regno);
|
rtx insn;
|
rtx insn;
|
|
|
if (!*p_r1)
|
if (!*p_r1)
|
{
|
{
|
df_set_regs_ever_live (1, true);
|
df_set_regs_ever_live (1, true);
|
*p_r1 = gen_rtx_REG (Pmode, 1);
|
*p_r1 = gen_rtx_REG (Pmode, 1);
|
*p_ep = gen_rtx_REG (Pmode, 30);
|
*p_ep = gen_rtx_REG (Pmode, 30);
|
}
|
}
|
|
|
if (TARGET_DEBUG)
|
if (TARGET_DEBUG)
|
fprintf (stderr, "\
|
fprintf (stderr, "\
|
Saved %d bytes (%d uses of register %s) in function %s, starting as insn %d, ending at %d\n",
|
Saved %d bytes (%d uses of register %s) in function %s, starting as insn %d, ending at %d\n",
|
2 * (uses - 3), uses, reg_names[regno],
|
2 * (uses - 3), uses, reg_names[regno],
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
|
INSN_UID (first_insn), INSN_UID (last_insn));
|
INSN_UID (first_insn), INSN_UID (last_insn));
|
|
|
if (GET_CODE (first_insn) == NOTE)
|
if (GET_CODE (first_insn) == NOTE)
|
first_insn = next_nonnote_insn (first_insn);
|
first_insn = next_nonnote_insn (first_insn);
|
|
|
last_insn = next_nonnote_insn (last_insn);
|
last_insn = next_nonnote_insn (last_insn);
|
for (insn = first_insn; insn && insn != last_insn; insn = NEXT_INSN (insn))
|
for (insn = first_insn; insn && insn != last_insn; insn = NEXT_INSN (insn))
|
{
|
{
|
if (GET_CODE (insn) == INSN)
|
if (GET_CODE (insn) == INSN)
|
{
|
{
|
rtx pattern = single_set (insn);
|
rtx pattern = single_set (insn);
|
|
|
/* Replace the memory references. */
|
/* Replace the memory references. */
|
if (pattern)
|
if (pattern)
|
{
|
{
|
rtx *p_mem;
|
rtx *p_mem;
|
/* Memory operands are signed by default. */
|
/* Memory operands are signed by default. */
|
int unsignedp = FALSE;
|
int unsignedp = FALSE;
|
|
|
if (GET_CODE (SET_DEST (pattern)) == MEM
|
if (GET_CODE (SET_DEST (pattern)) == MEM
|
&& GET_CODE (SET_SRC (pattern)) == MEM)
|
&& GET_CODE (SET_SRC (pattern)) == MEM)
|
p_mem = (rtx *)0;
|
p_mem = (rtx *)0;
|
|
|
else if (GET_CODE (SET_DEST (pattern)) == MEM)
|
else if (GET_CODE (SET_DEST (pattern)) == MEM)
|
p_mem = &SET_DEST (pattern);
|
p_mem = &SET_DEST (pattern);
|
|
|
else if (GET_CODE (SET_SRC (pattern)) == MEM)
|
else if (GET_CODE (SET_SRC (pattern)) == MEM)
|
p_mem = &SET_SRC (pattern);
|
p_mem = &SET_SRC (pattern);
|
|
|
else if (GET_CODE (SET_SRC (pattern)) == SIGN_EXTEND
|
else if (GET_CODE (SET_SRC (pattern)) == SIGN_EXTEND
|
&& GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
|
&& GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
|
p_mem = &XEXP (SET_SRC (pattern), 0);
|
p_mem = &XEXP (SET_SRC (pattern), 0);
|
|
|
else if (GET_CODE (SET_SRC (pattern)) == ZERO_EXTEND
|
else if (GET_CODE (SET_SRC (pattern)) == ZERO_EXTEND
|
&& GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
|
&& GET_CODE (XEXP (SET_SRC (pattern), 0)) == MEM)
|
{
|
{
|
p_mem = &XEXP (SET_SRC (pattern), 0);
|
p_mem = &XEXP (SET_SRC (pattern), 0);
|
unsignedp = TRUE;
|
unsignedp = TRUE;
|
}
|
}
|
else
|
else
|
p_mem = (rtx *)0;
|
p_mem = (rtx *)0;
|
|
|
if (p_mem)
|
if (p_mem)
|
{
|
{
|
rtx addr = XEXP (*p_mem, 0);
|
rtx addr = XEXP (*p_mem, 0);
|
|
|
if (GET_CODE (addr) == REG && REGNO (addr) == (unsigned) regno)
|
if (GET_CODE (addr) == REG && REGNO (addr) == (unsigned) regno)
|
*p_mem = change_address (*p_mem, VOIDmode, *p_ep);
|
*p_mem = change_address (*p_mem, VOIDmode, *p_ep);
|
|
|
else if (GET_CODE (addr) == PLUS
|
else if (GET_CODE (addr) == PLUS
|
&& GET_CODE (XEXP (addr, 0)) == REG
|
&& GET_CODE (XEXP (addr, 0)) == REG
|
&& REGNO (XEXP (addr, 0)) == (unsigned) regno
|
&& REGNO (XEXP (addr, 0)) == (unsigned) regno
|
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
|
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
|
&& ((INTVAL (XEXP (addr, 1)))
|
&& ((INTVAL (XEXP (addr, 1)))
|
< ep_memory_offset (GET_MODE (*p_mem),
|
< ep_memory_offset (GET_MODE (*p_mem),
|
unsignedp))
|
unsignedp))
|
&& ((INTVAL (XEXP (addr, 1))) >= 0))
|
&& ((INTVAL (XEXP (addr, 1))) >= 0))
|
*p_mem = change_address (*p_mem, VOIDmode,
|
*p_mem = change_address (*p_mem, VOIDmode,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
*p_ep,
|
*p_ep,
|
XEXP (addr, 1)));
|
XEXP (addr, 1)));
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Optimize back to back cases of ep <- r1 & r1 <- ep. */
|
/* Optimize back to back cases of ep <- r1 & r1 <- ep. */
|
insn = prev_nonnote_insn (first_insn);
|
insn = prev_nonnote_insn (first_insn);
|
if (insn && GET_CODE (insn) == INSN
|
if (insn && GET_CODE (insn) == INSN
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& GET_CODE (PATTERN (insn)) == SET
|
&& SET_DEST (PATTERN (insn)) == *p_ep
|
&& SET_DEST (PATTERN (insn)) == *p_ep
|
&& SET_SRC (PATTERN (insn)) == *p_r1)
|
&& SET_SRC (PATTERN (insn)) == *p_r1)
|
delete_insn (insn);
|
delete_insn (insn);
|
else
|
else
|
emit_insn_before (gen_rtx_SET (Pmode, *p_r1, *p_ep), first_insn);
|
emit_insn_before (gen_rtx_SET (Pmode, *p_r1, *p_ep), first_insn);
|
|
|
emit_insn_before (gen_rtx_SET (Pmode, *p_ep, reg), first_insn);
|
emit_insn_before (gen_rtx_SET (Pmode, *p_ep, reg), first_insn);
|
emit_insn_before (gen_rtx_SET (Pmode, *p_ep, *p_r1), last_insn);
|
emit_insn_before (gen_rtx_SET (Pmode, *p_ep, *p_r1), last_insn);
|
}
|
}
|
|
|
|
|
/* TARGET_MACHINE_DEPENDENT_REORG. On the 850, we use it to implement
|
/* TARGET_MACHINE_DEPENDENT_REORG. On the 850, we use it to implement
|
the -mep mode to copy heavily used pointers to ep to use the implicit
|
the -mep mode to copy heavily used pointers to ep to use the implicit
|
addressing. */
|
addressing. */
|
|
|
static void
|
static void
|
v850_reorg (void)
|
v850_reorg (void)
|
{
|
{
|
struct
|
struct
|
{
|
{
|
int uses;
|
int uses;
|
rtx first_insn;
|
rtx first_insn;
|
rtx last_insn;
|
rtx last_insn;
|
}
|
}
|
regs[FIRST_PSEUDO_REGISTER];
|
regs[FIRST_PSEUDO_REGISTER];
|
|
|
int i;
|
int i;
|
int use_ep = FALSE;
|
int use_ep = FALSE;
|
rtx r1 = NULL_RTX;
|
rtx r1 = NULL_RTX;
|
rtx ep = NULL_RTX;
|
rtx ep = NULL_RTX;
|
rtx insn;
|
rtx insn;
|
rtx pattern;
|
rtx pattern;
|
|
|
/* If not ep mode, just return now. */
|
/* If not ep mode, just return now. */
|
if (!TARGET_EP)
|
if (!TARGET_EP)
|
return;
|
return;
|
|
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
regs[i].uses = 0;
|
regs[i].uses = 0;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
}
|
}
|
|
|
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
|
{
|
{
|
switch (GET_CODE (insn))
|
switch (GET_CODE (insn))
|
{
|
{
|
/* End of basic block */
|
/* End of basic block */
|
default:
|
default:
|
if (!use_ep)
|
if (!use_ep)
|
{
|
{
|
int max_uses = -1;
|
int max_uses = -1;
|
int max_regno = -1;
|
int max_regno = -1;
|
|
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
if (max_uses < regs[i].uses)
|
if (max_uses < regs[i].uses)
|
{
|
{
|
max_uses = regs[i].uses;
|
max_uses = regs[i].uses;
|
max_regno = i;
|
max_regno = i;
|
}
|
}
|
}
|
}
|
|
|
if (max_uses > 3)
|
if (max_uses > 3)
|
substitute_ep_register (regs[max_regno].first_insn,
|
substitute_ep_register (regs[max_regno].first_insn,
|
regs[max_regno].last_insn,
|
regs[max_regno].last_insn,
|
max_uses, max_regno, &r1, &ep);
|
max_uses, max_regno, &r1, &ep);
|
}
|
}
|
|
|
use_ep = FALSE;
|
use_ep = FALSE;
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
regs[i].uses = 0;
|
regs[i].uses = 0;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
}
|
}
|
break;
|
break;
|
|
|
case NOTE:
|
case NOTE:
|
break;
|
break;
|
|
|
case INSN:
|
case INSN:
|
pattern = single_set (insn);
|
pattern = single_set (insn);
|
|
|
/* See if there are any memory references we can shorten */
|
/* See if there are any memory references we can shorten */
|
if (pattern)
|
if (pattern)
|
{
|
{
|
rtx src = SET_SRC (pattern);
|
rtx src = SET_SRC (pattern);
|
rtx dest = SET_DEST (pattern);
|
rtx dest = SET_DEST (pattern);
|
rtx mem;
|
rtx mem;
|
/* Memory operands are signed by default. */
|
/* Memory operands are signed by default. */
|
int unsignedp = FALSE;
|
int unsignedp = FALSE;
|
|
|
/* We might have (SUBREG (MEM)) here, so just get rid of the
|
/* We might have (SUBREG (MEM)) here, so just get rid of the
|
subregs to make this code simpler. */
|
subregs to make this code simpler. */
|
if (GET_CODE (dest) == SUBREG
|
if (GET_CODE (dest) == SUBREG
|
&& (GET_CODE (SUBREG_REG (dest)) == MEM
|
&& (GET_CODE (SUBREG_REG (dest)) == MEM
|
|| GET_CODE (SUBREG_REG (dest)) == REG))
|
|| GET_CODE (SUBREG_REG (dest)) == REG))
|
alter_subreg (&dest);
|
alter_subreg (&dest);
|
if (GET_CODE (src) == SUBREG
|
if (GET_CODE (src) == SUBREG
|
&& (GET_CODE (SUBREG_REG (src)) == MEM
|
&& (GET_CODE (SUBREG_REG (src)) == MEM
|
|| GET_CODE (SUBREG_REG (src)) == REG))
|
|| GET_CODE (SUBREG_REG (src)) == REG))
|
alter_subreg (&src);
|
alter_subreg (&src);
|
|
|
if (GET_CODE (dest) == MEM && GET_CODE (src) == MEM)
|
if (GET_CODE (dest) == MEM && GET_CODE (src) == MEM)
|
mem = NULL_RTX;
|
mem = NULL_RTX;
|
|
|
else if (GET_CODE (dest) == MEM)
|
else if (GET_CODE (dest) == MEM)
|
mem = dest;
|
mem = dest;
|
|
|
else if (GET_CODE (src) == MEM)
|
else if (GET_CODE (src) == MEM)
|
mem = src;
|
mem = src;
|
|
|
else if (GET_CODE (src) == SIGN_EXTEND
|
else if (GET_CODE (src) == SIGN_EXTEND
|
&& GET_CODE (XEXP (src, 0)) == MEM)
|
&& GET_CODE (XEXP (src, 0)) == MEM)
|
mem = XEXP (src, 0);
|
mem = XEXP (src, 0);
|
|
|
else if (GET_CODE (src) == ZERO_EXTEND
|
else if (GET_CODE (src) == ZERO_EXTEND
|
&& GET_CODE (XEXP (src, 0)) == MEM)
|
&& GET_CODE (XEXP (src, 0)) == MEM)
|
{
|
{
|
mem = XEXP (src, 0);
|
mem = XEXP (src, 0);
|
unsignedp = TRUE;
|
unsignedp = TRUE;
|
}
|
}
|
else
|
else
|
mem = NULL_RTX;
|
mem = NULL_RTX;
|
|
|
if (mem && ep_memory_operand (mem, GET_MODE (mem), unsignedp))
|
if (mem && ep_memory_operand (mem, GET_MODE (mem), unsignedp))
|
use_ep = TRUE;
|
use_ep = TRUE;
|
|
|
else if (!use_ep && mem
|
else if (!use_ep && mem
|
&& GET_MODE_SIZE (GET_MODE (mem)) <= UNITS_PER_WORD)
|
&& GET_MODE_SIZE (GET_MODE (mem)) <= UNITS_PER_WORD)
|
{
|
{
|
rtx addr = XEXP (mem, 0);
|
rtx addr = XEXP (mem, 0);
|
int regno = -1;
|
int regno = -1;
|
int short_p;
|
int short_p;
|
|
|
if (GET_CODE (addr) == REG)
|
if (GET_CODE (addr) == REG)
|
{
|
{
|
short_p = TRUE;
|
short_p = TRUE;
|
regno = REGNO (addr);
|
regno = REGNO (addr);
|
}
|
}
|
|
|
else if (GET_CODE (addr) == PLUS
|
else if (GET_CODE (addr) == PLUS
|
&& GET_CODE (XEXP (addr, 0)) == REG
|
&& GET_CODE (XEXP (addr, 0)) == REG
|
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
|
&& GET_CODE (XEXP (addr, 1)) == CONST_INT
|
&& ((INTVAL (XEXP (addr, 1)))
|
&& ((INTVAL (XEXP (addr, 1)))
|
< ep_memory_offset (GET_MODE (mem), unsignedp))
|
< ep_memory_offset (GET_MODE (mem), unsignedp))
|
&& ((INTVAL (XEXP (addr, 1))) >= 0))
|
&& ((INTVAL (XEXP (addr, 1))) >= 0))
|
{
|
{
|
short_p = TRUE;
|
short_p = TRUE;
|
regno = REGNO (XEXP (addr, 0));
|
regno = REGNO (XEXP (addr, 0));
|
}
|
}
|
|
|
else
|
else
|
short_p = FALSE;
|
short_p = FALSE;
|
|
|
if (short_p)
|
if (short_p)
|
{
|
{
|
regs[regno].uses++;
|
regs[regno].uses++;
|
regs[regno].last_insn = insn;
|
regs[regno].last_insn = insn;
|
if (!regs[regno].first_insn)
|
if (!regs[regno].first_insn)
|
regs[regno].first_insn = insn;
|
regs[regno].first_insn = insn;
|
}
|
}
|
}
|
}
|
|
|
/* Loading up a register in the basic block zaps any savings
|
/* Loading up a register in the basic block zaps any savings
|
for the register */
|
for the register */
|
if (GET_CODE (dest) == REG)
|
if (GET_CODE (dest) == REG)
|
{
|
{
|
enum machine_mode mode = GET_MODE (dest);
|
enum machine_mode mode = GET_MODE (dest);
|
int regno;
|
int regno;
|
int endregno;
|
int endregno;
|
|
|
regno = REGNO (dest);
|
regno = REGNO (dest);
|
endregno = regno + HARD_REGNO_NREGS (regno, mode);
|
endregno = regno + HARD_REGNO_NREGS (regno, mode);
|
|
|
if (!use_ep)
|
if (!use_ep)
|
{
|
{
|
/* See if we can use the pointer before this
|
/* See if we can use the pointer before this
|
modification. */
|
modification. */
|
int max_uses = -1;
|
int max_uses = -1;
|
int max_regno = -1;
|
int max_regno = -1;
|
|
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
if (max_uses < regs[i].uses)
|
if (max_uses < regs[i].uses)
|
{
|
{
|
max_uses = regs[i].uses;
|
max_uses = regs[i].uses;
|
max_regno = i;
|
max_regno = i;
|
}
|
}
|
}
|
}
|
|
|
if (max_uses > 3
|
if (max_uses > 3
|
&& max_regno >= regno
|
&& max_regno >= regno
|
&& max_regno < endregno)
|
&& max_regno < endregno)
|
{
|
{
|
substitute_ep_register (regs[max_regno].first_insn,
|
substitute_ep_register (regs[max_regno].first_insn,
|
regs[max_regno].last_insn,
|
regs[max_regno].last_insn,
|
max_uses, max_regno, &r1,
|
max_uses, max_regno, &r1,
|
&ep);
|
&ep);
|
|
|
/* Since we made a substitution, zap all remembered
|
/* Since we made a substitution, zap all remembered
|
registers. */
|
registers. */
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
regs[i].uses = 0;
|
regs[i].uses = 0;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
for (i = regno; i < endregno; i++)
|
for (i = regno; i < endregno; i++)
|
{
|
{
|
regs[i].uses = 0;
|
regs[i].uses = 0;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].first_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
regs[i].last_insn = NULL_RTX;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
|
|
/* # of registers saved by the interrupt handler. */
|
/* # of registers saved by the interrupt handler. */
|
#define INTERRUPT_FIXED_NUM 4
|
#define INTERRUPT_FIXED_NUM 4
|
|
|
/* # of bytes for registers saved by the interrupt handler. */
|
/* # of bytes for registers saved by the interrupt handler. */
|
#define INTERRUPT_FIXED_SAVE_SIZE (4 * INTERRUPT_FIXED_NUM)
|
#define INTERRUPT_FIXED_SAVE_SIZE (4 * INTERRUPT_FIXED_NUM)
|
|
|
/* # of registers saved in register parameter area. */
|
/* # of registers saved in register parameter area. */
|
#define INTERRUPT_REGPARM_NUM 4
|
#define INTERRUPT_REGPARM_NUM 4
|
/* # of words saved for other registers. */
|
/* # of words saved for other registers. */
|
#define INTERRUPT_ALL_SAVE_NUM \
|
#define INTERRUPT_ALL_SAVE_NUM \
|
(30 - INTERRUPT_FIXED_NUM + INTERRUPT_REGPARM_NUM)
|
(30 - INTERRUPT_FIXED_NUM + INTERRUPT_REGPARM_NUM)
|
|
|
#define INTERRUPT_ALL_SAVE_SIZE (4 * INTERRUPT_ALL_SAVE_NUM)
|
#define INTERRUPT_ALL_SAVE_SIZE (4 * INTERRUPT_ALL_SAVE_NUM)
|
|
|
int
|
int
|
compute_register_save_size (long * p_reg_saved)
|
compute_register_save_size (long * p_reg_saved)
|
{
|
{
|
int size = 0;
|
int size = 0;
|
int i;
|
int i;
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
int call_p = df_regs_ever_live_p (LINK_POINTER_REGNUM);
|
int call_p = df_regs_ever_live_p (LINK_POINTER_REGNUM);
|
long reg_saved = 0;
|
long reg_saved = 0;
|
|
|
/* Count the return pointer if we need to save it. */
|
/* Count the return pointer if we need to save it. */
|
if (crtl->profile && !call_p)
|
if (crtl->profile && !call_p)
|
{
|
{
|
df_set_regs_ever_live (LINK_POINTER_REGNUM, true);
|
df_set_regs_ever_live (LINK_POINTER_REGNUM, true);
|
call_p = 1;
|
call_p = 1;
|
}
|
}
|
|
|
/* Count space for the register saves. */
|
/* Count space for the register saves. */
|
if (interrupt_handler)
|
if (interrupt_handler)
|
{
|
{
|
for (i = 0; i <= 31; i++)
|
for (i = 0; i <= 31; i++)
|
switch (i)
|
switch (i)
|
{
|
{
|
default:
|
default:
|
if (df_regs_ever_live_p (i) || call_p)
|
if (df_regs_ever_live_p (i) || call_p)
|
{
|
{
|
size += 4;
|
size += 4;
|
reg_saved |= 1L << i;
|
reg_saved |= 1L << i;
|
}
|
}
|
break;
|
break;
|
|
|
/* We don't save/restore r0 or the stack pointer */
|
/* We don't save/restore r0 or the stack pointer */
|
case 0:
|
case 0:
|
case STACK_POINTER_REGNUM:
|
case STACK_POINTER_REGNUM:
|
break;
|
break;
|
|
|
/* For registers with fixed use, we save them, set them to the
|
/* For registers with fixed use, we save them, set them to the
|
appropriate value, and then restore them.
|
appropriate value, and then restore them.
|
These registers are handled specially, so don't list them
|
These registers are handled specially, so don't list them
|
on the list of registers to save in the prologue. */
|
on the list of registers to save in the prologue. */
|
case 1: /* temp used to hold ep */
|
case 1: /* temp used to hold ep */
|
case 4: /* gp */
|
case 4: /* gp */
|
case 10: /* temp used to call interrupt save/restore */
|
case 10: /* temp used to call interrupt save/restore */
|
case EP_REGNUM: /* ep */
|
case EP_REGNUM: /* ep */
|
size += 4;
|
size += 4;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Find the first register that needs to be saved. */
|
/* Find the first register that needs to be saved. */
|
for (i = 0; i <= 31; i++)
|
for (i = 0; i <= 31; i++)
|
if (df_regs_ever_live_p (i) && ((! call_used_regs[i])
|
if (df_regs_ever_live_p (i) && ((! call_used_regs[i])
|
|| i == LINK_POINTER_REGNUM))
|
|| i == LINK_POINTER_REGNUM))
|
break;
|
break;
|
|
|
/* If it is possible that an out-of-line helper function might be
|
/* If it is possible that an out-of-line helper function might be
|
used to generate the prologue for the current function, then we
|
used to generate the prologue for the current function, then we
|
need to cover the possibility that such a helper function will
|
need to cover the possibility that such a helper function will
|
be used, despite the fact that there might be gaps in the list of
|
be used, despite the fact that there might be gaps in the list of
|
registers that need to be saved. To detect this we note that the
|
registers that need to be saved. To detect this we note that the
|
helper functions always push at least register r29 (provided
|
helper functions always push at least register r29 (provided
|
that the function is not an interrupt handler). */
|
that the function is not an interrupt handler). */
|
|
|
if (TARGET_PROLOG_FUNCTION
|
if (TARGET_PROLOG_FUNCTION
|
&& (i == 2 || ((i >= 20) && (i < 30))))
|
&& (i == 2 || ((i >= 20) && (i < 30))))
|
{
|
{
|
if (i == 2)
|
if (i == 2)
|
{
|
{
|
size += 4;
|
size += 4;
|
reg_saved |= 1L << i;
|
reg_saved |= 1L << i;
|
|
|
i = 20;
|
i = 20;
|
}
|
}
|
|
|
/* Helper functions save all registers between the starting
|
/* Helper functions save all registers between the starting
|
register and the last register, regardless of whether they
|
register and the last register, regardless of whether they
|
are actually used by the function or not. */
|
are actually used by the function or not. */
|
for (; i <= 29; i++)
|
for (; i <= 29; i++)
|
{
|
{
|
size += 4;
|
size += 4;
|
reg_saved |= 1L << i;
|
reg_saved |= 1L << i;
|
}
|
}
|
|
|
if (df_regs_ever_live_p (LINK_POINTER_REGNUM))
|
if (df_regs_ever_live_p (LINK_POINTER_REGNUM))
|
{
|
{
|
size += 4;
|
size += 4;
|
reg_saved |= 1L << LINK_POINTER_REGNUM;
|
reg_saved |= 1L << LINK_POINTER_REGNUM;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
for (; i <= 31; i++)
|
for (; i <= 31; i++)
|
if (df_regs_ever_live_p (i) && ((! call_used_regs[i])
|
if (df_regs_ever_live_p (i) && ((! call_used_regs[i])
|
|| i == LINK_POINTER_REGNUM))
|
|| i == LINK_POINTER_REGNUM))
|
{
|
{
|
size += 4;
|
size += 4;
|
reg_saved |= 1L << i;
|
reg_saved |= 1L << i;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (p_reg_saved)
|
if (p_reg_saved)
|
*p_reg_saved = reg_saved;
|
*p_reg_saved = reg_saved;
|
|
|
return size;
|
return size;
|
}
|
}
|
|
|
int
|
int
|
compute_frame_size (int size, long * p_reg_saved)
|
compute_frame_size (int size, long * p_reg_saved)
|
{
|
{
|
return (size
|
return (size
|
+ compute_register_save_size (p_reg_saved)
|
+ compute_register_save_size (p_reg_saved)
|
+ crtl->outgoing_args_size);
|
+ crtl->outgoing_args_size);
|
}
|
}
|
|
|
|
|
void
|
void
|
expand_prologue (void)
|
expand_prologue (void)
|
{
|
{
|
unsigned int i;
|
unsigned int i;
|
int offset;
|
int offset;
|
unsigned int size = get_frame_size ();
|
unsigned int size = get_frame_size ();
|
unsigned int actual_fsize;
|
unsigned int actual_fsize;
|
unsigned int init_stack_alloc = 0;
|
unsigned int init_stack_alloc = 0;
|
rtx save_regs[32];
|
rtx save_regs[32];
|
rtx save_all;
|
rtx save_all;
|
unsigned int num_save;
|
unsigned int num_save;
|
unsigned int default_stack;
|
unsigned int default_stack;
|
int code;
|
int code;
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
long reg_saved = 0;
|
long reg_saved = 0;
|
|
|
actual_fsize = compute_frame_size (size, ®_saved);
|
actual_fsize = compute_frame_size (size, ®_saved);
|
|
|
/* Save/setup global registers for interrupt functions right now. */
|
/* Save/setup global registers for interrupt functions right now. */
|
if (interrupt_handler)
|
if (interrupt_handler)
|
{
|
{
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
emit_insn (gen_callt_save_interrupt ());
|
emit_insn (gen_callt_save_interrupt ());
|
else
|
else
|
emit_insn (gen_save_interrupt ());
|
emit_insn (gen_save_interrupt ());
|
|
|
actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
|
actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
|
|
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
|
actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
|
}
|
}
|
|
|
/* Save arg registers to the stack if necessary. */
|
/* Save arg registers to the stack if necessary. */
|
else if (crtl->args.info.anonymous_args)
|
else if (crtl->args.info.anonymous_args)
|
{
|
{
|
if (TARGET_PROLOG_FUNCTION && TARGET_V850E && !TARGET_DISABLE_CALLT)
|
if (TARGET_PROLOG_FUNCTION && TARGET_V850E && !TARGET_DISABLE_CALLT)
|
emit_insn (gen_save_r6_r9_v850e ());
|
emit_insn (gen_save_r6_r9_v850e ());
|
else if (TARGET_PROLOG_FUNCTION && ! TARGET_LONG_CALLS)
|
else if (TARGET_PROLOG_FUNCTION && ! TARGET_LONG_CALLS)
|
emit_insn (gen_save_r6_r9 ());
|
emit_insn (gen_save_r6_r9 ());
|
else
|
else
|
{
|
{
|
offset = 0;
|
offset = 0;
|
for (i = 6; i < 10; i++)
|
for (i = 6; i < 10; i++)
|
{
|
{
|
emit_move_insn (gen_rtx_MEM (SImode,
|
emit_move_insn (gen_rtx_MEM (SImode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)),
|
offset)),
|
gen_rtx_REG (SImode, i));
|
gen_rtx_REG (SImode, i));
|
offset += 4;
|
offset += 4;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Identify all of the saved registers. */
|
/* Identify all of the saved registers. */
|
num_save = 0;
|
num_save = 0;
|
default_stack = 0;
|
default_stack = 0;
|
for (i = 1; i < 31; i++)
|
for (i = 1; i < 31; i++)
|
{
|
{
|
if (((1L << i) & reg_saved) != 0)
|
if (((1L << i) & reg_saved) != 0)
|
save_regs[num_save++] = gen_rtx_REG (Pmode, i);
|
save_regs[num_save++] = gen_rtx_REG (Pmode, i);
|
}
|
}
|
|
|
/* If the return pointer is saved, the helper functions also allocate
|
/* If the return pointer is saved, the helper functions also allocate
|
16 bytes of stack for arguments to be saved in. */
|
16 bytes of stack for arguments to be saved in. */
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
{
|
{
|
save_regs[num_save++] = gen_rtx_REG (Pmode, LINK_POINTER_REGNUM);
|
save_regs[num_save++] = gen_rtx_REG (Pmode, LINK_POINTER_REGNUM);
|
default_stack = 16;
|
default_stack = 16;
|
}
|
}
|
|
|
/* See if we have an insn that allocates stack space and saves the particular
|
/* See if we have an insn that allocates stack space and saves the particular
|
registers we want to. */
|
registers we want to. */
|
save_all = NULL_RTX;
|
save_all = NULL_RTX;
|
if (TARGET_PROLOG_FUNCTION && num_save > 0 && actual_fsize >= default_stack)
|
if (TARGET_PROLOG_FUNCTION && num_save > 0 && actual_fsize >= default_stack)
|
{
|
{
|
int alloc_stack = (4 * num_save) + default_stack;
|
int alloc_stack = (4 * num_save) + default_stack;
|
int unalloc_stack = actual_fsize - alloc_stack;
|
int unalloc_stack = actual_fsize - alloc_stack;
|
int save_func_len = 4;
|
int save_func_len = 4;
|
int save_normal_len;
|
int save_normal_len;
|
|
|
if (unalloc_stack)
|
if (unalloc_stack)
|
save_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
|
save_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
|
|
|
/* see if we would have used ep to save the stack */
|
/* see if we would have used ep to save the stack */
|
if (TARGET_EP && num_save > 3 && (unsigned)actual_fsize < 255)
|
if (TARGET_EP && num_save > 3 && (unsigned)actual_fsize < 255)
|
save_normal_len = (3 * 2) + (2 * num_save);
|
save_normal_len = (3 * 2) + (2 * num_save);
|
else
|
else
|
save_normal_len = 4 * num_save;
|
save_normal_len = 4 * num_save;
|
|
|
save_normal_len += CONST_OK_FOR_J (actual_fsize) ? 2 : 4;
|
save_normal_len += CONST_OK_FOR_J (actual_fsize) ? 2 : 4;
|
|
|
/* Don't bother checking if we don't actually save any space.
|
/* Don't bother checking if we don't actually save any space.
|
This happens for instance if one register is saved and additional
|
This happens for instance if one register is saved and additional
|
stack space is allocated. */
|
stack space is allocated. */
|
if (save_func_len < save_normal_len)
|
if (save_func_len < save_normal_len)
|
{
|
{
|
save_all = gen_rtx_PARALLEL
|
save_all = gen_rtx_PARALLEL
|
(VOIDmode,
|
(VOIDmode,
|
rtvec_alloc (num_save + 1
|
rtvec_alloc (num_save + 1
|
+ (TARGET_V850 ? (TARGET_LONG_CALLS ? 2 : 1) : 0)));
|
+ (TARGET_V850 ? (TARGET_LONG_CALLS ? 2 : 1) : 0)));
|
|
|
XVECEXP (save_all, 0, 0)
|
XVECEXP (save_all, 0, 0)
|
= gen_rtx_SET (VOIDmode,
|
= gen_rtx_SET (VOIDmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx, -alloc_stack));
|
plus_constant (stack_pointer_rtx, -alloc_stack));
|
|
|
offset = - default_stack;
|
offset = - default_stack;
|
for (i = 0; i < num_save; i++)
|
for (i = 0; i < num_save; i++)
|
{
|
{
|
XVECEXP (save_all, 0, i+1)
|
XVECEXP (save_all, 0, i+1)
|
= gen_rtx_SET (VOIDmode,
|
= gen_rtx_SET (VOIDmode,
|
gen_rtx_MEM (Pmode,
|
gen_rtx_MEM (Pmode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)),
|
offset)),
|
save_regs[i]);
|
save_regs[i]);
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
|
|
if (TARGET_V850)
|
if (TARGET_V850)
|
{
|
{
|
XVECEXP (save_all, 0, num_save + 1)
|
XVECEXP (save_all, 0, num_save + 1)
|
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 10));
|
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 10));
|
|
|
if (TARGET_LONG_CALLS)
|
if (TARGET_LONG_CALLS)
|
XVECEXP (save_all, 0, num_save + 2)
|
XVECEXP (save_all, 0, num_save + 2)
|
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
|
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 11));
|
}
|
}
|
|
|
code = recog (save_all, NULL_RTX, NULL);
|
code = recog (save_all, NULL_RTX, NULL);
|
if (code >= 0)
|
if (code >= 0)
|
{
|
{
|
rtx insn = emit_insn (save_all);
|
rtx insn = emit_insn (save_all);
|
INSN_CODE (insn) = code;
|
INSN_CODE (insn) = code;
|
actual_fsize -= alloc_stack;
|
actual_fsize -= alloc_stack;
|
|
|
if (TARGET_DEBUG)
|
if (TARGET_DEBUG)
|
fprintf (stderr, "\
|
fprintf (stderr, "\
|
Saved %d bytes via prologue function (%d vs. %d) for function %s\n",
|
Saved %d bytes via prologue function (%d vs. %d) for function %s\n",
|
save_normal_len - save_func_len,
|
save_normal_len - save_func_len,
|
save_normal_len, save_func_len,
|
save_normal_len, save_func_len,
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
|
}
|
}
|
else
|
else
|
save_all = NULL_RTX;
|
save_all = NULL_RTX;
|
}
|
}
|
}
|
}
|
|
|
/* If no prolog save function is available, store the registers the old
|
/* If no prolog save function is available, store the registers the old
|
fashioned way (one by one). */
|
fashioned way (one by one). */
|
if (!save_all)
|
if (!save_all)
|
{
|
{
|
/* Special case interrupt functions that save all registers for a call. */
|
/* Special case interrupt functions that save all registers for a call. */
|
if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
{
|
{
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
emit_insn (gen_callt_save_all_interrupt ());
|
emit_insn (gen_callt_save_all_interrupt ());
|
else
|
else
|
emit_insn (gen_save_all_interrupt ());
|
emit_insn (gen_save_all_interrupt ());
|
}
|
}
|
else
|
else
|
{
|
{
|
/* If the stack is too big, allocate it in chunks so we can do the
|
/* If the stack is too big, allocate it in chunks so we can do the
|
register saves. We use the register save size so we use the ep
|
register saves. We use the register save size so we use the ep
|
register. */
|
register. */
|
if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
|
if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
|
init_stack_alloc = compute_register_save_size (NULL);
|
init_stack_alloc = compute_register_save_size (NULL);
|
else
|
else
|
init_stack_alloc = actual_fsize;
|
init_stack_alloc = actual_fsize;
|
|
|
/* Save registers at the beginning of the stack frame. */
|
/* Save registers at the beginning of the stack frame. */
|
offset = init_stack_alloc - 4;
|
offset = init_stack_alloc - 4;
|
|
|
if (init_stack_alloc)
|
if (init_stack_alloc)
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (- (signed) init_stack_alloc)));
|
GEN_INT (- (signed) init_stack_alloc)));
|
|
|
/* Save the return pointer first. */
|
/* Save the return pointer first. */
|
if (num_save > 0 && REGNO (save_regs[num_save-1]) == LINK_POINTER_REGNUM)
|
if (num_save > 0 && REGNO (save_regs[num_save-1]) == LINK_POINTER_REGNUM)
|
{
|
{
|
emit_move_insn (gen_rtx_MEM (SImode,
|
emit_move_insn (gen_rtx_MEM (SImode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)),
|
offset)),
|
save_regs[--num_save]);
|
save_regs[--num_save]);
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
|
|
for (i = 0; i < num_save; i++)
|
for (i = 0; i < num_save; i++)
|
{
|
{
|
emit_move_insn (gen_rtx_MEM (SImode,
|
emit_move_insn (gen_rtx_MEM (SImode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)),
|
offset)),
|
save_regs[i]);
|
save_regs[i]);
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Allocate the rest of the stack that was not allocated above (either it is
|
/* Allocate the rest of the stack that was not allocated above (either it is
|
> 32K or we just called a function to save the registers and needed more
|
> 32K or we just called a function to save the registers and needed more
|
stack. */
|
stack. */
|
if (actual_fsize > init_stack_alloc)
|
if (actual_fsize > init_stack_alloc)
|
{
|
{
|
int diff = actual_fsize - init_stack_alloc;
|
int diff = actual_fsize - init_stack_alloc;
|
if (CONST_OK_FOR_K (diff))
|
if (CONST_OK_FOR_K (diff))
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (-diff)));
|
GEN_INT (-diff)));
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
emit_move_insn (reg, GEN_INT (-diff));
|
emit_move_insn (reg, GEN_INT (-diff));
|
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
|
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
|
}
|
}
|
}
|
}
|
|
|
/* If we need a frame pointer, set it up now. */
|
/* If we need a frame pointer, set it up now. */
|
if (frame_pointer_needed)
|
if (frame_pointer_needed)
|
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
|
}
|
}
|
|
|
|
|
void
|
void
|
expand_epilogue (void)
|
expand_epilogue (void)
|
{
|
{
|
unsigned int i;
|
unsigned int i;
|
int offset;
|
int offset;
|
unsigned int size = get_frame_size ();
|
unsigned int size = get_frame_size ();
|
long reg_saved = 0;
|
long reg_saved = 0;
|
int actual_fsize = compute_frame_size (size, ®_saved);
|
int actual_fsize = compute_frame_size (size, ®_saved);
|
unsigned int init_stack_free = 0;
|
unsigned int init_stack_free = 0;
|
rtx restore_regs[32];
|
rtx restore_regs[32];
|
rtx restore_all;
|
rtx restore_all;
|
unsigned int num_restore;
|
unsigned int num_restore;
|
unsigned int default_stack;
|
unsigned int default_stack;
|
int code;
|
int code;
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
int interrupt_handler = v850_interrupt_function_p (current_function_decl);
|
|
|
/* Eliminate the initial stack stored by interrupt functions. */
|
/* Eliminate the initial stack stored by interrupt functions. */
|
if (interrupt_handler)
|
if (interrupt_handler)
|
{
|
{
|
actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
|
actual_fsize -= INTERRUPT_FIXED_SAVE_SIZE;
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
|
actual_fsize -= INTERRUPT_ALL_SAVE_SIZE;
|
}
|
}
|
|
|
/* Cut off any dynamic stack created. */
|
/* Cut off any dynamic stack created. */
|
if (frame_pointer_needed)
|
if (frame_pointer_needed)
|
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
|
emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
|
|
|
/* Identify all of the saved registers. */
|
/* Identify all of the saved registers. */
|
num_restore = 0;
|
num_restore = 0;
|
default_stack = 0;
|
default_stack = 0;
|
for (i = 1; i < 31; i++)
|
for (i = 1; i < 31; i++)
|
{
|
{
|
if (((1L << i) & reg_saved) != 0)
|
if (((1L << i) & reg_saved) != 0)
|
restore_regs[num_restore++] = gen_rtx_REG (Pmode, i);
|
restore_regs[num_restore++] = gen_rtx_REG (Pmode, i);
|
}
|
}
|
|
|
/* If the return pointer is saved, the helper functions also allocate
|
/* If the return pointer is saved, the helper functions also allocate
|
16 bytes of stack for arguments to be saved in. */
|
16 bytes of stack for arguments to be saved in. */
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
{
|
{
|
restore_regs[num_restore++] = gen_rtx_REG (Pmode, LINK_POINTER_REGNUM);
|
restore_regs[num_restore++] = gen_rtx_REG (Pmode, LINK_POINTER_REGNUM);
|
default_stack = 16;
|
default_stack = 16;
|
}
|
}
|
|
|
/* See if we have an insn that restores the particular registers we
|
/* See if we have an insn that restores the particular registers we
|
want to. */
|
want to. */
|
restore_all = NULL_RTX;
|
restore_all = NULL_RTX;
|
|
|
if (TARGET_PROLOG_FUNCTION
|
if (TARGET_PROLOG_FUNCTION
|
&& num_restore > 0
|
&& num_restore > 0
|
&& actual_fsize >= (signed) default_stack
|
&& actual_fsize >= (signed) default_stack
|
&& !interrupt_handler)
|
&& !interrupt_handler)
|
{
|
{
|
int alloc_stack = (4 * num_restore) + default_stack;
|
int alloc_stack = (4 * num_restore) + default_stack;
|
int unalloc_stack = actual_fsize - alloc_stack;
|
int unalloc_stack = actual_fsize - alloc_stack;
|
int restore_func_len = 4;
|
int restore_func_len = 4;
|
int restore_normal_len;
|
int restore_normal_len;
|
|
|
if (unalloc_stack)
|
if (unalloc_stack)
|
restore_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
|
restore_func_len += CONST_OK_FOR_J (unalloc_stack) ? 2 : 4;
|
|
|
/* See if we would have used ep to restore the registers. */
|
/* See if we would have used ep to restore the registers. */
|
if (TARGET_EP && num_restore > 3 && (unsigned)actual_fsize < 255)
|
if (TARGET_EP && num_restore > 3 && (unsigned)actual_fsize < 255)
|
restore_normal_len = (3 * 2) + (2 * num_restore);
|
restore_normal_len = (3 * 2) + (2 * num_restore);
|
else
|
else
|
restore_normal_len = 4 * num_restore;
|
restore_normal_len = 4 * num_restore;
|
|
|
restore_normal_len += (CONST_OK_FOR_J (actual_fsize) ? 2 : 4) + 2;
|
restore_normal_len += (CONST_OK_FOR_J (actual_fsize) ? 2 : 4) + 2;
|
|
|
/* Don't bother checking if we don't actually save any space. */
|
/* Don't bother checking if we don't actually save any space. */
|
if (restore_func_len < restore_normal_len)
|
if (restore_func_len < restore_normal_len)
|
{
|
{
|
restore_all = gen_rtx_PARALLEL (VOIDmode,
|
restore_all = gen_rtx_PARALLEL (VOIDmode,
|
rtvec_alloc (num_restore + 2));
|
rtvec_alloc (num_restore + 2));
|
XVECEXP (restore_all, 0, 0) = gen_rtx_RETURN (VOIDmode);
|
XVECEXP (restore_all, 0, 0) = gen_rtx_RETURN (VOIDmode);
|
XVECEXP (restore_all, 0, 1)
|
XVECEXP (restore_all, 0, 1)
|
= gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
= gen_rtx_SET (VOIDmode, stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (alloc_stack)));
|
GEN_INT (alloc_stack)));
|
|
|
offset = alloc_stack - 4;
|
offset = alloc_stack - 4;
|
for (i = 0; i < num_restore; i++)
|
for (i = 0; i < num_restore; i++)
|
{
|
{
|
XVECEXP (restore_all, 0, i+2)
|
XVECEXP (restore_all, 0, i+2)
|
= gen_rtx_SET (VOIDmode,
|
= gen_rtx_SET (VOIDmode,
|
restore_regs[i],
|
restore_regs[i],
|
gen_rtx_MEM (Pmode,
|
gen_rtx_MEM (Pmode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)));
|
offset)));
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
|
|
code = recog (restore_all, NULL_RTX, NULL);
|
code = recog (restore_all, NULL_RTX, NULL);
|
|
|
if (code >= 0)
|
if (code >= 0)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
actual_fsize -= alloc_stack;
|
actual_fsize -= alloc_stack;
|
if (actual_fsize)
|
if (actual_fsize)
|
{
|
{
|
if (CONST_OK_FOR_K (actual_fsize))
|
if (CONST_OK_FOR_K (actual_fsize))
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (actual_fsize)));
|
GEN_INT (actual_fsize)));
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
emit_move_insn (reg, GEN_INT (actual_fsize));
|
emit_move_insn (reg, GEN_INT (actual_fsize));
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
reg));
|
reg));
|
}
|
}
|
}
|
}
|
|
|
insn = emit_jump_insn (restore_all);
|
insn = emit_jump_insn (restore_all);
|
INSN_CODE (insn) = code;
|
INSN_CODE (insn) = code;
|
|
|
if (TARGET_DEBUG)
|
if (TARGET_DEBUG)
|
fprintf (stderr, "\
|
fprintf (stderr, "\
|
Saved %d bytes via epilogue function (%d vs. %d) in function %s\n",
|
Saved %d bytes via epilogue function (%d vs. %d) in function %s\n",
|
restore_normal_len - restore_func_len,
|
restore_normal_len - restore_func_len,
|
restore_normal_len, restore_func_len,
|
restore_normal_len, restore_func_len,
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
|
IDENTIFIER_POINTER (DECL_NAME (current_function_decl)));
|
}
|
}
|
else
|
else
|
restore_all = NULL_RTX;
|
restore_all = NULL_RTX;
|
}
|
}
|
}
|
}
|
|
|
/* If no epilogue save function is available, restore the registers the
|
/* If no epilogue save function is available, restore the registers the
|
old fashioned way (one by one). */
|
old fashioned way (one by one). */
|
if (!restore_all)
|
if (!restore_all)
|
{
|
{
|
/* If the stack is large, we need to cut it down in 2 pieces. */
|
/* If the stack is large, we need to cut it down in 2 pieces. */
|
if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
|
if (actual_fsize && !CONST_OK_FOR_K (-actual_fsize))
|
init_stack_free = 4 * num_restore;
|
init_stack_free = 4 * num_restore;
|
else
|
else
|
init_stack_free = (signed) actual_fsize;
|
init_stack_free = (signed) actual_fsize;
|
|
|
/* Deallocate the rest of the stack if it is > 32K. */
|
/* Deallocate the rest of the stack if it is > 32K. */
|
if ((unsigned int) actual_fsize > init_stack_free)
|
if ((unsigned int) actual_fsize > init_stack_free)
|
{
|
{
|
int diff;
|
int diff;
|
|
|
diff = actual_fsize - ((interrupt_handler) ? 0 : init_stack_free);
|
diff = actual_fsize - ((interrupt_handler) ? 0 : init_stack_free);
|
|
|
if (CONST_OK_FOR_K (diff))
|
if (CONST_OK_FOR_K (diff))
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (diff)));
|
GEN_INT (diff)));
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
rtx reg = gen_rtx_REG (Pmode, 12);
|
emit_move_insn (reg, GEN_INT (diff));
|
emit_move_insn (reg, GEN_INT (diff));
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
reg));
|
reg));
|
}
|
}
|
}
|
}
|
|
|
/* Special case interrupt functions that save all registers
|
/* Special case interrupt functions that save all registers
|
for a call. */
|
for a call. */
|
if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
if (interrupt_handler && ((1L << LINK_POINTER_REGNUM) & reg_saved) != 0)
|
{
|
{
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
emit_insn (gen_callt_restore_all_interrupt ());
|
emit_insn (gen_callt_restore_all_interrupt ());
|
else
|
else
|
emit_insn (gen_restore_all_interrupt ());
|
emit_insn (gen_restore_all_interrupt ());
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Restore registers from the beginning of the stack frame. */
|
/* Restore registers from the beginning of the stack frame. */
|
offset = init_stack_free - 4;
|
offset = init_stack_free - 4;
|
|
|
/* Restore the return pointer first. */
|
/* Restore the return pointer first. */
|
if (num_restore > 0
|
if (num_restore > 0
|
&& REGNO (restore_regs [num_restore - 1]) == LINK_POINTER_REGNUM)
|
&& REGNO (restore_regs [num_restore - 1]) == LINK_POINTER_REGNUM)
|
{
|
{
|
emit_move_insn (restore_regs[--num_restore],
|
emit_move_insn (restore_regs[--num_restore],
|
gen_rtx_MEM (SImode,
|
gen_rtx_MEM (SImode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)));
|
offset)));
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
|
|
for (i = 0; i < num_restore; i++)
|
for (i = 0; i < num_restore; i++)
|
{
|
{
|
emit_move_insn (restore_regs[i],
|
emit_move_insn (restore_regs[i],
|
gen_rtx_MEM (SImode,
|
gen_rtx_MEM (SImode,
|
plus_constant (stack_pointer_rtx,
|
plus_constant (stack_pointer_rtx,
|
offset)));
|
offset)));
|
|
|
emit_use (restore_regs[i]);
|
emit_use (restore_regs[i]);
|
offset -= 4;
|
offset -= 4;
|
}
|
}
|
|
|
/* Cut back the remainder of the stack. */
|
/* Cut back the remainder of the stack. */
|
if (init_stack_free)
|
if (init_stack_free)
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
emit_insn (gen_addsi3 (stack_pointer_rtx,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
GEN_INT (init_stack_free)));
|
GEN_INT (init_stack_free)));
|
}
|
}
|
|
|
/* And return or use reti for interrupt handlers. */
|
/* And return or use reti for interrupt handlers. */
|
if (interrupt_handler)
|
if (interrupt_handler)
|
{
|
{
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
if (TARGET_V850E && ! TARGET_DISABLE_CALLT)
|
emit_insn (gen_callt_return_interrupt ());
|
emit_insn (gen_callt_return_interrupt ());
|
else
|
else
|
emit_jump_insn (gen_return_interrupt ());
|
emit_jump_insn (gen_return_interrupt ());
|
}
|
}
|
else if (actual_fsize)
|
else if (actual_fsize)
|
emit_jump_insn (gen_return_internal ());
|
emit_jump_insn (gen_return_internal ());
|
else
|
else
|
emit_jump_insn (gen_return_simple ());
|
emit_jump_insn (gen_return_simple ());
|
}
|
}
|
|
|
v850_interrupt_cache_p = FALSE;
|
v850_interrupt_cache_p = FALSE;
|
v850_interrupt_p = FALSE;
|
v850_interrupt_p = FALSE;
|
}
|
}
|
|
|
|
|
/* Update the condition code from the insn. */
|
/* Update the condition code from the insn. */
|
|
|
void
|
void
|
notice_update_cc (rtx body, rtx insn)
|
notice_update_cc (rtx body, rtx insn)
|
{
|
{
|
switch (get_attr_cc (insn))
|
switch (get_attr_cc (insn))
|
{
|
{
|
case CC_NONE:
|
case CC_NONE:
|
/* Insn does not affect CC at all. */
|
/* Insn does not affect CC at all. */
|
break;
|
break;
|
|
|
case CC_NONE_0HIT:
|
case CC_NONE_0HIT:
|
/* Insn does not change CC, but the 0'th operand has been changed. */
|
/* Insn does not change CC, but the 0'th operand has been changed. */
|
if (cc_status.value1 != 0
|
if (cc_status.value1 != 0
|
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
|
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
|
cc_status.value1 = 0;
|
cc_status.value1 = 0;
|
break;
|
break;
|
|
|
case CC_SET_ZN:
|
case CC_SET_ZN:
|
/* Insn sets the Z,N flags of CC to recog_data.operand[0].
|
/* Insn sets the Z,N flags of CC to recog_data.operand[0].
|
V,C is in an unusable state. */
|
V,C is in an unusable state. */
|
CC_STATUS_INIT;
|
CC_STATUS_INIT;
|
cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
|
cc_status.flags |= CC_OVERFLOW_UNUSABLE | CC_NO_CARRY;
|
cc_status.value1 = recog_data.operand[0];
|
cc_status.value1 = recog_data.operand[0];
|
break;
|
break;
|
|
|
case CC_SET_ZNV:
|
case CC_SET_ZNV:
|
/* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
|
/* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
|
C is in an unusable state. */
|
C is in an unusable state. */
|
CC_STATUS_INIT;
|
CC_STATUS_INIT;
|
cc_status.flags |= CC_NO_CARRY;
|
cc_status.flags |= CC_NO_CARRY;
|
cc_status.value1 = recog_data.operand[0];
|
cc_status.value1 = recog_data.operand[0];
|
break;
|
break;
|
|
|
case CC_COMPARE:
|
case CC_COMPARE:
|
/* The insn is a compare instruction. */
|
/* The insn is a compare instruction. */
|
CC_STATUS_INIT;
|
CC_STATUS_INIT;
|
cc_status.value1 = SET_SRC (body);
|
cc_status.value1 = SET_SRC (body);
|
break;
|
break;
|
|
|
case CC_CLOBBER:
|
case CC_CLOBBER:
|
/* Insn doesn't leave CC in a usable state. */
|
/* Insn doesn't leave CC in a usable state. */
|
CC_STATUS_INIT;
|
CC_STATUS_INIT;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* Retrieve the data area that has been chosen for the given decl. */
|
/* Retrieve the data area that has been chosen for the given decl. */
|
|
|
v850_data_area
|
v850_data_area
|
v850_get_data_area (tree decl)
|
v850_get_data_area (tree decl)
|
{
|
{
|
if (lookup_attribute ("sda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
if (lookup_attribute ("sda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
return DATA_AREA_SDA;
|
return DATA_AREA_SDA;
|
|
|
if (lookup_attribute ("tda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
if (lookup_attribute ("tda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
return DATA_AREA_TDA;
|
return DATA_AREA_TDA;
|
|
|
if (lookup_attribute ("zda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
if (lookup_attribute ("zda", DECL_ATTRIBUTES (decl)) != NULL_TREE)
|
return DATA_AREA_ZDA;
|
return DATA_AREA_ZDA;
|
|
|
return DATA_AREA_NORMAL;
|
return DATA_AREA_NORMAL;
|
}
|
}
|
|
|
/* Store the indicated data area in the decl's attributes. */
|
/* Store the indicated data area in the decl's attributes. */
|
|
|
static void
|
static void
|
v850_set_data_area (tree decl, v850_data_area data_area)
|
v850_set_data_area (tree decl, v850_data_area data_area)
|
{
|
{
|
tree name;
|
tree name;
|
|
|
switch (data_area)
|
switch (data_area)
|
{
|
{
|
case DATA_AREA_SDA: name = get_identifier ("sda"); break;
|
case DATA_AREA_SDA: name = get_identifier ("sda"); break;
|
case DATA_AREA_TDA: name = get_identifier ("tda"); break;
|
case DATA_AREA_TDA: name = get_identifier ("tda"); break;
|
case DATA_AREA_ZDA: name = get_identifier ("zda"); break;
|
case DATA_AREA_ZDA: name = get_identifier ("zda"); break;
|
default:
|
default:
|
return;
|
return;
|
}
|
}
|
|
|
DECL_ATTRIBUTES (decl) = tree_cons
|
DECL_ATTRIBUTES (decl) = tree_cons
|
(name, NULL, DECL_ATTRIBUTES (decl));
|
(name, NULL, DECL_ATTRIBUTES (decl));
|
}
|
}
|
|
|
/* Handle an "interrupt" attribute; arguments as in
|
/* Handle an "interrupt" attribute; arguments as in
|
struct attribute_spec.handler. */
|
struct attribute_spec.handler. */
|
static tree
|
static tree
|
v850_handle_interrupt_attribute (tree * node,
|
v850_handle_interrupt_attribute (tree * node,
|
tree name,
|
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_DECL)
|
if (TREE_CODE (*node) != FUNCTION_DECL)
|
{
|
{
|
warning (OPT_Wattributes, "%qE attribute only applies to functions",
|
warning (OPT_Wattributes, "%qE attribute only applies to functions",
|
name);
|
name);
|
*no_add_attrs = true;
|
*no_add_attrs = true;
|
}
|
}
|
|
|
return NULL_TREE;
|
return NULL_TREE;
|
}
|
}
|
|
|
/* Handle a "sda", "tda" or "zda" attribute; arguments as in
|
/* Handle a "sda", "tda" or "zda" attribute; arguments as in
|
struct attribute_spec.handler. */
|
struct attribute_spec.handler. */
|
static tree
|
static tree
|
v850_handle_data_area_attribute (tree* node,
|
v850_handle_data_area_attribute (tree* node,
|
tree name,
|
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)
|
{
|
{
|
v850_data_area data_area;
|
v850_data_area data_area;
|
v850_data_area area;
|
v850_data_area area;
|
tree decl = *node;
|
tree decl = *node;
|
|
|
/* Implement data area attribute. */
|
/* Implement data area attribute. */
|
if (is_attribute_p ("sda", name))
|
if (is_attribute_p ("sda", name))
|
data_area = DATA_AREA_SDA;
|
data_area = DATA_AREA_SDA;
|
else if (is_attribute_p ("tda", name))
|
else if (is_attribute_p ("tda", name))
|
data_area = DATA_AREA_TDA;
|
data_area = DATA_AREA_TDA;
|
else if (is_attribute_p ("zda", name))
|
else if (is_attribute_p ("zda", name))
|
data_area = DATA_AREA_ZDA;
|
data_area = DATA_AREA_ZDA;
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
|
|
switch (TREE_CODE (decl))
|
switch (TREE_CODE (decl))
|
{
|
{
|
case VAR_DECL:
|
case VAR_DECL:
|
if (current_function_decl != NULL_TREE)
|
if (current_function_decl != NULL_TREE)
|
{
|
{
|
error_at (DECL_SOURCE_LOCATION (decl),
|
error_at (DECL_SOURCE_LOCATION (decl),
|
"data area attributes cannot be specified for "
|
"data area attributes cannot be specified for "
|
"local variables");
|
"local variables");
|
*no_add_attrs = true;
|
*no_add_attrs = true;
|
}
|
}
|
|
|
/* Drop through. */
|
/* Drop through. */
|
|
|
case FUNCTION_DECL:
|
case FUNCTION_DECL:
|
area = v850_get_data_area (decl);
|
area = v850_get_data_area (decl);
|
if (area != DATA_AREA_NORMAL && data_area != area)
|
if (area != DATA_AREA_NORMAL && data_area != area)
|
{
|
{
|
error ("data area of %q+D conflicts with previous declaration",
|
error ("data area of %q+D conflicts with previous declaration",
|
decl);
|
decl);
|
*no_add_attrs = true;
|
*no_add_attrs = true;
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return NULL_TREE;
|
return NULL_TREE;
|
}
|
}
|
|
|
|
|
/* Return nonzero if FUNC is an interrupt function as specified
|
/* Return nonzero if FUNC is an interrupt function as specified
|
by the "interrupt" attribute. */
|
by the "interrupt" attribute. */
|
|
|
int
|
int
|
v850_interrupt_function_p (tree func)
|
v850_interrupt_function_p (tree func)
|
{
|
{
|
tree a;
|
tree a;
|
int ret = 0;
|
int ret = 0;
|
|
|
if (v850_interrupt_cache_p)
|
if (v850_interrupt_cache_p)
|
return v850_interrupt_p;
|
return v850_interrupt_p;
|
|
|
if (TREE_CODE (func) != FUNCTION_DECL)
|
if (TREE_CODE (func) != FUNCTION_DECL)
|
return 0;
|
return 0;
|
|
|
a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
|
a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
|
if (a != NULL_TREE)
|
if (a != NULL_TREE)
|
ret = 1;
|
ret = 1;
|
|
|
else
|
else
|
{
|
{
|
a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
|
a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
|
ret = a != NULL_TREE;
|
ret = a != NULL_TREE;
|
}
|
}
|
|
|
/* Its not safe to trust global variables until after function inlining has
|
/* Its not safe to trust global variables until after function inlining has
|
been done. */
|
been done. */
|
if (reload_completed | reload_in_progress)
|
if (reload_completed | reload_in_progress)
|
v850_interrupt_p = ret;
|
v850_interrupt_p = ret;
|
|
|
return ret;
|
return ret;
|
}
|
}
|
|
|
|
|
static void
|
static void
|
v850_encode_data_area (tree decl, rtx symbol)
|
v850_encode_data_area (tree decl, rtx symbol)
|
{
|
{
|
int flags;
|
int flags;
|
|
|
/* Map explicit sections into the appropriate attribute */
|
/* Map explicit sections into the appropriate attribute */
|
if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
{
|
{
|
if (DECL_SECTION_NAME (decl))
|
if (DECL_SECTION_NAME (decl))
|
{
|
{
|
const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
|
const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
|
|
|
if (streq (name, ".zdata") || streq (name, ".zbss"))
|
if (streq (name, ".zdata") || streq (name, ".zbss"))
|
v850_set_data_area (decl, DATA_AREA_ZDA);
|
v850_set_data_area (decl, DATA_AREA_ZDA);
|
|
|
else if (streq (name, ".sdata") || streq (name, ".sbss"))
|
else if (streq (name, ".sdata") || streq (name, ".sbss"))
|
v850_set_data_area (decl, DATA_AREA_SDA);
|
v850_set_data_area (decl, DATA_AREA_SDA);
|
|
|
else if (streq (name, ".tdata"))
|
else if (streq (name, ".tdata"))
|
v850_set_data_area (decl, DATA_AREA_TDA);
|
v850_set_data_area (decl, DATA_AREA_TDA);
|
}
|
}
|
|
|
/* If no attribute, support -m{zda,sda,tda}=n */
|
/* If no attribute, support -m{zda,sda,tda}=n */
|
else
|
else
|
{
|
{
|
int size = int_size_in_bytes (TREE_TYPE (decl));
|
int size = int_size_in_bytes (TREE_TYPE (decl));
|
if (size <= 0)
|
if (size <= 0)
|
;
|
;
|
|
|
else if (size <= small_memory [(int) SMALL_MEMORY_TDA].max)
|
else if (size <= small_memory [(int) SMALL_MEMORY_TDA].max)
|
v850_set_data_area (decl, DATA_AREA_TDA);
|
v850_set_data_area (decl, DATA_AREA_TDA);
|
|
|
else if (size <= small_memory [(int) SMALL_MEMORY_SDA].max)
|
else if (size <= small_memory [(int) SMALL_MEMORY_SDA].max)
|
v850_set_data_area (decl, DATA_AREA_SDA);
|
v850_set_data_area (decl, DATA_AREA_SDA);
|
|
|
else if (size <= small_memory [(int) SMALL_MEMORY_ZDA].max)
|
else if (size <= small_memory [(int) SMALL_MEMORY_ZDA].max)
|
v850_set_data_area (decl, DATA_AREA_ZDA);
|
v850_set_data_area (decl, DATA_AREA_ZDA);
|
}
|
}
|
|
|
if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
if (v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
return;
|
return;
|
}
|
}
|
|
|
flags = SYMBOL_REF_FLAGS (symbol);
|
flags = SYMBOL_REF_FLAGS (symbol);
|
switch (v850_get_data_area (decl))
|
switch (v850_get_data_area (decl))
|
{
|
{
|
case DATA_AREA_ZDA: flags |= SYMBOL_FLAG_ZDA; break;
|
case DATA_AREA_ZDA: flags |= SYMBOL_FLAG_ZDA; break;
|
case DATA_AREA_TDA: flags |= SYMBOL_FLAG_TDA; break;
|
case DATA_AREA_TDA: flags |= SYMBOL_FLAG_TDA; break;
|
case DATA_AREA_SDA: flags |= SYMBOL_FLAG_SDA; break;
|
case DATA_AREA_SDA: flags |= SYMBOL_FLAG_SDA; break;
|
default: gcc_unreachable ();
|
default: gcc_unreachable ();
|
}
|
}
|
SYMBOL_REF_FLAGS (symbol) = flags;
|
SYMBOL_REF_FLAGS (symbol) = flags;
|
}
|
}
|
|
|
static void
|
static void
|
v850_encode_section_info (tree decl, rtx rtl, int first)
|
v850_encode_section_info (tree decl, rtx rtl, int first)
|
{
|
{
|
default_encode_section_info (decl, rtl, first);
|
default_encode_section_info (decl, rtl, first);
|
|
|
if (TREE_CODE (decl) == VAR_DECL
|
if (TREE_CODE (decl) == VAR_DECL
|
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
|
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
|
v850_encode_data_area (decl, XEXP (rtl, 0));
|
v850_encode_data_area (decl, XEXP (rtl, 0));
|
}
|
}
|
|
|
/* Construct a JR instruction to a routine that will perform the equivalent of
|
/* Construct a JR instruction to a routine that will perform the equivalent of
|
the RTL passed in as an argument. This RTL is a function epilogue that
|
the RTL passed in as an argument. This RTL is a function epilogue that
|
pops registers off the stack and possibly releases some extra stack space
|
pops registers off the stack and possibly releases some extra stack space
|
as well. The code has already verified that the RTL matches these
|
as well. The code has already verified that the RTL matches these
|
requirements. */
|
requirements. */
|
char *
|
char *
|
construct_restore_jr (rtx op)
|
construct_restore_jr (rtx op)
|
{
|
{
|
int count = XVECLEN (op, 0);
|
int count = XVECLEN (op, 0);
|
int stack_bytes;
|
int stack_bytes;
|
unsigned long int mask;
|
unsigned long int mask;
|
unsigned long int first;
|
unsigned long int first;
|
unsigned long int last;
|
unsigned long int last;
|
int i;
|
int i;
|
static char buff [100]; /* XXX */
|
static char buff [100]; /* XXX */
|
|
|
if (count <= 2)
|
if (count <= 2)
|
{
|
{
|
error ("bogus JR construction: %d", count);
|
error ("bogus JR construction: %d", count);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Work out how many bytes to pop off the stack before retrieving
|
/* Work out how many bytes to pop off the stack before retrieving
|
registers. */
|
registers. */
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 1)) == SET);
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 1)) == SET);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) == CONST_INT);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) == CONST_INT);
|
|
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
|
|
|
/* Each pop will remove 4 bytes from the stack.... */
|
/* Each pop will remove 4 bytes from the stack.... */
|
stack_bytes -= (count - 2) * 4;
|
stack_bytes -= (count - 2) * 4;
|
|
|
/* Make sure that the amount we are popping either 0 or 16 bytes. */
|
/* Make sure that the amount we are popping either 0 or 16 bytes. */
|
if (stack_bytes != 0 && stack_bytes != 16)
|
if (stack_bytes != 0 && stack_bytes != 16)
|
{
|
{
|
error ("bad amount of stack space removal: %d", stack_bytes);
|
error ("bad amount of stack space removal: %d", stack_bytes);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Now compute the bit mask of registers to push. */
|
/* Now compute the bit mask of registers to push. */
|
mask = 0;
|
mask = 0;
|
for (i = 2; i < count; i++)
|
for (i = 2; i < count; i++)
|
{
|
{
|
rtx vector_element = XVECEXP (op, 0, i);
|
rtx vector_element = XVECEXP (op, 0, i);
|
|
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (SET_DEST (vector_element)) == REG);
|
gcc_assert (GET_CODE (SET_DEST (vector_element)) == REG);
|
gcc_assert (register_is_ok_for_epilogue (SET_DEST (vector_element),
|
gcc_assert (register_is_ok_for_epilogue (SET_DEST (vector_element),
|
SImode));
|
SImode));
|
|
|
mask |= 1 << REGNO (SET_DEST (vector_element));
|
mask |= 1 << REGNO (SET_DEST (vector_element));
|
}
|
}
|
|
|
/* Scan for the first register to pop. */
|
/* Scan for the first register to pop. */
|
for (first = 0; first < 32; first++)
|
for (first = 0; first < 32; first++)
|
{
|
{
|
if (mask & (1 << first))
|
if (mask & (1 << first))
|
break;
|
break;
|
}
|
}
|
|
|
gcc_assert (first < 32);
|
gcc_assert (first < 32);
|
|
|
/* Discover the last register to pop. */
|
/* Discover the last register to pop. */
|
if (mask & (1 << LINK_POINTER_REGNUM))
|
if (mask & (1 << LINK_POINTER_REGNUM))
|
{
|
{
|
gcc_assert (stack_bytes == 16);
|
gcc_assert (stack_bytes == 16);
|
|
|
last = LINK_POINTER_REGNUM;
|
last = LINK_POINTER_REGNUM;
|
}
|
}
|
else
|
else
|
{
|
{
|
gcc_assert (!stack_bytes);
|
gcc_assert (!stack_bytes);
|
gcc_assert (mask & (1 << 29));
|
gcc_assert (mask & (1 << 29));
|
|
|
last = 29;
|
last = 29;
|
}
|
}
|
|
|
/* Note, it is possible to have gaps in the register mask.
|
/* Note, it is possible to have gaps in the register mask.
|
We ignore this here, and generate a JR anyway. We will
|
We ignore this here, and generate a JR anyway. We will
|
be popping more registers than is strictly necessary, but
|
be popping more registers than is strictly necessary, but
|
it does save code space. */
|
it does save code space. */
|
|
|
if (TARGET_LONG_CALLS)
|
if (TARGET_LONG_CALLS)
|
{
|
{
|
char name[40];
|
char name[40];
|
|
|
if (first == last)
|
if (first == last)
|
sprintf (name, "__return_%s", reg_names [first]);
|
sprintf (name, "__return_%s", reg_names [first]);
|
else
|
else
|
sprintf (name, "__return_%s_%s", reg_names [first], reg_names [last]);
|
sprintf (name, "__return_%s_%s", reg_names [first], reg_names [last]);
|
|
|
sprintf (buff, "movhi hi(%s), r0, r6\n\tmovea lo(%s), r6, r6\n\tjmp r6",
|
sprintf (buff, "movhi hi(%s), r0, r6\n\tmovea lo(%s), r6, r6\n\tjmp r6",
|
name, name);
|
name, name);
|
}
|
}
|
else
|
else
|
{
|
{
|
if (first == last)
|
if (first == last)
|
sprintf (buff, "jr __return_%s", reg_names [first]);
|
sprintf (buff, "jr __return_%s", reg_names [first]);
|
else
|
else
|
sprintf (buff, "jr __return_%s_%s", reg_names [first], reg_names [last]);
|
sprintf (buff, "jr __return_%s_%s", reg_names [first], reg_names [last]);
|
}
|
}
|
|
|
return buff;
|
return buff;
|
}
|
}
|
|
|
|
|
/* Construct a JARL instruction to a routine that will perform the equivalent
|
/* Construct a JARL instruction to a routine that will perform the equivalent
|
of the RTL passed as a parameter. This RTL is a function prologue that
|
of the RTL passed as a parameter. This RTL is a function prologue that
|
saves some of the registers r20 - r31 onto the stack, and possibly acquires
|
saves some of the registers r20 - r31 onto the stack, and possibly acquires
|
some stack space as well. The code has already verified that the RTL
|
some stack space as well. The code has already verified that the RTL
|
matches these requirements. */
|
matches these requirements. */
|
char *
|
char *
|
construct_save_jarl (rtx op)
|
construct_save_jarl (rtx op)
|
{
|
{
|
int count = XVECLEN (op, 0);
|
int count = XVECLEN (op, 0);
|
int stack_bytes;
|
int stack_bytes;
|
unsigned long int mask;
|
unsigned long int mask;
|
unsigned long int first;
|
unsigned long int first;
|
unsigned long int last;
|
unsigned long int last;
|
int i;
|
int i;
|
static char buff [100]; /* XXX */
|
static char buff [100]; /* XXX */
|
|
|
if (count <= 2)
|
if (count <= 2)
|
{
|
{
|
error ("bogus JARL construction: %d\n", count);
|
error ("bogus JARL construction: %d\n", count);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Paranoia. */
|
/* Paranoia. */
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 0)) == SET);
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 0)) == SET);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) == PLUS);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) == PLUS);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0)) == REG);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0)) == REG);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) == CONST_INT);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) == CONST_INT);
|
|
|
/* Work out how many bytes to push onto the stack after storing the
|
/* Work out how many bytes to push onto the stack after storing the
|
registers. */
|
registers. */
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
|
|
|
/* Each push will put 4 bytes from the stack.... */
|
/* Each push will put 4 bytes from the stack.... */
|
stack_bytes += (count - (TARGET_LONG_CALLS ? 3 : 2)) * 4;
|
stack_bytes += (count - (TARGET_LONG_CALLS ? 3 : 2)) * 4;
|
|
|
/* Make sure that the amount we are popping either 0 or 16 bytes. */
|
/* Make sure that the amount we are popping either 0 or 16 bytes. */
|
if (stack_bytes != 0 && stack_bytes != -16)
|
if (stack_bytes != 0 && stack_bytes != -16)
|
{
|
{
|
error ("bad amount of stack space removal: %d", stack_bytes);
|
error ("bad amount of stack space removal: %d", stack_bytes);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Now compute the bit mask of registers to push. */
|
/* Now compute the bit mask of registers to push. */
|
mask = 0;
|
mask = 0;
|
for (i = 1; i < count - (TARGET_LONG_CALLS ? 2 : 1); i++)
|
for (i = 1; i < count - (TARGET_LONG_CALLS ? 2 : 1); i++)
|
{
|
{
|
rtx vector_element = XVECEXP (op, 0, i);
|
rtx vector_element = XVECEXP (op, 0, i);
|
|
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (SET_SRC (vector_element)) == REG);
|
gcc_assert (GET_CODE (SET_SRC (vector_element)) == REG);
|
gcc_assert (register_is_ok_for_epilogue (SET_SRC (vector_element),
|
gcc_assert (register_is_ok_for_epilogue (SET_SRC (vector_element),
|
SImode));
|
SImode));
|
|
|
mask |= 1 << REGNO (SET_SRC (vector_element));
|
mask |= 1 << REGNO (SET_SRC (vector_element));
|
}
|
}
|
|
|
/* Scan for the first register to push. */
|
/* Scan for the first register to push. */
|
for (first = 0; first < 32; first++)
|
for (first = 0; first < 32; first++)
|
{
|
{
|
if (mask & (1 << first))
|
if (mask & (1 << first))
|
break;
|
break;
|
}
|
}
|
|
|
gcc_assert (first < 32);
|
gcc_assert (first < 32);
|
|
|
/* Discover the last register to push. */
|
/* Discover the last register to push. */
|
if (mask & (1 << LINK_POINTER_REGNUM))
|
if (mask & (1 << LINK_POINTER_REGNUM))
|
{
|
{
|
gcc_assert (stack_bytes == -16);
|
gcc_assert (stack_bytes == -16);
|
|
|
last = LINK_POINTER_REGNUM;
|
last = LINK_POINTER_REGNUM;
|
}
|
}
|
else
|
else
|
{
|
{
|
gcc_assert (!stack_bytes);
|
gcc_assert (!stack_bytes);
|
gcc_assert (mask & (1 << 29));
|
gcc_assert (mask & (1 << 29));
|
|
|
last = 29;
|
last = 29;
|
}
|
}
|
|
|
/* Note, it is possible to have gaps in the register mask.
|
/* Note, it is possible to have gaps in the register mask.
|
We ignore this here, and generate a JARL anyway. We will
|
We ignore this here, and generate a JARL anyway. We will
|
be pushing more registers than is strictly necessary, but
|
be pushing more registers than is strictly necessary, but
|
it does save code space. */
|
it does save code space. */
|
|
|
if (TARGET_LONG_CALLS)
|
if (TARGET_LONG_CALLS)
|
{
|
{
|
char name[40];
|
char name[40];
|
|
|
if (first == last)
|
if (first == last)
|
sprintf (name, "__save_%s", reg_names [first]);
|
sprintf (name, "__save_%s", reg_names [first]);
|
else
|
else
|
sprintf (name, "__save_%s_%s", reg_names [first], reg_names [last]);
|
sprintf (name, "__save_%s_%s", reg_names [first], reg_names [last]);
|
|
|
sprintf (buff, "movhi hi(%s), r0, r11\n\tmovea lo(%s), r11, r11\n\tjarl .+4, r10\n\tadd 4, r10\n\tjmp r11",
|
sprintf (buff, "movhi hi(%s), r0, r11\n\tmovea lo(%s), r11, r11\n\tjarl .+4, r10\n\tadd 4, r10\n\tjmp r11",
|
name, name);
|
name, name);
|
}
|
}
|
else
|
else
|
{
|
{
|
if (first == last)
|
if (first == last)
|
sprintf (buff, "jarl __save_%s, r10", reg_names [first]);
|
sprintf (buff, "jarl __save_%s, r10", reg_names [first]);
|
else
|
else
|
sprintf (buff, "jarl __save_%s_%s, r10", reg_names [first],
|
sprintf (buff, "jarl __save_%s_%s, r10", reg_names [first],
|
reg_names [last]);
|
reg_names [last]);
|
}
|
}
|
|
|
return buff;
|
return buff;
|
}
|
}
|
|
|
extern tree last_assemble_variable_decl;
|
extern tree last_assemble_variable_decl;
|
extern int size_directive_output;
|
extern int size_directive_output;
|
|
|
/* A version of asm_output_aligned_bss() that copes with the special
|
/* A version of asm_output_aligned_bss() that copes with the special
|
data areas of the v850. */
|
data areas of the v850. */
|
void
|
void
|
v850_output_aligned_bss (FILE * file,
|
v850_output_aligned_bss (FILE * file,
|
tree decl,
|
tree decl,
|
const char * name,
|
const char * name,
|
unsigned HOST_WIDE_INT size,
|
unsigned HOST_WIDE_INT size,
|
int align)
|
int align)
|
{
|
{
|
switch (v850_get_data_area (decl))
|
switch (v850_get_data_area (decl))
|
{
|
{
|
case DATA_AREA_ZDA:
|
case DATA_AREA_ZDA:
|
switch_to_section (zbss_section);
|
switch_to_section (zbss_section);
|
break;
|
break;
|
|
|
case DATA_AREA_SDA:
|
case DATA_AREA_SDA:
|
switch_to_section (sbss_section);
|
switch_to_section (sbss_section);
|
break;
|
break;
|
|
|
case DATA_AREA_TDA:
|
case DATA_AREA_TDA:
|
switch_to_section (tdata_section);
|
switch_to_section (tdata_section);
|
|
|
default:
|
default:
|
switch_to_section (bss_section);
|
switch_to_section (bss_section);
|
break;
|
break;
|
}
|
}
|
|
|
ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
|
ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
|
#ifdef ASM_DECLARE_OBJECT_NAME
|
#ifdef ASM_DECLARE_OBJECT_NAME
|
last_assemble_variable_decl = decl;
|
last_assemble_variable_decl = decl;
|
ASM_DECLARE_OBJECT_NAME (file, name, decl);
|
ASM_DECLARE_OBJECT_NAME (file, name, decl);
|
#else
|
#else
|
/* Standard thing is just output label for the object. */
|
/* Standard thing is just output label for the object. */
|
ASM_OUTPUT_LABEL (file, name);
|
ASM_OUTPUT_LABEL (file, name);
|
#endif /* ASM_DECLARE_OBJECT_NAME */
|
#endif /* ASM_DECLARE_OBJECT_NAME */
|
ASM_OUTPUT_SKIP (file, size ? size : 1);
|
ASM_OUTPUT_SKIP (file, size ? size : 1);
|
}
|
}
|
|
|
/* Called via the macro ASM_OUTPUT_DECL_COMMON */
|
/* Called via the macro ASM_OUTPUT_DECL_COMMON */
|
void
|
void
|
v850_output_common (FILE * file,
|
v850_output_common (FILE * file,
|
tree decl,
|
tree decl,
|
const char * name,
|
const char * name,
|
int size,
|
int size,
|
int align)
|
int align)
|
{
|
{
|
if (decl == NULL_TREE)
|
if (decl == NULL_TREE)
|
{
|
{
|
fprintf (file, "%s", COMMON_ASM_OP);
|
fprintf (file, "%s", COMMON_ASM_OP);
|
}
|
}
|
else
|
else
|
{
|
{
|
switch (v850_get_data_area (decl))
|
switch (v850_get_data_area (decl))
|
{
|
{
|
case DATA_AREA_ZDA:
|
case DATA_AREA_ZDA:
|
fprintf (file, "%s", ZCOMMON_ASM_OP);
|
fprintf (file, "%s", ZCOMMON_ASM_OP);
|
break;
|
break;
|
|
|
case DATA_AREA_SDA:
|
case DATA_AREA_SDA:
|
fprintf (file, "%s", SCOMMON_ASM_OP);
|
fprintf (file, "%s", SCOMMON_ASM_OP);
|
break;
|
break;
|
|
|
case DATA_AREA_TDA:
|
case DATA_AREA_TDA:
|
fprintf (file, "%s", TCOMMON_ASM_OP);
|
fprintf (file, "%s", TCOMMON_ASM_OP);
|
break;
|
break;
|
|
|
default:
|
default:
|
fprintf (file, "%s", COMMON_ASM_OP);
|
fprintf (file, "%s", COMMON_ASM_OP);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
assemble_name (file, name);
|
assemble_name (file, name);
|
fprintf (file, ",%u,%u\n", size, align / BITS_PER_UNIT);
|
fprintf (file, ",%u,%u\n", size, align / BITS_PER_UNIT);
|
}
|
}
|
|
|
/* Called via the macro ASM_OUTPUT_DECL_LOCAL */
|
/* Called via the macro ASM_OUTPUT_DECL_LOCAL */
|
void
|
void
|
v850_output_local (FILE * file,
|
v850_output_local (FILE * file,
|
tree decl,
|
tree decl,
|
const char * name,
|
const char * name,
|
int size,
|
int size,
|
int align)
|
int align)
|
{
|
{
|
fprintf (file, "%s", LOCAL_ASM_OP);
|
fprintf (file, "%s", LOCAL_ASM_OP);
|
assemble_name (file, name);
|
assemble_name (file, name);
|
fprintf (file, "\n");
|
fprintf (file, "\n");
|
|
|
ASM_OUTPUT_ALIGNED_DECL_COMMON (file, decl, name, size, align);
|
ASM_OUTPUT_ALIGNED_DECL_COMMON (file, decl, name, size, align);
|
}
|
}
|
|
|
/* Add data area to the given declaration if a ghs data area pragma is
|
/* Add data area to the given declaration if a ghs data area pragma is
|
currently in effect (#pragma ghs startXXX/endXXX). */
|
currently in effect (#pragma ghs startXXX/endXXX). */
|
static void
|
static void
|
v850_insert_attributes (tree decl, tree * attr_ptr ATTRIBUTE_UNUSED )
|
v850_insert_attributes (tree decl, tree * attr_ptr ATTRIBUTE_UNUSED )
|
{
|
{
|
if (data_area_stack
|
if (data_area_stack
|
&& data_area_stack->data_area
|
&& data_area_stack->data_area
|
&& current_function_decl == NULL_TREE
|
&& current_function_decl == NULL_TREE
|
&& (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == CONST_DECL)
|
&& (TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == CONST_DECL)
|
&& v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
&& v850_get_data_area (decl) == DATA_AREA_NORMAL)
|
v850_set_data_area (decl, data_area_stack->data_area);
|
v850_set_data_area (decl, data_area_stack->data_area);
|
|
|
/* Initialize the default names of the v850 specific sections,
|
/* Initialize the default names of the v850 specific sections,
|
if this has not been done before. */
|
if this has not been done before. */
|
|
|
if (GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA] == NULL)
|
if (GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA] == NULL)
|
{
|
{
|
GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA]
|
GHS_default_section_names [(int) GHS_SECTION_KIND_SDATA]
|
= build_string (sizeof (".sdata")-1, ".sdata");
|
= build_string (sizeof (".sdata")-1, ".sdata");
|
|
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ROSDATA]
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ROSDATA]
|
= build_string (sizeof (".rosdata")-1, ".rosdata");
|
= build_string (sizeof (".rosdata")-1, ".rosdata");
|
|
|
GHS_default_section_names [(int) GHS_SECTION_KIND_TDATA]
|
GHS_default_section_names [(int) GHS_SECTION_KIND_TDATA]
|
= build_string (sizeof (".tdata")-1, ".tdata");
|
= build_string (sizeof (".tdata")-1, ".tdata");
|
|
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ZDATA]
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ZDATA]
|
= build_string (sizeof (".zdata")-1, ".zdata");
|
= build_string (sizeof (".zdata")-1, ".zdata");
|
|
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ROZDATA]
|
GHS_default_section_names [(int) GHS_SECTION_KIND_ROZDATA]
|
= build_string (sizeof (".rozdata")-1, ".rozdata");
|
= build_string (sizeof (".rozdata")-1, ".rozdata");
|
}
|
}
|
|
|
if (current_function_decl == NULL_TREE
|
if (current_function_decl == NULL_TREE
|
&& (TREE_CODE (decl) == VAR_DECL
|
&& (TREE_CODE (decl) == VAR_DECL
|
|| TREE_CODE (decl) == CONST_DECL
|
|| TREE_CODE (decl) == CONST_DECL
|
|| TREE_CODE (decl) == FUNCTION_DECL)
|
|| TREE_CODE (decl) == FUNCTION_DECL)
|
&& (!DECL_EXTERNAL (decl) || DECL_INITIAL (decl))
|
&& (!DECL_EXTERNAL (decl) || DECL_INITIAL (decl))
|
&& !DECL_SECTION_NAME (decl))
|
&& !DECL_SECTION_NAME (decl))
|
{
|
{
|
enum GHS_section_kind kind = GHS_SECTION_KIND_DEFAULT;
|
enum GHS_section_kind kind = GHS_SECTION_KIND_DEFAULT;
|
tree chosen_section;
|
tree chosen_section;
|
|
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
if (TREE_CODE (decl) == FUNCTION_DECL)
|
kind = GHS_SECTION_KIND_TEXT;
|
kind = GHS_SECTION_KIND_TEXT;
|
else
|
else
|
{
|
{
|
/* First choose a section kind based on the data area of the decl. */
|
/* First choose a section kind based on the data area of the decl. */
|
switch (v850_get_data_area (decl))
|
switch (v850_get_data_area (decl))
|
{
|
{
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
|
|
case DATA_AREA_SDA:
|
case DATA_AREA_SDA:
|
kind = ((TREE_READONLY (decl))
|
kind = ((TREE_READONLY (decl))
|
? GHS_SECTION_KIND_ROSDATA
|
? GHS_SECTION_KIND_ROSDATA
|
: GHS_SECTION_KIND_SDATA);
|
: GHS_SECTION_KIND_SDATA);
|
break;
|
break;
|
|
|
case DATA_AREA_TDA:
|
case DATA_AREA_TDA:
|
kind = GHS_SECTION_KIND_TDATA;
|
kind = GHS_SECTION_KIND_TDATA;
|
break;
|
break;
|
|
|
case DATA_AREA_ZDA:
|
case DATA_AREA_ZDA:
|
kind = ((TREE_READONLY (decl))
|
kind = ((TREE_READONLY (decl))
|
? GHS_SECTION_KIND_ROZDATA
|
? GHS_SECTION_KIND_ROZDATA
|
: GHS_SECTION_KIND_ZDATA);
|
: GHS_SECTION_KIND_ZDATA);
|
break;
|
break;
|
|
|
case DATA_AREA_NORMAL: /* default data area */
|
case DATA_AREA_NORMAL: /* default data area */
|
if (TREE_READONLY (decl))
|
if (TREE_READONLY (decl))
|
kind = GHS_SECTION_KIND_RODATA;
|
kind = GHS_SECTION_KIND_RODATA;
|
else if (DECL_INITIAL (decl))
|
else if (DECL_INITIAL (decl))
|
kind = GHS_SECTION_KIND_DATA;
|
kind = GHS_SECTION_KIND_DATA;
|
else
|
else
|
kind = GHS_SECTION_KIND_BSS;
|
kind = GHS_SECTION_KIND_BSS;
|
}
|
}
|
}
|
}
|
|
|
/* Now, if the section kind has been explicitly renamed,
|
/* Now, if the section kind has been explicitly renamed,
|
then attach a section attribute. */
|
then attach a section attribute. */
|
chosen_section = GHS_current_section_names [(int) kind];
|
chosen_section = GHS_current_section_names [(int) kind];
|
|
|
/* Otherwise, if this kind of section needs an explicit section
|
/* Otherwise, if this kind of section needs an explicit section
|
attribute, then also attach one. */
|
attribute, then also attach one. */
|
if (chosen_section == NULL)
|
if (chosen_section == NULL)
|
chosen_section = GHS_default_section_names [(int) kind];
|
chosen_section = GHS_default_section_names [(int) kind];
|
|
|
if (chosen_section)
|
if (chosen_section)
|
{
|
{
|
/* Only set the section name if specified by a pragma, because
|
/* Only set the section name if specified by a pragma, because
|
otherwise it will force those variables to get allocated storage
|
otherwise it will force those variables to get allocated storage
|
in this module, rather than by the linker. */
|
in this module, rather than by the linker. */
|
DECL_SECTION_NAME (decl) = chosen_section;
|
DECL_SECTION_NAME (decl) = chosen_section;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Construct a DISPOSE instruction that is the equivalent of
|
/* Construct a DISPOSE instruction that is the equivalent of
|
the given RTX. We have already verified that this should
|
the given RTX. We have already verified that this should
|
be possible. */
|
be possible. */
|
|
|
char *
|
char *
|
construct_dispose_instruction (rtx op)
|
construct_dispose_instruction (rtx op)
|
{
|
{
|
int count = XVECLEN (op, 0);
|
int count = XVECLEN (op, 0);
|
int stack_bytes;
|
int stack_bytes;
|
unsigned long int mask;
|
unsigned long int mask;
|
int i;
|
int i;
|
static char buff[ 100 ]; /* XXX */
|
static char buff[ 100 ]; /* XXX */
|
int use_callt = 0;
|
int use_callt = 0;
|
|
|
if (count <= 2)
|
if (count <= 2)
|
{
|
{
|
error ("bogus DISPOSE construction: %d", count);
|
error ("bogus DISPOSE construction: %d", count);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Work out how many bytes to pop off the
|
/* Work out how many bytes to pop off the
|
stack before retrieving registers. */
|
stack before retrieving registers. */
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 1)) == SET);
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 1)) == SET);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) == CONST_INT);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1)) == CONST_INT);
|
|
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1));
|
|
|
/* Each pop will remove 4 bytes from the stack.... */
|
/* Each pop will remove 4 bytes from the stack.... */
|
stack_bytes -= (count - 2) * 4;
|
stack_bytes -= (count - 2) * 4;
|
|
|
/* Make sure that the amount we are popping
|
/* Make sure that the amount we are popping
|
will fit into the DISPOSE instruction. */
|
will fit into the DISPOSE instruction. */
|
if (stack_bytes > 128)
|
if (stack_bytes > 128)
|
{
|
{
|
error ("too much stack space to dispose of: %d", stack_bytes);
|
error ("too much stack space to dispose of: %d", stack_bytes);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Now compute the bit mask of registers to push. */
|
/* Now compute the bit mask of registers to push. */
|
mask = 0;
|
mask = 0;
|
|
|
for (i = 2; i < count; i++)
|
for (i = 2; i < count; i++)
|
{
|
{
|
rtx vector_element = XVECEXP (op, 0, i);
|
rtx vector_element = XVECEXP (op, 0, i);
|
|
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (SET_DEST (vector_element)) == REG);
|
gcc_assert (GET_CODE (SET_DEST (vector_element)) == REG);
|
gcc_assert (register_is_ok_for_epilogue (SET_DEST (vector_element),
|
gcc_assert (register_is_ok_for_epilogue (SET_DEST (vector_element),
|
SImode));
|
SImode));
|
|
|
if (REGNO (SET_DEST (vector_element)) == 2)
|
if (REGNO (SET_DEST (vector_element)) == 2)
|
use_callt = 1;
|
use_callt = 1;
|
else
|
else
|
mask |= 1 << REGNO (SET_DEST (vector_element));
|
mask |= 1 << REGNO (SET_DEST (vector_element));
|
}
|
}
|
|
|
if (! TARGET_DISABLE_CALLT
|
if (! TARGET_DISABLE_CALLT
|
&& (use_callt || stack_bytes == 0 || stack_bytes == 16))
|
&& (use_callt || stack_bytes == 0 || stack_bytes == 16))
|
{
|
{
|
if (use_callt)
|
if (use_callt)
|
{
|
{
|
sprintf (buff, "callt ctoff(__callt_return_r2_r%d)", (mask & (1 << 31)) ? 31 : 29);
|
sprintf (buff, "callt ctoff(__callt_return_r2_r%d)", (mask & (1 << 31)) ? 31 : 29);
|
return buff;
|
return buff;
|
}
|
}
|
else
|
else
|
{
|
{
|
for (i = 20; i < 32; i++)
|
for (i = 20; i < 32; i++)
|
if (mask & (1 << i))
|
if (mask & (1 << i))
|
break;
|
break;
|
|
|
if (i == 31)
|
if (i == 31)
|
sprintf (buff, "callt ctoff(__callt_return_r31c)");
|
sprintf (buff, "callt ctoff(__callt_return_r31c)");
|
else
|
else
|
sprintf (buff, "callt ctoff(__callt_return_r%d_r%d%s)",
|
sprintf (buff, "callt ctoff(__callt_return_r%d_r%d%s)",
|
i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
|
i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
static char regs [100]; /* XXX */
|
static char regs [100]; /* XXX */
|
int done_one;
|
int done_one;
|
|
|
/* Generate the DISPOSE instruction. Note we could just issue the
|
/* Generate the DISPOSE instruction. Note we could just issue the
|
bit mask as a number as the assembler can cope with this, but for
|
bit mask as a number as the assembler can cope with this, but for
|
the sake of our readers we turn it into a textual description. */
|
the sake of our readers we turn it into a textual description. */
|
regs[0] = 0;
|
regs[0] = 0;
|
done_one = 0;
|
done_one = 0;
|
|
|
for (i = 20; i < 32; i++)
|
for (i = 20; i < 32; i++)
|
{
|
{
|
if (mask & (1 << i))
|
if (mask & (1 << i))
|
{
|
{
|
int first;
|
int first;
|
|
|
if (done_one)
|
if (done_one)
|
strcat (regs, ", ");
|
strcat (regs, ", ");
|
else
|
else
|
done_one = 1;
|
done_one = 1;
|
|
|
first = i;
|
first = i;
|
strcat (regs, reg_names[ first ]);
|
strcat (regs, reg_names[ first ]);
|
|
|
for (i++; i < 32; i++)
|
for (i++; i < 32; i++)
|
if ((mask & (1 << i)) == 0)
|
if ((mask & (1 << i)) == 0)
|
break;
|
break;
|
|
|
if (i > first + 1)
|
if (i > first + 1)
|
{
|
{
|
strcat (regs, " - ");
|
strcat (regs, " - ");
|
strcat (regs, reg_names[ i - 1 ] );
|
strcat (regs, reg_names[ i - 1 ] );
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
sprintf (buff, "dispose %d {%s}, r31", stack_bytes / 4, regs);
|
sprintf (buff, "dispose %d {%s}, r31", stack_bytes / 4, regs);
|
}
|
}
|
|
|
return buff;
|
return buff;
|
}
|
}
|
|
|
/* Construct a PREPARE instruction that is the equivalent of
|
/* Construct a PREPARE instruction that is the equivalent of
|
the given RTL. We have already verified that this should
|
the given RTL. We have already verified that this should
|
be possible. */
|
be possible. */
|
|
|
char *
|
char *
|
construct_prepare_instruction (rtx op)
|
construct_prepare_instruction (rtx op)
|
{
|
{
|
int count = XVECLEN (op, 0);
|
int count = XVECLEN (op, 0);
|
int stack_bytes;
|
int stack_bytes;
|
unsigned long int mask;
|
unsigned long int mask;
|
int i;
|
int i;
|
static char buff[ 100 ]; /* XXX */
|
static char buff[ 100 ]; /* XXX */
|
int use_callt = 0;
|
int use_callt = 0;
|
|
|
if (count <= 1)
|
if (count <= 1)
|
{
|
{
|
error ("bogus PREPEARE construction: %d", count);
|
error ("bogus PREPEARE construction: %d", count);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Work out how many bytes to push onto
|
/* Work out how many bytes to push onto
|
the stack after storing the registers. */
|
the stack after storing the registers. */
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 0)) == SET);
|
gcc_assert (GET_CODE (XVECEXP (op, 0, 0)) == SET);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) == PLUS);
|
gcc_assert (GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) == PLUS);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) == CONST_INT);
|
gcc_assert (GET_CODE (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1)) == CONST_INT);
|
|
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
|
stack_bytes = INTVAL (XEXP (SET_SRC (XVECEXP (op, 0, 0)), 1));
|
|
|
/* Each push will put 4 bytes from the stack. */
|
/* Each push will put 4 bytes from the stack. */
|
stack_bytes += (count - 1) * 4;
|
stack_bytes += (count - 1) * 4;
|
|
|
/* Make sure that the amount we are popping
|
/* Make sure that the amount we are popping
|
will fit into the DISPOSE instruction. */
|
will fit into the DISPOSE instruction. */
|
if (stack_bytes < -128)
|
if (stack_bytes < -128)
|
{
|
{
|
error ("too much stack space to prepare: %d", stack_bytes);
|
error ("too much stack space to prepare: %d", stack_bytes);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
/* Now compute the bit mask of registers to push. */
|
/* Now compute the bit mask of registers to push. */
|
mask = 0;
|
mask = 0;
|
for (i = 1; i < count; i++)
|
for (i = 1; i < count; i++)
|
{
|
{
|
rtx vector_element = XVECEXP (op, 0, i);
|
rtx vector_element = XVECEXP (op, 0, i);
|
|
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (vector_element) == SET);
|
gcc_assert (GET_CODE (SET_SRC (vector_element)) == REG);
|
gcc_assert (GET_CODE (SET_SRC (vector_element)) == REG);
|
gcc_assert (register_is_ok_for_epilogue (SET_SRC (vector_element),
|
gcc_assert (register_is_ok_for_epilogue (SET_SRC (vector_element),
|
SImode));
|
SImode));
|
|
|
if (REGNO (SET_SRC (vector_element)) == 2)
|
if (REGNO (SET_SRC (vector_element)) == 2)
|
use_callt = 1;
|
use_callt = 1;
|
else
|
else
|
mask |= 1 << REGNO (SET_SRC (vector_element));
|
mask |= 1 << REGNO (SET_SRC (vector_element));
|
}
|
}
|
|
|
if ((! TARGET_DISABLE_CALLT)
|
if ((! TARGET_DISABLE_CALLT)
|
&& (use_callt || stack_bytes == 0 || stack_bytes == -16))
|
&& (use_callt || stack_bytes == 0 || stack_bytes == -16))
|
{
|
{
|
if (use_callt)
|
if (use_callt)
|
{
|
{
|
sprintf (buff, "callt ctoff(__callt_save_r2_r%d)", (mask & (1 << 31)) ? 31 : 29 );
|
sprintf (buff, "callt ctoff(__callt_save_r2_r%d)", (mask & (1 << 31)) ? 31 : 29 );
|
return buff;
|
return buff;
|
}
|
}
|
|
|
for (i = 20; i < 32; i++)
|
for (i = 20; i < 32; i++)
|
if (mask & (1 << i))
|
if (mask & (1 << i))
|
break;
|
break;
|
|
|
if (i == 31)
|
if (i == 31)
|
sprintf (buff, "callt ctoff(__callt_save_r31c)");
|
sprintf (buff, "callt ctoff(__callt_save_r31c)");
|
else
|
else
|
sprintf (buff, "callt ctoff(__callt_save_r%d_r%d%s)",
|
sprintf (buff, "callt ctoff(__callt_save_r%d_r%d%s)",
|
i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
|
i, (mask & (1 << 31)) ? 31 : 29, stack_bytes ? "c" : "");
|
}
|
}
|
else
|
else
|
{
|
{
|
static char regs [100]; /* XXX */
|
static char regs [100]; /* XXX */
|
int done_one;
|
int done_one;
|
|
|
|
|
/* Generate the PREPARE instruction. Note we could just issue the
|
/* Generate the PREPARE instruction. Note we could just issue the
|
bit mask as a number as the assembler can cope with this, but for
|
bit mask as a number as the assembler can cope with this, but for
|
the sake of our readers we turn it into a textual description. */
|
the sake of our readers we turn it into a textual description. */
|
regs[0] = 0;
|
regs[0] = 0;
|
done_one = 0;
|
done_one = 0;
|
|
|
for (i = 20; i < 32; i++)
|
for (i = 20; i < 32; i++)
|
{
|
{
|
if (mask & (1 << i))
|
if (mask & (1 << i))
|
{
|
{
|
int first;
|
int first;
|
|
|
if (done_one)
|
if (done_one)
|
strcat (regs, ", ");
|
strcat (regs, ", ");
|
else
|
else
|
done_one = 1;
|
done_one = 1;
|
|
|
first = i;
|
first = i;
|
strcat (regs, reg_names[ first ]);
|
strcat (regs, reg_names[ first ]);
|
|
|
for (i++; i < 32; i++)
|
for (i++; i < 32; i++)
|
if ((mask & (1 << i)) == 0)
|
if ((mask & (1 << i)) == 0)
|
break;
|
break;
|
|
|
if (i > first + 1)
|
if (i > first + 1)
|
{
|
{
|
strcat (regs, " - ");
|
strcat (regs, " - ");
|
strcat (regs, reg_names[ i - 1 ] );
|
strcat (regs, reg_names[ i - 1 ] );
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
sprintf (buff, "prepare {%s}, %d", regs, (- stack_bytes) / 4);
|
sprintf (buff, "prepare {%s}, %d", regs, (- stack_bytes) / 4);
|
}
|
}
|
|
|
return buff;
|
return buff;
|
}
|
}
|
|
|
/* Return an RTX indicating where the return address to the
|
/* Return an RTX indicating where the return address to the
|
calling function can be found. */
|
calling function can be found. */
|
|
|
rtx
|
rtx
|
v850_return_addr (int count)
|
v850_return_addr (int count)
|
{
|
{
|
if (count != 0)
|
if (count != 0)
|
return const0_rtx;
|
return const0_rtx;
|
|
|
return get_hard_reg_initial_val (Pmode, LINK_POINTER_REGNUM);
|
return get_hard_reg_initial_val (Pmode, LINK_POINTER_REGNUM);
|
}
|
}
|
|
|
/* Implement TARGET_ASM_INIT_SECTIONS. */
|
/* Implement TARGET_ASM_INIT_SECTIONS. */
|
|
|
static void
|
static void
|
v850_asm_init_sections (void)
|
v850_asm_init_sections (void)
|
{
|
{
|
rosdata_section
|
rosdata_section
|
= get_unnamed_section (0, output_section_asm_op,
|
= get_unnamed_section (0, output_section_asm_op,
|
"\t.section .rosdata,\"a\"");
|
"\t.section .rosdata,\"a\"");
|
|
|
rozdata_section
|
rozdata_section
|
= get_unnamed_section (0, output_section_asm_op,
|
= get_unnamed_section (0, output_section_asm_op,
|
"\t.section .rozdata,\"a\"");
|
"\t.section .rozdata,\"a\"");
|
|
|
tdata_section
|
tdata_section
|
= get_unnamed_section (SECTION_WRITE, output_section_asm_op,
|
= get_unnamed_section (SECTION_WRITE, output_section_asm_op,
|
"\t.section .tdata,\"aw\"");
|
"\t.section .tdata,\"aw\"");
|
|
|
zdata_section
|
zdata_section
|
= get_unnamed_section (SECTION_WRITE, output_section_asm_op,
|
= get_unnamed_section (SECTION_WRITE, output_section_asm_op,
|
"\t.section .zdata,\"aw\"");
|
"\t.section .zdata,\"aw\"");
|
|
|
zbss_section
|
zbss_section
|
= get_unnamed_section (SECTION_WRITE | SECTION_BSS,
|
= get_unnamed_section (SECTION_WRITE | SECTION_BSS,
|
output_section_asm_op,
|
output_section_asm_op,
|
"\t.section .zbss,\"aw\"");
|
"\t.section .zbss,\"aw\"");
|
}
|
}
|
|
|
static section *
|
static section *
|
v850_select_section (tree exp,
|
v850_select_section (tree exp,
|
int reloc ATTRIBUTE_UNUSED,
|
int reloc ATTRIBUTE_UNUSED,
|
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
|
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
|
{
|
{
|
if (TREE_CODE (exp) == VAR_DECL)
|
if (TREE_CODE (exp) == VAR_DECL)
|
{
|
{
|
int is_const;
|
int is_const;
|
if (!TREE_READONLY (exp)
|
if (!TREE_READONLY (exp)
|
|| TREE_SIDE_EFFECTS (exp)
|
|| TREE_SIDE_EFFECTS (exp)
|
|| !DECL_INITIAL (exp)
|
|| !DECL_INITIAL (exp)
|
|| (DECL_INITIAL (exp) != error_mark_node
|
|| (DECL_INITIAL (exp) != error_mark_node
|
&& !TREE_CONSTANT (DECL_INITIAL (exp))))
|
&& !TREE_CONSTANT (DECL_INITIAL (exp))))
|
is_const = FALSE;
|
is_const = FALSE;
|
else
|
else
|
is_const = TRUE;
|
is_const = TRUE;
|
|
|
switch (v850_get_data_area (exp))
|
switch (v850_get_data_area (exp))
|
{
|
{
|
case DATA_AREA_ZDA:
|
case DATA_AREA_ZDA:
|
return is_const ? rozdata_section : zdata_section;
|
return is_const ? rozdata_section : zdata_section;
|
|
|
case DATA_AREA_TDA:
|
case DATA_AREA_TDA:
|
return tdata_section;
|
return tdata_section;
|
|
|
case DATA_AREA_SDA:
|
case DATA_AREA_SDA:
|
return is_const ? rosdata_section : sdata_section;
|
return is_const ? rosdata_section : sdata_section;
|
|
|
default:
|
default:
|
return is_const ? readonly_data_section : data_section;
|
return is_const ? readonly_data_section : data_section;
|
}
|
}
|
}
|
}
|
return readonly_data_section;
|
return readonly_data_section;
|
}
|
}
|
|
|
/* Worker function for TARGET_RETURN_IN_MEMORY. */
|
/* Worker function for TARGET_RETURN_IN_MEMORY. */
|
|
|
static bool
|
static bool
|
v850_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
|
v850_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
|
{
|
{
|
/* Return values > 8 bytes in length in memory. */
|
/* Return values > 8 bytes in length in memory. */
|
return int_size_in_bytes (type) > 8 || TYPE_MODE (type) == BLKmode;
|
return int_size_in_bytes (type) > 8 || TYPE_MODE (type) == BLKmode;
|
}
|
}
|
|
|
/* Worker function for TARGET_FUNCTION_VALUE. */
|
/* Worker function for TARGET_FUNCTION_VALUE. */
|
|
|
rtx
|
rtx
|
v850_function_value (const_tree valtype,
|
v850_function_value (const_tree valtype,
|
const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
|
const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
|
bool outgoing ATTRIBUTE_UNUSED)
|
bool outgoing ATTRIBUTE_UNUSED)
|
{
|
{
|
return gen_rtx_REG (TYPE_MODE (valtype), 10);
|
return gen_rtx_REG (TYPE_MODE (valtype), 10);
|
}
|
}
|
|
|
|
|
/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */
|
/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */
|
|
|
static void
|
static void
|
v850_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
|
v850_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
|
enum machine_mode mode ATTRIBUTE_UNUSED,
|
enum machine_mode mode ATTRIBUTE_UNUSED,
|
tree type ATTRIBUTE_UNUSED,
|
tree type ATTRIBUTE_UNUSED,
|
int *pretend_arg_size ATTRIBUTE_UNUSED,
|
int *pretend_arg_size ATTRIBUTE_UNUSED,
|
int second_time ATTRIBUTE_UNUSED)
|
int second_time ATTRIBUTE_UNUSED)
|
{
|
{
|
ca->anonymous_args = (!TARGET_GHS ? 1 : 0);
|
ca->anonymous_args = (!TARGET_GHS ? 1 : 0);
|
}
|
}
|
|
|
/* Worker function for TARGET_CAN_ELIMINATE. */
|
/* Worker function for TARGET_CAN_ELIMINATE. */
|
|
|
static bool
|
static bool
|
v850_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
v850_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
{
|
{
|
return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
|
return (to == STACK_POINTER_REGNUM ? ! frame_pointer_needed : true);
|
}
|
}
|
|
|
|
|
/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE. */
|
/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE. */
|
|
|
static void
|
static void
|
v850_asm_trampoline_template (FILE *f)
|
v850_asm_trampoline_template (FILE *f)
|
{
|
{
|
fprintf (f, "\tjarl .+4,r12\n");
|
fprintf (f, "\tjarl .+4,r12\n");
|
fprintf (f, "\tld.w 12[r12],r20\n");
|
fprintf (f, "\tld.w 12[r12],r20\n");
|
fprintf (f, "\tld.w 16[r12],r12\n");
|
fprintf (f, "\tld.w 16[r12],r12\n");
|
fprintf (f, "\tjmp [r12]\n");
|
fprintf (f, "\tjmp [r12]\n");
|
fprintf (f, "\tnop\n");
|
fprintf (f, "\tnop\n");
|
fprintf (f, "\t.long 0\n");
|
fprintf (f, "\t.long 0\n");
|
fprintf (f, "\t.long 0\n");
|
fprintf (f, "\t.long 0\n");
|
}
|
}
|
|
|
/* Worker function for TARGET_TRAMPOLINE_INIT. */
|
/* Worker function for TARGET_TRAMPOLINE_INIT. */
|
|
|
static void
|
static void
|
v850_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
|
v850_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
|
{
|
{
|
rtx mem, fnaddr = XEXP (DECL_RTL (fndecl), 0);
|
rtx mem, fnaddr = XEXP (DECL_RTL (fndecl), 0);
|
|
|
emit_block_move (m_tramp, assemble_trampoline_template (),
|
emit_block_move (m_tramp, assemble_trampoline_template (),
|
GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
|
GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
|
|
|
mem = adjust_address (m_tramp, SImode, 16);
|
mem = adjust_address (m_tramp, SImode, 16);
|
emit_move_insn (mem, chain_value);
|
emit_move_insn (mem, chain_value);
|
mem = adjust_address (m_tramp, SImode, 20);
|
mem = adjust_address (m_tramp, SImode, 20);
|
emit_move_insn (mem, fnaddr);
|
emit_move_insn (mem, fnaddr);
|
}
|
}
|
|
|
#include "gt-v850.h"
|
#include "gt-v850.h"
|
|
|