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

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [gnu-src/] [gcc-4.2.2/] [gcc/] [config/] [xtensa/] [xtensa.c] - Diff between revs 38 and 154

Only display areas with differences | Details | Blame | View Log

Rev 38 Rev 154
/* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
/* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
   Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
   Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
   Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
   Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify it under
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
Software Foundation; either version 3, or (at your option) any later
version.
version.
 
 
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
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 "rtl.h"
#include "rtl.h"
#include "regs.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "basic-block.h"
#include "real.h"
#include "real.h"
#include "insn-config.h"
#include "insn-config.h"
#include "conditions.h"
#include "conditions.h"
#include "insn-flags.h"
#include "insn-flags.h"
#include "insn-attr.h"
#include "insn-attr.h"
#include "insn-codes.h"
#include "insn-codes.h"
#include "recog.h"
#include "recog.h"
#include "output.h"
#include "output.h"
#include "tree.h"
#include "tree.h"
#include "expr.h"
#include "expr.h"
#include "flags.h"
#include "flags.h"
#include "reload.h"
#include "reload.h"
#include "tm_p.h"
#include "tm_p.h"
#include "function.h"
#include "function.h"
#include "toplev.h"
#include "toplev.h"
#include "optabs.h"
#include "optabs.h"
#include "libfuncs.h"
#include "libfuncs.h"
#include "ggc.h"
#include "ggc.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
#include "langhooks.h"
#include "langhooks.h"
#include "tree-gimple.h"
#include "tree-gimple.h"
 
 
 
 
/* Enumeration for all of the relational tests, so that we can build
/* Enumeration for all of the relational tests, so that we can build
   arrays indexed by the test type, and not worry about the order
   arrays indexed by the test type, and not worry about the order
   of EQ, NE, etc.  */
   of EQ, NE, etc.  */
 
 
enum internal_test
enum internal_test
{
{
  ITEST_EQ,
  ITEST_EQ,
  ITEST_NE,
  ITEST_NE,
  ITEST_GT,
  ITEST_GT,
  ITEST_GE,
  ITEST_GE,
  ITEST_LT,
  ITEST_LT,
  ITEST_LE,
  ITEST_LE,
  ITEST_GTU,
  ITEST_GTU,
  ITEST_GEU,
  ITEST_GEU,
  ITEST_LTU,
  ITEST_LTU,
  ITEST_LEU,
  ITEST_LEU,
  ITEST_MAX
  ITEST_MAX
};
};
 
 
/* Cached operands, and operator to compare for use in set/branch on
/* Cached operands, and operator to compare for use in set/branch on
   condition codes.  */
   condition codes.  */
rtx branch_cmp[2];
rtx branch_cmp[2];
 
 
/* what type of branch to use */
/* what type of branch to use */
enum cmp_type branch_type;
enum cmp_type branch_type;
 
 
/* Array giving truth value on whether or not a given hard register
/* Array giving truth value on whether or not a given hard register
   can support a given mode.  */
   can support a given mode.  */
char xtensa_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
char xtensa_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
 
 
/* Current frame size calculated by compute_frame_size.  */
/* Current frame size calculated by compute_frame_size.  */
unsigned xtensa_current_frame_size;
unsigned xtensa_current_frame_size;
 
 
/* Largest block move to handle in-line.  */
/* Largest block move to handle in-line.  */
#define LARGEST_MOVE_RATIO 15
#define LARGEST_MOVE_RATIO 15
 
 
/* Define the structure for the machine field in struct function.  */
/* Define the structure for the machine field in struct function.  */
struct machine_function GTY(())
struct machine_function GTY(())
{
{
  int accesses_prev_frame;
  int accesses_prev_frame;
  bool need_a7_copy;
  bool need_a7_copy;
  bool vararg_a7;
  bool vararg_a7;
  rtx vararg_a7_copy;
  rtx vararg_a7_copy;
  rtx set_frame_ptr_insn;
  rtx set_frame_ptr_insn;
};
};
 
 
/* Vector, indexed by hard register number, which contains 1 for a
/* Vector, indexed by hard register number, which contains 1 for a
   register that is allowable in a candidate for leaf function
   register that is allowable in a candidate for leaf function
   treatment.  */
   treatment.  */
 
 
const char xtensa_leaf_regs[FIRST_PSEUDO_REGISTER] =
const char xtensa_leaf_regs[FIRST_PSEUDO_REGISTER] =
{
{
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1,
  1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1
  1
};
};
 
 
/* Map hard register number to register class */
/* Map hard register number to register class */
const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] =
const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] =
{
{
  RL_REGS,      SP_REG,         RL_REGS,        RL_REGS,
  RL_REGS,      SP_REG,         RL_REGS,        RL_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        GR_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        GR_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        RL_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        RL_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        RL_REGS,
  RL_REGS,      RL_REGS,        RL_REGS,        RL_REGS,
  AR_REGS,      AR_REGS,        BR_REGS,
  AR_REGS,      AR_REGS,        BR_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  FP_REGS,      FP_REGS,        FP_REGS,        FP_REGS,
  ACC_REG,
  ACC_REG,
};
};
 
 
/* Map register constraint character to register class.  */
/* Map register constraint character to register class.  */
enum reg_class xtensa_char_to_class[256] =
enum reg_class xtensa_char_to_class[256] =
{
{
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
  NO_REGS,      NO_REGS,        NO_REGS,        NO_REGS,
};
};
 
 
static enum internal_test map_test_to_internal_test (enum rtx_code);
static enum internal_test map_test_to_internal_test (enum rtx_code);
static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *);
static rtx gen_int_relational (enum rtx_code, rtx, rtx, int *);
static rtx gen_float_relational (enum rtx_code, rtx, rtx);
static rtx gen_float_relational (enum rtx_code, rtx, rtx);
static rtx gen_conditional_move (rtx);
static rtx gen_conditional_move (rtx);
static rtx fixup_subreg_mem (rtx);
static rtx fixup_subreg_mem (rtx);
static struct machine_function * xtensa_init_machine_status (void);
static struct machine_function * xtensa_init_machine_status (void);
static bool xtensa_return_in_msb (tree);
static bool xtensa_return_in_msb (tree);
static void printx (FILE *, signed int);
static void printx (FILE *, signed int);
static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
static rtx xtensa_builtin_saveregs (void);
static rtx xtensa_builtin_saveregs (void);
static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
                                                        int) ATTRIBUTE_UNUSED;
                                                        int) ATTRIBUTE_UNUSED;
static section *xtensa_select_rtx_section (enum machine_mode, rtx,
static section *xtensa_select_rtx_section (enum machine_mode, rtx,
                                           unsigned HOST_WIDE_INT);
                                           unsigned HOST_WIDE_INT);
static bool xtensa_rtx_costs (rtx, int, int, int *);
static bool xtensa_rtx_costs (rtx, int, int, int *);
static tree xtensa_build_builtin_va_list (void);
static tree xtensa_build_builtin_va_list (void);
static bool xtensa_return_in_memory (tree, tree);
static bool xtensa_return_in_memory (tree, tree);
static tree xtensa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static tree xtensa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 
 
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
  REG_ALLOC_ORDER;
  REG_ALLOC_ORDER;


 
 
/* This macro generates the assembly code for function exit,
/* This macro generates the assembly code for function exit,
   on machines that need it.  If FUNCTION_EPILOGUE is not defined
   on machines that need it.  If FUNCTION_EPILOGUE is not defined
   then individual return instructions are generated for each
   then individual return instructions are generated for each
   return statement.  Args are same as for FUNCTION_PROLOGUE.  */
   return statement.  Args are same as for FUNCTION_PROLOGUE.  */
 
 
#undef TARGET_ASM_FUNCTION_EPILOGUE
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE xtensa_function_epilogue
#define TARGET_ASM_FUNCTION_EPILOGUE xtensa_function_epilogue
 
 
/* These hooks specify assembly directives for creating certain kinds
/* These hooks specify assembly directives for creating certain kinds
   of integer object.  */
   of integer object.  */
 
 
#undef TARGET_ASM_ALIGNED_SI_OP
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
 
 
#undef TARGET_ASM_SELECT_RTX_SECTION
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION  xtensa_select_rtx_section
#define TARGET_ASM_SELECT_RTX_SECTION  xtensa_select_rtx_section
 
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
 
 
#undef TARGET_RTX_COSTS
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS xtensa_rtx_costs
#define TARGET_RTX_COSTS xtensa_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST hook_int_rtx_0
#define TARGET_ADDRESS_COST hook_int_rtx_0
 
 
#undef TARGET_BUILD_BUILTIN_VA_LIST
#undef TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST xtensa_build_builtin_va_list
#define TARGET_BUILD_BUILTIN_VA_LIST xtensa_build_builtin_va_list
 
 
#undef TARGET_PROMOTE_FUNCTION_ARGS
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
#undef TARGET_PROMOTE_FUNCTION_RETURN
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
 
 
#undef TARGET_RETURN_IN_MEMORY
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY xtensa_return_in_memory
#define TARGET_RETURN_IN_MEMORY xtensa_return_in_memory
#undef TARGET_SPLIT_COMPLEX_ARG
#undef TARGET_SPLIT_COMPLEX_ARG
#define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true
#define TARGET_SPLIT_COMPLEX_ARG hook_bool_tree_true
#undef TARGET_MUST_PASS_IN_STACK
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
 
 
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
#define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
#define TARGET_GIMPLIFY_VA_ARG_EXPR xtensa_gimplify_va_arg_expr
#define TARGET_GIMPLIFY_VA_ARG_EXPR xtensa_gimplify_va_arg_expr
 
 
#undef TARGET_RETURN_IN_MSB
#undef TARGET_RETURN_IN_MSB
#define TARGET_RETURN_IN_MSB xtensa_return_in_msb
#define TARGET_RETURN_IN_MSB xtensa_return_in_msb
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;


 
 
/*
/*
 * Functions to test Xtensa immediate operand validity.
 * Functions to test Xtensa immediate operand validity.
 */
 */
 
 
bool
bool
xtensa_simm8 (HOST_WIDE_INT v)
xtensa_simm8 (HOST_WIDE_INT v)
{
{
  return v >= -128 && v <= 127;
  return v >= -128 && v <= 127;
}
}
 
 
 
 
bool
bool
xtensa_simm8x256 (HOST_WIDE_INT v)
xtensa_simm8x256 (HOST_WIDE_INT v)
{
{
  return (v & 255) == 0 && (v >= -32768 && v <= 32512);
  return (v & 255) == 0 && (v >= -32768 && v <= 32512);
}
}
 
 
 
 
bool
bool
xtensa_simm12b (HOST_WIDE_INT v)
xtensa_simm12b (HOST_WIDE_INT v)
{
{
  return v >= -2048 && v <= 2047;
  return v >= -2048 && v <= 2047;
}
}
 
 
 
 
static bool
static bool
xtensa_uimm8 (HOST_WIDE_INT v)
xtensa_uimm8 (HOST_WIDE_INT v)
{
{
  return v >= 0 && v <= 255;
  return v >= 0 && v <= 255;
}
}
 
 
 
 
static bool
static bool
xtensa_uimm8x2 (HOST_WIDE_INT v)
xtensa_uimm8x2 (HOST_WIDE_INT v)
{
{
  return (v & 1) == 0 && (v >= 0 && v <= 510);
  return (v & 1) == 0 && (v >= 0 && v <= 510);
}
}
 
 
 
 
static bool
static bool
xtensa_uimm8x4 (HOST_WIDE_INT v)
xtensa_uimm8x4 (HOST_WIDE_INT v)
{
{
  return (v & 3) == 0 && (v >= 0 && v <= 1020);
  return (v & 3) == 0 && (v >= 0 && v <= 1020);
}
}
 
 
 
 
static bool
static bool
xtensa_b4const (HOST_WIDE_INT v)
xtensa_b4const (HOST_WIDE_INT v)
{
{
  switch (v)
  switch (v)
    {
    {
    case -1:
    case -1:
    case 1:
    case 1:
    case 2:
    case 2:
    case 3:
    case 3:
    case 4:
    case 4:
    case 5:
    case 5:
    case 6:
    case 6:
    case 7:
    case 7:
    case 8:
    case 8:
    case 10:
    case 10:
    case 12:
    case 12:
    case 16:
    case 16:
    case 32:
    case 32:
    case 64:
    case 64:
    case 128:
    case 128:
    case 256:
    case 256:
      return true;
      return true;
    }
    }
  return false;
  return false;
}
}
 
 
 
 
bool
bool
xtensa_b4const_or_zero (HOST_WIDE_INT v)
xtensa_b4const_or_zero (HOST_WIDE_INT v)
{
{
  if (v == 0)
  if (v == 0)
    return true;
    return true;
  return xtensa_b4const (v);
  return xtensa_b4const (v);
}
}
 
 
 
 
bool
bool
xtensa_b4constu (HOST_WIDE_INT v)
xtensa_b4constu (HOST_WIDE_INT v)
{
{
  switch (v)
  switch (v)
    {
    {
    case 32768:
    case 32768:
    case 65536:
    case 65536:
    case 2:
    case 2:
    case 3:
    case 3:
    case 4:
    case 4:
    case 5:
    case 5:
    case 6:
    case 6:
    case 7:
    case 7:
    case 8:
    case 8:
    case 10:
    case 10:
    case 12:
    case 12:
    case 16:
    case 16:
    case 32:
    case 32:
    case 64:
    case 64:
    case 128:
    case 128:
    case 256:
    case 256:
      return true;
      return true;
    }
    }
  return false;
  return false;
}
}
 
 
 
 
bool
bool
xtensa_mask_immediate (HOST_WIDE_INT v)
xtensa_mask_immediate (HOST_WIDE_INT v)
{
{
#define MAX_MASK_SIZE 16
#define MAX_MASK_SIZE 16
  int mask_size;
  int mask_size;
 
 
  for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
  for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
    {
    {
      if ((v & 1) == 0)
      if ((v & 1) == 0)
        return false;
        return false;
      v = v >> 1;
      v = v >> 1;
      if (v == 0)
      if (v == 0)
        return true;
        return true;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
 
 
bool
bool
xtensa_const_ok_for_letter_p (HOST_WIDE_INT v, int c)
xtensa_const_ok_for_letter_p (HOST_WIDE_INT v, int c)
{
{
  switch (c)
  switch (c)
    {
    {
    case 'I': return xtensa_simm12b (v);
    case 'I': return xtensa_simm12b (v);
    case 'J': return xtensa_simm8 (v);
    case 'J': return xtensa_simm8 (v);
    case 'K': return (v == 0 || xtensa_b4const (v));
    case 'K': return (v == 0 || xtensa_b4const (v));
    case 'L': return xtensa_b4constu (v);
    case 'L': return xtensa_b4constu (v);
    case 'M': return (v >= -32 && v <= 95);
    case 'M': return (v >= -32 && v <= 95);
    case 'N': return xtensa_simm8x256 (v);
    case 'N': return xtensa_simm8x256 (v);
    case 'O': return (v == -1 || (v >= 1 && v <= 15));
    case 'O': return (v == -1 || (v >= 1 && v <= 15));
    case 'P': return xtensa_mask_immediate (v);
    case 'P': return xtensa_mask_immediate (v);
    default: break;
    default: break;
    }
    }
  return false;
  return false;
}
}
 
 
 
 
/* This is just like the standard true_regnum() function except that it
/* This is just like the standard true_regnum() function except that it
   works even when reg_renumber is not initialized.  */
   works even when reg_renumber is not initialized.  */
 
 
int
int
xt_true_regnum (rtx x)
xt_true_regnum (rtx x)
{
{
  if (GET_CODE (x) == REG)
  if (GET_CODE (x) == REG)
    {
    {
      if (reg_renumber
      if (reg_renumber
          && REGNO (x) >= FIRST_PSEUDO_REGISTER
          && REGNO (x) >= FIRST_PSEUDO_REGISTER
          && reg_renumber[REGNO (x)] >= 0)
          && reg_renumber[REGNO (x)] >= 0)
        return reg_renumber[REGNO (x)];
        return reg_renumber[REGNO (x)];
      return REGNO (x);
      return REGNO (x);
    }
    }
  if (GET_CODE (x) == SUBREG)
  if (GET_CODE (x) == SUBREG)
    {
    {
      int base = xt_true_regnum (SUBREG_REG (x));
      int base = xt_true_regnum (SUBREG_REG (x));
      if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
      if (base >= 0 && base < FIRST_PSEUDO_REGISTER)
        return base + subreg_regno_offset (REGNO (SUBREG_REG (x)),
        return base + subreg_regno_offset (REGNO (SUBREG_REG (x)),
                                           GET_MODE (SUBREG_REG (x)),
                                           GET_MODE (SUBREG_REG (x)),
                                           SUBREG_BYTE (x), GET_MODE (x));
                                           SUBREG_BYTE (x), GET_MODE (x));
    }
    }
  return -1;
  return -1;
}
}
 
 
 
 
int
int
xtensa_valid_move (enum machine_mode mode, rtx *operands)
xtensa_valid_move (enum machine_mode mode, rtx *operands)
{
{
  /* Either the destination or source must be a register, and the
  /* Either the destination or source must be a register, and the
     MAC16 accumulator doesn't count.  */
     MAC16 accumulator doesn't count.  */
 
 
  if (register_operand (operands[0], mode))
  if (register_operand (operands[0], mode))
    {
    {
      int dst_regnum = xt_true_regnum (operands[0]);
      int dst_regnum = xt_true_regnum (operands[0]);
 
 
      /* The stack pointer can only be assigned with a MOVSP opcode.  */
      /* The stack pointer can only be assigned with a MOVSP opcode.  */
      if (dst_regnum == STACK_POINTER_REGNUM)
      if (dst_regnum == STACK_POINTER_REGNUM)
        return (mode == SImode
        return (mode == SImode
                && register_operand (operands[1], mode)
                && register_operand (operands[1], mode)
                && !ACC_REG_P (xt_true_regnum (operands[1])));
                && !ACC_REG_P (xt_true_regnum (operands[1])));
 
 
      if (!ACC_REG_P (dst_regnum))
      if (!ACC_REG_P (dst_regnum))
        return true;
        return true;
    }
    }
  if (register_operand (operands[1], mode))
  if (register_operand (operands[1], mode))
    {
    {
      int src_regnum = xt_true_regnum (operands[1]);
      int src_regnum = xt_true_regnum (operands[1]);
      if (!ACC_REG_P (src_regnum))
      if (!ACC_REG_P (src_regnum))
        return true;
        return true;
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
int
int
smalloffset_mem_p (rtx op)
smalloffset_mem_p (rtx op)
{
{
  if (GET_CODE (op) == MEM)
  if (GET_CODE (op) == MEM)
    {
    {
      rtx addr = XEXP (op, 0);
      rtx addr = XEXP (op, 0);
      if (GET_CODE (addr) == REG)
      if (GET_CODE (addr) == REG)
        return REG_OK_FOR_BASE_P (addr);
        return REG_OK_FOR_BASE_P (addr);
      if (GET_CODE (addr) == PLUS)
      if (GET_CODE (addr) == PLUS)
        {
        {
          rtx offset = XEXP (addr, 0);
          rtx offset = XEXP (addr, 0);
          HOST_WIDE_INT val;
          HOST_WIDE_INT val;
          if (GET_CODE (offset) != CONST_INT)
          if (GET_CODE (offset) != CONST_INT)
            offset = XEXP (addr, 1);
            offset = XEXP (addr, 1);
          if (GET_CODE (offset) != CONST_INT)
          if (GET_CODE (offset) != CONST_INT)
            return FALSE;
            return FALSE;
 
 
          val = INTVAL (offset);
          val = INTVAL (offset);
          return (val & 3) == 0 && (val >= 0 && val <= 60);
          return (val & 3) == 0 && (val >= 0 && val <= 60);
        }
        }
    }
    }
  return FALSE;
  return FALSE;
}
}
 
 
 
 
int
int
constantpool_address_p (rtx addr)
constantpool_address_p (rtx addr)
{
{
  rtx sym = addr;
  rtx sym = addr;
 
 
  if (GET_CODE (addr) == CONST)
  if (GET_CODE (addr) == CONST)
    {
    {
      rtx offset;
      rtx offset;
 
 
      /* Only handle (PLUS (SYM, OFFSET)) form.  */
      /* Only handle (PLUS (SYM, OFFSET)) form.  */
      addr = XEXP (addr, 0);
      addr = XEXP (addr, 0);
      if (GET_CODE (addr) != PLUS)
      if (GET_CODE (addr) != PLUS)
        return FALSE;
        return FALSE;
 
 
      /* Make sure the address is word aligned.  */
      /* Make sure the address is word aligned.  */
      offset = XEXP (addr, 1);
      offset = XEXP (addr, 1);
      if ((GET_CODE (offset) != CONST_INT)
      if ((GET_CODE (offset) != CONST_INT)
          || ((INTVAL (offset) & 3) != 0))
          || ((INTVAL (offset) & 3) != 0))
        return FALSE;
        return FALSE;
 
 
      sym = XEXP (addr, 0);
      sym = XEXP (addr, 0);
    }
    }
 
 
  if ((GET_CODE (sym) == SYMBOL_REF)
  if ((GET_CODE (sym) == SYMBOL_REF)
      && CONSTANT_POOL_ADDRESS_P (sym))
      && CONSTANT_POOL_ADDRESS_P (sym))
    return TRUE;
    return TRUE;
  return FALSE;
  return FALSE;
}
}
 
 
 
 
int
int
constantpool_mem_p (rtx op)
constantpool_mem_p (rtx op)
{
{
  if (GET_CODE (op) == SUBREG)
  if (GET_CODE (op) == SUBREG)
    op = SUBREG_REG (op);
    op = SUBREG_REG (op);
  if (GET_CODE (op) == MEM)
  if (GET_CODE (op) == MEM)
    return constantpool_address_p (XEXP (op, 0));
    return constantpool_address_p (XEXP (op, 0));
  return FALSE;
  return FALSE;
}
}
 
 
 
 
void
void
xtensa_extend_reg (rtx dst, rtx src)
xtensa_extend_reg (rtx dst, rtx src)
{
{
  rtx temp = gen_reg_rtx (SImode);
  rtx temp = gen_reg_rtx (SImode);
  rtx shift = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (GET_MODE (src)));
  rtx shift = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (GET_MODE (src)));
 
 
  /* Generate paradoxical subregs as needed so that the modes match.  */
  /* Generate paradoxical subregs as needed so that the modes match.  */
  src = simplify_gen_subreg (SImode, src, GET_MODE (src), 0);
  src = simplify_gen_subreg (SImode, src, GET_MODE (src), 0);
  dst = simplify_gen_subreg (SImode, dst, GET_MODE (dst), 0);
  dst = simplify_gen_subreg (SImode, dst, GET_MODE (dst), 0);
 
 
  emit_insn (gen_ashlsi3 (temp, src, shift));
  emit_insn (gen_ashlsi3 (temp, src, shift));
  emit_insn (gen_ashrsi3 (dst, temp, shift));
  emit_insn (gen_ashrsi3 (dst, temp, shift));
}
}
 
 
 
 
bool
bool
xtensa_mem_offset (unsigned v, enum machine_mode mode)
xtensa_mem_offset (unsigned v, enum machine_mode mode)
{
{
  switch (mode)
  switch (mode)
    {
    {
    case BLKmode:
    case BLKmode:
      /* Handle the worst case for block moves.  See xtensa_expand_block_move
      /* Handle the worst case for block moves.  See xtensa_expand_block_move
         where we emit an optimized block move operation if the block can be
         where we emit an optimized block move operation if the block can be
         moved in < "move_ratio" pieces.  The worst case is when the block is
         moved in < "move_ratio" pieces.  The worst case is when the block is
         aligned but has a size of (3 mod 4) (does this happen?) so that the
         aligned but has a size of (3 mod 4) (does this happen?) so that the
         last piece requires a byte load/store.  */
         last piece requires a byte load/store.  */
      return (xtensa_uimm8 (v)
      return (xtensa_uimm8 (v)
              && xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
              && xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
 
 
    case QImode:
    case QImode:
      return xtensa_uimm8 (v);
      return xtensa_uimm8 (v);
 
 
    case HImode:
    case HImode:
      return xtensa_uimm8x2 (v);
      return xtensa_uimm8x2 (v);
 
 
    case DFmode:
    case DFmode:
      return (xtensa_uimm8x4 (v) && xtensa_uimm8x4 (v + 4));
      return (xtensa_uimm8x4 (v) && xtensa_uimm8x4 (v + 4));
 
 
    default:
    default:
      break;
      break;
    }
    }
 
 
  return xtensa_uimm8x4 (v);
  return xtensa_uimm8x4 (v);
}
}
 
 
 
 
bool
bool
xtensa_extra_constraint (rtx op, int c)
xtensa_extra_constraint (rtx op, int c)
{
{
  /* Allow pseudo registers during reload.  */
  /* Allow pseudo registers during reload.  */
  if (GET_CODE (op) != MEM)
  if (GET_CODE (op) != MEM)
    return (c >= 'R' && c <= 'U'
    return (c >= 'R' && c <= 'U'
            && reload_in_progress && GET_CODE (op) == REG
            && reload_in_progress && GET_CODE (op) == REG
            && REGNO (op) >= FIRST_PSEUDO_REGISTER);
            && REGNO (op) >= FIRST_PSEUDO_REGISTER);
 
 
  switch (c)
  switch (c)
    {
    {
    case 'R': return smalloffset_mem_p (op);
    case 'R': return smalloffset_mem_p (op);
    case 'T': return !TARGET_CONST16 && constantpool_mem_p (op);
    case 'T': return !TARGET_CONST16 && constantpool_mem_p (op);
    case 'U': return !constantpool_mem_p (op);
    case 'U': return !constantpool_mem_p (op);
    default: break;
    default: break;
    }
    }
  return false;
  return false;
}
}
 
 
 
 
/* Make normal rtx_code into something we can index from an array.  */
/* Make normal rtx_code into something we can index from an array.  */
 
 
static enum internal_test
static enum internal_test
map_test_to_internal_test (enum rtx_code test_code)
map_test_to_internal_test (enum rtx_code test_code)
{
{
  enum internal_test test = ITEST_MAX;
  enum internal_test test = ITEST_MAX;
 
 
  switch (test_code)
  switch (test_code)
    {
    {
    default:                    break;
    default:                    break;
    case EQ:  test = ITEST_EQ;  break;
    case EQ:  test = ITEST_EQ;  break;
    case NE:  test = ITEST_NE;  break;
    case NE:  test = ITEST_NE;  break;
    case GT:  test = ITEST_GT;  break;
    case GT:  test = ITEST_GT;  break;
    case GE:  test = ITEST_GE;  break;
    case GE:  test = ITEST_GE;  break;
    case LT:  test = ITEST_LT;  break;
    case LT:  test = ITEST_LT;  break;
    case LE:  test = ITEST_LE;  break;
    case LE:  test = ITEST_LE;  break;
    case GTU: test = ITEST_GTU; break;
    case GTU: test = ITEST_GTU; break;
    case GEU: test = ITEST_GEU; break;
    case GEU: test = ITEST_GEU; break;
    case LTU: test = ITEST_LTU; break;
    case LTU: test = ITEST_LTU; break;
    case LEU: test = ITEST_LEU; break;
    case LEU: test = ITEST_LEU; break;
    }
    }
 
 
  return test;
  return test;
}
}
 
 
 
 
/* Generate the code to compare two integer values.  The return value is
/* Generate the code to compare two integer values.  The return value is
   the comparison expression.  */
   the comparison expression.  */
 
 
static rtx
static rtx
gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
                    rtx cmp0, /* first operand to compare */
                    rtx cmp0, /* first operand to compare */
                    rtx cmp1, /* second operand to compare */
                    rtx cmp1, /* second operand to compare */
                    int *p_invert /* whether branch needs to reverse test */)
                    int *p_invert /* whether branch needs to reverse test */)
{
{
  struct cmp_info
  struct cmp_info
  {
  {
    enum rtx_code test_code;    /* test code to use in insn */
    enum rtx_code test_code;    /* test code to use in insn */
    bool (*const_range_p) (HOST_WIDE_INT); /* range check function */
    bool (*const_range_p) (HOST_WIDE_INT); /* range check function */
    int const_add;              /* constant to add (convert LE -> LT) */
    int const_add;              /* constant to add (convert LE -> LT) */
    int reverse_regs;           /* reverse registers in test */
    int reverse_regs;           /* reverse registers in test */
    int invert_const;           /* != 0 if invert value if cmp1 is constant */
    int invert_const;           /* != 0 if invert value if cmp1 is constant */
    int invert_reg;             /* != 0 if invert value if cmp1 is register */
    int invert_reg;             /* != 0 if invert value if cmp1 is register */
    int unsignedp;              /* != 0 for unsigned comparisons.  */
    int unsignedp;              /* != 0 for unsigned comparisons.  */
  };
  };
 
 
  static struct cmp_info info[ (int)ITEST_MAX ] = {
  static struct cmp_info info[ (int)ITEST_MAX ] = {
 
 
    { EQ,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* EQ  */
    { EQ,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* EQ  */
    { NE,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* NE  */
    { NE,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* NE  */
 
 
    { LT,       xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },  /* GT  */
    { LT,       xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },  /* GT  */
    { GE,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* GE  */
    { GE,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* GE  */
    { LT,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* LT  */
    { LT,       xtensa_b4const_or_zero, 0, 0, 0, 0, 0 },     /* LT  */
    { GE,       xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },  /* LE  */
    { GE,       xtensa_b4const_or_zero, 1, 1, 1, 0, 0 },  /* LE  */
 
 
    { LTU,      xtensa_b4constu,        1, 1, 1, 0, 1 }, /* GTU */
    { LTU,      xtensa_b4constu,        1, 1, 1, 0, 1 }, /* GTU */
    { GEU,      xtensa_b4constu,        0, 0, 0, 0, 1 },    /* GEU */
    { GEU,      xtensa_b4constu,        0, 0, 0, 0, 1 },    /* GEU */
    { LTU,      xtensa_b4constu,        0, 0, 0, 0, 1 },    /* LTU */
    { LTU,      xtensa_b4constu,        0, 0, 0, 0, 1 },    /* LTU */
    { GEU,      xtensa_b4constu,        1, 1, 1, 0, 1 }, /* LEU */
    { GEU,      xtensa_b4constu,        1, 1, 1, 0, 1 }, /* LEU */
  };
  };
 
 
  enum internal_test test;
  enum internal_test test;
  enum machine_mode mode;
  enum machine_mode mode;
  struct cmp_info *p_info;
  struct cmp_info *p_info;
 
 
  test = map_test_to_internal_test (test_code);
  test = map_test_to_internal_test (test_code);
  gcc_assert (test != ITEST_MAX);
  gcc_assert (test != ITEST_MAX);
 
 
  p_info = &info[ (int)test ];
  p_info = &info[ (int)test ];
 
 
  mode = GET_MODE (cmp0);
  mode = GET_MODE (cmp0);
  if (mode == VOIDmode)
  if (mode == VOIDmode)
    mode = GET_MODE (cmp1);
    mode = GET_MODE (cmp1);
 
 
  /* Make sure we can handle any constants given to us.  */
  /* Make sure we can handle any constants given to us.  */
  if (GET_CODE (cmp1) == CONST_INT)
  if (GET_CODE (cmp1) == CONST_INT)
    {
    {
      HOST_WIDE_INT value = INTVAL (cmp1);
      HOST_WIDE_INT value = INTVAL (cmp1);
      unsigned HOST_WIDE_INT uvalue = (unsigned HOST_WIDE_INT)value;
      unsigned HOST_WIDE_INT uvalue = (unsigned HOST_WIDE_INT)value;
 
 
      /* if the immediate overflows or does not fit in the immediate field,
      /* if the immediate overflows or does not fit in the immediate field,
         spill it to a register */
         spill it to a register */
 
 
      if ((p_info->unsignedp ?
      if ((p_info->unsignedp ?
           (uvalue + p_info->const_add > uvalue) :
           (uvalue + p_info->const_add > uvalue) :
           (value + p_info->const_add > value)) != (p_info->const_add > 0))
           (value + p_info->const_add > value)) != (p_info->const_add > 0))
        {
        {
          cmp1 = force_reg (mode, cmp1);
          cmp1 = force_reg (mode, cmp1);
        }
        }
      else if (!(p_info->const_range_p) (value + p_info->const_add))
      else if (!(p_info->const_range_p) (value + p_info->const_add))
        {
        {
          cmp1 = force_reg (mode, cmp1);
          cmp1 = force_reg (mode, cmp1);
        }
        }
    }
    }
  else if ((GET_CODE (cmp1) != REG) && (GET_CODE (cmp1) != SUBREG))
  else if ((GET_CODE (cmp1) != REG) && (GET_CODE (cmp1) != SUBREG))
    {
    {
      cmp1 = force_reg (mode, cmp1);
      cmp1 = force_reg (mode, cmp1);
    }
    }
 
 
  /* See if we need to invert the result.  */
  /* See if we need to invert the result.  */
  *p_invert = ((GET_CODE (cmp1) == CONST_INT)
  *p_invert = ((GET_CODE (cmp1) == CONST_INT)
               ? p_info->invert_const
               ? p_info->invert_const
               : p_info->invert_reg);
               : p_info->invert_reg);
 
 
  /* Comparison to constants, may involve adding 1 to change a LT into LE.
  /* Comparison to constants, may involve adding 1 to change a LT into LE.
     Comparison between two registers, may involve switching operands.  */
     Comparison between two registers, may involve switching operands.  */
  if (GET_CODE (cmp1) == CONST_INT)
  if (GET_CODE (cmp1) == CONST_INT)
    {
    {
      if (p_info->const_add != 0)
      if (p_info->const_add != 0)
        cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add);
        cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add);
 
 
    }
    }
  else if (p_info->reverse_regs)
  else if (p_info->reverse_regs)
    {
    {
      rtx temp = cmp0;
      rtx temp = cmp0;
      cmp0 = cmp1;
      cmp0 = cmp1;
      cmp1 = temp;
      cmp1 = temp;
    }
    }
 
 
  return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);
  return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);
}
}
 
 
 
 
/* Generate the code to compare two float values.  The return value is
/* Generate the code to compare two float values.  The return value is
   the comparison expression.  */
   the comparison expression.  */
 
 
static rtx
static rtx
gen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
gen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
                      rtx cmp0, /* first operand to compare */
                      rtx cmp0, /* first operand to compare */
                      rtx cmp1 /* second operand to compare */)
                      rtx cmp1 /* second operand to compare */)
{
{
  rtx (*gen_fn) (rtx, rtx, rtx);
  rtx (*gen_fn) (rtx, rtx, rtx);
  rtx brtmp;
  rtx brtmp;
  int reverse_regs, invert;
  int reverse_regs, invert;
 
 
  switch (test_code)
  switch (test_code)
    {
    {
    case EQ: reverse_regs = 0; invert = 0; gen_fn = gen_seq_sf; break;
    case EQ: reverse_regs = 0; invert = 0; gen_fn = gen_seq_sf; break;
    case NE: reverse_regs = 0; invert = 1; gen_fn = gen_seq_sf; break;
    case NE: reverse_regs = 0; invert = 1; gen_fn = gen_seq_sf; break;
    case LE: reverse_regs = 0; invert = 0; gen_fn = gen_sle_sf; break;
    case LE: reverse_regs = 0; invert = 0; gen_fn = gen_sle_sf; break;
    case GT: reverse_regs = 1; invert = 0; gen_fn = gen_slt_sf; break;
    case GT: reverse_regs = 1; invert = 0; gen_fn = gen_slt_sf; break;
    case LT: reverse_regs = 0; invert = 0; gen_fn = gen_slt_sf; break;
    case LT: reverse_regs = 0; invert = 0; gen_fn = gen_slt_sf; break;
    case GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break;
    case GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break;
    default:
    default:
      fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
      fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
      reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
      reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
    }
    }
 
 
  if (reverse_regs)
  if (reverse_regs)
    {
    {
      rtx temp = cmp0;
      rtx temp = cmp0;
      cmp0 = cmp1;
      cmp0 = cmp1;
      cmp1 = temp;
      cmp1 = temp;
    }
    }
 
 
  brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM);
  brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM);
  emit_insn (gen_fn (brtmp, cmp0, cmp1));
  emit_insn (gen_fn (brtmp, cmp0, cmp1));
 
 
  return gen_rtx_fmt_ee (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
  return gen_rtx_fmt_ee (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
}
}
 
 
 
 
void
void
xtensa_expand_conditional_branch (rtx *operands, enum rtx_code test_code)
xtensa_expand_conditional_branch (rtx *operands, enum rtx_code test_code)
{
{
  enum cmp_type type = branch_type;
  enum cmp_type type = branch_type;
  rtx cmp0 = branch_cmp[0];
  rtx cmp0 = branch_cmp[0];
  rtx cmp1 = branch_cmp[1];
  rtx cmp1 = branch_cmp[1];
  rtx cmp;
  rtx cmp;
  int invert;
  int invert;
  rtx label1, label2;
  rtx label1, label2;
 
 
  switch (type)
  switch (type)
    {
    {
    case CMP_DF:
    case CMP_DF:
    default:
    default:
      fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
      fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
 
 
    case CMP_SI:
    case CMP_SI:
      invert = FALSE;
      invert = FALSE;
      cmp = gen_int_relational (test_code, cmp0, cmp1, &invert);
      cmp = gen_int_relational (test_code, cmp0, cmp1, &invert);
      break;
      break;
 
 
    case CMP_SF:
    case CMP_SF:
      if (!TARGET_HARD_FLOAT)
      if (!TARGET_HARD_FLOAT)
        fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
        fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
      invert = FALSE;
      invert = FALSE;
      cmp = gen_float_relational (test_code, cmp0, cmp1);
      cmp = gen_float_relational (test_code, cmp0, cmp1);
      break;
      break;
    }
    }
 
 
  /* Generate the branch.  */
  /* Generate the branch.  */
 
 
  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
  label2 = pc_rtx;
  label2 = pc_rtx;
 
 
  if (invert)
  if (invert)
    {
    {
      label2 = label1;
      label2 = label1;
      label1 = pc_rtx;
      label1 = pc_rtx;
    }
    }
 
 
  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                               gen_rtx_IF_THEN_ELSE (VOIDmode, cmp,
                               gen_rtx_IF_THEN_ELSE (VOIDmode, cmp,
                                                     label1,
                                                     label1,
                                                     label2)));
                                                     label2)));
}
}
 
 
 
 
static rtx
static rtx
gen_conditional_move (rtx cmp)
gen_conditional_move (rtx cmp)
{
{
  enum rtx_code code = GET_CODE (cmp);
  enum rtx_code code = GET_CODE (cmp);
  rtx op0 = branch_cmp[0];
  rtx op0 = branch_cmp[0];
  rtx op1 = branch_cmp[1];
  rtx op1 = branch_cmp[1];
 
 
  if (branch_type == CMP_SI)
  if (branch_type == CMP_SI)
    {
    {
      /* Jump optimization calls get_condition() which canonicalizes
      /* Jump optimization calls get_condition() which canonicalizes
         comparisons like (GE x <const>) to (GT x <const-1>).
         comparisons like (GE x <const>) to (GT x <const-1>).
         Transform those comparisons back to GE, since that is the
         Transform those comparisons back to GE, since that is the
         comparison supported in Xtensa.  We shouldn't have to
         comparison supported in Xtensa.  We shouldn't have to
         transform <LE x const> comparisons, because neither
         transform <LE x const> comparisons, because neither
         xtensa_expand_conditional_branch() nor get_condition() will
         xtensa_expand_conditional_branch() nor get_condition() will
         produce them.  */
         produce them.  */
 
 
      if ((code == GT) && (op1 == constm1_rtx))
      if ((code == GT) && (op1 == constm1_rtx))
        {
        {
          code = GE;
          code = GE;
          op1 = const0_rtx;
          op1 = const0_rtx;
        }
        }
      cmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
      cmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
 
 
      if (boolean_operator (cmp, VOIDmode))
      if (boolean_operator (cmp, VOIDmode))
        {
        {
          /* Swap the operands to make const0 second.  */
          /* Swap the operands to make const0 second.  */
          if (op0 == const0_rtx)
          if (op0 == const0_rtx)
            {
            {
              op0 = op1;
              op0 = op1;
              op1 = const0_rtx;
              op1 = const0_rtx;
            }
            }
 
 
          /* If not comparing against zero, emit a comparison (subtract).  */
          /* If not comparing against zero, emit a comparison (subtract).  */
          if (op1 != const0_rtx)
          if (op1 != const0_rtx)
            {
            {
              op0 = expand_binop (SImode, sub_optab, op0, op1,
              op0 = expand_binop (SImode, sub_optab, op0, op1,
                                  0, 0, OPTAB_LIB_WIDEN);
                                  0, 0, OPTAB_LIB_WIDEN);
              op1 = const0_rtx;
              op1 = const0_rtx;
            }
            }
        }
        }
      else if (branch_operator (cmp, VOIDmode))
      else if (branch_operator (cmp, VOIDmode))
        {
        {
          /* Swap the operands to make const0 second.  */
          /* Swap the operands to make const0 second.  */
          if (op0 == const0_rtx)
          if (op0 == const0_rtx)
            {
            {
              op0 = op1;
              op0 = op1;
              op1 = const0_rtx;
              op1 = const0_rtx;
 
 
              switch (code)
              switch (code)
                {
                {
                case LT: code = GE; break;
                case LT: code = GE; break;
                case GE: code = LT; break;
                case GE: code = LT; break;
                default: gcc_unreachable ();
                default: gcc_unreachable ();
                }
                }
            }
            }
 
 
          if (op1 != const0_rtx)
          if (op1 != const0_rtx)
            return 0;
            return 0;
        }
        }
      else
      else
        return 0;
        return 0;
 
 
      return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
      return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
    }
    }
 
 
  if (TARGET_HARD_FLOAT && (branch_type == CMP_SF))
  if (TARGET_HARD_FLOAT && (branch_type == CMP_SF))
    return gen_float_relational (code, op0, op1);
    return gen_float_relational (code, op0, op1);
 
 
  return 0;
  return 0;
}
}
 
 
 
 
int
int
xtensa_expand_conditional_move (rtx *operands, int isflt)
xtensa_expand_conditional_move (rtx *operands, int isflt)
{
{
  rtx cmp;
  rtx cmp;
  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
 
 
  if (!(cmp = gen_conditional_move (operands[1])))
  if (!(cmp = gen_conditional_move (operands[1])))
    return 0;
    return 0;
 
 
  if (isflt)
  if (isflt)
    gen_fn = (branch_type == CMP_SI
    gen_fn = (branch_type == CMP_SI
              ? gen_movsfcc_internal0
              ? gen_movsfcc_internal0
              : gen_movsfcc_internal1);
              : gen_movsfcc_internal1);
  else
  else
    gen_fn = (branch_type == CMP_SI
    gen_fn = (branch_type == CMP_SI
              ? gen_movsicc_internal0
              ? gen_movsicc_internal0
              : gen_movsicc_internal1);
              : gen_movsicc_internal1);
 
 
  emit_insn (gen_fn (operands[0], XEXP (cmp, 0),
  emit_insn (gen_fn (operands[0], XEXP (cmp, 0),
                     operands[2], operands[3], cmp));
                     operands[2], operands[3], cmp));
  return 1;
  return 1;
}
}
 
 
 
 
int
int
xtensa_expand_scc (rtx *operands)
xtensa_expand_scc (rtx *operands)
{
{
  rtx dest = operands[0];
  rtx dest = operands[0];
  rtx cmp = operands[1];
  rtx cmp = operands[1];
  rtx one_tmp, zero_tmp;
  rtx one_tmp, zero_tmp;
  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
  rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
 
 
  if (!(cmp = gen_conditional_move (cmp)))
  if (!(cmp = gen_conditional_move (cmp)))
    return 0;
    return 0;
 
 
  one_tmp = gen_reg_rtx (SImode);
  one_tmp = gen_reg_rtx (SImode);
  zero_tmp = gen_reg_rtx (SImode);
  zero_tmp = gen_reg_rtx (SImode);
  emit_insn (gen_movsi (one_tmp, const_true_rtx));
  emit_insn (gen_movsi (one_tmp, const_true_rtx));
  emit_insn (gen_movsi (zero_tmp, const0_rtx));
  emit_insn (gen_movsi (zero_tmp, const0_rtx));
 
 
  gen_fn = (branch_type == CMP_SI
  gen_fn = (branch_type == CMP_SI
            ? gen_movsicc_internal0
            ? gen_movsicc_internal0
            : gen_movsicc_internal1);
            : gen_movsicc_internal1);
  emit_insn (gen_fn (dest, XEXP (cmp, 0), one_tmp, zero_tmp, cmp));
  emit_insn (gen_fn (dest, XEXP (cmp, 0), one_tmp, zero_tmp, cmp));
  return 1;
  return 1;
}
}
 
 
 
 
/* Split OP[1] into OP[2,3] and likewise for OP[0] into OP[0,1].  MODE is
/* Split OP[1] into OP[2,3] and likewise for OP[0] into OP[0,1].  MODE is
   for the output, i.e., the input operands are twice as big as MODE.  */
   for the output, i.e., the input operands are twice as big as MODE.  */
 
 
void
void
xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
{
{
  switch (GET_CODE (operands[1]))
  switch (GET_CODE (operands[1]))
    {
    {
    case REG:
    case REG:
      operands[3] = gen_rtx_REG (mode, REGNO (operands[1]) + 1);
      operands[3] = gen_rtx_REG (mode, REGNO (operands[1]) + 1);
      operands[2] = gen_rtx_REG (mode, REGNO (operands[1]));
      operands[2] = gen_rtx_REG (mode, REGNO (operands[1]));
      break;
      break;
 
 
    case MEM:
    case MEM:
      operands[3] = adjust_address (operands[1], mode, GET_MODE_SIZE (mode));
      operands[3] = adjust_address (operands[1], mode, GET_MODE_SIZE (mode));
      operands[2] = adjust_address (operands[1], mode, 0);
      operands[2] = adjust_address (operands[1], mode, 0);
      break;
      break;
 
 
    case CONST_INT:
    case CONST_INT:
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      split_double (operands[1], &operands[2], &operands[3]);
      split_double (operands[1], &operands[2], &operands[3]);
      break;
      break;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  switch (GET_CODE (operands[0]))
  switch (GET_CODE (operands[0]))
    {
    {
    case REG:
    case REG:
      operands[1] = gen_rtx_REG (mode, REGNO (operands[0]) + 1);
      operands[1] = gen_rtx_REG (mode, REGNO (operands[0]) + 1);
      operands[0] = gen_rtx_REG (mode, REGNO (operands[0]));
      operands[0] = gen_rtx_REG (mode, REGNO (operands[0]));
      break;
      break;
 
 
    case MEM:
    case MEM:
      operands[1] = adjust_address (operands[0], mode, GET_MODE_SIZE (mode));
      operands[1] = adjust_address (operands[0], mode, GET_MODE_SIZE (mode));
      operands[0] = adjust_address (operands[0], mode, 0);
      operands[0] = adjust_address (operands[0], mode, 0);
      break;
      break;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
 
 
/* Emit insns to move operands[1] into operands[0].
/* Emit insns to move operands[1] into operands[0].
   Return 1 if we have written out everything that needs to be done to
   Return 1 if we have written out everything that needs to be done to
   do the move.  Otherwise, return 0 and the caller will emit the move
   do the move.  Otherwise, return 0 and the caller will emit the move
   normally.  */
   normally.  */
 
 
int
int
xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
{
{
  if (CONSTANT_P (operands[1])
  if (CONSTANT_P (operands[1])
      && (GET_CODE (operands[1]) != CONST_INT
      && (GET_CODE (operands[1]) != CONST_INT
          || !xtensa_simm12b (INTVAL (operands[1]))))
          || !xtensa_simm12b (INTVAL (operands[1]))))
    {
    {
      if (!TARGET_CONST16)
      if (!TARGET_CONST16)
        operands[1] = force_const_mem (SImode, operands[1]);
        operands[1] = force_const_mem (SImode, operands[1]);
 
 
      /* PC-relative loads are always SImode, and CONST16 is only
      /* PC-relative loads are always SImode, and CONST16 is only
         supported in the movsi pattern, so add a SUBREG for any other
         supported in the movsi pattern, so add a SUBREG for any other
         (smaller) mode.  */
         (smaller) mode.  */
 
 
      if (mode != SImode)
      if (mode != SImode)
        {
        {
          if (register_operand (operands[0], mode))
          if (register_operand (operands[0], mode))
            {
            {
              operands[0] = simplify_gen_subreg (SImode, operands[0], mode, 0);
              operands[0] = simplify_gen_subreg (SImode, operands[0], mode, 0);
              emit_move_insn (operands[0], operands[1]);
              emit_move_insn (operands[0], operands[1]);
              return 1;
              return 1;
            }
            }
          else
          else
            {
            {
              operands[1] = force_reg (SImode, operands[1]);
              operands[1] = force_reg (SImode, operands[1]);
              operands[1] = gen_lowpart_SUBREG (mode, operands[1]);
              operands[1] = gen_lowpart_SUBREG (mode, operands[1]);
            }
            }
        }
        }
    }
    }
 
 
  if (!(reload_in_progress | reload_completed)
  if (!(reload_in_progress | reload_completed)
      && !xtensa_valid_move (mode, operands))
      && !xtensa_valid_move (mode, operands))
    operands[1] = force_reg (mode, operands[1]);
    operands[1] = force_reg (mode, operands[1]);
 
 
  operands[1] = xtensa_copy_incoming_a7 (operands[1]);
  operands[1] = xtensa_copy_incoming_a7 (operands[1]);
 
 
  /* During reload we don't want to emit (subreg:X (mem:Y)) since that
  /* During reload we don't want to emit (subreg:X (mem:Y)) since that
     instruction won't be recognized after reload, so we remove the
     instruction won't be recognized after reload, so we remove the
     subreg and adjust mem accordingly.  */
     subreg and adjust mem accordingly.  */
  if (reload_in_progress)
  if (reload_in_progress)
    {
    {
      operands[0] = fixup_subreg_mem (operands[0]);
      operands[0] = fixup_subreg_mem (operands[0]);
      operands[1] = fixup_subreg_mem (operands[1]);
      operands[1] = fixup_subreg_mem (operands[1]);
    }
    }
  return 0;
  return 0;
}
}
 
 
 
 
static rtx
static rtx
fixup_subreg_mem (rtx x)
fixup_subreg_mem (rtx x)
{
{
  if (GET_CODE (x) == SUBREG
  if (GET_CODE (x) == SUBREG
      && GET_CODE (SUBREG_REG (x)) == REG
      && GET_CODE (SUBREG_REG (x)) == REG
      && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
      && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
    {
    {
      rtx temp =
      rtx temp =
        gen_rtx_SUBREG (GET_MODE (x),
        gen_rtx_SUBREG (GET_MODE (x),
                        reg_equiv_mem [REGNO (SUBREG_REG (x))],
                        reg_equiv_mem [REGNO (SUBREG_REG (x))],
                        SUBREG_BYTE (x));
                        SUBREG_BYTE (x));
      x = alter_subreg (&temp);
      x = alter_subreg (&temp);
    }
    }
  return x;
  return x;
}
}
 
 
 
 
/* Check if an incoming argument in a7 is expected to be used soon and
/* Check if an incoming argument in a7 is expected to be used soon and
   if OPND is a register or register pair that includes a7.  If so,
   if OPND is a register or register pair that includes a7.  If so,
   create a new pseudo and copy a7 into that pseudo at the very
   create a new pseudo and copy a7 into that pseudo at the very
   beginning of the function, followed by the special "set_frame_ptr"
   beginning of the function, followed by the special "set_frame_ptr"
   unspec_volatile insn.  The return value is either the original
   unspec_volatile insn.  The return value is either the original
   operand, if it is not a7, or the new pseudo containing a copy of
   operand, if it is not a7, or the new pseudo containing a copy of
   the incoming argument.  This is necessary because the register
   the incoming argument.  This is necessary because the register
   allocator will ignore conflicts with a7 and may either assign some
   allocator will ignore conflicts with a7 and may either assign some
   other pseudo to a7 or use a7 as the hard_frame_pointer, clobbering
   other pseudo to a7 or use a7 as the hard_frame_pointer, clobbering
   the incoming argument in a7.  By copying the argument out of a7 as
   the incoming argument in a7.  By copying the argument out of a7 as
   the very first thing, and then immediately following that with an
   the very first thing, and then immediately following that with an
   unspec_volatile to keep the scheduler away, we should avoid any
   unspec_volatile to keep the scheduler away, we should avoid any
   problems.  Putting the set_frame_ptr insn at the beginning, with
   problems.  Putting the set_frame_ptr insn at the beginning, with
   only the a7 copy before it, also makes it easier for the prologue
   only the a7 copy before it, also makes it easier for the prologue
   expander to initialize the frame pointer after the a7 copy and to
   expander to initialize the frame pointer after the a7 copy and to
   fix up the a7 copy to use the stack pointer instead of the frame
   fix up the a7 copy to use the stack pointer instead of the frame
   pointer.  */
   pointer.  */
 
 
rtx
rtx
xtensa_copy_incoming_a7 (rtx opnd)
xtensa_copy_incoming_a7 (rtx opnd)
{
{
  rtx entry_insns = 0;
  rtx entry_insns = 0;
  rtx reg, tmp;
  rtx reg, tmp;
  enum machine_mode mode;
  enum machine_mode mode;
 
 
  if (!cfun->machine->need_a7_copy)
  if (!cfun->machine->need_a7_copy)
    return opnd;
    return opnd;
 
 
  /* This function should never be called again once a7 has been copied.  */
  /* This function should never be called again once a7 has been copied.  */
  gcc_assert (!cfun->machine->set_frame_ptr_insn);
  gcc_assert (!cfun->machine->set_frame_ptr_insn);
 
 
  mode = GET_MODE (opnd);
  mode = GET_MODE (opnd);
 
 
  /* The operand using a7 may come in a later instruction, so just return
  /* The operand using a7 may come in a later instruction, so just return
     the original operand if it doesn't use a7.  */
     the original operand if it doesn't use a7.  */
  reg = opnd;
  reg = opnd;
  if (GET_CODE (reg) == SUBREG)
  if (GET_CODE (reg) == SUBREG)
    {
    {
      gcc_assert (SUBREG_BYTE (reg) == 0);
      gcc_assert (SUBREG_BYTE (reg) == 0);
      reg = SUBREG_REG (reg);
      reg = SUBREG_REG (reg);
    }
    }
  if (GET_CODE (reg) != REG
  if (GET_CODE (reg) != REG
      || REGNO (reg) > A7_REG
      || REGNO (reg) > A7_REG
      || REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
      || REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
    return opnd;
    return opnd;
 
 
  /* 1-word args will always be in a7; 2-word args in a6/a7.  */
  /* 1-word args will always be in a7; 2-word args in a6/a7.  */
  gcc_assert (REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) - 1 == A7_REG);
  gcc_assert (REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) - 1 == A7_REG);
 
 
  cfun->machine->need_a7_copy = false;
  cfun->machine->need_a7_copy = false;
 
 
  /* Copy a7 to a new pseudo at the function entry.  Use gen_raw_REG to
  /* Copy a7 to a new pseudo at the function entry.  Use gen_raw_REG to
     create the REG for a7 so that hard_frame_pointer_rtx is not used.  */
     create the REG for a7 so that hard_frame_pointer_rtx is not used.  */
 
 
  start_sequence ();
  start_sequence ();
  tmp = gen_reg_rtx (mode);
  tmp = gen_reg_rtx (mode);
 
 
  switch (mode)
  switch (mode)
    {
    {
    case DFmode:
    case DFmode:
    case DImode:
    case DImode:
      emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
      emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
                                     gen_rtx_REG (SImode, A7_REG - 1)));
                                     gen_rtx_REG (SImode, A7_REG - 1)));
      emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
      emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
                                     gen_raw_REG (SImode, A7_REG)));
                                     gen_raw_REG (SImode, A7_REG)));
      break;
      break;
    case SFmode:
    case SFmode:
      emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
      emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
      break;
      break;
    case SImode:
    case SImode:
      emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      break;
      break;
    case HImode:
    case HImode:
      emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      break;
      break;
    case QImode:
    case QImode:
      emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
      break;
      break;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
  cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
  entry_insns = get_insns ();
  entry_insns = get_insns ();
  end_sequence ();
  end_sequence ();
 
 
  if (cfun->machine->vararg_a7)
  if (cfun->machine->vararg_a7)
    {
    {
      /* This is called from within builtin_saveregs, which will insert the
      /* This is called from within builtin_saveregs, which will insert the
         saveregs code at the function entry, ahead of anything placed at
         saveregs code at the function entry, ahead of anything placed at
         the function entry now.  Instead, save the sequence to be inserted
         the function entry now.  Instead, save the sequence to be inserted
         at the beginning of the saveregs code.  */
         at the beginning of the saveregs code.  */
      cfun->machine->vararg_a7_copy = entry_insns;
      cfun->machine->vararg_a7_copy = entry_insns;
    }
    }
  else
  else
    {
    {
      /* Put entry_insns after the NOTE that starts the function.  If
      /* Put entry_insns after the NOTE that starts the function.  If
         this is inside a start_sequence, make the outer-level insn
         this is inside a start_sequence, make the outer-level insn
         chain current, so the code is placed at the start of the
         chain current, so the code is placed at the start of the
         function.  */
         function.  */
      push_topmost_sequence ();
      push_topmost_sequence ();
      /* Do not use entry_of_function() here.  This is called from within
      /* Do not use entry_of_function() here.  This is called from within
         expand_function_start, when the CFG still holds GIMPLE.  */
         expand_function_start, when the CFG still holds GIMPLE.  */
      emit_insn_after (entry_insns, get_insns ());
      emit_insn_after (entry_insns, get_insns ());
      pop_topmost_sequence ();
      pop_topmost_sequence ();
    }
    }
 
 
  return tmp;
  return tmp;
}
}
 
 
 
 
/* Try to expand a block move operation to a sequence of RTL move
/* Try to expand a block move operation to a sequence of RTL move
   instructions.  If not optimizing, or if the block size is not a
   instructions.  If not optimizing, or if the block size is not a
   constant, or if the block is too large, the expansion fails and GCC
   constant, or if the block is too large, the expansion fails and GCC
   falls back to calling memcpy().
   falls back to calling memcpy().
 
 
   operands[0] is the destination
   operands[0] is the destination
   operands[1] is the source
   operands[1] is the source
   operands[2] is the length
   operands[2] is the length
   operands[3] is the alignment */
   operands[3] is the alignment */
 
 
int
int
xtensa_expand_block_move (rtx *operands)
xtensa_expand_block_move (rtx *operands)
{
{
  static const enum machine_mode mode_from_align[] =
  static const enum machine_mode mode_from_align[] =
  {
  {
    VOIDmode, QImode, HImode, VOIDmode, SImode,
    VOIDmode, QImode, HImode, VOIDmode, SImode,
  };
  };
 
 
  rtx dst_mem = operands[0];
  rtx dst_mem = operands[0];
  rtx src_mem = operands[1];
  rtx src_mem = operands[1];
  HOST_WIDE_INT bytes, align;
  HOST_WIDE_INT bytes, align;
  int num_pieces, move_ratio;
  int num_pieces, move_ratio;
  rtx temp[2];
  rtx temp[2];
  enum machine_mode mode[2];
  enum machine_mode mode[2];
  int amount[2];
  int amount[2];
  bool active[2];
  bool active[2];
  int phase = 0;
  int phase = 0;
  int next;
  int next;
  int offset_ld = 0;
  int offset_ld = 0;
  int offset_st = 0;
  int offset_st = 0;
  rtx x;
  rtx x;
 
 
  /* If this is not a fixed size move, just call memcpy.  */
  /* If this is not a fixed size move, just call memcpy.  */
  if (!optimize || (GET_CODE (operands[2]) != CONST_INT))
  if (!optimize || (GET_CODE (operands[2]) != CONST_INT))
    return 0;
    return 0;
 
 
  bytes = INTVAL (operands[2]);
  bytes = INTVAL (operands[2]);
  align = INTVAL (operands[3]);
  align = INTVAL (operands[3]);
 
 
  /* Anything to move?  */
  /* Anything to move?  */
  if (bytes <= 0)
  if (bytes <= 0)
    return 0;
    return 0;
 
 
  if (align > MOVE_MAX)
  if (align > MOVE_MAX)
    align = MOVE_MAX;
    align = MOVE_MAX;
 
 
  /* Decide whether to expand inline based on the optimization level.  */
  /* Decide whether to expand inline based on the optimization level.  */
  move_ratio = 4;
  move_ratio = 4;
  if (optimize > 2)
  if (optimize > 2)
    move_ratio = LARGEST_MOVE_RATIO;
    move_ratio = LARGEST_MOVE_RATIO;
  num_pieces = (bytes / align) + (bytes % align); /* Close enough anyway.  */
  num_pieces = (bytes / align) + (bytes % align); /* Close enough anyway.  */
  if (num_pieces > move_ratio)
  if (num_pieces > move_ratio)
    return 0;
    return 0;
 
 
  x = XEXP (dst_mem, 0);
  x = XEXP (dst_mem, 0);
  if (!REG_P (x))
  if (!REG_P (x))
    {
    {
      x = force_reg (Pmode, x);
      x = force_reg (Pmode, x);
      dst_mem = replace_equiv_address (dst_mem, x);
      dst_mem = replace_equiv_address (dst_mem, x);
    }
    }
 
 
  x = XEXP (src_mem, 0);
  x = XEXP (src_mem, 0);
  if (!REG_P (x))
  if (!REG_P (x))
    {
    {
      x = force_reg (Pmode, x);
      x = force_reg (Pmode, x);
      src_mem = replace_equiv_address (src_mem, x);
      src_mem = replace_equiv_address (src_mem, x);
    }
    }
 
 
  active[0] = active[1] = false;
  active[0] = active[1] = false;
 
 
  do
  do
    {
    {
      next = phase;
      next = phase;
      phase ^= 1;
      phase ^= 1;
 
 
      if (bytes > 0)
      if (bytes > 0)
        {
        {
          int next_amount;
          int next_amount;
 
 
          next_amount = (bytes >= 4 ? 4 : (bytes >= 2 ? 2 : 1));
          next_amount = (bytes >= 4 ? 4 : (bytes >= 2 ? 2 : 1));
          next_amount = MIN (next_amount, align);
          next_amount = MIN (next_amount, align);
 
 
          amount[next] = next_amount;
          amount[next] = next_amount;
          mode[next] = mode_from_align[next_amount];
          mode[next] = mode_from_align[next_amount];
          temp[next] = gen_reg_rtx (mode[next]);
          temp[next] = gen_reg_rtx (mode[next]);
 
 
          x = adjust_address (src_mem, mode[next], offset_ld);
          x = adjust_address (src_mem, mode[next], offset_ld);
          emit_insn (gen_rtx_SET (VOIDmode, temp[next], x));
          emit_insn (gen_rtx_SET (VOIDmode, temp[next], x));
 
 
          offset_ld += next_amount;
          offset_ld += next_amount;
          bytes -= next_amount;
          bytes -= next_amount;
          active[next] = true;
          active[next] = true;
        }
        }
 
 
      if (active[phase])
      if (active[phase])
        {
        {
          active[phase] = false;
          active[phase] = false;
 
 
          x = adjust_address (dst_mem, mode[phase], offset_st);
          x = adjust_address (dst_mem, mode[phase], offset_st);
          emit_insn (gen_rtx_SET (VOIDmode, x, temp[phase]));
          emit_insn (gen_rtx_SET (VOIDmode, x, temp[phase]));
 
 
          offset_st += amount[phase];
          offset_st += amount[phase];
        }
        }
    }
    }
  while (active[next]);
  while (active[next]);
 
 
  return 1;
  return 1;
}
}
 
 
 
 
void
void
xtensa_expand_nonlocal_goto (rtx *operands)
xtensa_expand_nonlocal_goto (rtx *operands)
{
{
  rtx goto_handler = operands[1];
  rtx goto_handler = operands[1];
  rtx containing_fp = operands[3];
  rtx containing_fp = operands[3];
 
 
  /* Generate a call to "__xtensa_nonlocal_goto" (in libgcc); the code
  /* Generate a call to "__xtensa_nonlocal_goto" (in libgcc); the code
     is too big to generate in-line.  */
     is too big to generate in-line.  */
 
 
  if (GET_CODE (containing_fp) != REG)
  if (GET_CODE (containing_fp) != REG)
    containing_fp = force_reg (Pmode, containing_fp);
    containing_fp = force_reg (Pmode, containing_fp);
 
 
  goto_handler = replace_rtx (copy_rtx (goto_handler),
  goto_handler = replace_rtx (copy_rtx (goto_handler),
                              virtual_stack_vars_rtx,
                              virtual_stack_vars_rtx,
                              containing_fp);
                              containing_fp);
 
 
  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
                     0, VOIDmode, 2,
                     0, VOIDmode, 2,
                     containing_fp, Pmode,
                     containing_fp, Pmode,
                     goto_handler, Pmode);
                     goto_handler, Pmode);
}
}
 
 
 
 
static struct machine_function *
static struct machine_function *
xtensa_init_machine_status (void)
xtensa_init_machine_status (void)
{
{
  return ggc_alloc_cleared (sizeof (struct machine_function));
  return ggc_alloc_cleared (sizeof (struct machine_function));
}
}
 
 
 
 
void
void
xtensa_setup_frame_addresses (void)
xtensa_setup_frame_addresses (void)
{
{
  /* Set flag to cause FRAME_POINTER_REQUIRED to be set.  */
  /* Set flag to cause FRAME_POINTER_REQUIRED to be set.  */
  cfun->machine->accesses_prev_frame = 1;
  cfun->machine->accesses_prev_frame = 1;
 
 
  emit_library_call
  emit_library_call
    (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
    (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
     0, VOIDmode, 0);
     0, VOIDmode, 0);
}
}
 
 
 
 
/* Emit the assembly for the end of a zero-cost loop.  Normally we just emit
/* Emit the assembly for the end of a zero-cost loop.  Normally we just emit
   a comment showing where the end of the loop is.  However, if there is a
   a comment showing where the end of the loop is.  However, if there is a
   label or a branch at the end of the loop then we need to place a nop
   label or a branch at the end of the loop then we need to place a nop
   there.  If the loop ends with a label we need the nop so that branches
   there.  If the loop ends with a label we need the nop so that branches
   targeting that label will target the nop (and thus remain in the loop),
   targeting that label will target the nop (and thus remain in the loop),
   instead of targeting the instruction after the loop (and thus exiting
   instead of targeting the instruction after the loop (and thus exiting
   the loop).  If the loop ends with a branch, we need the nop in case the
   the loop).  If the loop ends with a branch, we need the nop in case the
   branch is targeting a location inside the loop.  When the branch
   branch is targeting a location inside the loop.  When the branch
   executes it will cause the loop count to be decremented even if it is
   executes it will cause the loop count to be decremented even if it is
   taken (because it is the last instruction in the loop), so we need to
   taken (because it is the last instruction in the loop), so we need to
   nop after the branch to prevent the loop count from being decremented
   nop after the branch to prevent the loop count from being decremented
   when the branch is taken.  */
   when the branch is taken.  */
 
 
void
void
xtensa_emit_loop_end (rtx insn, rtx *operands)
xtensa_emit_loop_end (rtx insn, rtx *operands)
{
{
  char done = 0;
  char done = 0;
 
 
  for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
  for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
    {
    {
      switch (GET_CODE (insn))
      switch (GET_CODE (insn))
        {
        {
        case NOTE:
        case NOTE:
        case BARRIER:
        case BARRIER:
          break;
          break;
 
 
        case CODE_LABEL:
        case CODE_LABEL:
          output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
          output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
          done = 1;
          done = 1;
          break;
          break;
 
 
        default:
        default:
          {
          {
            rtx body = PATTERN (insn);
            rtx body = PATTERN (insn);
 
 
            if (GET_CODE (body) == JUMP_INSN)
            if (GET_CODE (body) == JUMP_INSN)
              {
              {
                output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
                output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
                done = 1;
                done = 1;
              }
              }
            else if ((GET_CODE (body) != USE)
            else if ((GET_CODE (body) != USE)
                     && (GET_CODE (body) != CLOBBER))
                     && (GET_CODE (body) != CLOBBER))
              done = 1;
              done = 1;
          }
          }
          break;
          break;
        }
        }
    }
    }
 
 
  output_asm_insn ("# loop end for %0", operands);
  output_asm_insn ("# loop end for %0", operands);
}
}
 
 
 
 
char *
char *
xtensa_emit_call (int callop, rtx *operands)
xtensa_emit_call (int callop, rtx *operands)
{
{
  static char result[64];
  static char result[64];
  rtx tgt = operands[callop];
  rtx tgt = operands[callop];
 
 
  if (GET_CODE (tgt) == CONST_INT)
  if (GET_CODE (tgt) == CONST_INT)
    sprintf (result, "call8\t0x%lx", INTVAL (tgt));
    sprintf (result, "call8\t0x%lx", INTVAL (tgt));
  else if (register_operand (tgt, VOIDmode))
  else if (register_operand (tgt, VOIDmode))
    sprintf (result, "callx8\t%%%d", callop);
    sprintf (result, "callx8\t%%%d", callop);
  else
  else
    sprintf (result, "call8\t%%%d", callop);
    sprintf (result, "call8\t%%%d", callop);
 
 
  return result;
  return result;
}
}
 
 
 
 
/* Return the debugger register number to use for 'regno'.  */
/* Return the debugger register number to use for 'regno'.  */
 
 
int
int
xtensa_dbx_register_number (int regno)
xtensa_dbx_register_number (int regno)
{
{
  int first = -1;
  int first = -1;
 
 
  if (GP_REG_P (regno))
  if (GP_REG_P (regno))
    {
    {
      regno -= GP_REG_FIRST;
      regno -= GP_REG_FIRST;
      first = 0;
      first = 0;
    }
    }
  else if (BR_REG_P (regno))
  else if (BR_REG_P (regno))
    {
    {
      regno -= BR_REG_FIRST;
      regno -= BR_REG_FIRST;
      first = 16;
      first = 16;
    }
    }
  else if (FP_REG_P (regno))
  else if (FP_REG_P (regno))
    {
    {
      regno -= FP_REG_FIRST;
      regno -= FP_REG_FIRST;
      first = 48;
      first = 48;
    }
    }
  else if (ACC_REG_P (regno))
  else if (ACC_REG_P (regno))
    {
    {
      first = 0x200;    /* Start of Xtensa special registers.  */
      first = 0x200;    /* Start of Xtensa special registers.  */
      regno = 16;       /* ACCLO is special register 16.  */
      regno = 16;       /* ACCLO is special register 16.  */
    }
    }
 
 
  /* When optimizing, we sometimes get asked about pseudo-registers
  /* When optimizing, we sometimes get asked about pseudo-registers
     that don't represent hard registers.  Return 0 for these.  */
     that don't represent hard registers.  Return 0 for these.  */
  if (first == -1)
  if (first == -1)
    return 0;
    return 0;
 
 
  return first + regno;
  return first + regno;
}
}
 
 
 
 
/* Argument support functions.  */
/* Argument support functions.  */
 
 
/* Initialize CUMULATIVE_ARGS for a function.  */
/* Initialize CUMULATIVE_ARGS for a function.  */
 
 
void
void
init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
{
{
  cum->arg_words = 0;
  cum->arg_words = 0;
  cum->incoming = incoming;
  cum->incoming = incoming;
}
}
 
 
 
 
/* Advance the argument to the next argument position.  */
/* Advance the argument to the next argument position.  */
 
 
void
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
{
{
  int words, max;
  int words, max;
  int *arg_words;
  int *arg_words;
 
 
  arg_words = &cum->arg_words;
  arg_words = &cum->arg_words;
  max = MAX_ARGS_IN_REGISTERS;
  max = MAX_ARGS_IN_REGISTERS;
 
 
  words = (((mode != BLKmode)
  words = (((mode != BLKmode)
            ? (int) GET_MODE_SIZE (mode)
            ? (int) GET_MODE_SIZE (mode)
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
 
  if (*arg_words < max
  if (*arg_words < max
      && (targetm.calls.must_pass_in_stack (mode, type)
      && (targetm.calls.must_pass_in_stack (mode, type)
          || *arg_words + words > max))
          || *arg_words + words > max))
    *arg_words = max;
    *arg_words = max;
 
 
  *arg_words += words;
  *arg_words += words;
}
}
 
 
 
 
/* Return an RTL expression containing the register for the given mode,
/* Return an RTL expression containing the register for the given mode,
   or 0 if the argument is to be passed on the stack.  INCOMING_P is nonzero
   or 0 if the argument is to be passed on the stack.  INCOMING_P is nonzero
   if this is an incoming argument to the current function.  */
   if this is an incoming argument to the current function.  */
 
 
rtx
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
              int incoming_p)
              int incoming_p)
{
{
  int regbase, words, max;
  int regbase, words, max;
  int *arg_words;
  int *arg_words;
  int regno;
  int regno;
 
 
  arg_words = &cum->arg_words;
  arg_words = &cum->arg_words;
  regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
  regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
  max = MAX_ARGS_IN_REGISTERS;
  max = MAX_ARGS_IN_REGISTERS;
 
 
  words = (((mode != BLKmode)
  words = (((mode != BLKmode)
            ? (int) GET_MODE_SIZE (mode)
            ? (int) GET_MODE_SIZE (mode)
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
            : int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
 
  if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
  if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
    {
    {
      int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_WORD;
      int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_WORD;
      *arg_words = (*arg_words + align - 1) & -align;
      *arg_words = (*arg_words + align - 1) & -align;
    }
    }
 
 
  if (*arg_words + words > max)
  if (*arg_words + words > max)
    return (rtx)0;
    return (rtx)0;
 
 
  regno = regbase + *arg_words;
  regno = regbase + *arg_words;
 
 
  if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
  if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
    cfun->machine->need_a7_copy = true;
    cfun->machine->need_a7_copy = true;
 
 
  return gen_rtx_REG (mode, regno);
  return gen_rtx_REG (mode, regno);
}
}
 
 
 
 
int
int
function_arg_boundary (enum machine_mode mode, tree type)
function_arg_boundary (enum machine_mode mode, tree type)
{
{
  unsigned int alignment;
  unsigned int alignment;
 
 
  alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
  alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
  if (alignment < PARM_BOUNDARY)
  if (alignment < PARM_BOUNDARY)
    alignment = PARM_BOUNDARY;
    alignment = PARM_BOUNDARY;
  if (alignment > STACK_BOUNDARY)
  if (alignment > STACK_BOUNDARY)
    alignment = STACK_BOUNDARY;
    alignment = STACK_BOUNDARY;
  return alignment;
  return alignment;
}
}
 
 
 
 
static bool
static bool
xtensa_return_in_msb (tree valtype)
xtensa_return_in_msb (tree valtype)
{
{
  return (TARGET_BIG_ENDIAN
  return (TARGET_BIG_ENDIAN
          && AGGREGATE_TYPE_P (valtype)
          && AGGREGATE_TYPE_P (valtype)
          && int_size_in_bytes (valtype) >= UNITS_PER_WORD);
          && int_size_in_bytes (valtype) >= UNITS_PER_WORD);
}
}
 
 
 
 
void
void
override_options (void)
override_options (void)
{
{
  int regno;
  int regno;
  enum machine_mode mode;
  enum machine_mode mode;
 
 
  if (!TARGET_BOOLEANS && TARGET_HARD_FLOAT)
  if (!TARGET_BOOLEANS && TARGET_HARD_FLOAT)
    error ("boolean registers required for the floating-point option");
    error ("boolean registers required for the floating-point option");
 
 
  xtensa_char_to_class['q'] = SP_REG;
  xtensa_char_to_class['q'] = SP_REG;
  xtensa_char_to_class['a'] = GR_REGS;
  xtensa_char_to_class['a'] = GR_REGS;
  xtensa_char_to_class['b'] = ((TARGET_BOOLEANS) ? BR_REGS : NO_REGS);
  xtensa_char_to_class['b'] = ((TARGET_BOOLEANS) ? BR_REGS : NO_REGS);
  xtensa_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
  xtensa_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
  xtensa_char_to_class['A'] = ((TARGET_MAC16) ? ACC_REG : NO_REGS);
  xtensa_char_to_class['A'] = ((TARGET_MAC16) ? ACC_REG : NO_REGS);
  xtensa_char_to_class['B'] = ((TARGET_SEXT) ? GR_REGS : NO_REGS);
  xtensa_char_to_class['B'] = ((TARGET_SEXT) ? GR_REGS : NO_REGS);
  xtensa_char_to_class['C'] = ((TARGET_MUL16) ? GR_REGS: NO_REGS);
  xtensa_char_to_class['C'] = ((TARGET_MUL16) ? GR_REGS: NO_REGS);
  xtensa_char_to_class['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
  xtensa_char_to_class['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
  xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
  xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
  xtensa_char_to_class['W'] = ((TARGET_CONST16) ? GR_REGS: NO_REGS);
  xtensa_char_to_class['W'] = ((TARGET_CONST16) ? GR_REGS: NO_REGS);
 
 
  /* Set up array giving whether a given register can hold a given mode.  */
  /* Set up array giving whether a given register can hold a given mode.  */
  for (mode = VOIDmode;
  for (mode = VOIDmode;
       mode != MAX_MACHINE_MODE;
       mode != MAX_MACHINE_MODE;
       mode = (enum machine_mode) ((int) mode + 1))
       mode = (enum machine_mode) ((int) mode + 1))
    {
    {
      int size = GET_MODE_SIZE (mode);
      int size = GET_MODE_SIZE (mode);
      enum mode_class class = GET_MODE_CLASS (mode);
      enum mode_class class = GET_MODE_CLASS (mode);
 
 
      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
        {
        {
          int temp;
          int temp;
 
 
          if (ACC_REG_P (regno))
          if (ACC_REG_P (regno))
            temp = (TARGET_MAC16
            temp = (TARGET_MAC16
                    && (class == MODE_INT) && (size <= UNITS_PER_WORD));
                    && (class == MODE_INT) && (size <= UNITS_PER_WORD));
          else if (GP_REG_P (regno))
          else if (GP_REG_P (regno))
            temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
            temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
          else if (FP_REG_P (regno))
          else if (FP_REG_P (regno))
            temp = (TARGET_HARD_FLOAT && (mode == SFmode));
            temp = (TARGET_HARD_FLOAT && (mode == SFmode));
          else if (BR_REG_P (regno))
          else if (BR_REG_P (regno))
            temp = (TARGET_BOOLEANS && (mode == CCmode));
            temp = (TARGET_BOOLEANS && (mode == CCmode));
          else
          else
            temp = FALSE;
            temp = FALSE;
 
 
          xtensa_hard_regno_mode_ok[(int) mode][regno] = temp;
          xtensa_hard_regno_mode_ok[(int) mode][regno] = temp;
        }
        }
    }
    }
 
 
  init_machine_status = xtensa_init_machine_status;
  init_machine_status = xtensa_init_machine_status;
 
 
  /* Check PIC settings.  PIC is only supported when using L32R
  /* Check PIC settings.  PIC is only supported when using L32R
     instructions, and some targets need to always use PIC.  */
     instructions, and some targets need to always use PIC.  */
  if (flag_pic && TARGET_CONST16)
  if (flag_pic && TARGET_CONST16)
    error ("-f%s is not supported with CONST16 instructions",
    error ("-f%s is not supported with CONST16 instructions",
           (flag_pic > 1 ? "PIC" : "pic"));
           (flag_pic > 1 ? "PIC" : "pic"));
  else if (XTENSA_ALWAYS_PIC)
  else if (XTENSA_ALWAYS_PIC)
    {
    {
      if (TARGET_CONST16)
      if (TARGET_CONST16)
        error ("PIC is required but not supported with CONST16 instructions");
        error ("PIC is required but not supported with CONST16 instructions");
      flag_pic = 1;
      flag_pic = 1;
    }
    }
  /* There's no need for -fPIC (as opposed to -fpic) on Xtensa.  */
  /* There's no need for -fPIC (as opposed to -fpic) on Xtensa.  */
  if (flag_pic > 1)
  if (flag_pic > 1)
    flag_pic = 1;
    flag_pic = 1;
 
 
  /* Hot/cold partitioning does not work on this architecture, because of
  /* Hot/cold partitioning does not work on this architecture, because of
     constant pools (the load instruction cannot necessarily reach that far).
     constant pools (the load instruction cannot necessarily reach that far).
     Therefore disable it on this architecture.  */
     Therefore disable it on this architecture.  */
  if (flag_reorder_blocks_and_partition)
  if (flag_reorder_blocks_and_partition)
    {
    {
      flag_reorder_blocks_and_partition = 0;
      flag_reorder_blocks_and_partition = 0;
      flag_reorder_blocks = 1;
      flag_reorder_blocks = 1;
    }
    }
}
}
 
 
 
 
/* A C compound statement to output to stdio stream STREAM the
/* A C compound statement to output to stdio stream STREAM the
   assembler syntax for an instruction operand X.  X is an RTL
   assembler syntax for an instruction operand X.  X is an RTL
   expression.
   expression.
 
 
   CODE is a value that can be used to specify one of several ways
   CODE is a value that can be used to specify one of several ways
   of printing the operand.  It is used when identical operands
   of printing the operand.  It is used when identical operands
   must be printed differently depending on the context.  CODE
   must be printed differently depending on the context.  CODE
   comes from the '%' specification that was used to request
   comes from the '%' specification that was used to request
   printing of the operand.  If the specification was just '%DIGIT'
   printing of the operand.  If the specification was just '%DIGIT'
   then CODE is 0; if the specification was '%LTR DIGIT' then CODE
   then CODE is 0; if the specification was '%LTR DIGIT' then CODE
   is the ASCII code for LTR.
   is the ASCII code for LTR.
 
 
   If X is a register, this macro should print the register's name.
   If X is a register, this macro should print the register's name.
   The names can be found in an array 'reg_names' whose type is
   The names can be found in an array 'reg_names' whose type is
   'char *[]'.  'reg_names' is initialized from 'REGISTER_NAMES'.
   'char *[]'.  'reg_names' is initialized from 'REGISTER_NAMES'.
 
 
   When the machine description has a specification '%PUNCT' (a '%'
   When the machine description has a specification '%PUNCT' (a '%'
   followed by a punctuation character), this macro is called with
   followed by a punctuation character), this macro is called with
   a null pointer for X and the punctuation character for CODE.
   a null pointer for X and the punctuation character for CODE.
 
 
   'a', 'c', 'l', and 'n' are reserved.
   'a', 'c', 'l', and 'n' are reserved.
 
 
   The Xtensa specific codes are:
   The Xtensa specific codes are:
 
 
   'd'  CONST_INT, print as signed decimal
   'd'  CONST_INT, print as signed decimal
   'x'  CONST_INT, print as signed hexadecimal
   'x'  CONST_INT, print as signed hexadecimal
   'K'  CONST_INT, print number of bits in mask for EXTUI
   'K'  CONST_INT, print number of bits in mask for EXTUI
   'R'  CONST_INT, print (X & 0x1f)
   'R'  CONST_INT, print (X & 0x1f)
   'L'  CONST_INT, print ((32 - X) & 0x1f)
   'L'  CONST_INT, print ((32 - X) & 0x1f)
   'D'  REG, print second register of double-word register operand
   'D'  REG, print second register of double-word register operand
   'N'  MEM, print address of next word following a memory operand
   'N'  MEM, print address of next word following a memory operand
   'v'  MEM, if memory reference is volatile, output a MEMW before it
   'v'  MEM, if memory reference is volatile, output a MEMW before it
   't'  any constant, add "@h" suffix for top 16 bits
   't'  any constant, add "@h" suffix for top 16 bits
   'b'  any constant, add "@l" suffix for bottom 16 bits
   'b'  any constant, add "@l" suffix for bottom 16 bits
*/
*/
 
 
static void
static void
printx (FILE *file, signed int val)
printx (FILE *file, signed int val)
{
{
  /* Print a hexadecimal value in a nice way.  */
  /* Print a hexadecimal value in a nice way.  */
  if ((val > -0xa) && (val < 0xa))
  if ((val > -0xa) && (val < 0xa))
    fprintf (file, "%d", val);
    fprintf (file, "%d", val);
  else if (val < 0)
  else if (val < 0)
    fprintf (file, "-0x%x", -val);
    fprintf (file, "-0x%x", -val);
  else
  else
    fprintf (file, "0x%x", val);
    fprintf (file, "0x%x", val);
}
}
 
 
 
 
void
void
print_operand (FILE *file, rtx x, int letter)
print_operand (FILE *file, rtx x, int letter)
{
{
  if (!x)
  if (!x)
    error ("PRINT_OPERAND null pointer");
    error ("PRINT_OPERAND null pointer");
 
 
  switch (letter)
  switch (letter)
    {
    {
    case 'D':
    case 'D':
      if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
      if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
        fprintf (file, "%s", reg_names[xt_true_regnum (x) + 1]);
        fprintf (file, "%s", reg_names[xt_true_regnum (x) + 1]);
      else
      else
        output_operand_lossage ("invalid %%D value");
        output_operand_lossage ("invalid %%D value");
      break;
      break;
 
 
    case 'v':
    case 'v':
      if (GET_CODE (x) == MEM)
      if (GET_CODE (x) == MEM)
        {
        {
          /* For a volatile memory reference, emit a MEMW before the
          /* For a volatile memory reference, emit a MEMW before the
             load or store.  */
             load or store.  */
          if (MEM_VOLATILE_P (x))
          if (MEM_VOLATILE_P (x))
            fprintf (file, "memw\n\t");
            fprintf (file, "memw\n\t");
        }
        }
      else
      else
        output_operand_lossage ("invalid %%v value");
        output_operand_lossage ("invalid %%v value");
      break;
      break;
 
 
    case 'N':
    case 'N':
      if (GET_CODE (x) == MEM
      if (GET_CODE (x) == MEM
          && (GET_MODE (x) == DFmode || GET_MODE (x) == DImode))
          && (GET_MODE (x) == DFmode || GET_MODE (x) == DImode))
        {
        {
          x = adjust_address (x, GET_MODE (x) == DFmode ? SFmode : SImode, 4);
          x = adjust_address (x, GET_MODE (x) == DFmode ? SFmode : SImode, 4);
          output_address (XEXP (x, 0));
          output_address (XEXP (x, 0));
        }
        }
      else
      else
        output_operand_lossage ("invalid %%N value");
        output_operand_lossage ("invalid %%N value");
      break;
      break;
 
 
    case 'K':
    case 'K':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        {
        {
          int num_bits = 0;
          int num_bits = 0;
          unsigned val = INTVAL (x);
          unsigned val = INTVAL (x);
          while (val & 1)
          while (val & 1)
            {
            {
              num_bits += 1;
              num_bits += 1;
              val = val >> 1;
              val = val >> 1;
            }
            }
          if ((val != 0) || (num_bits == 0) || (num_bits > 16))
          if ((val != 0) || (num_bits == 0) || (num_bits > 16))
            fatal_insn ("invalid mask", x);
            fatal_insn ("invalid mask", x);
 
 
          fprintf (file, "%d", num_bits);
          fprintf (file, "%d", num_bits);
        }
        }
      else
      else
        output_operand_lossage ("invalid %%K value");
        output_operand_lossage ("invalid %%K value");
      break;
      break;
 
 
    case 'L':
    case 'L':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        fprintf (file, "%ld", (32 - INTVAL (x)) & 0x1f);
        fprintf (file, "%ld", (32 - INTVAL (x)) & 0x1f);
      else
      else
        output_operand_lossage ("invalid %%L value");
        output_operand_lossage ("invalid %%L value");
      break;
      break;
 
 
    case 'R':
    case 'R':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        fprintf (file, "%ld", INTVAL (x) & 0x1f);
        fprintf (file, "%ld", INTVAL (x) & 0x1f);
      else
      else
        output_operand_lossage ("invalid %%R value");
        output_operand_lossage ("invalid %%R value");
      break;
      break;
 
 
    case 'x':
    case 'x':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        printx (file, INTVAL (x));
        printx (file, INTVAL (x));
      else
      else
        output_operand_lossage ("invalid %%x value");
        output_operand_lossage ("invalid %%x value");
      break;
      break;
 
 
    case 'd':
    case 'd':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        fprintf (file, "%ld", INTVAL (x));
        fprintf (file, "%ld", INTVAL (x));
      else
      else
        output_operand_lossage ("invalid %%d value");
        output_operand_lossage ("invalid %%d value");
      break;
      break;
 
 
    case 't':
    case 't':
    case 'b':
    case 'b':
      if (GET_CODE (x) == CONST_INT)
      if (GET_CODE (x) == CONST_INT)
        {
        {
          printx (file, INTVAL (x));
          printx (file, INTVAL (x));
          fputs (letter == 't' ? "@h" : "@l", file);
          fputs (letter == 't' ? "@h" : "@l", file);
        }
        }
      else if (GET_CODE (x) == CONST_DOUBLE)
      else if (GET_CODE (x) == CONST_DOUBLE)
        {
        {
          REAL_VALUE_TYPE r;
          REAL_VALUE_TYPE r;
          REAL_VALUE_FROM_CONST_DOUBLE (r, x);
          REAL_VALUE_FROM_CONST_DOUBLE (r, x);
          if (GET_MODE (x) == SFmode)
          if (GET_MODE (x) == SFmode)
            {
            {
              long l;
              long l;
              REAL_VALUE_TO_TARGET_SINGLE (r, l);
              REAL_VALUE_TO_TARGET_SINGLE (r, l);
              fprintf (file, "0x%08lx@%c", l, letter == 't' ? 'h' : 'l');
              fprintf (file, "0x%08lx@%c", l, letter == 't' ? 'h' : 'l');
            }
            }
          else
          else
            output_operand_lossage ("invalid %%t/%%b value");
            output_operand_lossage ("invalid %%t/%%b value");
        }
        }
      else if (GET_CODE (x) == CONST)
      else if (GET_CODE (x) == CONST)
        {
        {
          /* X must be a symbolic constant on ELF.  Write an expression
          /* X must be a symbolic constant on ELF.  Write an expression
             suitable for 'const16' that sets the high or low 16 bits.  */
             suitable for 'const16' that sets the high or low 16 bits.  */
          if (GET_CODE (XEXP (x, 0)) != PLUS
          if (GET_CODE (XEXP (x, 0)) != PLUS
              || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
              || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
                  && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
                  && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
              || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
              || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
            output_operand_lossage ("invalid %%t/%%b value");
            output_operand_lossage ("invalid %%t/%%b value");
          print_operand (file, XEXP (XEXP (x, 0), 0), 0);
          print_operand (file, XEXP (XEXP (x, 0), 0), 0);
          fputs (letter == 't' ? "@h" : "@l", file);
          fputs (letter == 't' ? "@h" : "@l", file);
          /* There must be a non-alphanumeric character between 'h' or 'l'
          /* There must be a non-alphanumeric character between 'h' or 'l'
             and the number.  The '-' is added by print_operand() already.  */
             and the number.  The '-' is added by print_operand() already.  */
          if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0)
          if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0)
            fputs ("+", file);
            fputs ("+", file);
          print_operand (file, XEXP (XEXP (x, 0), 1), 0);
          print_operand (file, XEXP (XEXP (x, 0), 1), 0);
        }
        }
      else
      else
        {
        {
          output_addr_const (file, x);
          output_addr_const (file, x);
          fputs (letter == 't' ? "@h" : "@l", file);
          fputs (letter == 't' ? "@h" : "@l", file);
        }
        }
      break;
      break;
 
 
    default:
    default:
      if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
      if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
        fprintf (file, "%s", reg_names[xt_true_regnum (x)]);
        fprintf (file, "%s", reg_names[xt_true_regnum (x)]);
      else if (GET_CODE (x) == MEM)
      else if (GET_CODE (x) == MEM)
        output_address (XEXP (x, 0));
        output_address (XEXP (x, 0));
      else if (GET_CODE (x) == CONST_INT)
      else if (GET_CODE (x) == CONST_INT)
        fprintf (file, "%ld", INTVAL (x));
        fprintf (file, "%ld", INTVAL (x));
      else
      else
        output_addr_const (file, x);
        output_addr_const (file, x);
    }
    }
}
}
 
 
 
 
/* A C compound statement to output to stdio stream STREAM the
/* A C compound statement to output to stdio stream STREAM the
   assembler syntax for an instruction operand that is a memory
   assembler syntax for an instruction operand that is a memory
   reference whose address is ADDR.  ADDR is an RTL expression.  */
   reference whose address is ADDR.  ADDR is an RTL expression.  */
 
 
void
void
print_operand_address (FILE *file, rtx addr)
print_operand_address (FILE *file, rtx addr)
{
{
  if (!addr)
  if (!addr)
    error ("PRINT_OPERAND_ADDRESS, null pointer");
    error ("PRINT_OPERAND_ADDRESS, null pointer");
 
 
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    default:
    default:
      fatal_insn ("invalid address", addr);
      fatal_insn ("invalid address", addr);
      break;
      break;
 
 
    case REG:
    case REG:
      fprintf (file, "%s, 0", reg_names [REGNO (addr)]);
      fprintf (file, "%s, 0", reg_names [REGNO (addr)]);
      break;
      break;
 
 
    case PLUS:
    case PLUS:
      {
      {
        rtx reg = (rtx)0;
        rtx reg = (rtx)0;
        rtx offset = (rtx)0;
        rtx offset = (rtx)0;
        rtx arg0 = XEXP (addr, 0);
        rtx arg0 = XEXP (addr, 0);
        rtx arg1 = XEXP (addr, 1);
        rtx arg1 = XEXP (addr, 1);
 
 
        if (GET_CODE (arg0) == REG)
        if (GET_CODE (arg0) == REG)
          {
          {
            reg = arg0;
            reg = arg0;
            offset = arg1;
            offset = arg1;
          }
          }
        else if (GET_CODE (arg1) == REG)
        else if (GET_CODE (arg1) == REG)
          {
          {
            reg = arg1;
            reg = arg1;
            offset = arg0;
            offset = arg0;
          }
          }
        else
        else
          fatal_insn ("no register in address", addr);
          fatal_insn ("no register in address", addr);
 
 
        if (CONSTANT_P (offset))
        if (CONSTANT_P (offset))
          {
          {
            fprintf (file, "%s, ", reg_names [REGNO (reg)]);
            fprintf (file, "%s, ", reg_names [REGNO (reg)]);
            output_addr_const (file, offset);
            output_addr_const (file, offset);
          }
          }
        else
        else
          fatal_insn ("address offset not a constant", addr);
          fatal_insn ("address offset not a constant", addr);
      }
      }
      break;
      break;
 
 
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
    case CONST_INT:
    case CONST_INT:
    case CONST:
    case CONST:
      output_addr_const (file, addr);
      output_addr_const (file, addr);
      break;
      break;
    }
    }
}
}
 
 
 
 
void
void
xtensa_output_literal (FILE *file, rtx x, enum machine_mode mode, int labelno)
xtensa_output_literal (FILE *file, rtx x, enum machine_mode mode, int labelno)
{
{
  long value_long[2];
  long value_long[2];
  REAL_VALUE_TYPE r;
  REAL_VALUE_TYPE r;
  int size;
  int size;
  rtx first, second;
  rtx first, second;
 
 
  fprintf (file, "\t.literal .LC%u, ", (unsigned) labelno);
  fprintf (file, "\t.literal .LC%u, ", (unsigned) labelno);
 
 
  switch (GET_MODE_CLASS (mode))
  switch (GET_MODE_CLASS (mode))
    {
    {
    case MODE_FLOAT:
    case MODE_FLOAT:
      gcc_assert (GET_CODE (x) == CONST_DOUBLE);
      gcc_assert (GET_CODE (x) == CONST_DOUBLE);
 
 
      REAL_VALUE_FROM_CONST_DOUBLE (r, x);
      REAL_VALUE_FROM_CONST_DOUBLE (r, x);
      switch (mode)
      switch (mode)
        {
        {
        case SFmode:
        case SFmode:
          REAL_VALUE_TO_TARGET_SINGLE (r, value_long[0]);
          REAL_VALUE_TO_TARGET_SINGLE (r, value_long[0]);
          if (HOST_BITS_PER_LONG > 32)
          if (HOST_BITS_PER_LONG > 32)
            value_long[0] &= 0xffffffff;
            value_long[0] &= 0xffffffff;
          fprintf (file, "0x%08lx\n", value_long[0]);
          fprintf (file, "0x%08lx\n", value_long[0]);
          break;
          break;
 
 
        case DFmode:
        case DFmode:
          REAL_VALUE_TO_TARGET_DOUBLE (r, value_long);
          REAL_VALUE_TO_TARGET_DOUBLE (r, value_long);
          if (HOST_BITS_PER_LONG > 32)
          if (HOST_BITS_PER_LONG > 32)
            {
            {
              value_long[0] &= 0xffffffff;
              value_long[0] &= 0xffffffff;
              value_long[1] &= 0xffffffff;
              value_long[1] &= 0xffffffff;
            }
            }
          fprintf (file, "0x%08lx, 0x%08lx\n",
          fprintf (file, "0x%08lx, 0x%08lx\n",
                   value_long[0], value_long[1]);
                   value_long[0], value_long[1]);
          break;
          break;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      break;
      break;
 
 
    case MODE_INT:
    case MODE_INT:
    case MODE_PARTIAL_INT:
    case MODE_PARTIAL_INT:
      size = GET_MODE_SIZE (mode);
      size = GET_MODE_SIZE (mode);
      switch (size)
      switch (size)
        {
        {
        case 4:
        case 4:
          output_addr_const (file, x);
          output_addr_const (file, x);
          fputs ("\n", file);
          fputs ("\n", file);
          break;
          break;
 
 
        case 8:
        case 8:
          split_double (x, &first, &second);
          split_double (x, &first, &second);
          output_addr_const (file, first);
          output_addr_const (file, first);
          fputs (", ", file);
          fputs (", ", file);
          output_addr_const (file, second);
          output_addr_const (file, second);
          fputs ("\n", file);
          fputs ("\n", file);
          break;
          break;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
      break;
      break;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
 
 
/* Return the bytes needed to compute the frame pointer from the current
/* Return the bytes needed to compute the frame pointer from the current
   stack pointer.  */
   stack pointer.  */
 
 
#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
#define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
#define XTENSA_STACK_ALIGN(LOC) (((LOC) + STACK_BYTES-1) & ~(STACK_BYTES-1))
#define XTENSA_STACK_ALIGN(LOC) (((LOC) + STACK_BYTES-1) & ~(STACK_BYTES-1))
 
 
long
long
compute_frame_size (int size)
compute_frame_size (int size)
{
{
  /* Add space for the incoming static chain value.  */
  /* Add space for the incoming static chain value.  */
  if (cfun->static_chain_decl != NULL)
  if (cfun->static_chain_decl != NULL)
    size += (1 * UNITS_PER_WORD);
    size += (1 * UNITS_PER_WORD);
 
 
  xtensa_current_frame_size =
  xtensa_current_frame_size =
    XTENSA_STACK_ALIGN (size
    XTENSA_STACK_ALIGN (size
                        + current_function_outgoing_args_size
                        + current_function_outgoing_args_size
                        + (WINDOW_SIZE * UNITS_PER_WORD));
                        + (WINDOW_SIZE * UNITS_PER_WORD));
  return xtensa_current_frame_size;
  return xtensa_current_frame_size;
}
}
 
 
 
 
int
int
xtensa_frame_pointer_required (void)
xtensa_frame_pointer_required (void)
{
{
  /* The code to expand builtin_frame_addr and builtin_return_addr
  /* The code to expand builtin_frame_addr and builtin_return_addr
     currently uses the hard_frame_pointer instead of frame_pointer.
     currently uses the hard_frame_pointer instead of frame_pointer.
     This seems wrong but maybe it's necessary for other architectures.
     This seems wrong but maybe it's necessary for other architectures.
     This function is derived from the i386 code.  */
     This function is derived from the i386 code.  */
 
 
  if (cfun->machine->accesses_prev_frame)
  if (cfun->machine->accesses_prev_frame)
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
 
 
void
void
xtensa_expand_prologue (void)
xtensa_expand_prologue (void)
{
{
  HOST_WIDE_INT total_size;
  HOST_WIDE_INT total_size;
  rtx size_rtx;
  rtx size_rtx;
 
 
  total_size = compute_frame_size (get_frame_size ());
  total_size = compute_frame_size (get_frame_size ());
  size_rtx = GEN_INT (total_size);
  size_rtx = GEN_INT (total_size);
 
 
  if (total_size < (1 << (12+3)))
  if (total_size < (1 << (12+3)))
    emit_insn (gen_entry (size_rtx, size_rtx));
    emit_insn (gen_entry (size_rtx, size_rtx));
  else
  else
    {
    {
      /* Use a8 as a temporary since a0-a7 may be live.  */
      /* Use a8 as a temporary since a0-a7 may be live.  */
      rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
      rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
      emit_insn (gen_entry (size_rtx, GEN_INT (MIN_FRAME_SIZE)));
      emit_insn (gen_entry (size_rtx, GEN_INT (MIN_FRAME_SIZE)));
      emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE));
      emit_move_insn (tmp_reg, GEN_INT (total_size - MIN_FRAME_SIZE));
      emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg));
      emit_insn (gen_subsi3 (tmp_reg, stack_pointer_rtx, tmp_reg));
      emit_move_insn (stack_pointer_rtx, tmp_reg);
      emit_move_insn (stack_pointer_rtx, tmp_reg);
    }
    }
 
 
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    {
    {
      if (cfun->machine->set_frame_ptr_insn)
      if (cfun->machine->set_frame_ptr_insn)
        {
        {
          rtx first, insn;
          rtx first, insn;
 
 
          push_topmost_sequence ();
          push_topmost_sequence ();
          first = get_insns ();
          first = get_insns ();
          pop_topmost_sequence ();
          pop_topmost_sequence ();
 
 
          /* For all instructions prior to set_frame_ptr_insn, replace
          /* For all instructions prior to set_frame_ptr_insn, replace
             hard_frame_pointer references with stack_pointer.  */
             hard_frame_pointer references with stack_pointer.  */
          for (insn = first;
          for (insn = first;
               insn != cfun->machine->set_frame_ptr_insn;
               insn != cfun->machine->set_frame_ptr_insn;
               insn = NEXT_INSN (insn))
               insn = NEXT_INSN (insn))
            {
            {
              if (INSN_P (insn))
              if (INSN_P (insn))
                PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
                PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
                                              hard_frame_pointer_rtx,
                                              hard_frame_pointer_rtx,
                                              stack_pointer_rtx);
                                              stack_pointer_rtx);
            }
            }
        }
        }
      else
      else
        emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
        emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
    }
    }
}
}
 
 
 
 
/* Clear variables at function end.  */
/* Clear variables at function end.  */
 
 
void
void
xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
                          HOST_WIDE_INT size ATTRIBUTE_UNUSED)
                          HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
{
  xtensa_current_frame_size = 0;
  xtensa_current_frame_size = 0;
}
}
 
 
 
 
rtx
rtx
xtensa_return_addr (int count, rtx frame)
xtensa_return_addr (int count, rtx frame)
{
{
  rtx result, retaddr;
  rtx result, retaddr;
 
 
  if (count == -1)
  if (count == -1)
    retaddr = gen_rtx_REG (Pmode, A0_REG);
    retaddr = gen_rtx_REG (Pmode, A0_REG);
  else
  else
    {
    {
      rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
      rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
      addr = memory_address (Pmode, addr);
      addr = memory_address (Pmode, addr);
      retaddr = gen_reg_rtx (Pmode);
      retaddr = gen_reg_rtx (Pmode);
      emit_move_insn (retaddr, gen_rtx_MEM (Pmode, addr));
      emit_move_insn (retaddr, gen_rtx_MEM (Pmode, addr));
    }
    }
 
 
  /* The 2 most-significant bits of the return address on Xtensa hold
  /* The 2 most-significant bits of the return address on Xtensa hold
     the register window size.  To get the real return address, these
     the register window size.  To get the real return address, these
     bits must be replaced with the high bits from the current PC.  */
     bits must be replaced with the high bits from the current PC.  */
 
 
  result = gen_reg_rtx (Pmode);
  result = gen_reg_rtx (Pmode);
  emit_insn (gen_fix_return_addr (result, retaddr));
  emit_insn (gen_fix_return_addr (result, retaddr));
  return result;
  return result;
}
}
 
 
 
 
/* Create the va_list data type.
/* Create the va_list data type.
 
 
   This structure is set up by __builtin_saveregs.  The __va_reg field
   This structure is set up by __builtin_saveregs.  The __va_reg field
   points to a stack-allocated region holding the contents of the
   points to a stack-allocated region holding the contents of the
   incoming argument registers.  The __va_ndx field is an index
   incoming argument registers.  The __va_ndx field is an index
   initialized to the position of the first unnamed (variable)
   initialized to the position of the first unnamed (variable)
   argument.  This same index is also used to address the arguments
   argument.  This same index is also used to address the arguments
   passed in memory.  Thus, the __va_stk field is initialized to point
   passed in memory.  Thus, the __va_stk field is initialized to point
   to the position of the first argument in memory offset to account
   to the position of the first argument in memory offset to account
   for the arguments passed in registers and to account for the size
   for the arguments passed in registers and to account for the size
   of the argument registers not being 16-byte aligned.  E.G., there
   of the argument registers not being 16-byte aligned.  E.G., there
   are 6 argument registers of 4 bytes each, but we want the __va_ndx
   are 6 argument registers of 4 bytes each, but we want the __va_ndx
   for the first stack argument to have the maximal alignment of 16
   for the first stack argument to have the maximal alignment of 16
   bytes, so we offset the __va_stk address by 32 bytes so that
   bytes, so we offset the __va_stk address by 32 bytes so that
   __va_stk[32] references the first argument on the stack.  */
   __va_stk[32] references the first argument on the stack.  */
 
 
static tree
static tree
xtensa_build_builtin_va_list (void)
xtensa_build_builtin_va_list (void)
{
{
  tree f_stk, f_reg, f_ndx, record, type_decl;
  tree f_stk, f_reg, f_ndx, record, type_decl;
 
 
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
  type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
  type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
 
 
  f_stk = build_decl (FIELD_DECL, get_identifier ("__va_stk"),
  f_stk = build_decl (FIELD_DECL, get_identifier ("__va_stk"),
                      ptr_type_node);
                      ptr_type_node);
  f_reg = build_decl (FIELD_DECL, get_identifier ("__va_reg"),
  f_reg = build_decl (FIELD_DECL, get_identifier ("__va_reg"),
                      ptr_type_node);
                      ptr_type_node);
  f_ndx = build_decl (FIELD_DECL, get_identifier ("__va_ndx"),
  f_ndx = build_decl (FIELD_DECL, get_identifier ("__va_ndx"),
                      integer_type_node);
                      integer_type_node);
 
 
  DECL_FIELD_CONTEXT (f_stk) = record;
  DECL_FIELD_CONTEXT (f_stk) = record;
  DECL_FIELD_CONTEXT (f_reg) = record;
  DECL_FIELD_CONTEXT (f_reg) = record;
  DECL_FIELD_CONTEXT (f_ndx) = record;
  DECL_FIELD_CONTEXT (f_ndx) = record;
 
 
  TREE_CHAIN (record) = type_decl;
  TREE_CHAIN (record) = type_decl;
  TYPE_NAME (record) = type_decl;
  TYPE_NAME (record) = type_decl;
  TYPE_FIELDS (record) = f_stk;
  TYPE_FIELDS (record) = f_stk;
  TREE_CHAIN (f_stk) = f_reg;
  TREE_CHAIN (f_stk) = f_reg;
  TREE_CHAIN (f_reg) = f_ndx;
  TREE_CHAIN (f_reg) = f_ndx;
 
 
  layout_type (record);
  layout_type (record);
  return record;
  return record;
}
}
 
 
 
 
/* Save the incoming argument registers on the stack.  Returns the
/* Save the incoming argument registers on the stack.  Returns the
   address of the saved registers.  */
   address of the saved registers.  */
 
 
static rtx
static rtx
xtensa_builtin_saveregs (void)
xtensa_builtin_saveregs (void)
{
{
  rtx gp_regs, dest;
  rtx gp_regs, dest;
  int arg_words = current_function_args_info.arg_words;
  int arg_words = current_function_args_info.arg_words;
  int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
  int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
 
 
  if (gp_left <= 0)
  if (gp_left <= 0)
    return const0_rtx;
    return const0_rtx;
 
 
  /* Allocate the general-purpose register space.  */
  /* Allocate the general-purpose register space.  */
  gp_regs = assign_stack_local
  gp_regs = assign_stack_local
    (BLKmode, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1);
    (BLKmode, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1);
  set_mem_alias_set (gp_regs, get_varargs_alias_set ());
  set_mem_alias_set (gp_regs, get_varargs_alias_set ());
 
 
  /* Now store the incoming registers.  */
  /* Now store the incoming registers.  */
  dest = change_address (gp_regs, SImode,
  dest = change_address (gp_regs, SImode,
                         plus_constant (XEXP (gp_regs, 0),
                         plus_constant (XEXP (gp_regs, 0),
                                        arg_words * UNITS_PER_WORD));
                                        arg_words * UNITS_PER_WORD));
  cfun->machine->need_a7_copy = true;
  cfun->machine->need_a7_copy = true;
  cfun->machine->vararg_a7 = true;
  cfun->machine->vararg_a7 = true;
  move_block_from_reg (GP_ARG_FIRST + arg_words, dest, gp_left);
  move_block_from_reg (GP_ARG_FIRST + arg_words, dest, gp_left);
  gcc_assert (cfun->machine->vararg_a7_copy != 0);
  gcc_assert (cfun->machine->vararg_a7_copy != 0);
  emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ());
  emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ());
 
 
  return XEXP (gp_regs, 0);
  return XEXP (gp_regs, 0);
}
}
 
 
 
 
/* Implement `va_start' for varargs and stdarg.  We look at the
/* Implement `va_start' for varargs and stdarg.  We look at the
   current function to fill in an initial va_list.  */
   current function to fill in an initial va_list.  */
 
 
void
void
xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
{
{
  tree f_stk, stk;
  tree f_stk, stk;
  tree f_reg, reg;
  tree f_reg, reg;
  tree f_ndx, ndx;
  tree f_ndx, ndx;
  tree t, u;
  tree t, u;
  int arg_words;
  int arg_words;
 
 
  arg_words = current_function_args_info.arg_words;
  arg_words = current_function_args_info.arg_words;
 
 
  f_stk = TYPE_FIELDS (va_list_type_node);
  f_stk = TYPE_FIELDS (va_list_type_node);
  f_reg = TREE_CHAIN (f_stk);
  f_reg = TREE_CHAIN (f_stk);
  f_ndx = TREE_CHAIN (f_reg);
  f_ndx = TREE_CHAIN (f_reg);
 
 
  stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
  stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
 
 
  /* Call __builtin_saveregs; save the result in __va_reg */
  /* Call __builtin_saveregs; save the result in __va_reg */
  u = make_tree (ptr_type_node, expand_builtin_saveregs ());
  u = make_tree (ptr_type_node, expand_builtin_saveregs ());
  t = build2 (MODIFY_EXPR, ptr_type_node, reg, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, reg, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  /* Set the __va_stk member to ($arg_ptr - 32).  */
  /* Set the __va_stk member to ($arg_ptr - 32).  */
  u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
  u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
  u = fold_build2 (PLUS_EXPR, ptr_type_node, u,
  u = fold_build2 (PLUS_EXPR, ptr_type_node, u,
                   build_int_cst (NULL_TREE, -32));
                   build_int_cst (NULL_TREE, -32));
  t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
 
  /* Set the __va_ndx member.  If the first variable argument is on
  /* Set the __va_ndx member.  If the first variable argument is on
     the stack, adjust __va_ndx by 2 words to account for the extra
     the stack, adjust __va_ndx by 2 words to account for the extra
     alignment offset for __va_stk.  */
     alignment offset for __va_stk.  */
  if (arg_words >= MAX_ARGS_IN_REGISTERS)
  if (arg_words >= MAX_ARGS_IN_REGISTERS)
    arg_words += 2;
    arg_words += 2;
  u = build_int_cst (NULL_TREE, arg_words * UNITS_PER_WORD);
  u = build_int_cst (NULL_TREE, arg_words * UNITS_PER_WORD);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, u);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, u);
  TREE_SIDE_EFFECTS (t) = 1;
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
}
 
 
 
 
/* Implement `va_arg'.  */
/* Implement `va_arg'.  */
 
 
static tree
static tree
xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
                             tree *post_p ATTRIBUTE_UNUSED)
                             tree *post_p ATTRIBUTE_UNUSED)
{
{
  tree f_stk, stk;
  tree f_stk, stk;
  tree f_reg, reg;
  tree f_reg, reg;
  tree f_ndx, ndx;
  tree f_ndx, ndx;
  tree type_size, array, orig_ndx, addr, size, va_size, t;
  tree type_size, array, orig_ndx, addr, size, va_size, t;
  tree lab_false, lab_over, lab_false2;
  tree lab_false, lab_over, lab_false2;
  bool indirect;
  bool indirect;
 
 
  indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
  indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
  if (indirect)
  if (indirect)
    type = build_pointer_type (type);
    type = build_pointer_type (type);
 
 
  /* Handle complex values as separate real and imaginary parts.  */
  /* Handle complex values as separate real and imaginary parts.  */
  if (TREE_CODE (type) == COMPLEX_TYPE)
  if (TREE_CODE (type) == COMPLEX_TYPE)
    {
    {
      tree real_part, imag_part;
      tree real_part, imag_part;
 
 
      real_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
      real_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
                                               pre_p, NULL);
                                               pre_p, NULL);
      real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
      real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
 
 
      imag_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
      imag_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
                                               pre_p, NULL);
                                               pre_p, NULL);
      imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
      imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
 
 
      return build2 (COMPLEX_EXPR, type, real_part, imag_part);
      return build2 (COMPLEX_EXPR, type, real_part, imag_part);
    }
    }
 
 
  f_stk = TYPE_FIELDS (va_list_type_node);
  f_stk = TYPE_FIELDS (va_list_type_node);
  f_reg = TREE_CHAIN (f_stk);
  f_reg = TREE_CHAIN (f_stk);
  f_ndx = TREE_CHAIN (f_reg);
  f_ndx = TREE_CHAIN (f_reg);
 
 
  stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
  stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
 
 
  type_size = size_in_bytes (type);
  type_size = size_in_bytes (type);
  va_size = round_up (type_size, UNITS_PER_WORD);
  va_size = round_up (type_size, UNITS_PER_WORD);
  gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
  gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
 
 
 
 
  /* First align __va_ndx if necessary for this arg:
  /* First align __va_ndx if necessary for this arg:
 
 
     orig_ndx = (AP).__va_ndx;
     orig_ndx = (AP).__va_ndx;
     if (__alignof__ (TYPE) > 4 )
     if (__alignof__ (TYPE) > 4 )
       orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
       orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
                        & -__alignof__ (TYPE)); */
                        & -__alignof__ (TYPE)); */
 
 
  orig_ndx = get_initialized_tmp_var (ndx, pre_p, NULL);
  orig_ndx = get_initialized_tmp_var (ndx, pre_p, NULL);
 
 
  if (TYPE_ALIGN (type) > BITS_PER_WORD)
  if (TYPE_ALIGN (type) > BITS_PER_WORD)
    {
    {
      int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_UNIT;
      int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_UNIT;
 
 
      t = build2 (PLUS_EXPR, integer_type_node, orig_ndx,
      t = build2 (PLUS_EXPR, integer_type_node, orig_ndx,
                  build_int_cst (NULL_TREE, align - 1));
                  build_int_cst (NULL_TREE, align - 1));
      t = build2 (BIT_AND_EXPR, integer_type_node, t,
      t = build2 (BIT_AND_EXPR, integer_type_node, t,
                  build_int_cst (NULL_TREE, -align));
                  build_int_cst (NULL_TREE, -align));
      t = build2 (MODIFY_EXPR, integer_type_node, orig_ndx, t);
      t = build2 (MODIFY_EXPR, integer_type_node, orig_ndx, t);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
    }
    }
 
 
 
 
  /* Increment __va_ndx to point past the argument:
  /* Increment __va_ndx to point past the argument:
 
 
     (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
     (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
 
 
  t = fold_convert (integer_type_node, va_size);
  t = fold_convert (integer_type_node, va_size);
  t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, t);
  t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, t);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
  gimplify_and_add (t, pre_p);
  gimplify_and_add (t, pre_p);
 
 
 
 
  /* Check if the argument is in registers:
  /* Check if the argument is in registers:
 
 
     if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
     if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
         && !must_pass_in_stack (type))
         && !must_pass_in_stack (type))
        __array = (AP).__va_reg; */
        __array = (AP).__va_reg; */
 
 
  array = create_tmp_var (ptr_type_node, NULL);
  array = create_tmp_var (ptr_type_node, NULL);
 
 
  lab_over = NULL;
  lab_over = NULL;
  if (!targetm.calls.must_pass_in_stack (TYPE_MODE (type), type))
  if (!targetm.calls.must_pass_in_stack (TYPE_MODE (type), type))
    {
    {
      lab_false = create_artificial_label ();
      lab_false = create_artificial_label ();
      lab_over = create_artificial_label ();
      lab_over = create_artificial_label ();
 
 
      t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
      t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
      t = build2 (GT_EXPR, boolean_type_node, ndx, t);
      t = build2 (GT_EXPR, boolean_type_node, ndx, t);
      t = build3 (COND_EXPR, void_type_node, t,
      t = build3 (COND_EXPR, void_type_node, t,
                  build1 (GOTO_EXPR, void_type_node, lab_false),
                  build1 (GOTO_EXPR, void_type_node, lab_false),
                  NULL_TREE);
                  NULL_TREE);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
 
 
      t = build2 (MODIFY_EXPR, void_type_node, array, reg);
      t = build2 (MODIFY_EXPR, void_type_node, array, reg);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
 
 
      t = build1 (GOTO_EXPR, void_type_node, lab_over);
      t = build1 (GOTO_EXPR, void_type_node, lab_over);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
 
 
      t = build1 (LABEL_EXPR, void_type_node, lab_false);
      t = build1 (LABEL_EXPR, void_type_node, lab_false);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
    }
    }
 
 
 
 
  /* ...otherwise, the argument is on the stack (never split between
  /* ...otherwise, the argument is on the stack (never split between
     registers and the stack -- change __va_ndx if necessary):
     registers and the stack -- change __va_ndx if necessary):
 
 
     else
     else
       {
       {
         if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
         if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
             (AP).__va_ndx = 32 + __va_size (TYPE);
             (AP).__va_ndx = 32 + __va_size (TYPE);
         __array = (AP).__va_stk;
         __array = (AP).__va_stk;
       } */
       } */
 
 
  lab_false2 = create_artificial_label ();
  lab_false2 = create_artificial_label ();
 
 
  t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
  t = build_int_cst (NULL_TREE, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD);
  t = build2 (GT_EXPR, boolean_type_node, orig_ndx, t);
  t = build2 (GT_EXPR, boolean_type_node, orig_ndx, t);
  t = build3 (COND_EXPR, void_type_node, t,
  t = build3 (COND_EXPR, void_type_node, t,
              build1 (GOTO_EXPR, void_type_node, lab_false2),
              build1 (GOTO_EXPR, void_type_node, lab_false2),
              NULL_TREE);
              NULL_TREE);
  gimplify_and_add (t, pre_p);
  gimplify_and_add (t, pre_p);
 
 
  t = size_binop (PLUS_EXPR, va_size, size_int (32));
  t = size_binop (PLUS_EXPR, va_size, size_int (32));
  t = fold_convert (integer_type_node, t);
  t = fold_convert (integer_type_node, t);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
  t = build2 (MODIFY_EXPR, integer_type_node, ndx, t);
  gimplify_and_add (t, pre_p);
  gimplify_and_add (t, pre_p);
 
 
  t = build1 (LABEL_EXPR, void_type_node, lab_false2);
  t = build1 (LABEL_EXPR, void_type_node, lab_false2);
  gimplify_and_add (t, pre_p);
  gimplify_and_add (t, pre_p);
 
 
  t = build2 (MODIFY_EXPR, void_type_node, array, stk);
  t = build2 (MODIFY_EXPR, void_type_node, array, stk);
  gimplify_and_add (t, pre_p);
  gimplify_and_add (t, pre_p);
 
 
  if (lab_over)
  if (lab_over)
    {
    {
      t = build1 (LABEL_EXPR, void_type_node, lab_over);
      t = build1 (LABEL_EXPR, void_type_node, lab_over);
      gimplify_and_add (t, pre_p);
      gimplify_and_add (t, pre_p);
    }
    }
 
 
 
 
  /* Given the base array pointer (__array) and index to the subsequent
  /* Given the base array pointer (__array) and index to the subsequent
     argument (__va_ndx), find the address:
     argument (__va_ndx), find the address:
 
 
     __array + (AP).__va_ndx - (BYTES_BIG_ENDIAN && sizeof (TYPE) < 4
     __array + (AP).__va_ndx - (BYTES_BIG_ENDIAN && sizeof (TYPE) < 4
                                ? sizeof (TYPE)
                                ? sizeof (TYPE)
                                : __va_size (TYPE))
                                : __va_size (TYPE))
 
 
     The results are endian-dependent because values smaller than one word
     The results are endian-dependent because values smaller than one word
     are aligned differently.  */
     are aligned differently.  */
 
 
 
 
  if (BYTES_BIG_ENDIAN && TREE_CODE (type_size) == INTEGER_CST)
  if (BYTES_BIG_ENDIAN && TREE_CODE (type_size) == INTEGER_CST)
    {
    {
      t = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
      t = size_int (PARM_BOUNDARY / BITS_PER_UNIT);
      t = fold_build2 (GE_EXPR, boolean_type_node, type_size, t);
      t = fold_build2 (GE_EXPR, boolean_type_node, type_size, t);
      t = fold_build3 (COND_EXPR, sizetype, t, va_size, type_size);
      t = fold_build3 (COND_EXPR, sizetype, t, va_size, type_size);
      size = t;
      size = t;
    }
    }
  else
  else
    size = va_size;
    size = va_size;
 
 
  t = fold_convert (ptr_type_node, ndx);
  t = fold_convert (ptr_type_node, ndx);
  addr = build2 (PLUS_EXPR, ptr_type_node, array, t);
  addr = build2 (PLUS_EXPR, ptr_type_node, array, t);
  t = fold_convert (ptr_type_node, size);
  t = fold_convert (ptr_type_node, size);
  addr = build2 (MINUS_EXPR, ptr_type_node, addr, t);
  addr = build2 (MINUS_EXPR, ptr_type_node, addr, t);
 
 
  addr = fold_convert (build_pointer_type (type), addr);
  addr = fold_convert (build_pointer_type (type), addr);
  if (indirect)
  if (indirect)
    addr = build_va_arg_indirect_ref (addr);
    addr = build_va_arg_indirect_ref (addr);
  return build_va_arg_indirect_ref (addr);
  return build_va_arg_indirect_ref (addr);
}
}
 
 
 
 
enum reg_class
enum reg_class
xtensa_preferred_reload_class (rtx x, enum reg_class class, int isoutput)
xtensa_preferred_reload_class (rtx x, enum reg_class class, int isoutput)
{
{
  if (!isoutput && CONSTANT_P (x) && GET_CODE (x) == CONST_DOUBLE)
  if (!isoutput && CONSTANT_P (x) && GET_CODE (x) == CONST_DOUBLE)
    return NO_REGS;
    return NO_REGS;
 
 
  /* Don't use the stack pointer or hard frame pointer for reloads!
  /* Don't use the stack pointer or hard frame pointer for reloads!
     The hard frame pointer would normally be OK except that it may
     The hard frame pointer would normally be OK except that it may
     briefly hold an incoming argument in the prologue, and reload
     briefly hold an incoming argument in the prologue, and reload
     won't know that it is live because the hard frame pointer is
     won't know that it is live because the hard frame pointer is
     treated specially.  */
     treated specially.  */
 
 
  if (class == AR_REGS || class == GR_REGS)
  if (class == AR_REGS || class == GR_REGS)
    return RL_REGS;
    return RL_REGS;
 
 
  return class;
  return class;
}
}
 
 
 
 
enum reg_class
enum reg_class
xtensa_secondary_reload_class (enum reg_class class,
xtensa_secondary_reload_class (enum reg_class class,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               rtx x, int isoutput)
                               rtx x, int isoutput)
{
{
  int regno;
  int regno;
 
 
  if (GET_CODE (x) == SIGN_EXTEND)
  if (GET_CODE (x) == SIGN_EXTEND)
    x = XEXP (x, 0);
    x = XEXP (x, 0);
  regno = xt_true_regnum (x);
  regno = xt_true_regnum (x);
 
 
  if (!isoutput)
  if (!isoutput)
    {
    {
      if (class == FP_REGS && constantpool_mem_p (x))
      if (class == FP_REGS && constantpool_mem_p (x))
        return RL_REGS;
        return RL_REGS;
    }
    }
 
 
  if (ACC_REG_P (regno))
  if (ACC_REG_P (regno))
    return ((class == GR_REGS || class == RL_REGS) ? NO_REGS : RL_REGS);
    return ((class == GR_REGS || class == RL_REGS) ? NO_REGS : RL_REGS);
  if (class == ACC_REG)
  if (class == ACC_REG)
    return (GP_REG_P (regno) ? NO_REGS : RL_REGS);
    return (GP_REG_P (regno) ? NO_REGS : RL_REGS);
 
 
  return NO_REGS;
  return NO_REGS;
}
}
 
 
 
 
void
void
order_regs_for_local_alloc (void)
order_regs_for_local_alloc (void)
{
{
  if (!leaf_function_p ())
  if (!leaf_function_p ())
    {
    {
      memcpy (reg_alloc_order, reg_nonleaf_alloc_order,
      memcpy (reg_alloc_order, reg_nonleaf_alloc_order,
              FIRST_PSEUDO_REGISTER * sizeof (int));
              FIRST_PSEUDO_REGISTER * sizeof (int));
    }
    }
  else
  else
    {
    {
      int i, num_arg_regs;
      int i, num_arg_regs;
      int nxt = 0;
      int nxt = 0;
 
 
      /* Use the AR registers in increasing order (skipping a0 and a1)
      /* Use the AR registers in increasing order (skipping a0 and a1)
         but save the incoming argument registers for a last resort.  */
         but save the incoming argument registers for a last resort.  */
      num_arg_regs = current_function_args_info.arg_words;
      num_arg_regs = current_function_args_info.arg_words;
      if (num_arg_regs > MAX_ARGS_IN_REGISTERS)
      if (num_arg_regs > MAX_ARGS_IN_REGISTERS)
        num_arg_regs = MAX_ARGS_IN_REGISTERS;
        num_arg_regs = MAX_ARGS_IN_REGISTERS;
      for (i = GP_ARG_FIRST; i < 16 - num_arg_regs; i++)
      for (i = GP_ARG_FIRST; i < 16 - num_arg_regs; i++)
        reg_alloc_order[nxt++] = i + num_arg_regs;
        reg_alloc_order[nxt++] = i + num_arg_regs;
      for (i = 0; i < num_arg_regs; i++)
      for (i = 0; i < num_arg_regs; i++)
        reg_alloc_order[nxt++] = GP_ARG_FIRST + i;
        reg_alloc_order[nxt++] = GP_ARG_FIRST + i;
 
 
      /* List the coprocessor registers in order.  */
      /* List the coprocessor registers in order.  */
      for (i = 0; i < BR_REG_NUM; i++)
      for (i = 0; i < BR_REG_NUM; i++)
        reg_alloc_order[nxt++] = BR_REG_FIRST + i;
        reg_alloc_order[nxt++] = BR_REG_FIRST + i;
 
 
      /* List the FP registers in order for now.  */
      /* List the FP registers in order for now.  */
      for (i = 0; i < 16; i++)
      for (i = 0; i < 16; i++)
        reg_alloc_order[nxt++] = FP_REG_FIRST + i;
        reg_alloc_order[nxt++] = FP_REG_FIRST + i;
 
 
      /* GCC requires that we list *all* the registers....  */
      /* GCC requires that we list *all* the registers....  */
      reg_alloc_order[nxt++] = 0;        /* a0 = return address */
      reg_alloc_order[nxt++] = 0;        /* a0 = return address */
      reg_alloc_order[nxt++] = 1;       /* a1 = stack pointer */
      reg_alloc_order[nxt++] = 1;       /* a1 = stack pointer */
      reg_alloc_order[nxt++] = 16;      /* pseudo frame pointer */
      reg_alloc_order[nxt++] = 16;      /* pseudo frame pointer */
      reg_alloc_order[nxt++] = 17;      /* pseudo arg pointer */
      reg_alloc_order[nxt++] = 17;      /* pseudo arg pointer */
 
 
      reg_alloc_order[nxt++] = ACC_REG_FIRST;   /* MAC16 accumulator */
      reg_alloc_order[nxt++] = ACC_REG_FIRST;   /* MAC16 accumulator */
    }
    }
}
}
 
 
 
 
/* Some Xtensa targets support multiple bss sections.  If the section
/* Some Xtensa targets support multiple bss sections.  If the section
   name ends with ".bss", add SECTION_BSS to the flags.  */
   name ends with ".bss", add SECTION_BSS to the flags.  */
 
 
static unsigned int
static unsigned int
xtensa_multibss_section_type_flags (tree decl, const char *name, int reloc)
xtensa_multibss_section_type_flags (tree decl, const char *name, int reloc)
{
{
  unsigned int flags = default_section_type_flags (decl, name, reloc);
  unsigned int flags = default_section_type_flags (decl, name, reloc);
  const char *suffix;
  const char *suffix;
 
 
  suffix = strrchr (name, '.');
  suffix = strrchr (name, '.');
  if (suffix && strcmp (suffix, ".bss") == 0)
  if (suffix && strcmp (suffix, ".bss") == 0)
    {
    {
      if (!decl || (TREE_CODE (decl) == VAR_DECL
      if (!decl || (TREE_CODE (decl) == VAR_DECL
                    && DECL_INITIAL (decl) == NULL_TREE))
                    && DECL_INITIAL (decl) == NULL_TREE))
        flags |= SECTION_BSS;  /* @nobits */
        flags |= SECTION_BSS;  /* @nobits */
      else
      else
        warning (0, "only uninitialized variables can be placed in a "
        warning (0, "only uninitialized variables can be placed in a "
                 ".bss section");
                 ".bss section");
    }
    }
 
 
  return flags;
  return flags;
}
}
 
 
 
 
/* The literal pool stays with the function.  */
/* The literal pool stays with the function.  */
 
 
static section *
static section *
xtensa_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
xtensa_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
                           rtx x ATTRIBUTE_UNUSED,
                           rtx x ATTRIBUTE_UNUSED,
                           unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
                           unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
{
  return function_section (current_function_decl);
  return function_section (current_function_decl);
}
}
 
 
 
 
/* Compute a (partial) cost for rtx X.  Return true if the complete
/* Compute a (partial) cost for rtx X.  Return true if the complete
   cost has been computed, and false if subexpressions should be
   cost has been computed, and false if subexpressions should be
   scanned.  In either case, *TOTAL contains the cost result.  */
   scanned.  In either case, *TOTAL contains the cost result.  */
 
 
static bool
static bool
xtensa_rtx_costs (rtx x, int code, int outer_code, int *total)
xtensa_rtx_costs (rtx x, int code, int outer_code, int *total)
{
{
  switch (code)
  switch (code)
    {
    {
    case CONST_INT:
    case CONST_INT:
      switch (outer_code)
      switch (outer_code)
        {
        {
        case SET:
        case SET:
          if (xtensa_simm12b (INTVAL (x)))
          if (xtensa_simm12b (INTVAL (x)))
            {
            {
              *total = 4;
              *total = 4;
              return true;
              return true;
            }
            }
          break;
          break;
        case PLUS:
        case PLUS:
          if (xtensa_simm8 (INTVAL (x))
          if (xtensa_simm8 (INTVAL (x))
              || xtensa_simm8x256 (INTVAL (x)))
              || xtensa_simm8x256 (INTVAL (x)))
            {
            {
              *total = 0;
              *total = 0;
              return true;
              return true;
            }
            }
          break;
          break;
        case AND:
        case AND:
          if (xtensa_mask_immediate (INTVAL (x)))
          if (xtensa_mask_immediate (INTVAL (x)))
            {
            {
              *total = 0;
              *total = 0;
              return true;
              return true;
            }
            }
          break;
          break;
        case COMPARE:
        case COMPARE:
          if ((INTVAL (x) == 0) || xtensa_b4const (INTVAL (x)))
          if ((INTVAL (x) == 0) || xtensa_b4const (INTVAL (x)))
            {
            {
              *total = 0;
              *total = 0;
              return true;
              return true;
            }
            }
          break;
          break;
        case ASHIFT:
        case ASHIFT:
        case ASHIFTRT:
        case ASHIFTRT:
        case LSHIFTRT:
        case LSHIFTRT:
        case ROTATE:
        case ROTATE:
        case ROTATERT:
        case ROTATERT:
          /* No way to tell if X is the 2nd operand so be conservative.  */
          /* No way to tell if X is the 2nd operand so be conservative.  */
        default: break;
        default: break;
        }
        }
      if (xtensa_simm12b (INTVAL (x)))
      if (xtensa_simm12b (INTVAL (x)))
        *total = 5;
        *total = 5;
      else if (TARGET_CONST16)
      else if (TARGET_CONST16)
        *total = COSTS_N_INSNS (2);
        *total = COSTS_N_INSNS (2);
      else
      else
        *total = 6;
        *total = 6;
      return true;
      return true;
 
 
    case CONST:
    case CONST:
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
      if (TARGET_CONST16)
      if (TARGET_CONST16)
        *total = COSTS_N_INSNS (2);
        *total = COSTS_N_INSNS (2);
      else
      else
        *total = 5;
        *total = 5;
      return true;
      return true;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      if (TARGET_CONST16)
      if (TARGET_CONST16)
        *total = COSTS_N_INSNS (4);
        *total = COSTS_N_INSNS (4);
      else
      else
        *total = 7;
        *total = 7;
      return true;
      return true;
 
 
    case MEM:
    case MEM:
      {
      {
        int num_words =
        int num_words =
          (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) ?  2 : 1;
          (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) ?  2 : 1;
 
 
        if (memory_address_p (GET_MODE (x), XEXP ((x), 0)))
        if (memory_address_p (GET_MODE (x), XEXP ((x), 0)))
          *total = COSTS_N_INSNS (num_words);
          *total = COSTS_N_INSNS (num_words);
        else
        else
          *total = COSTS_N_INSNS (2*num_words);
          *total = COSTS_N_INSNS (2*num_words);
        return true;
        return true;
      }
      }
 
 
    case FFS:
    case FFS:
      *total = COSTS_N_INSNS (TARGET_NSA ? 5 : 50);
      *total = COSTS_N_INSNS (TARGET_NSA ? 5 : 50);
      return true;
      return true;
 
 
    case NOT:
    case NOT:
      *total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 3 : 2);
      *total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 3 : 2);
      return true;
      return true;
 
 
    case AND:
    case AND:
    case IOR:
    case IOR:
    case XOR:
    case XOR:
      if (GET_MODE (x) == DImode)
      if (GET_MODE (x) == DImode)
        *total = COSTS_N_INSNS (2);
        *total = COSTS_N_INSNS (2);
      else
      else
        *total = COSTS_N_INSNS (1);
        *total = COSTS_N_INSNS (1);
      return true;
      return true;
 
 
    case ASHIFT:
    case ASHIFT:
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      if (GET_MODE (x) == DImode)
      if (GET_MODE (x) == DImode)
        *total = COSTS_N_INSNS (50);
        *total = COSTS_N_INSNS (50);
      else
      else
        *total = COSTS_N_INSNS (1);
        *total = COSTS_N_INSNS (1);
      return true;
      return true;
 
 
    case ABS:
    case ABS:
      {
      {
        enum machine_mode xmode = GET_MODE (x);
        enum machine_mode xmode = GET_MODE (x);
        if (xmode == SFmode)
        if (xmode == SFmode)
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
        else if (xmode == DFmode)
        else if (xmode == DFmode)
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        else
        else
          *total = COSTS_N_INSNS (4);
          *total = COSTS_N_INSNS (4);
        return true;
        return true;
      }
      }
 
 
    case PLUS:
    case PLUS:
    case MINUS:
    case MINUS:
      {
      {
        enum machine_mode xmode = GET_MODE (x);
        enum machine_mode xmode = GET_MODE (x);
        if (xmode == SFmode)
        if (xmode == SFmode)
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 1 : 50);
        else if (xmode == DFmode || xmode == DImode)
        else if (xmode == DFmode || xmode == DImode)
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        else
        else
          *total = COSTS_N_INSNS (1);
          *total = COSTS_N_INSNS (1);
        return true;
        return true;
      }
      }
 
 
    case NEG:
    case NEG:
      *total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 4 : 2);
      *total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 4 : 2);
      return true;
      return true;
 
 
    case MULT:
    case MULT:
      {
      {
        enum machine_mode xmode = GET_MODE (x);
        enum machine_mode xmode = GET_MODE (x);
        if (xmode == SFmode)
        if (xmode == SFmode)
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 4 : 50);
          *total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 4 : 50);
        else if (xmode == DFmode || xmode == DImode)
        else if (xmode == DFmode || xmode == DImode)
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        else if (TARGET_MUL32)
        else if (TARGET_MUL32)
          *total = COSTS_N_INSNS (4);
          *total = COSTS_N_INSNS (4);
        else if (TARGET_MAC16)
        else if (TARGET_MAC16)
          *total = COSTS_N_INSNS (16);
          *total = COSTS_N_INSNS (16);
        else if (TARGET_MUL16)
        else if (TARGET_MUL16)
          *total = COSTS_N_INSNS (12);
          *total = COSTS_N_INSNS (12);
        else
        else
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        return true;
        return true;
      }
      }
 
 
    case DIV:
    case DIV:
    case MOD:
    case MOD:
      {
      {
        enum machine_mode xmode = GET_MODE (x);
        enum machine_mode xmode = GET_MODE (x);
        if (xmode == SFmode)
        if (xmode == SFmode)
          {
          {
            *total = COSTS_N_INSNS (TARGET_HARD_FLOAT_DIV ? 8 : 50);
            *total = COSTS_N_INSNS (TARGET_HARD_FLOAT_DIV ? 8 : 50);
            return true;
            return true;
          }
          }
        else if (xmode == DFmode)
        else if (xmode == DFmode)
          {
          {
            *total = COSTS_N_INSNS (50);
            *total = COSTS_N_INSNS (50);
            return true;
            return true;
          }
          }
      }
      }
      /* Fall through.  */
      /* Fall through.  */
 
 
    case UDIV:
    case UDIV:
    case UMOD:
    case UMOD:
      {
      {
        enum machine_mode xmode = GET_MODE (x);
        enum machine_mode xmode = GET_MODE (x);
        if (xmode == DImode)
        if (xmode == DImode)
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        else if (TARGET_DIV32)
        else if (TARGET_DIV32)
          *total = COSTS_N_INSNS (32);
          *total = COSTS_N_INSNS (32);
        else
        else
          *total = COSTS_N_INSNS (50);
          *total = COSTS_N_INSNS (50);
        return true;
        return true;
      }
      }
 
 
    case SQRT:
    case SQRT:
      if (GET_MODE (x) == SFmode)
      if (GET_MODE (x) == SFmode)
        *total = COSTS_N_INSNS (TARGET_HARD_FLOAT_SQRT ? 8 : 50);
        *total = COSTS_N_INSNS (TARGET_HARD_FLOAT_SQRT ? 8 : 50);
      else
      else
        *total = COSTS_N_INSNS (50);
        *total = COSTS_N_INSNS (50);
      return true;
      return true;
 
 
    case SMIN:
    case SMIN:
    case UMIN:
    case UMIN:
    case SMAX:
    case SMAX:
    case UMAX:
    case UMAX:
      *total = COSTS_N_INSNS (TARGET_MINMAX ? 1 : 50);
      *total = COSTS_N_INSNS (TARGET_MINMAX ? 1 : 50);
      return true;
      return true;
 
 
    case SIGN_EXTRACT:
    case SIGN_EXTRACT:
    case SIGN_EXTEND:
    case SIGN_EXTEND:
      *total = COSTS_N_INSNS (TARGET_SEXT ? 1 : 2);
      *total = COSTS_N_INSNS (TARGET_SEXT ? 1 : 2);
      return true;
      return true;
 
 
    case ZERO_EXTRACT:
    case ZERO_EXTRACT:
    case ZERO_EXTEND:
    case ZERO_EXTEND:
      *total = COSTS_N_INSNS (1);
      *total = COSTS_N_INSNS (1);
      return true;
      return true;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
 
 
static bool
static bool
xtensa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
xtensa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
{
  return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)
  return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)
          > 4 * UNITS_PER_WORD);
          > 4 * UNITS_PER_WORD);
}
}
 
 
#include "gt-xtensa.h"
#include "gt-xtensa.h"
 
 

powered by: WebSVN 2.1.0

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