X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fconfig%2Fxtensa%2Fxtensa.c;h=50467b4602cf38c3229d7ca2c2a6631692375298;hb=506d7b686cfb5b66f2945219d8863233d7dac645;hp=cc5db918ecc95feaede866da528054ea22e5324f;hpb=18dbd95060c2ae1fa1316e6fe312eb6d440ebe16;p=gcc.git
diff --git a/gcc/config/xtensa/xtensa.c b/gcc/config/xtensa/xtensa.c
index cc5db918ecc..50467b4602c 100644
--- a/gcc/config/xtensa/xtensa.c
+++ b/gcc/config/xtensa/xtensa.c
@@ -1,12 +1,13 @@
/* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
- Copyright 2001,2002,2003 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ Free Software Foundation, Inc.
Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +16,8 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+. */
#include "config.h"
#include "system.h"
@@ -43,30 +43,33 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "function.h"
#include "toplev.h"
#include "optabs.h"
-#include "output.h"
#include "libfuncs.h"
#include "ggc.h"
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
+#include "gimple.h"
+#include "df.h"
+
/* Enumeration for all of the relational tests, so that we can build
arrays indexed by the test type, and not worry about the order
of EQ, NE, etc. */
-enum internal_test {
- ITEST_EQ,
- ITEST_NE,
- ITEST_GT,
- ITEST_GE,
- ITEST_LT,
- ITEST_LE,
- ITEST_GTU,
- ITEST_GEU,
- ITEST_LTU,
- ITEST_LEU,
- ITEST_MAX
- };
+enum internal_test
+{
+ ITEST_EQ,
+ ITEST_NE,
+ ITEST_GT,
+ ITEST_GE,
+ ITEST_LT,
+ ITEST_LE,
+ ITEST_GTU,
+ ITEST_GEU,
+ ITEST_LTU,
+ ITEST_LEU,
+ ITEST_MAX
+};
/* Cached operands, and operator to compare for use in set/branch on
condition codes. */
@@ -82,16 +85,17 @@ char xtensa_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
/* Current frame size calculated by compute_frame_size. */
unsigned xtensa_current_frame_size;
-/* Tables of ld/st opcode names for block moves */
-const char *xtensa_ld_opcodes[(int) MAX_MACHINE_MODE];
-const char *xtensa_st_opcodes[(int) MAX_MACHINE_MODE];
+/* Largest block move to handle in-line. */
#define LARGEST_MOVE_RATIO 15
/* Define the structure for the machine field in struct function. */
-struct machine_function GTY(())
+struct GTY(()) machine_function
{
int accesses_prev_frame;
- bool incoming_a7_copied;
+ bool need_a7_copy;
+ bool vararg_a7;
+ rtx vararg_a7_copy;
+ rtx set_frame_ptr_insn;
};
/* Vector, indexed by hard register number, which contains 1 for a
@@ -121,106 +125,36 @@ const enum reg_class xtensa_regno_to_class[FIRST_PSEUDO_REGISTER] =
ACC_REG,
};
-/* Map register constraint character to register class. */
-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,
-};
+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_float_relational (enum rtx_code, rtx, rtx);
+static rtx gen_conditional_move (rtx);
+static rtx fixup_subreg_mem (rtx);
+static struct machine_function * xtensa_init_machine_status (void);
+static rtx xtensa_legitimize_tls_address (rtx);
+static rtx xtensa_legitimize_address (rtx, rtx, enum machine_mode);
+static bool xtensa_return_in_msb (const_tree);
+static void printx (FILE *, signed int);
+static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
+static rtx xtensa_builtin_saveregs (void);
+static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
+ int) ATTRIBUTE_UNUSED;
+static section *xtensa_select_rtx_section (enum machine_mode, rtx,
+ unsigned HOST_WIDE_INT);
+static bool xtensa_rtx_costs (rtx, int, int, int *, bool);
+static tree xtensa_build_builtin_va_list (void);
+static bool xtensa_return_in_memory (const_tree, const_tree);
+static tree xtensa_gimplify_va_arg_expr (tree, tree, gimple_seq *,
+ gimple_seq *);
+static rtx xtensa_function_value (const_tree, const_tree, bool);
+static void xtensa_init_builtins (void);
+static tree xtensa_fold_builtin (tree, tree, bool);
+static rtx xtensa_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static void xtensa_va_start (tree, rtx);
-static int b4const_or_zero PARAMS ((int));
-static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
-static rtx gen_int_relational PARAMS ((enum rtx_code, rtx, rtx, int *));
-static rtx gen_float_relational PARAMS ((enum rtx_code, rtx, rtx));
-static rtx gen_conditional_move PARAMS ((rtx));
-static rtx fixup_subreg_mem PARAMS ((rtx x));
-static enum machine_mode xtensa_find_mode_for_size PARAMS ((unsigned));
-static struct machine_function * xtensa_init_machine_status PARAMS ((void));
-static void xtensa_reorg PARAMS ((void));
-static void printx PARAMS ((FILE *, signed int));
-static unsigned int xtensa_multibss_section_type_flags
- PARAMS ((tree, const char *, int));
-static void xtensa_select_rtx_section
- PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT));
-static bool xtensa_rtx_costs PARAMS ((rtx, int, int, int *));
-
-static rtx frame_size_const;
-static int current_function_arg_words;
static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
REG_ALLOC_ORDER;
-/* This macro generates the assembly code for function entry.
- FILE is a stdio stream to output the code to.
- SIZE is an int: how many units of temporary storage to allocate.
- Refer to the array 'regs_ever_live' to determine which registers
- to save; 'regs_ever_live[I]' is nonzero if register number I
- is ever used in the function. This macro is responsible for
- knowing which registers should not be saved even if used. */
-
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE xtensa_function_prologue
/* This macro generates the assembly code for function exit,
on machines that need it. If FUNCTION_EPILOGUE is not defined
@@ -239,29 +173,117 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION xtensa_select_rtx_section
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS xtensa_legitimize_address
+
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS xtensa_rtx_costs
#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_0
-
-#undef TARGET_MACHINE_DEPENDENT_REORG
-#define TARGET_MACHINE_DEPENDENT_REORG xtensa_reorg
+#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST xtensa_build_builtin_va_list
+
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START xtensa_va_start
+
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY xtensa_return_in_memory
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE xtensa_function_value
+#undef TARGET_SPLIT_COMPLEX_ARG
+#define TARGET_SPLIT_COMPLEX_ARG hook_bool_const_tree_true
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS xtensa_builtin_saveregs
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR xtensa_gimplify_va_arg_expr
+
+#undef TARGET_RETURN_IN_MSB
+#define TARGET_RETURN_IN_MSB xtensa_return_in_msb
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS xtensa_init_builtins
+#undef TARGET_FOLD_BUILTIN
+#define TARGET_FOLD_BUILTIN xtensa_fold_builtin
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN xtensa_expand_builtin
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD xtensa_secondary_reload
+
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS (TARGET_THREADPTR && HAVE_AS_TLS)
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM xtensa_tls_referenced_p
struct gcc_target targetm = TARGET_INITIALIZER;
+
+/* Functions to test Xtensa immediate operand validity. */
+
+bool
+xtensa_simm8 (HOST_WIDE_INT v)
+{
+ return v >= -128 && v <= 127;
+}
-/*
- * Functions to test Xtensa immediate operand validity.
- */
-int
-xtensa_b4constu (v)
- int v;
+bool
+xtensa_simm8x256 (HOST_WIDE_INT v)
+{
+ return (v & 255) == 0 && (v >= -32768 && v <= 32512);
+}
+
+
+bool
+xtensa_simm12b (HOST_WIDE_INT v)
+{
+ return v >= -2048 && v <= 2047;
+}
+
+
+static bool
+xtensa_uimm8 (HOST_WIDE_INT v)
+{
+ return v >= 0 && v <= 255;
+}
+
+
+static bool
+xtensa_uimm8x2 (HOST_WIDE_INT v)
+{
+ return (v & 1) == 0 && (v >= 0 && v <= 510);
+}
+
+
+static bool
+xtensa_uimm8x4 (HOST_WIDE_INT v)
+{
+ return (v & 3) == 0 && (v >= 0 && v <= 1020);
+}
+
+
+static bool
+xtensa_b4const (HOST_WIDE_INT v)
{
switch (v)
{
- case 32768:
- case 65536:
+ case -1:
+ case 1:
case 2:
case 3:
case 4:
@@ -276,40 +298,28 @@ xtensa_b4constu (v)
case 64:
case 128:
case 256:
- return 1;
+ return true;
}
- return 0;
+ return false;
}
-int
-xtensa_simm8x256 (v)
- int v;
-{
- return (v & 255) == 0 && (v >= -32768 && v <= 32512);
-}
-int
-xtensa_ai4const (v)
- int v;
+bool
+xtensa_b4const_or_zero (HOST_WIDE_INT v)
{
- return (v == -1 || (v >= 1 && v <= 15));
+ if (v == 0)
+ return true;
+ return xtensa_b4const (v);
}
-int
-xtensa_simm7 (v)
- int v;
-{
- return v >= -32 && v <= 95;
-}
-int
-xtensa_b4const (v)
- int v;
+bool
+xtensa_b4constu (HOST_WIDE_INT v)
{
switch (v)
{
- case -1:
- case 1:
+ case 32768:
+ case 65536:
case 2:
case 3:
case 4:
@@ -324,58 +334,28 @@ xtensa_b4const (v)
case 64:
case 128:
case 256:
- return 1;
+ return true;
}
- return 0;
-}
-
-int
-xtensa_simm8 (v)
- int v;
-{
- return v >= -128 && v <= 127;
-}
-
-int
-xtensa_tp7 (v)
- int v;
-{
- return (v >= 7 && v <= 22);
+ return false;
}
-int
-xtensa_lsi4x4 (v)
- int v;
-{
- return (v & 3) == 0 && (v >= 0 && v <= 60);
-}
-int
-xtensa_simm12b (v)
- int v;
-{
- return v >= -2048 && v <= 2047;
-}
-
-int
-xtensa_uimm8 (v)
- int v;
+bool
+xtensa_mask_immediate (HOST_WIDE_INT v)
{
- return v >= 0 && v <= 255;
-}
+#define MAX_MASK_SIZE 16
+ int mask_size;
-int
-xtensa_uimm8x2 (v)
- int v;
-{
- return (v & 1) == 0 && (v >= 0 && v <= 510);
-}
+ for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
+ {
+ if ((v & 1) == 0)
+ return false;
+ v = v >> 1;
+ if (v == 0)
+ return true;
+ }
-int
-xtensa_uimm8x4 (v)
- int v;
-{
- return (v & 3) == 0 && (v >= 0 && v <= 1020);
+ return false;
}
@@ -383,8 +363,7 @@ xtensa_uimm8x4 (v)
works even when reg_renumber is not initialized. */
int
-xt_true_regnum (x)
- rtx x;
+xt_true_regnum (rtx x)
{
if (GET_CODE (x) == REG)
{
@@ -407,64 +386,7 @@ xt_true_regnum (x)
int
-add_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- return (xtensa_simm8 (INTVAL (op)) ||
- xtensa_simm8x256 (INTVAL (op)));
-
- return register_operand (op, mode);
-}
-
-
-int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- return xtensa_simm8 (INTVAL (op));
-
- return register_operand (op, mode);
-}
-
-
-int
-nonimmed_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- /* We cannot use the standard nonimmediate_operand() predicate because
- it includes constant pool memory operands. */
-
- if (memory_operand (op, mode))
- return !constantpool_address_p (XEXP (op, 0));
-
- return register_operand (op, mode);
-}
-
-
-int
-mem_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- /* We cannot use the standard memory_operand() predicate because
- it includes constant pool memory operands. */
-
- if (memory_operand (op, mode))
- return !constantpool_address_p (XEXP (op, 0));
-
- return FALSE;
-}
-
-
-int
-xtensa_valid_move (mode, operands)
- 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
MAC16 accumulator doesn't count. */
@@ -493,158 +415,24 @@ xtensa_valid_move (mode, operands)
int
-mask_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- return xtensa_mask_immediate (INTVAL (op));
-
- return register_operand (op, mode);
-}
-
-
-int
-extui_fldsz_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return ((GET_CODE (op) == CONST_INT)
- && xtensa_mask_immediate ((1 << INTVAL (op)) - 1));
-}
-
-
-int
-sext_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (TARGET_SEXT)
- return nonimmed_operand (op, mode);
- return mem_operand (op, mode);
-}
-
-
-int
-sext_fldsz_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return ((GET_CODE (op) == CONST_INT) && xtensa_tp7 (INTVAL (op) - 1));
-}
-
-
-int
-lsbitnum_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (op) == CONST_INT)
- {
- return (BITS_BIG_ENDIAN
- ? (INTVAL (op) == BITS_PER_WORD-1)
- : (INTVAL (op) == 0));
- }
- return FALSE;
-}
-
-
-static int
-b4const_or_zero (v)
- int v;
-{
- if (v == 0)
- return TRUE;
- return xtensa_b4const (v);
-}
-
-
-int
-branch_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- return b4const_or_zero (INTVAL (op));
-
- return register_operand (op, mode);
-}
-
-
-int
-ubranch_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
- return xtensa_b4constu (INTVAL (op));
-
- return register_operand (op, mode);
-}
-
-
-int
-call_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if ((GET_CODE (op) == REG)
- && (op != arg_pointer_rtx)
- && ((REGNO (op) < FRAME_POINTER_REGNUM)
- || (REGNO (op) > LAST_VIRTUAL_REGISTER)))
- return TRUE;
-
- if (CONSTANT_ADDRESS_P (op))
- {
- /* Direct calls only allowed to static functions with PIC. */
- return (!flag_pic
- || (GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op)));
- }
-
- return FALSE;
-}
-
-
-int
-move_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return TRUE;
-
- /* Accept CONSTANT_P_RTX, since it will be gone by CSE1 and
- result in 0/1. */
- if (GET_CODE (op) == CONSTANT_P_RTX)
- return TRUE;
-
- if (GET_CODE (op) == CONST_INT)
- return xtensa_simm12b (INTVAL (op));
-
- if (GET_CODE (op) == MEM)
- return memory_address_p (mode, XEXP (op, 0));
-
- return FALSE;
-}
-
-
-int
-smalloffset_mem_p (op)
- rtx op;
+smalloffset_mem_p (rtx op)
{
if (GET_CODE (op) == MEM)
{
rtx addr = XEXP (op, 0);
if (GET_CODE (addr) == REG)
- return REG_OK_FOR_BASE_P (addr);
+ return BASE_REG_P (addr, 0);
if (GET_CODE (addr) == PLUS)
{
rtx offset = XEXP (addr, 0);
+ HOST_WIDE_INT val;
if (GET_CODE (offset) != CONST_INT)
offset = XEXP (addr, 1);
if (GET_CODE (offset) != CONST_INT)
return FALSE;
- return xtensa_lsi4x4 (INTVAL (offset));
+
+ val = INTVAL (offset);
+ return (val & 3) == 0 && (val >= 0 && val <= 60);
}
}
return FALSE;
@@ -652,18 +440,7 @@ smalloffset_mem_p (op)
int
-smalloffset_double_mem_p (op)
- rtx op;
-{
- if (!smalloffset_mem_p (op))
- return FALSE;
- return smalloffset_mem_p (adjust_address (op, GET_MODE (op), 4));
-}
-
-
-int
-constantpool_address_p (addr)
- rtx addr;
+constantpool_address_p (rtx addr)
{
rtx sym = addr;
@@ -671,12 +448,12 @@ constantpool_address_p (addr)
{
rtx offset;
- /* only handle (PLUS (SYM, OFFSET)) form */
+ /* Only handle (PLUS (SYM, OFFSET)) form. */
addr = XEXP (addr, 0);
if (GET_CODE (addr) != PLUS)
return FALSE;
- /* make sure the address is word aligned */
+ /* Make sure the address is word aligned. */
offset = XEXP (addr, 1);
if ((GET_CODE (offset) != CONST_INT)
|| ((INTVAL (offset) & 3) != 0))
@@ -693,83 +470,35 @@ constantpool_address_p (addr)
int
-constantpool_mem_p (op)
- rtx op;
+constantpool_mem_p (rtx op)
{
- if (GET_CODE (op) == MEM)
- return constantpool_address_p (XEXP (op, 0));
- return FALSE;
-}
-
-
-int
-non_const_move_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (register_operand (op, mode))
- return 1;
if (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) == MEM)
- return memory_address_p (mode, XEXP (op, 0));
+ return constantpool_address_p (XEXP (op, 0));
return FALSE;
}
-/* Accept the floating point constant 1 in the appropriate mode. */
+/* Return TRUE if X is a thread-local symbol. */
-int
-const_float_1_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+xtensa_tls_symbol_p (rtx x)
{
- REAL_VALUE_TYPE d;
- static REAL_VALUE_TYPE onedf;
- static REAL_VALUE_TYPE onesf;
- static int one_initialized;
+ if (! TARGET_HAVE_TLS)
+ return false;
- if ((GET_CODE (op) != CONST_DOUBLE)
- || (mode != GET_MODE (op))
- || (mode != DFmode && mode != SFmode))
- return FALSE;
-
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-
- if (! one_initialized)
- {
- onedf = REAL_VALUE_ATOF ("1.0", DFmode);
- onesf = REAL_VALUE_ATOF ("1.0", SFmode);
- one_initialized = TRUE;
- }
-
- if (mode == DFmode)
- return REAL_VALUES_EQUAL (d, onedf);
- else
- return REAL_VALUES_EQUAL (d, onesf);
-}
-
-
-int
-fpmem_offset_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (op) == CONST_INT)
- return xtensa_mem_offset (INTVAL (op), SFmode);
- return 0;
+ return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
}
void
-xtensa_extend_reg (dst, src)
- rtx dst;
- rtx src;
+xtensa_extend_reg (rtx dst, rtx src)
{
rtx temp = gen_reg_rtx (SImode);
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);
dst = simplify_gen_subreg (SImode, dst, GET_MODE (dst), 0);
@@ -778,118 +507,8 @@ xtensa_extend_reg (dst, src)
}
-void
-xtensa_load_constant (dst, src)
- rtx dst;
- rtx src;
-{
- enum machine_mode mode = GET_MODE (dst);
- src = force_const_mem (SImode, src);
-
- /* PC-relative loads are always SImode so we have to add a SUBREG if that
- is not the desired mode */
-
- if (mode != SImode)
- {
- if (register_operand (dst, mode))
- dst = simplify_gen_subreg (SImode, dst, mode, 0);
- else
- {
- src = force_reg (SImode, src);
- src = gen_lowpart_SUBREG (mode, src);
- }
- }
-
- emit_move_insn (dst, src);
-}
-
-
-int
-branch_operator (x, mode)
- rtx x;
- enum machine_mode mode;
-{
- if (GET_MODE (x) != mode)
- return FALSE;
-
- switch (GET_CODE (x))
- {
- case EQ:
- case NE:
- case LT:
- case GE:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-
-int
-ubranch_operator (x, mode)
- rtx x;
- enum machine_mode mode;
-{
- if (GET_MODE (x) != mode)
- return FALSE;
-
- switch (GET_CODE (x))
- {
- case LTU:
- case GEU:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-
-int
-boolean_operator (x, mode)
- rtx x;
- enum machine_mode mode;
-{
- if (GET_MODE (x) != mode)
- return FALSE;
-
- switch (GET_CODE (x))
- {
- case EQ:
- case NE:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-
-int
-xtensa_mask_immediate (v)
- int v;
-{
-#define MAX_MASK_SIZE 16
- int mask_size;
-
- for (mask_size = 1; mask_size <= MAX_MASK_SIZE; mask_size++)
- {
- if ((v & 1) == 0)
- return FALSE;
- v = v >> 1;
- if (v == 0)
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-int
-xtensa_mem_offset (v, mode)
- unsigned v;
- enum machine_mode mode;
+bool
+xtensa_mem_offset (unsigned v, enum machine_mode mode)
{
switch (mode)
{
@@ -899,8 +518,8 @@ xtensa_mem_offset (v, mode)
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
last piece requires a byte load/store. */
- return (xtensa_uimm8 (v) &&
- xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
+ return (xtensa_uimm8 (v)
+ && xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO));
case QImode:
return xtensa_uimm8 (v);
@@ -919,11 +538,10 @@ xtensa_mem_offset (v, mode)
}
-/* 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
-map_test_to_internal_test (test_code)
- enum rtx_code test_code;
+map_test_to_internal_test (enum rtx_code test_code)
{
enum internal_test test = ITEST_MAX;
@@ -950,15 +568,15 @@ map_test_to_internal_test (test_code)
the comparison expression. */
static rtx
-gen_int_relational (test_code, cmp0, cmp1, p_invert)
- enum rtx_code test_code; /* relational test (EQ, etc) */
- rtx cmp0; /* first operand to compare */
- rtx cmp1; /* second operand to compare */
- int *p_invert; /* whether branch needs to reverse its test */
+gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
+ rtx cmp0, /* first operand to compare */
+ rtx cmp1, /* second operand to compare */
+ 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 */
- int (*const_range_p) PARAMS ((int)); /* predicate function to check range */
+ bool (*const_range_p) (HOST_WIDE_INT); /* range check function */
int const_add; /* constant to add (convert LE -> LT) */
int reverse_regs; /* reverse registers in test */
int invert_const; /* != 0 if invert value if cmp1 is constant */
@@ -968,13 +586,13 @@ gen_int_relational (test_code, cmp0, cmp1, p_invert)
static struct cmp_info info[ (int)ITEST_MAX ] = {
- { EQ, b4const_or_zero, 0, 0, 0, 0, 0 }, /* EQ */
- { NE, b4const_or_zero, 0, 0, 0, 0, 0 }, /* NE */
+ { EQ, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* EQ */
+ { NE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* NE */
- { LT, b4const_or_zero, 1, 1, 1, 0, 0 }, /* GT */
- { GE, b4const_or_zero, 0, 0, 0, 0, 0 }, /* GE */
- { LT, b4const_or_zero, 0, 0, 0, 0, 0 }, /* LT */
- { GE, b4const_or_zero, 1, 1, 1, 0, 0 }, /* LE */
+ { LT, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 }, /* GT */
+ { GE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* GE */
+ { LT, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* LT */
+ { GE, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 }, /* LE */
{ LTU, xtensa_b4constu, 1, 1, 1, 0, 1 }, /* GTU */
{ GEU, xtensa_b4constu, 0, 0, 0, 0, 1 }, /* GEU */
@@ -987,8 +605,7 @@ gen_int_relational (test_code, cmp0, cmp1, p_invert)
struct cmp_info *p_info;
test = map_test_to_internal_test (test_code);
- if (test == ITEST_MAX)
- abort ();
+ gcc_assert (test != ITEST_MAX);
p_info = &info[ (int)test ];
@@ -1041,7 +658,7 @@ gen_int_relational (test_code, cmp0, cmp1, p_invert)
cmp1 = temp;
}
- return gen_rtx (p_info->test_code, VOIDmode, cmp0, cmp1);
+ return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);
}
@@ -1049,12 +666,11 @@ gen_int_relational (test_code, cmp0, cmp1, p_invert)
the comparison expression. */
static rtx
-gen_float_relational (test_code, cmp0, cmp1)
- enum rtx_code test_code; /* relational test (EQ, etc) */
- rtx cmp0; /* first operand to compare */
- rtx cmp1; /* second operand to compare */
+gen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
+ rtx cmp0, /* first operand to compare */
+ rtx cmp1 /* second operand to compare */)
{
- rtx (*gen_fn) PARAMS ((rtx, rtx, rtx));
+ rtx (*gen_fn) (rtx, rtx, rtx);
rtx brtmp;
int reverse_regs, invert;
@@ -1066,8 +682,18 @@ gen_float_relational (test_code, cmp0, cmp1)
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 GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break;
- default:
- fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+ case UNEQ: reverse_regs = 0; invert = 0; gen_fn = gen_suneq_sf; break;
+ case LTGT: reverse_regs = 0; invert = 1; gen_fn = gen_suneq_sf; break;
+ case UNLE: reverse_regs = 0; invert = 0; gen_fn = gen_sunle_sf; break;
+ case UNGT: reverse_regs = 1; invert = 0; gen_fn = gen_sunlt_sf; break;
+ case UNLT: reverse_regs = 0; invert = 0; gen_fn = gen_sunlt_sf; break;
+ case UNGE: reverse_regs = 1; invert = 0; gen_fn = gen_sunle_sf; break;
+ case UNORDERED:
+ reverse_regs = 0; invert = 0; gen_fn = gen_sunordered_sf; break;
+ case ORDERED:
+ reverse_regs = 0; invert = 1; gen_fn = gen_sunordered_sf; break;
+ default:
+ fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
}
@@ -1081,14 +707,12 @@ gen_float_relational (test_code, cmp0, cmp1)
brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM);
emit_insn (gen_fn (brtmp, cmp0, cmp1));
- return gen_rtx (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
+ return gen_rtx_fmt_ee (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);
}
void
-xtensa_expand_conditional_branch (operands, test_code)
- rtx *operands;
- enum rtx_code test_code;
+xtensa_expand_conditional_branch (rtx *operands, enum rtx_code test_code)
{
enum cmp_type type = branch_type;
rtx cmp0 = branch_cmp[0];
@@ -1101,7 +725,7 @@ xtensa_expand_conditional_branch (operands, test_code)
{
case CMP_DF:
default:
- fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+ fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
case CMP_SI:
invert = FALSE;
@@ -1110,7 +734,8 @@ xtensa_expand_conditional_branch (operands, test_code)
case CMP_SF:
if (!TARGET_HARD_FLOAT)
- fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+ fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode,
+ cmp0, cmp1));
invert = FALSE;
cmp = gen_float_relational (test_code, cmp0, cmp1);
break;
@@ -1135,8 +760,7 @@ xtensa_expand_conditional_branch (operands, test_code)
static rtx
-gen_conditional_move (cmp)
- rtx cmp;
+gen_conditional_move (rtx cmp)
{
enum rtx_code code = GET_CODE (cmp);
rtx op0 = branch_cmp[0];
@@ -1157,18 +781,18 @@ gen_conditional_move (cmp)
code = GE;
op1 = const0_rtx;
}
- cmp = gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
+ cmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
if (boolean_operator (cmp, VOIDmode))
{
- /* swap the operands to make const0 second */
+ /* Swap the operands to make const0 second. */
if (op0 == const0_rtx)
{
op0 = op1;
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)
{
op0 = expand_binop (SImode, sub_optab, op0, op1,
@@ -1178,7 +802,7 @@ gen_conditional_move (cmp)
}
else if (branch_operator (cmp, VOIDmode))
{
- /* swap the operands to make const0 second */
+ /* Swap the operands to make const0 second. */
if (op0 == const0_rtx)
{
op0 = op1;
@@ -1188,7 +812,7 @@ gen_conditional_move (cmp)
{
case LT: code = GE; break;
case GE: code = LT; break;
- default: abort ();
+ default: gcc_unreachable ();
}
}
@@ -1198,7 +822,7 @@ gen_conditional_move (cmp)
else
return 0;
- return gen_rtx (code, VOIDmode, op0, op1);
+ return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
}
if (TARGET_HARD_FLOAT && (branch_type == CMP_SF))
@@ -1209,12 +833,10 @@ gen_conditional_move (cmp)
int
-xtensa_expand_conditional_move (operands, isflt)
- rtx *operands;
- int isflt;
+xtensa_expand_conditional_move (rtx *operands, int isflt)
{
rtx cmp;
- rtx (*gen_fn) PARAMS ((rtx, rtx, rtx, rtx, rtx));
+ rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
if (!(cmp = gen_conditional_move (operands[1])))
return 0;
@@ -1235,13 +857,12 @@ xtensa_expand_conditional_move (operands, isflt)
int
-xtensa_expand_scc (operands)
- rtx *operands;
+xtensa_expand_scc (rtx *operands)
{
rtx dest = operands[0];
rtx cmp = operands[1];
rtx one_tmp, zero_tmp;
- rtx (*gen_fn) PARAMS ((rtx, rtx, rtx, rtx, rtx));
+ rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx);
if (!(cmp = gen_conditional_move (cmp)))
return 0;
@@ -1259,35 +880,118 @@ xtensa_expand_scc (operands)
}
-/* Emit insns to move operands[1] into operands[0].
+/* 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. */
+
+void
+xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
+{
+ switch (GET_CODE (operands[1]))
+ {
+ case REG:
+ operands[3] = gen_rtx_REG (mode, REGNO (operands[1]) + 1);
+ operands[2] = gen_rtx_REG (mode, REGNO (operands[1]));
+ break;
+
+ case MEM:
+ operands[3] = adjust_address (operands[1], mode, GET_MODE_SIZE (mode));
+ operands[2] = adjust_address (operands[1], mode, 0);
+ break;
+
+ case CONST_INT:
+ case CONST_DOUBLE:
+ split_double (operands[1], &operands[2], &operands[3]);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ switch (GET_CODE (operands[0]))
+ {
+ case REG:
+ operands[1] = gen_rtx_REG (mode, REGNO (operands[0]) + 1);
+ operands[0] = gen_rtx_REG (mode, REGNO (operands[0]));
+ break;
+
+ case MEM:
+ operands[1] = adjust_address (operands[0], mode, GET_MODE_SIZE (mode));
+ operands[0] = adjust_address (operands[0], mode, 0);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Emit insns to move operands[1] into operands[0].
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
normally. */
int
-xtensa_emit_move_sequence (operands, mode)
- rtx *operands;
- enum machine_mode mode;
+xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
{
- if (CONSTANT_P (operands[1])
- && GET_CODE (operands[1]) != CONSTANT_P_RTX
- && (GET_CODE (operands[1]) != CONST_INT
- || !xtensa_simm12b (INTVAL (operands[1]))))
- {
- xtensa_load_constant (operands[0], operands[1]);
- return 1;
- }
+ rtx src = operands[1];
- if (!(reload_in_progress | reload_completed))
+ if (CONSTANT_P (src)
+ && (GET_CODE (src) != CONST_INT || ! xtensa_simm12b (INTVAL (src))))
{
- if (!xtensa_valid_move (mode, operands))
- operands[1] = force_reg (mode, operands[1]);
+ rtx dst = operands[0];
+
+ if (xtensa_tls_referenced_p (src))
+ {
+ rtx addend = NULL;
+
+ if (GET_CODE (src) == CONST && GET_CODE (XEXP (src, 0)) == PLUS)
+ {
+ addend = XEXP (XEXP (src, 0), 1);
+ src = XEXP (XEXP (src, 0), 0);
+ }
+
+ src = xtensa_legitimize_tls_address (src);
+ if (addend)
+ {
+ src = gen_rtx_PLUS (mode, src, addend);
+ src = force_operand (src, dst);
+ }
+ emit_move_insn (dst, src);
+ return 1;
+ }
- if (xtensa_copy_incoming_a7 (operands, mode))
- return 1;
+ if (! TARGET_CONST16)
+ {
+ src = force_const_mem (SImode, src);
+ operands[1] = src;
+ }
+
+ /* PC-relative loads are always SImode, and CONST16 is only
+ supported in the movsi pattern, so add a SUBREG for any other
+ (smaller) mode. */
+
+ if (mode != SImode)
+ {
+ if (register_operand (dst, mode))
+ {
+ emit_move_insn (simplify_gen_subreg (SImode, dst, mode, 0), src);
+ return 1;
+ }
+ else
+ {
+ src = force_reg (SImode, src);
+ src = gen_lowpart_SUBREG (mode, src);
+ operands[1] = src;
+ }
+ }
}
+ if (!(reload_in_progress | reload_completed)
+ && !xtensa_valid_move (mode, operands))
+ operands[1] = force_reg (mode, operands[1]);
+
+ operands[1] = xtensa_copy_incoming_a7 (operands[1]);
+
/* 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
subreg and adjust mem accordingly. */
@@ -1299,9 +1003,9 @@ xtensa_emit_move_sequence (operands, mode)
return 0;
}
+
static rtx
-fixup_subreg_mem (x)
- rtx x;
+fixup_subreg_mem (rtx x)
{
if (GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
@@ -1317,78 +1021,128 @@ fixup_subreg_mem (x)
}
-/* Check if this move is copying an incoming argument in a7. If so,
- emit the move, followed by the special "set_frame_ptr"
- unspec_volatile insn, at the very beginning of the function. This
- is necessary because the register allocator will ignore conflicts
- with a7 and may assign some other pseudo to a7. If that pseudo was
- assigned prior to this move, it would clobber the incoming argument
- in a7. By copying the argument out of a7 as the very first thing,
- and then immediately following that with an unspec_volatile to keep
- the scheduler away, we should avoid any problems. */
+/* 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,
+ create a new pseudo and copy a7 into that pseudo at the very
+ beginning of the function, followed by the special "set_frame_ptr"
+ unspec_volatile insn. The return value is either the original
+ operand, if it is not a7, or the new pseudo containing a copy of
+ the incoming argument. This is necessary because the register
+ allocator will ignore conflicts with a7 and may either assign some
+ 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 very first thing, and then immediately following that with an
+ unspec_volatile to keep the scheduler away, we should avoid any
+ problems. Putting the set_frame_ptr insn at the beginning, with
+ 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
+ fix up the a7 copy to use the stack pointer instead of the frame
+ pointer. */
-bool
-xtensa_copy_incoming_a7 (operands, mode)
- rtx *operands;
- enum machine_mode mode;
+rtx
+xtensa_copy_incoming_a7 (rtx opnd)
{
- if (a7_overlap_mentioned_p (operands[1])
- && !cfun->machine->incoming_a7_copied)
+ rtx entry_insns = 0;
+ rtx reg, tmp;
+ enum machine_mode mode;
+
+ if (!cfun->machine->need_a7_copy)
+ return opnd;
+
+ /* This function should never be called again once a7 has been copied. */
+ gcc_assert (!cfun->machine->set_frame_ptr_insn);
+
+ mode = GET_MODE (opnd);
+
+ /* The operand using a7 may come in a later instruction, so just return
+ the original operand if it doesn't use a7. */
+ reg = opnd;
+ if (GET_CODE (reg) == SUBREG)
{
- rtx mov;
- switch (mode)
- {
- case DFmode:
- mov = gen_movdf_internal (operands[0], operands[1]);
- break;
- case SFmode:
- mov = gen_movsf_internal (operands[0], operands[1]);
- break;
- case DImode:
- mov = gen_movdi_internal (operands[0], operands[1]);
- break;
- case SImode:
- mov = gen_movsi_internal (operands[0], operands[1]);
- break;
- case HImode:
- mov = gen_movhi_internal (operands[0], operands[1]);
- break;
- case QImode:
- mov = gen_movqi_internal (operands[0], operands[1]);
- break;
- default:
- abort ();
- }
+ gcc_assert (SUBREG_BYTE (reg) == 0);
+ reg = SUBREG_REG (reg);
+ }
+ if (GET_CODE (reg) != REG
+ || REGNO (reg) > A7_REG
+ || REGNO (reg) + HARD_REGNO_NREGS (A7_REG, mode) <= A7_REG)
+ return opnd;
- /* Insert the instructions before any other argument copies.
- (The set_frame_ptr insn comes _after_ the move, so push it
- out first.) */
- push_topmost_sequence ();
- emit_insn_after (gen_set_frame_ptr (), get_insns ());
- emit_insn_after (mov, get_insns ());
- pop_topmost_sequence ();
+ /* 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);
+
+ cfun->machine->need_a7_copy = false;
+
+ /* 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. */
+
+ start_sequence ();
+ tmp = gen_reg_rtx (mode);
+
+ switch (mode)
+ {
+ case DFmode:
+ case DImode:
+ /* Copy the value out of A7 here but keep the first word in A6 until
+ after the set_frame_ptr insn. Otherwise, the register allocator
+ may decide to put "subreg (tmp, 0)" in A7 and clobber the incoming
+ value. */
+ emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 4),
+ gen_raw_REG (SImode, A7_REG)));
+ break;
+ case SFmode:
+ emit_insn (gen_movsf_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case SImode:
+ emit_insn (gen_movsi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case HImode:
+ emit_insn (gen_movhi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ case QImode:
+ emit_insn (gen_movqi_internal (tmp, gen_raw_REG (mode, A7_REG)));
+ break;
+ default:
+ gcc_unreachable ();
+ }
- /* Ideally the incoming argument in a7 would only be copied
- once, since propagating a7 into the body of a function
- will almost certainly lead to errors. However, there is
- at least one harmless case (in GCSE) where the original
- copy from a7 is changed to copy into a new pseudo. Thus,
- we use a flag to only do this special treatment for the
- first copy of a7. */
+ cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
- cfun->machine->incoming_a7_copied = true;
+ /* For DF and DI mode arguments, copy the incoming value in A6 now. */
+ if (mode == DFmode || mode == DImode)
+ emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
+ gen_rtx_REG (SImode, A7_REG - 1)));
+ entry_insns = get_insns ();
+ end_sequence ();
- return 1;
+ if (cfun->machine->vararg_a7)
+ {
+ /* This is called from within builtin_saveregs, which will insert the
+ saveregs code at the function entry, ahead of anything placed at
+ the function entry now. Instead, save the sequence to be inserted
+ at the beginning of the saveregs code. */
+ cfun->machine->vararg_a7_copy = entry_insns;
+ }
+ else
+ {
+ /* Put entry_insns after the NOTE that starts the function. If
+ this is inside a start_sequence, make the outer-level insn
+ chain current, so the code is placed at the start of the
+ function. */
+ push_topmost_sequence ();
+ /* Do not use entry_of_function() here. This is called from within
+ expand_function_start, when the CFG still holds GIMPLE. */
+ emit_insn_after (entry_insns, get_insns ());
+ pop_topmost_sequence ();
}
- return 0;
+ return tmp;
}
-/* Try to expand a block move operation to an RTL block move instruction.
- If not optimizing or if the block size is not a constant or if the
- block is small, the expansion fails and GCC falls back to calling
- memcpy().
+/* 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
+ constant, or if the block is too large, the expansion fails and GCC
+ falls back to calling memcpy().
operands[0] is the destination
operands[1] is the source
@@ -1396,211 +1150,390 @@ xtensa_copy_incoming_a7 (operands, mode)
operands[3] is the alignment */
int
-xtensa_expand_block_move (operands)
- rtx *operands;
+xtensa_expand_block_move (rtx *operands)
{
- rtx dest = operands[0];
- rtx src = operands[1];
- int bytes = INTVAL (operands[2]);
- int align = XINT (operands[3], 0);
- int num_pieces, move_ratio;
+ static const enum machine_mode mode_from_align[] =
+ {
+ VOIDmode, QImode, HImode, VOIDmode, SImode,
+ };
- /* If this is not a fixed size move, just call memcpy */
+ rtx dst_mem = operands[0];
+ rtx src_mem = operands[1];
+ HOST_WIDE_INT bytes, align;
+ int num_pieces, move_ratio;
+ rtx temp[2];
+ enum machine_mode mode[2];
+ int amount[2];
+ bool active[2];
+ int phase = 0;
+ int next;
+ int offset_ld = 0;
+ int offset_st = 0;
+ rtx x;
+
+ /* If this is not a fixed size move, just call memcpy. */
if (!optimize || (GET_CODE (operands[2]) != CONST_INT))
return 0;
- /* Anything to move? */
+ bytes = INTVAL (operands[2]);
+ align = INTVAL (operands[3]);
+
+ /* Anything to move? */
if (bytes <= 0)
- return 1;
+ return 0;
if (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;
if (optimize > 2)
move_ratio = LARGEST_MOVE_RATIO;
- num_pieces = (bytes / align) + (bytes % align); /* close enough anyway */
- if (num_pieces >= move_ratio)
+ num_pieces = (bytes / align) + (bytes % align); /* Close enough anyway. */
+ if (num_pieces > move_ratio)
return 0;
- /* make sure the memory addresses are valid */
- operands[0] = validize_mem (dest);
- operands[1] = validize_mem (src);
+ x = XEXP (dst_mem, 0);
+ if (!REG_P (x))
+ {
+ x = force_reg (Pmode, x);
+ dst_mem = replace_equiv_address (dst_mem, x);
+ }
+
+ x = XEXP (src_mem, 0);
+ if (!REG_P (x))
+ {
+ x = force_reg (Pmode, x);
+ src_mem = replace_equiv_address (src_mem, x);
+ }
+
+ active[0] = active[1] = false;
+
+ do
+ {
+ next = phase;
+ phase ^= 1;
+
+ if (bytes > 0)
+ {
+ int next_amount;
+
+ next_amount = (bytes >= 4 ? 4 : (bytes >= 2 ? 2 : 1));
+ next_amount = MIN (next_amount, align);
+
+ amount[next] = next_amount;
+ mode[next] = mode_from_align[next_amount];
+ temp[next] = gen_reg_rtx (mode[next]);
+
+ x = adjust_address (src_mem, mode[next], offset_ld);
+ emit_insn (gen_rtx_SET (VOIDmode, temp[next], x));
+
+ offset_ld += next_amount;
+ bytes -= next_amount;
+ active[next] = true;
+ }
+
+ if (active[phase])
+ {
+ active[phase] = false;
+
+ x = adjust_address (dst_mem, mode[phase], offset_st);
+ emit_insn (gen_rtx_SET (VOIDmode, x, temp[phase]));
+
+ offset_st += amount[phase];
+ }
+ }
+ while (active[next]);
+
+ return 1;
+}
+
+
+void
+xtensa_expand_nonlocal_goto (rtx *operands)
+{
+ rtx goto_handler = operands[1];
+ rtx containing_fp = operands[3];
+
+ /* Generate a call to "__xtensa_nonlocal_goto" (in libgcc); the code
+ is too big to generate in-line. */
+
+ if (GET_CODE (containing_fp) != REG)
+ containing_fp = force_reg (Pmode, containing_fp);
+
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
+ 0, VOIDmode, 2,
+ containing_fp, Pmode,
+ goto_handler, Pmode);
+}
+
+
+static struct machine_function *
+xtensa_init_machine_status (void)
+{
+ return GGC_CNEW (struct machine_function);
+}
+
+
+/* Shift VAL of mode MODE left by COUNT bits. */
+
+static inline rtx
+xtensa_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
+{
+ val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
+ NULL_RTX, 1, OPTAB_DIRECT);
+ return expand_simple_binop (SImode, ASHIFT, val, count,
+ NULL_RTX, 1, OPTAB_DIRECT);
+}
+
+
+/* Structure to hold the initial parameters for a compare_and_swap operation
+ in HImode and QImode. */
+
+struct alignment_context
+{
+ rtx memsi; /* SI aligned memory location. */
+ rtx shift; /* Bit offset with regard to lsb. */
+ rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */
+ rtx modemaski; /* ~modemask */
+};
+
+
+/* Initialize structure AC for word access to HI and QI mode memory. */
+
+static void
+init_alignment_context (struct alignment_context *ac, rtx mem)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ rtx byteoffset = NULL_RTX;
+ bool aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
+
+ if (aligned)
+ ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */
+ else
+ {
+ /* Alignment is unknown. */
+ rtx addr, align;
+
+ /* Force the address into a register. */
+ addr = force_reg (Pmode, XEXP (mem, 0));
+
+ /* Align it to SImode. */
+ align = expand_simple_binop (Pmode, AND, addr,
+ GEN_INT (-GET_MODE_SIZE (SImode)),
+ NULL_RTX, 1, OPTAB_DIRECT);
+ /* Generate MEM. */
+ ac->memsi = gen_rtx_MEM (SImode, align);
+ MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
+ set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
+ set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
+
+ byteoffset = expand_simple_binop (Pmode, AND, addr,
+ GEN_INT (GET_MODE_SIZE (SImode) - 1),
+ NULL_RTX, 1, OPTAB_DIRECT);
+ }
+
+ /* Calculate shiftcount. */
+ if (TARGET_BIG_ENDIAN)
+ {
+ ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
+ if (!aligned)
+ ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ }
+ else
+ {
+ if (aligned)
+ ac->shift = NULL_RTX;
+ else
+ ac->shift = byteoffset;
+ }
+
+ if (ac->shift != NULL_RTX)
+ {
+ /* Shift is the byte count, but we need the bitcount. */
+ ac->shift = expand_simple_binop (SImode, MULT, ac->shift,
+ GEN_INT (BITS_PER_UNIT),
+ NULL_RTX, 1, OPTAB_DIRECT);
+ ac->modemask = expand_simple_binop (SImode, ASHIFT,
+ GEN_INT (GET_MODE_MASK (mode)),
+ ac->shift,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ }
+ else
+ ac->modemask = GEN_INT (GET_MODE_MASK (mode));
- emit_insn (gen_movstrsi_internal (operands[0], operands[1],
- operands[2], operands[3]));
- return 1;
+ ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
}
-/* Emit a sequence of instructions to implement a block move, trying
- to hide load delay slots as much as possible. Load N values into
- temporary registers, store those N values, and repeat until the
- complete block has been moved. N=delay_slots+1 */
-
-struct meminsnbuf {
- char template[30];
- rtx operands[2];
-};
+/* Expand an atomic compare and swap operation for HImode and QImode.
+ MEM is the memory location, CMP the old value to compare MEM with
+ and NEW_RTX the value to set if CMP == MEM. */
void
-xtensa_emit_block_move (operands, tmpregs, delay_slots)
- rtx *operands;
- rtx *tmpregs;
- int delay_slots;
+xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new_rtx)
{
- rtx dest = operands[0];
- rtx src = operands[1];
- int bytes = INTVAL (operands[2]);
- int align = XINT (operands[3], 0);
- rtx from_addr = XEXP (src, 0);
- rtx to_addr = XEXP (dest, 0);
- int from_struct = MEM_IN_STRUCT_P (src);
- int to_struct = MEM_IN_STRUCT_P (dest);
- int offset = 0;
- int chunk_size, item_size;
- struct meminsnbuf *ldinsns, *stinsns;
- const char *ldname, *stname;
- enum machine_mode mode;
+ enum machine_mode mode = GET_MODE (mem);
+ struct alignment_context ac;
+ rtx tmp, cmpv, newv, val;
+ rtx oldval = gen_reg_rtx (SImode);
+ rtx res = gen_reg_rtx (SImode);
+ rtx csloop = gen_label_rtx ();
+ rtx csend = gen_label_rtx ();
- if (align > MOVE_MAX)
- align = MOVE_MAX;
- item_size = align;
- chunk_size = delay_slots + 1;
+ init_alignment_context (&ac, mem);
- ldinsns = (struct meminsnbuf *)
- alloca (chunk_size * sizeof (struct meminsnbuf));
- stinsns = (struct meminsnbuf *)
- alloca (chunk_size * sizeof (struct meminsnbuf));
+ if (ac.shift != NULL_RTX)
+ {
+ cmp = xtensa_expand_mask_and_shift (cmp, mode, ac.shift);
+ new_rtx = xtensa_expand_mask_and_shift (new_rtx, mode, ac.shift);
+ }
- mode = xtensa_find_mode_for_size (item_size);
- item_size = GET_MODE_SIZE (mode);
- ldname = xtensa_ld_opcodes[(int) mode];
- stname = xtensa_st_opcodes[(int) mode];
+ /* Load the surrounding word into VAL with the MEM value masked out. */
+ val = force_reg (SImode, expand_simple_binop (SImode, AND, ac.memsi,
+ ac.modemaski, NULL_RTX, 1,
+ OPTAB_DIRECT));
+ emit_label (csloop);
- while (bytes > 0)
- {
- int n;
+ /* Patch CMP and NEW_RTX into VAL at correct position. */
+ cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
+ NULL_RTX, 1, OPTAB_DIRECT));
+ newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new_rtx, val,
+ NULL_RTX, 1, OPTAB_DIRECT));
- for (n = 0; n < chunk_size; n++)
- {
- rtx addr, mem;
+ /* Jump to end if we're done. */
+ emit_insn (gen_sync_compare_and_swapsi (res, ac.memsi, cmpv, newv));
+ emit_cmp_and_jump_insns (res, cmpv, EQ, const0_rtx, SImode, true, csend);
- if (bytes == 0)
- {
- chunk_size = n;
- break;
- }
+ /* Check for changes outside mode. */
+ emit_move_insn (oldval, val);
+ tmp = expand_simple_binop (SImode, AND, res, ac.modemaski,
+ val, 1, OPTAB_DIRECT);
+ if (tmp != val)
+ emit_move_insn (val, tmp);
- if (bytes < item_size)
- {
- /* find a smaller item_size which we can load & store */
- item_size = bytes;
- mode = xtensa_find_mode_for_size (item_size);
- item_size = GET_MODE_SIZE (mode);
- ldname = xtensa_ld_opcodes[(int) mode];
- stname = xtensa_st_opcodes[(int) mode];
- }
+ /* Loop internal if so. */
+ emit_cmp_and_jump_insns (oldval, val, NE, const0_rtx, SImode, true, csloop);
- /* record the load instruction opcode and operands */
- addr = plus_constant (from_addr, offset);
- mem = gen_rtx_MEM (mode, addr);
- if (! memory_address_p (mode, addr))
- abort ();
- MEM_IN_STRUCT_P (mem) = from_struct;
- ldinsns[n].operands[0] = tmpregs[n];
- ldinsns[n].operands[1] = mem;
- sprintf (ldinsns[n].template, "%s\t%%0, %%1", ldname);
-
- /* record the store instruction opcode and operands */
- addr = plus_constant (to_addr, offset);
- mem = gen_rtx_MEM (mode, addr);
- if (! memory_address_p (mode, addr))
- abort ();
- MEM_IN_STRUCT_P (mem) = to_struct;
- stinsns[n].operands[0] = tmpregs[n];
- stinsns[n].operands[1] = mem;
- sprintf (stinsns[n].template, "%s\t%%0, %%1", stname);
-
- offset += item_size;
- bytes -= item_size;
- }
+ emit_label (csend);
- /* now output the loads followed by the stores */
- for (n = 0; n < chunk_size; n++)
- output_asm_insn (ldinsns[n].template, ldinsns[n].operands);
- for (n = 0; n < chunk_size; n++)
- output_asm_insn (stinsns[n].template, stinsns[n].operands);
- }
+ /* Return the correct part of the bitfield. */
+ convert_move (target,
+ (ac.shift == NULL_RTX ? res
+ : expand_simple_binop (SImode, LSHIFTRT, res, ac.shift,
+ NULL_RTX, 1, OPTAB_DIRECT)),
+ 1);
}
-static enum machine_mode
-xtensa_find_mode_for_size (item_size)
- unsigned item_size;
-{
- enum machine_mode mode, tmode;
+/* Expand an atomic operation CODE of mode MODE (either HImode or QImode --
+ the default expansion works fine for SImode). MEM is the memory location
+ and VAL the value to play with. If AFTER is true then store the value
+ MEM holds after the operation, if AFTER is false then store the value MEM
+ holds before the operation. If TARGET is zero then discard that value, else
+ store it to TARGET. */
- while (1)
+void
+xtensa_expand_atomic (enum rtx_code code, rtx target, rtx mem, rtx val,
+ bool after)
+{
+ enum machine_mode mode = GET_MODE (mem);
+ struct alignment_context ac;
+ rtx csloop = gen_label_rtx ();
+ rtx cmp, tmp;
+ rtx old = gen_reg_rtx (SImode);
+ rtx new_rtx = gen_reg_rtx (SImode);
+ rtx orig = NULL_RTX;
+
+ init_alignment_context (&ac, mem);
+
+ /* Prepare values before the compare-and-swap loop. */
+ if (ac.shift != NULL_RTX)
+ val = xtensa_expand_mask_and_shift (val, mode, ac.shift);
+ switch (code)
{
- mode = VOIDmode;
-
- /* find mode closest to but not bigger than item_size */
- for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode))
- if (GET_MODE_SIZE (tmode) <= item_size)
- mode = tmode;
- if (mode == VOIDmode)
- abort ();
+ case PLUS:
+ case MINUS:
+ orig = gen_reg_rtx (SImode);
+ convert_move (orig, val, 1);
+ break;
- item_size = GET_MODE_SIZE (mode);
+ case SET:
+ case IOR:
+ case XOR:
+ break;
- if (xtensa_ld_opcodes[(int) mode]
- && xtensa_st_opcodes[(int) mode])
- break;
+ case MULT: /* NAND */
+ case AND:
+ /* val = "11..111..1" */
+ val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ break;
- /* cannot load & store this mode; try something smaller */
- item_size -= 1;
+ default:
+ gcc_unreachable ();
}
- return mode;
-}
-
+ /* Load full word. Subsequent loads are performed by S32C1I. */
+ cmp = force_reg (SImode, ac.memsi);
-void
-xtensa_expand_nonlocal_goto (operands)
- rtx *operands;
-{
- rtx goto_handler = operands[1];
- rtx containing_fp = operands[3];
+ emit_label (csloop);
+ emit_move_insn (old, cmp);
- /* generate a call to "__xtensa_nonlocal_goto" (in libgcc); the code
- is too big to generate in-line */
+ switch (code)
+ {
+ case PLUS:
+ case MINUS:
+ val = expand_simple_binop (SImode, code, old, orig,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ val = expand_simple_binop (SImode, AND, val, ac.modemask,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ /* FALLTHRU */
+ case SET:
+ tmp = expand_simple_binop (SImode, AND, old, ac.modemaski,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ tmp = expand_simple_binop (SImode, IOR, tmp, val,
+ new_rtx, 1, OPTAB_DIRECT);
+ break;
- if (GET_CODE (containing_fp) != REG)
- containing_fp = force_reg (Pmode, containing_fp);
+ case AND:
+ case IOR:
+ case XOR:
+ tmp = expand_simple_binop (SImode, code, old, val,
+ new_rtx, 1, OPTAB_DIRECT);
+ break;
- goto_handler = replace_rtx (copy_rtx (goto_handler),
- virtual_stack_vars_rtx,
- containing_fp);
+ case MULT: /* NAND */
+ tmp = expand_simple_binop (SImode, XOR, old, ac.modemask,
+ NULL_RTX, 1, OPTAB_DIRECT);
+ tmp = expand_simple_binop (SImode, AND, tmp, val,
+ new_rtx, 1, OPTAB_DIRECT);
+ break;
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
- 0, VOIDmode, 2,
- containing_fp, Pmode,
- goto_handler, Pmode);
-}
+ default:
+ gcc_unreachable ();
+ }
+ if (tmp != new_rtx)
+ emit_move_insn (new_rtx, tmp);
+ emit_insn (gen_sync_compare_and_swapsi (cmp, ac.memsi, old, new_rtx));
+ emit_cmp_and_jump_insns (cmp, old, NE, const0_rtx, SImode, true, csloop);
-static struct machine_function *
-xtensa_init_machine_status ()
-{
- return ggc_alloc_cleared (sizeof (struct machine_function));
+ if (target)
+ {
+ tmp = (after ? new_rtx : cmp);
+ convert_move (target,
+ (ac.shift == NULL_RTX ? tmp
+ : expand_simple_binop (SImode, LSHIFTRT, tmp, ac.shift,
+ NULL_RTX, 1, OPTAB_DIRECT)),
+ 1);
+ }
}
void
-xtensa_setup_frame_addresses ()
+xtensa_setup_frame_addresses (void)
{
/* Set flag to cause FRAME_POINTER_REQUIRED to be set. */
cfun->machine->accesses_prev_frame = 1;
@@ -1615,19 +1548,17 @@ xtensa_setup_frame_addresses ()
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
there. If the loop ends with a label we need the nop so that branches
- targetting that label will target the nop (and thus remain in the loop),
- instead of targetting the instruction after the loop (and thus exiting
+ targeting that label will target the nop (and thus remain in the loop),
+ 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
- branch is targetting 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
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
when the branch is taken. */
void
-xtensa_emit_loop_end (insn, operands)
- rtx insn;
- rtx *operands;
+xtensa_emit_loop_end (rtx insn, rtx *operands)
{
char done = 0;
@@ -1666,9 +1597,102 @@ xtensa_emit_loop_end (insn, operands)
char *
-xtensa_emit_call (callop, operands)
- int callop;
- rtx *operands;
+xtensa_emit_branch (bool inverted, bool immed, rtx *operands)
+{
+ static char result[64];
+ enum rtx_code code;
+ const char *op;
+
+ code = GET_CODE (operands[3]);
+ switch (code)
+ {
+ case EQ: op = inverted ? "ne" : "eq"; break;
+ case NE: op = inverted ? "eq" : "ne"; break;
+ case LT: op = inverted ? "ge" : "lt"; break;
+ case GE: op = inverted ? "lt" : "ge"; break;
+ case LTU: op = inverted ? "geu" : "ltu"; break;
+ case GEU: op = inverted ? "ltu" : "geu"; break;
+ default: gcc_unreachable ();
+ }
+
+ if (immed)
+ {
+ if (INTVAL (operands[1]) == 0)
+ sprintf (result, "b%sz%s\t%%0, %%2", op,
+ (TARGET_DENSITY && (code == EQ || code == NE)) ? ".n" : "");
+ else
+ sprintf (result, "b%si\t%%0, %%d1, %%2", op);
+ }
+ else
+ sprintf (result, "b%s\t%%0, %%1, %%2", op);
+
+ return result;
+}
+
+
+char *
+xtensa_emit_bit_branch (bool inverted, bool immed, rtx *operands)
+{
+ static char result[64];
+ const char *op;
+
+ switch (GET_CODE (operands[3]))
+ {
+ case EQ: op = inverted ? "bs" : "bc"; break;
+ case NE: op = inverted ? "bc" : "bs"; break;
+ default: gcc_unreachable ();
+ }
+
+ if (immed)
+ {
+ unsigned bitnum = INTVAL (operands[1]) & 0x1f;
+ operands[1] = GEN_INT (bitnum);
+ sprintf (result, "b%si\t%%0, %%d1, %%2", op);
+ }
+ else
+ sprintf (result, "b%s\t%%0, %%1, %%2", op);
+
+ return result;
+}
+
+
+char *
+xtensa_emit_movcc (bool inverted, bool isfp, bool isbool, rtx *operands)
+{
+ static char result[64];
+ enum rtx_code code;
+ const char *op;
+
+ code = GET_CODE (operands[4]);
+ if (isbool)
+ {
+ switch (code)
+ {
+ case EQ: op = inverted ? "t" : "f"; break;
+ case NE: op = inverted ? "f" : "t"; break;
+ default: gcc_unreachable ();
+ }
+ }
+ else
+ {
+ switch (code)
+ {
+ case EQ: op = inverted ? "nez" : "eqz"; break;
+ case NE: op = inverted ? "eqz" : "nez"; break;
+ case LT: op = inverted ? "gez" : "ltz"; break;
+ case GE: op = inverted ? "ltz" : "gez"; break;
+ default: gcc_unreachable ();
+ }
+ }
+
+ sprintf (result, "mov%s%s\t%%0, %%%d, %%1",
+ op, isfp ? ".s" : "", inverted ? 3 : 2);
+ return result;
+}
+
+
+char *
+xtensa_emit_call (int callop, rtx *operands)
{
static char result[64];
rtx tgt = operands[callop];
@@ -1684,35 +1708,245 @@ xtensa_emit_call (callop, operands)
}
-/* Return the stabs register number to use for 'regno'. */
+bool
+xtensa_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
+{
+ /* Allow constant pool addresses. */
+ if (mode != BLKmode && GET_MODE_SIZE (mode) >= UNITS_PER_WORD
+ && ! TARGET_CONST16 && constantpool_address_p (addr)
+ && ! xtensa_tls_referenced_p (addr))
+ return true;
+
+ while (GET_CODE (addr) == SUBREG)
+ addr = SUBREG_REG (addr);
+
+ /* Allow base registers. */
+ if (GET_CODE (addr) == REG && BASE_REG_P (addr, strict))
+ return true;
+
+ /* Check for "register + offset" addressing. */
+ if (GET_CODE (addr) == PLUS)
+ {
+ rtx xplus0 = XEXP (addr, 0);
+ rtx xplus1 = XEXP (addr, 1);
+ enum rtx_code code0;
+ enum rtx_code code1;
+
+ while (GET_CODE (xplus0) == SUBREG)
+ xplus0 = SUBREG_REG (xplus0);
+ code0 = GET_CODE (xplus0);
+
+ while (GET_CODE (xplus1) == SUBREG)
+ xplus1 = SUBREG_REG (xplus1);
+ code1 = GET_CODE (xplus1);
+
+ /* Swap operands if necessary so the register is first. */
+ if (code0 != REG && code1 == REG)
+ {
+ xplus0 = XEXP (addr, 1);
+ xplus1 = XEXP (addr, 0);
+ code0 = GET_CODE (xplus0);
+ code1 = GET_CODE (xplus1);
+ }
+
+ if (code0 == REG && BASE_REG_P (xplus0, strict)
+ && code1 == CONST_INT
+ && xtensa_mem_offset (INTVAL (xplus1), mode))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* Construct the SYMBOL_REF for the _TLS_MODULE_BASE_ symbol. */
+
+static GTY(()) rtx xtensa_tls_module_base_symbol;
+
+static rtx
+xtensa_tls_module_base (void)
+{
+ if (! xtensa_tls_module_base_symbol)
+ {
+ xtensa_tls_module_base_symbol =
+ gen_rtx_SYMBOL_REF (Pmode, "_TLS_MODULE_BASE_");
+ SYMBOL_REF_FLAGS (xtensa_tls_module_base_symbol)
+ |= TLS_MODEL_GLOBAL_DYNAMIC << SYMBOL_FLAG_TLS_SHIFT;
+ }
+
+ return xtensa_tls_module_base_symbol;
+}
+
+
+static rtx
+xtensa_call_tls_desc (rtx sym, rtx *retp)
+{
+ rtx fn, arg, a10, call_insn, insns;
+
+ start_sequence ();
+ fn = gen_reg_rtx (Pmode);
+ arg = gen_reg_rtx (Pmode);
+ a10 = gen_rtx_REG (Pmode, 10);
+
+ emit_insn (gen_tls_func (fn, sym));
+ emit_insn (gen_tls_arg (arg, sym));
+ emit_move_insn (a10, arg);
+ call_insn = emit_call_insn (gen_tls_call (a10, fn, sym, const1_rtx));
+ CALL_INSN_FUNCTION_USAGE (call_insn)
+ = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, a10),
+ CALL_INSN_FUNCTION_USAGE (call_insn));
+ insns = get_insns ();
+ end_sequence ();
+
+ *retp = a10;
+ return insns;
+}
+
+
+static rtx
+xtensa_legitimize_tls_address (rtx x)
+{
+ unsigned int model = SYMBOL_REF_TLS_MODEL (x);
+ rtx dest, tp, ret, modbase, base, addend, insns;
+
+ dest = gen_reg_rtx (Pmode);
+ switch (model)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ insns = xtensa_call_tls_desc (x, &ret);
+ emit_libcall_block (insns, dest, ret, x);
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ base = gen_reg_rtx (Pmode);
+ modbase = xtensa_tls_module_base ();
+ insns = xtensa_call_tls_desc (modbase, &ret);
+ emit_libcall_block (insns, base, ret, modbase);
+ addend = force_reg (SImode, gen_sym_DTPOFF (x));
+ emit_insn (gen_addsi3 (dest, base, addend));
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ case TLS_MODEL_LOCAL_EXEC:
+ tp = gen_reg_rtx (SImode);
+ emit_insn (gen_load_tp (tp));
+ addend = force_reg (SImode, gen_sym_TPOFF (x));
+ emit_insn (gen_addsi3 (dest, tp, addend));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return dest;
+}
+
+
+rtx
+xtensa_legitimize_address (rtx x,
+ rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
+{
+ if (xtensa_tls_symbol_p (x))
+ return xtensa_legitimize_tls_address (x);
+
+ if (GET_CODE (x) == PLUS)
+ {
+ rtx plus0 = XEXP (x, 0);
+ rtx plus1 = XEXP (x, 1);
+
+ if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
+ {
+ plus0 = XEXP (x, 1);
+ plus1 = XEXP (x, 0);
+ }
+
+ /* Try to split up the offset to use an ADDMI instruction. */
+ if (GET_CODE (plus0) == REG
+ && GET_CODE (plus1) == CONST_INT
+ && !xtensa_mem_offset (INTVAL (plus1), mode)
+ && !xtensa_simm8 (INTVAL (plus1))
+ && xtensa_mem_offset (INTVAL (plus1) & 0xff, mode)
+ && xtensa_simm8x256 (INTVAL (plus1) & ~0xff))
+ {
+ rtx temp = gen_reg_rtx (Pmode);
+ rtx addmi_offset = GEN_INT (INTVAL (plus1) & ~0xff);
+ emit_insn (gen_rtx_SET (Pmode, temp,
+ gen_rtx_PLUS (Pmode, plus0, addmi_offset)));
+ return gen_rtx_PLUS (Pmode, temp, GEN_INT (INTVAL (plus1) & 0xff));
+ }
+ }
+
+ return x;
+}
+
+
+/* Helper for xtensa_tls_referenced_p. */
+
+static int
+xtensa_tls_referenced_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (*x) == SYMBOL_REF)
+ return SYMBOL_REF_TLS_MODEL (*x) != 0;
+
+ /* Ignore TLS references that have already been legitimized. */
+ if (GET_CODE (*x) == UNSPEC)
+ {
+ switch (XINT (*x, 1))
+ {
+ case UNSPEC_TPOFF:
+ case UNSPEC_DTPOFF:
+ case UNSPEC_TLS_FUNC:
+ case UNSPEC_TLS_ARG:
+ case UNSPEC_TLS_CALL:
+ return -1;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Return TRUE if X contains any TLS symbol references. */
+
+bool
+xtensa_tls_referenced_p (rtx x)
+{
+ if (! TARGET_HAVE_TLS)
+ return false;
+
+ return for_each_rtx (&x, xtensa_tls_referenced_p_1, NULL);
+}
+
+
+/* Return the debugger register number to use for 'regno'. */
int
-xtensa_dbx_register_number (regno)
- int regno;
+xtensa_dbx_register_number (int regno)
{
int first = -1;
-
- if (GP_REG_P (regno)) {
- regno -= GP_REG_FIRST;
- first = 0;
- }
- else if (BR_REG_P (regno)) {
- regno -= BR_REG_FIRST;
- first = 16;
- }
- else if (FP_REG_P (regno)) {
- regno -= FP_REG_FIRST;
- /* The current numbering convention is that TIE registers are
- numbered in libcc order beginning with 256. We can't guarantee
- that the FP registers will come first, so the following is just
- a guess. It seems like we should make a special case for FP
- registers and give them fixed numbers < 256. */
- first = 256;
- }
- else if (ACC_REG_P (regno))
+
+ if (GP_REG_P (regno))
{
+ regno -= GP_REG_FIRST;
first = 0;
- regno = -1;
+ }
+ else if (BR_REG_P (regno))
+ {
+ regno -= BR_REG_FIRST;
+ first = 16;
+ }
+ else if (FP_REG_P (regno))
+ {
+ regno -= FP_REG_FIRST;
+ first = 48;
+ }
+ else if (ACC_REG_P (regno))
+ {
+ first = 0x200; /* Start of Xtensa special registers. */
+ regno = 16; /* ACCLO is special register 16. */
}
/* When optimizing, we sometimes get asked about pseudo-registers
@@ -1729,21 +1963,17 @@ xtensa_dbx_register_number (regno)
/* Initialize CUMULATIVE_ARGS for a function. */
void
-init_cumulative_args (cum, fntype, libname)
- CUMULATIVE_ARGS *cum; /* argument info to initialize */
- tree fntype ATTRIBUTE_UNUSED; /* tree ptr for function decl */
- rtx libname ATTRIBUTE_UNUSED; /* SYMBOL_REF of library name or 0 */
+init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
{
cum->arg_words = 0;
+ cum->incoming = incoming;
}
+
/* Advance the argument to the next argument position. */
void
-function_arg_advance (cum, mode, type)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
+function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type)
{
int words, max;
int *arg_words;
@@ -1755,7 +1985,9 @@ function_arg_advance (cum, mode, type)
? (int) GET_MODE_SIZE (mode)
: int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
- if ((*arg_words + words > max) && (*arg_words < max))
+ if (*arg_words < max
+ && (targetm.calls.must_pass_in_stack (mode, type)
+ || *arg_words + words > max))
*arg_words = max;
*arg_words += words;
@@ -1763,19 +1995,16 @@ function_arg_advance (cum, mode, type)
/* Return an RTL expression containing the register for the given mode,
- or 0 if the argument is to be passed on the stack. */
+ 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. */
rtx
-function_arg (cum, mode, type, incoming_p)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
- int incoming_p; /* computing the incoming registers? */
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+ int incoming_p)
{
int regbase, words, max;
int *arg_words;
int regno;
- enum machine_mode result_mode;
arg_words = &cum->arg_words;
regbase = (incoming_p ? GP_ARG_FIRST : GP_OUTGOING_ARG_FIRST);
@@ -1786,44 +2015,48 @@ function_arg (cum, mode, type, incoming_p)
: int_size_in_bytes (type)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
if (type && (TYPE_ALIGN (type) > BITS_PER_WORD))
- *arg_words += (*arg_words & 1);
+ {
+ int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_WORD;
+ *arg_words = (*arg_words + align - 1) & -align;
+ }
if (*arg_words + words > max)
return (rtx)0;
regno = regbase + *arg_words;
- result_mode = (mode == BLKmode ? TYPE_MODE (type) : mode);
- /* We need to make sure that references to a7 are represented with
- rtx that is not equal to hard_frame_pointer_rtx. For BLKmode and
- modes bigger than 2 words (because we only have patterns for
- modes of 2 words or smaller), we can't control the expansion
- unless we explicitly list the individual registers in a PARALLEL. */
+ if (cum->incoming && regno <= A7_REG && regno + words > A7_REG)
+ cfun->machine->need_a7_copy = true;
- if ((mode == BLKmode || words > 2)
- && regno < A7_REG
- && regno + words > A7_REG)
- {
- rtx result;
- int n;
+ return gen_rtx_REG (mode, regno);
+}
+
+
+int
+function_arg_boundary (enum machine_mode mode, tree type)
+{
+ unsigned int alignment;
+
+ alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
+ if (alignment < PARM_BOUNDARY)
+ alignment = PARM_BOUNDARY;
+ if (alignment > STACK_BOUNDARY)
+ alignment = STACK_BOUNDARY;
+ return alignment;
+}
- result = gen_rtx_PARALLEL (result_mode, rtvec_alloc (words));
- for (n = 0; n < words; n++)
- {
- XVECEXP (result, 0, n) =
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_raw_REG (SImode, regno + n),
- GEN_INT (n * UNITS_PER_WORD));
- }
- return result;
- }
- return gen_raw_REG (result_mode, regno);
+static bool
+xtensa_return_in_msb (const_tree valtype)
+{
+ return (TARGET_BIG_ENDIAN
+ && AGGREGATE_TYPE_P (valtype)
+ && int_size_in_bytes (valtype) >= UNITS_PER_WORD);
}
void
-override_options ()
+override_options (void)
{
int regno;
enum machine_mode mode;
@@ -1831,39 +2064,21 @@ override_options ()
if (!TARGET_BOOLEANS && TARGET_HARD_FLOAT)
error ("boolean registers required for the floating-point option");
- /* set up the tables of ld/st opcode names for block moves */
- xtensa_ld_opcodes[(int) SImode] = "l32i";
- xtensa_ld_opcodes[(int) HImode] = "l16ui";
- xtensa_ld_opcodes[(int) QImode] = "l8ui";
- xtensa_st_opcodes[(int) SImode] = "s32i";
- xtensa_st_opcodes[(int) HImode] = "s16i";
- xtensa_st_opcodes[(int) QImode] = "s8i";
-
- xtensa_char_to_class['q'] = SP_REG;
- xtensa_char_to_class['a'] = GR_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['A'] = ((TARGET_MAC16) ? ACC_REG : 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['D'] = ((TARGET_DENSITY) ? GR_REGS: NO_REGS);
- xtensa_char_to_class['d'] = ((TARGET_DENSITY) ? AR_REGS: NO_REGS);
-
/* Set up array giving whether a given register can hold a given mode. */
for (mode = VOIDmode;
mode != MAX_MACHINE_MODE;
mode = (enum machine_mode) ((int) mode + 1))
{
int size = GET_MODE_SIZE (mode);
- enum mode_class class = GET_MODE_CLASS (mode);
+ enum mode_class mclass = GET_MODE_CLASS (mode);
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
int temp;
if (ACC_REG_P (regno))
- temp = (TARGET_MAC16 &&
- (class == MODE_INT) && (size <= UNITS_PER_WORD));
+ temp = (TARGET_MAC16
+ && (mclass == MODE_INT) && (size <= UNITS_PER_WORD));
else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
else if (FP_REG_P (regno))
@@ -1879,10 +2094,31 @@ override_options ()
init_machine_status = xtensa_init_machine_status;
- /* Check PIC settings. There's no need for -fPIC on Xtensa and
- some targets need to always use PIC. */
- if (flag_pic > 1 || (XTENSA_ALWAYS_PIC))
+ /* Check PIC settings. PIC is only supported when using L32R
+ instructions, and some targets need to always use PIC. */
+ if (flag_pic && TARGET_CONST16)
+ error ("-f%s is not supported with CONST16 instructions",
+ (flag_pic > 1 ? "PIC" : "pic"));
+ else if (XTENSA_ALWAYS_PIC)
+ {
+ if (TARGET_CONST16)
+ error ("PIC is required but not supported with CONST16 instructions");
+ flag_pic = 1;
+ }
+ /* There's no need for -fPIC (as opposed to -fpic) on Xtensa. */
+ if (flag_pic > 1)
flag_pic = 1;
+ if (flag_pic && !flag_pie)
+ flag_shlib = 1;
+
+ /* Hot/cold partitioning does not work on this architecture, because of
+ constant pools (the load instruction cannot necessarily reach that far).
+ Therefore disable it on this architecture. */
+ if (flag_reorder_blocks_and_partition)
+ {
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
}
@@ -1907,7 +2143,7 @@ override_options ()
a null pointer for X and the punctuation character for CODE.
'a', 'c', 'l', and 'n' are reserved.
-
+
The Xtensa specific codes are:
'd' CONST_INT, print as signed decimal
@@ -1918,14 +2154,14 @@ override_options ()
'D' REG, print second register of double-word register operand
'N' MEM, print address of next word following a memory operand
'v' MEM, if memory reference is volatile, output a MEMW before it
+ 't' any constant, add "@h" suffix for top 16 bits
+ 'b' any constant, add "@l" suffix for bottom 16 bits
*/
static void
-printx (file, val)
- 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))
fprintf (file, "%d", val);
else if (val < 0)
@@ -1936,94 +2172,143 @@ printx (file, val)
void
-print_operand (file, op, letter)
- FILE *file; /* file to write to */
- rtx op; /* operand to print */
- int letter; /* % or 0 */
+print_operand (FILE *file, rtx x, int letter)
{
- enum rtx_code code;
-
- if (! op)
+ if (!x)
error ("PRINT_OPERAND null pointer");
- code = GET_CODE (op);
- switch (code)
- {
- case REG:
- case SUBREG:
- {
- int regnum = xt_true_regnum (op);
- if (letter == 'D')
- regnum++;
- fprintf (file, "%s", reg_names[regnum]);
- break;
- }
-
- case MEM:
- /* For a volatile memory reference, emit a MEMW before the
- load or store. */
- if (letter == 'v')
- {
- if (MEM_VOLATILE_P (op) && TARGET_SERIALIZE_VOLATILE)
- fprintf (file, "memw\n\t");
- break;
- }
- else if (letter == 'N')
- {
- enum machine_mode mode;
- switch (GET_MODE (op))
- {
- case DFmode: mode = SFmode; break;
- case DImode: mode = SImode; break;
- default: abort ();
- }
- op = adjust_address (op, mode, 4);
- }
+ switch (letter)
+ {
+ case 'D':
+ if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ fprintf (file, "%s", reg_names[xt_true_regnum (x) + 1]);
+ else
+ output_operand_lossage ("invalid %%D value");
+ break;
- output_address (XEXP (op, 0));
- break;
+ case 'v':
+ if (GET_CODE (x) == MEM)
+ {
+ /* For a volatile memory reference, emit a MEMW before the
+ load or store. */
+ if (MEM_VOLATILE_P (x) && TARGET_SERIALIZE_VOLATILE)
+ fprintf (file, "memw\n\t");
+ }
+ else
+ output_operand_lossage ("invalid %%v value");
+ break;
- case CONST_INT:
- switch (letter)
+ case 'N':
+ if (GET_CODE (x) == MEM
+ && (GET_MODE (x) == DFmode || GET_MODE (x) == DImode))
{
- case 'K':
- {
- int num_bits = 0;
- unsigned val = INTVAL (op);
- while (val & 1)
- {
- num_bits += 1;
- val = val >> 1;
- }
- if ((val != 0) || (num_bits == 0) || (num_bits > 16))
- fatal_insn ("invalid mask", op);
+ x = adjust_address (x, GET_MODE (x) == DFmode ? SFmode : SImode, 4);
+ output_address (XEXP (x, 0));
+ }
+ else
+ output_operand_lossage ("invalid %%N value");
+ break;
- fprintf (file, "%d", num_bits);
- break;
- }
+ case 'K':
+ if (GET_CODE (x) == CONST_INT)
+ {
+ int num_bits = 0;
+ unsigned val = INTVAL (x);
+ while (val & 1)
+ {
+ num_bits += 1;
+ val = val >> 1;
+ }
+ if ((val != 0) || (num_bits == 0) || (num_bits > 16))
+ fatal_insn ("invalid mask", x);
- case 'L':
- fprintf (file, "%ld", (32 - INTVAL (op)) & 0x1f);
- break;
+ fprintf (file, "%d", num_bits);
+ }
+ else
+ output_operand_lossage ("invalid %%K value");
+ break;
- case 'R':
- fprintf (file, "%ld", INTVAL (op) & 0x1f);
- break;
+ case 'L':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", (32 - INTVAL (x)) & 0x1f);
+ else
+ output_operand_lossage ("invalid %%L value");
+ break;
- case 'x':
- printx (file, INTVAL (op));
- break;
+ case 'R':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x) & 0x1f);
+ else
+ output_operand_lossage ("invalid %%R value");
+ break;
- case 'd':
- default:
- fprintf (file, "%ld", INTVAL (op));
- break;
+ case 'x':
+ if (GET_CODE (x) == CONST_INT)
+ printx (file, INTVAL (x));
+ else
+ output_operand_lossage ("invalid %%x value");
+ break;
+ case 'd':
+ if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x));
+ else
+ output_operand_lossage ("invalid %%d value");
+ break;
+
+ case 't':
+ case 'b':
+ if (GET_CODE (x) == CONST_INT)
+ {
+ printx (file, INTVAL (x));
+ fputs (letter == 't' ? "@h" : "@l", file);
+ }
+ else if (GET_CODE (x) == CONST_DOUBLE)
+ {
+ REAL_VALUE_TYPE r;
+ REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+ if (GET_MODE (x) == SFmode)
+ {
+ long l;
+ REAL_VALUE_TO_TARGET_SINGLE (r, l);
+ fprintf (file, "0x%08lx@%c", l, letter == 't' ? 'h' : 'l');
+ }
+ else
+ output_operand_lossage ("invalid %%t/%%b value");
+ }
+ else if (GET_CODE (x) == CONST)
+ {
+ /* X must be a symbolic constant on ELF. Write an expression
+ suitable for 'const16' that sets the high or low 16 bits. */
+ if (GET_CODE (XEXP (x, 0)) != PLUS
+ || (GET_CODE (XEXP (XEXP (x, 0), 0)) != SYMBOL_REF
+ && GET_CODE (XEXP (XEXP (x, 0), 0)) != LABEL_REF)
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
+ output_operand_lossage ("invalid %%t/%%b value");
+ print_operand (file, XEXP (XEXP (x, 0), 0), 0);
+ fputs (letter == 't' ? "@h" : "@l", file);
+ /* There must be a non-alphanumeric character between 'h' or 'l'
+ and the number. The '-' is added by print_operand() already. */
+ if (INTVAL (XEXP (XEXP (x, 0), 1)) >= 0)
+ fputs ("+", file);
+ print_operand (file, XEXP (XEXP (x, 0), 1), 0);
+ }
+ else
+ {
+ output_addr_const (file, x);
+ fputs (letter == 't' ? "@h" : "@l", file);
}
break;
default:
- output_addr_const (file, op);
+ if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+ fprintf (file, "%s", reg_names[xt_true_regnum (x)]);
+ else if (GET_CODE (x) == MEM)
+ output_address (XEXP (x, 0));
+ else if (GET_CODE (x) == CONST_INT)
+ fprintf (file, "%ld", INTVAL (x));
+ else
+ output_addr_const (file, x);
}
}
@@ -2033,9 +2318,7 @@ print_operand (file, op, letter)
reference whose address is ADDR. ADDR is an RTL expression. */
void
-print_operand_address (file, addr)
- FILE *file;
- rtx addr;
+print_operand_address (FILE *file, rtx addr)
{
if (!addr)
error ("PRINT_OPERAND_ADDRESS, null pointer");
@@ -2090,41 +2373,75 @@ print_operand_address (file, addr)
}
+bool
+xtensa_output_addr_const_extra (FILE *fp, rtx x)
+{
+ if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_TPOFF:
+ output_addr_const (fp, XVECEXP (x, 0, 0));
+ fputs ("@TPOFF", fp);
+ return true;
+ case UNSPEC_DTPOFF:
+ output_addr_const (fp, XVECEXP (x, 0, 0));
+ fputs ("@DTPOFF", fp);
+ return true;
+ case UNSPEC_PLT:
+ if (flag_pic)
+ {
+ output_addr_const (fp, XVECEXP (x, 0, 0));
+ fputs ("@PLT", fp);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
void
-xtensa_output_literal (file, x, mode, labelno)
- 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];
REAL_VALUE_TYPE r;
int size;
+ rtx first, second;
fprintf (file, "\t.literal .LC%u, ", (unsigned) labelno);
switch (GET_MODE_CLASS (mode))
{
case MODE_FLOAT:
- if (GET_CODE (x) != CONST_DOUBLE)
- abort ();
+ gcc_assert (GET_CODE (x) == CONST_DOUBLE);
REAL_VALUE_FROM_CONST_DOUBLE (r, x);
switch (mode)
{
case SFmode:
REAL_VALUE_TO_TARGET_SINGLE (r, value_long[0]);
+ if (HOST_BITS_PER_LONG > 32)
+ value_long[0] &= 0xffffffff;
fprintf (file, "0x%08lx\n", value_long[0]);
break;
case DFmode:
REAL_VALUE_TO_TARGET_DOUBLE (r, value_long);
+ if (HOST_BITS_PER_LONG > 32)
+ {
+ value_long[0] &= 0xffffffff;
+ value_long[1] &= 0xffffffff;
+ }
fprintf (file, "0x%08lx, 0x%08lx\n",
value_long[0], value_long[1]);
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
@@ -2132,24 +2449,28 @@ xtensa_output_literal (file, x, mode, labelno)
case MODE_INT:
case MODE_PARTIAL_INT:
size = GET_MODE_SIZE (mode);
- if (size == 4)
+ switch (size)
{
+ case 4:
output_addr_const (file, x);
fputs ("\n", file);
- }
- else if (size == 8)
- {
- output_addr_const (file, operand_subword (x, 0, 0, DImode));
+ break;
+
+ case 8:
+ split_double (x, &first, &second);
+ output_addr_const (file, first);
fputs (", ", file);
- output_addr_const (file, operand_subword (x, 1, 0, DImode));
+ output_addr_const (file, second);
fputs ("\n", file);
+ break;
+
+ default:
+ gcc_unreachable ();
}
- else
- abort ();
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
@@ -2161,23 +2482,22 @@ xtensa_output_literal (file, x, mode, labelno)
#define XTENSA_STACK_ALIGN(LOC) (((LOC) + STACK_BYTES-1) & ~(STACK_BYTES-1))
long
-compute_frame_size (size)
- int size; /* # of var. bytes allocated */
+compute_frame_size (int size)
{
- /* add space for the incoming static chain value */
- if (current_function_needs_context)
+ /* Add space for the incoming static chain value. */
+ if (cfun->static_chain_decl != NULL)
size += (1 * UNITS_PER_WORD);
xtensa_current_frame_size =
XTENSA_STACK_ALIGN (size
- + current_function_outgoing_args_size
+ + crtl->outgoing_args_size
+ (WINDOW_SIZE * UNITS_PER_WORD));
return xtensa_current_frame_size;
}
int
-xtensa_frame_pointer_required ()
+xtensa_frame_pointer_required (void)
{
/* The code to expand builtin_frame_addr and builtin_return_addr
currently uses the hard_frame_pointer instead of frame_pointer.
@@ -2191,142 +2511,92 @@ xtensa_frame_pointer_required ()
}
-/* If the stack frame size is too big to fit in the immediate field of
- the ENTRY instruction, we need to store the frame size in the
- constant pool. However, the code in xtensa_function_prologue runs too
- late to be able to add anything to the constant pool. Since the
- final frame size isn't known until reload is complete, this seems
- like the best place to do it.
-
- There may also be some fixup required if there is an incoming argument
- in a7 and the function requires a frame pointer. */
-
-static void
-xtensa_reorg ()
-{
- rtx first, insn, set_frame_ptr_insn = 0;
-
- unsigned long tsize = compute_frame_size (get_frame_size ());
- first = get_insns ();
- if (tsize < (1 << (12+3)))
- frame_size_const = 0;
- else
- {
- frame_size_const = force_const_mem (SImode, GEN_INT (tsize - 16));;
-
- /* make sure the constant is used so it doesn't get eliminated
- from the constant pool */
- emit_insn_before (gen_rtx_USE (SImode, frame_size_const), first);
- }
+/* minimum frame = reg save area (4 words) plus static chain (1 word)
+ and the total number of words must be a multiple of 128 bits. */
+#define MIN_FRAME_SIZE (8 * UNITS_PER_WORD)
- if (!frame_pointer_needed)
- return;
+void
+xtensa_expand_prologue (void)
+{
+ HOST_WIDE_INT total_size;
+ rtx size_rtx;
+ rtx insn, note_rtx;
- /* Search all instructions, looking for the insn that sets up the
- frame pointer. This search will fail if the function does not
- have an incoming argument in $a7, but in that case, we can just
- set up the frame pointer at the very beginning of the
- function. */
+ total_size = compute_frame_size (get_frame_size ());
+ size_rtx = GEN_INT (total_size);
- for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (total_size < (1 << (12+3)))
+ insn = emit_insn (gen_entry (size_rtx));
+ else
{
- rtx pat;
-
- if (!INSN_P (insn))
- continue;
-
- pat = PATTERN (insn);
- if (GET_CODE (pat) == SET
- && GET_CODE (SET_SRC (pat)) == UNSPEC_VOLATILE
- && (XINT (SET_SRC (pat), 1) == UNSPECV_SET_FP))
- {
- set_frame_ptr_insn = insn;
- break;
- }
+ /* Use a8 as a temporary since a0-a7 may be live. */
+ rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
+ emit_insn (gen_entry (GEN_INT (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));
+ insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg));
}
- if (set_frame_ptr_insn)
+ if (frame_pointer_needed)
{
- /* for all instructions prior to set_frame_ptr_insn, replace
- hard_frame_pointer references with stack_pointer */
- for (insn = first; insn != set_frame_ptr_insn; insn = NEXT_INSN (insn))
+ if (cfun->machine->set_frame_ptr_insn)
{
- if (INSN_P (insn))
- PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
- hard_frame_pointer_rtx,
- stack_pointer_rtx);
- }
- }
- else
- {
- /* emit the frame pointer move immediately after the NOTE that starts
- the function */
- emit_insn_after (gen_movsi (hard_frame_pointer_rtx,
- stack_pointer_rtx), first);
- }
-}
-
+ rtx first;
-/* Set up the stack and frame (if desired) for the function. */
+ push_topmost_sequence ();
+ first = get_insns ();
+ pop_topmost_sequence ();
-void
-xtensa_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
-{
- unsigned long tsize = compute_frame_size (get_frame_size ());
-
- if (frame_pointer_needed)
- fprintf (file, "\t.frame\ta7, %ld\n", tsize);
- else
- fprintf (file, "\t.frame\tsp, %ld\n", tsize);
-
-
- if (tsize < (1 << (12+3)))
- {
- fprintf (file, "\tentry\tsp, %ld\n", tsize);
+ /* For all instructions prior to set_frame_ptr_insn, replace
+ hard_frame_pointer references with stack_pointer. */
+ for (insn = first;
+ insn != cfun->machine->set_frame_ptr_insn;
+ insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ {
+ PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
+ hard_frame_pointer_rtx,
+ stack_pointer_rtx);
+ df_insn_rescan (insn);
+ }
+ }
+ }
+ else
+ insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
+ stack_pointer_rtx));
}
- else
- {
- fprintf (file, "\tentry\tsp, 16\n");
- /* use a8 as a temporary since a0-a7 may be live */
- fprintf (file, "\tl32r\ta8, ");
- print_operand (file, frame_size_const, 0);
- fprintf (file, "\n\tsub\ta8, sp, a8\n");
- fprintf (file, "\tmovsp\tsp, a8\n");
- }
+ /* Create a note to describe the CFA. Because this is only used to set
+ DW_AT_frame_base for debug info, don't bother tracking changes through
+ each instruction in the prologue. It just takes up space. */
+ note_rtx = gen_rtx_SET (VOIDmode, (frame_pointer_needed
+ ? hard_frame_pointer_rtx
+ : stack_pointer_rtx),
+ plus_constant (stack_pointer_rtx, -total_size));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ note_rtx, REG_NOTES (insn));
}
-/* Do any necessary cleanup after a function to restore
- stack, frame, and regs. */
+/* Clear variables at function end. */
void
-xtensa_function_epilogue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
- rtx insn = get_last_insn ();
- /* If the last insn was a BARRIER, we don't have to write anything. */
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- if (insn == 0 || GET_CODE (insn) != BARRIER)
- fprintf (file, TARGET_DENSITY ? "\tretw.n\n" : "\tretw\n");
-
xtensa_current_frame_size = 0;
}
rtx
-xtensa_return_addr (count, frame)
- int count;
- rtx frame;
+xtensa_return_addr (int count, rtx frame)
{
- rtx result, retaddr;
+ rtx result, retaddr, curaddr, label;
if (count == -1)
- retaddr = gen_rtx_REG (Pmode, 0);
+ retaddr = gen_rtx_REG (Pmode, A0_REG);
else
{
rtx addr = plus_constant (frame, -4 * UNITS_PER_WORD);
@@ -2337,29 +2607,47 @@ xtensa_return_addr (count, frame)
/* The 2 most-significant bits of the return address on Xtensa hold
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 some address in the
+ code. */
+
+ /* Get the 2 high bits of a local label in the code. */
+ curaddr = gen_reg_rtx (Pmode);
+ label = gen_label_rtx ();
+ emit_label (label);
+ LABEL_PRESERVE_P (label) = 1;
+ emit_move_insn (curaddr, gen_rtx_LABEL_REF (Pmode, label));
+ emit_insn (gen_lshrsi3 (curaddr, curaddr, GEN_INT (30)));
+ emit_insn (gen_ashlsi3 (curaddr, curaddr, GEN_INT (30)));
+
+ /* Clear the 2 high bits of the return address. */
result = gen_reg_rtx (Pmode);
- emit_insn (gen_fix_return_addr (result, retaddr));
+ emit_insn (gen_ashlsi3 (result, retaddr, GEN_INT (2)));
+ emit_insn (gen_lshrsi3 (result, result, GEN_INT (2)));
+
+ /* Combine them to get the result. */
+ emit_insn (gen_iorsi3 (result, result, curaddr));
return result;
}
/* Create the va_list data type.
- This structure is set up by __builtin_saveregs. The __va_reg
- field points to a stack-allocated region holding the contents of the
- incoming argument registers. The __va_ndx field is an index initialized
- to the position of the first unnamed (variable) argument. This same index
- is also used to address the arguments passed in memory. Thus, the
- __va_stk field is initialized to point to the position of the first
- argument in memory offset to account for the arguments passed in
- registers. E.G., if there are 6 argument registers, and each register is
- 4 bytes, then __va_stk is set to $sp - (6 * 4); then __va_reg[N*4]
- references argument word N for 0 <= N < 6, and __va_stk[N*4] references
- argument word N for N >= 6. */
-
-tree
-xtensa_build_va_list ()
+
+ This structure is set up by __builtin_saveregs. The __va_reg field
+ points to a stack-allocated region holding the contents of the
+ incoming argument registers. The __va_ndx field is an index
+ initialized to the position of the first unnamed (variable)
+ argument. This same index is also used to address the arguments
+ passed in memory. Thus, the __va_stk field is initialized to point
+ to the position of the first argument in memory offset to account
+ for the arguments passed in registers and to account for the size
+ 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
+ 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
+ __va_stk[32] references the first argument on the stack. */
+
+static tree
+xtensa_build_builtin_va_list (void)
{
tree f_stk, f_reg, f_ndx, record, type_decl;
@@ -2391,36 +2679,30 @@ xtensa_build_va_list ()
/* Save the incoming argument registers on the stack. Returns the
address of the saved registers. */
-rtx
-xtensa_builtin_saveregs ()
+static rtx
+xtensa_builtin_saveregs (void)
{
- rtx gp_regs, dest;
- int arg_words = current_function_arg_words;
+ rtx gp_regs;
+ int arg_words = crtl->args.info.arg_words;
int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
- int i;
- if (gp_left == 0)
+ if (gp_left <= 0)
return const0_rtx;
- /* allocate the general-purpose register space */
+ /* Allocate the general-purpose register space. */
gp_regs = assign_stack_local
(BLKmode, MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1);
set_mem_alias_set (gp_regs, get_varargs_alias_set ());
/* Now store the incoming registers. */
- dest = change_address (gp_regs, SImode,
- plus_constant (XEXP (gp_regs, 0),
- arg_words * UNITS_PER_WORD));
-
- /* Note: Don't use move_block_from_reg() here because the incoming
- argument in a7 cannot be represented by hard_frame_pointer_rtx.
- Instead, call gen_raw_REG() directly so that we get a distinct
- instance of (REG:SI 7). */
- for (i = 0; i < gp_left; i++)
- {
- emit_move_insn (operand_subword (dest, i, 1, BLKmode),
- gen_raw_REG (SImode, GP_ARG_FIRST + arg_words + i));
- }
+ cfun->machine->need_a7_copy = true;
+ cfun->machine->vararg_a7 = true;
+ move_block_from_reg (GP_ARG_FIRST + arg_words,
+ adjust_address (gp_regs, BLKmode,
+ arg_words * UNITS_PER_WORD),
+ gp_left);
+ gcc_assert (cfun->machine->vararg_a7_copy != 0);
+ emit_insn_before (cfun->machine->vararg_a7_copy, get_insns ());
return XEXP (gp_regs, 0);
}
@@ -2429,10 +2711,8 @@ xtensa_builtin_saveregs ()
/* Implement `va_start' for varargs and stdarg. We look at the
current function to fill in an initial va_list. */
-void
-xtensa_va_start (valist, nextarg)
- tree valist;
- rtx nextarg ATTRIBUTE_UNUSED;
+static void
+xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
{
tree f_stk, stk;
tree f_reg, reg;
@@ -2440,34 +2720,39 @@ xtensa_va_start (valist, nextarg)
tree t, u;
int arg_words;
- arg_words = current_function_args_info.arg_words;
+ arg_words = crtl->args.info.arg_words;
f_stk = TYPE_FIELDS (va_list_type_node);
f_reg = TREE_CHAIN (f_stk);
f_ndx = TREE_CHAIN (f_reg);
- stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
- reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
- ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+ stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
+ reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), unshare_expr (valist),
+ f_reg, NULL_TREE);
+ ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist),
+ f_ndx, NULL_TREE);
/* Call __builtin_saveregs; save the result in __va_reg */
- current_function_arg_words = arg_words;
- u = make_tree (ptr_type_node, expand_builtin_saveregs ());
- t = build (MODIFY_EXPR, ptr_type_node, reg, u);
+ u = make_tree (sizetype, expand_builtin_saveregs ());
+ u = fold_convert (ptr_type_node, u);
+ t = build2 (MODIFY_EXPR, ptr_type_node, reg, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Set the __va_stk member to $arg_ptr - (size of __va_reg area) */
+ /* Set the __va_stk member to ($arg_ptr - 32). */
u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
- u = fold (build (PLUS_EXPR, ptr_type_node, u,
- build_int_2 (-MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, -1)));
- t = build (MODIFY_EXPR, ptr_type_node, stk, u);
+ u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u, size_int (-32));
+ t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- /* Set the __va_ndx member. */
- u = build_int_2 (arg_words * UNITS_PER_WORD, 0);
- t = build (MODIFY_EXPR, integer_type_node, ndx, u);
+ /* 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
+ alignment offset for __va_stk. */
+ if (arg_words >= MAX_ARGS_IN_REGISTERS)
+ arg_words += 2;
+ t = build2 (MODIFY_EXPR, integer_type_node, ndx,
+ build_int_cst (integer_type_node, arg_words * UNITS_PER_WORD));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
@@ -2475,136 +2760,150 @@ xtensa_va_start (valist, nextarg)
/* Implement `va_arg'. */
-rtx
-xtensa_va_arg (valist, type)
- tree valist, type;
+static tree
+xtensa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p ATTRIBUTE_UNUSED)
{
tree f_stk, stk;
tree f_reg, reg;
tree f_ndx, ndx;
- tree tmp, addr_tree, type_size;
- rtx array, orig_ndx, r, addr, size, va_size;
- rtx lab_false, lab_over, lab_false2;
+ tree type_size, array, orig_ndx, addr, size, va_size, t;
+ tree lab_false, lab_over, lab_false2;
+ bool indirect;
+
+ indirect = pass_by_reference (NULL, TYPE_MODE (type), type, false);
+ if (indirect)
+ type = build_pointer_type (type);
+
+ /* Handle complex values as separate real and imaginary parts. */
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ tree real_part, imag_part;
+
+ real_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
+ pre_p, NULL);
+ real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
+
+ imag_part = xtensa_gimplify_va_arg_expr (unshare_expr (valist),
+ TREE_TYPE (type),
+ pre_p, NULL);
+ imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
+
+ return build2 (COMPLEX_EXPR, type, real_part, imag_part);
+ }
f_stk = TYPE_FIELDS (va_list_type_node);
f_reg = TREE_CHAIN (f_stk);
f_ndx = TREE_CHAIN (f_reg);
- stk = build (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk);
- reg = build (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg);
- ndx = build (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx);
+ stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist,
+ f_stk, NULL_TREE);
+ reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), unshare_expr (valist),
+ f_reg, NULL_TREE);
+ ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), unshare_expr (valist),
+ f_ndx, NULL_TREE);
- type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+ type_size = size_in_bytes (type);
+ va_size = round_up (type_size, UNITS_PER_WORD);
+ gimplify_expr (&va_size, pre_p, NULL, is_gimple_val, fb_rvalue);
- va_size = gen_reg_rtx (SImode);
- tmp = fold (build (MULT_EXPR, sizetype,
- fold (build (TRUNC_DIV_EXPR, sizetype,
- fold (build (PLUS_EXPR, sizetype,
- type_size,
- size_int (UNITS_PER_WORD - 1))),
- size_int (UNITS_PER_WORD))),
- size_int (UNITS_PER_WORD)));
- r = expand_expr (tmp, va_size, SImode, EXPAND_NORMAL);
- if (r != va_size)
- emit_move_insn (va_size, r);
+ /* First align __va_ndx if necessary for this arg:
- /* First align __va_ndx to a double word boundary if necessary for this arg:
+ orig_ndx = (AP).__va_ndx;
+ if (__alignof__ (TYPE) > 4 )
+ orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
+ & -__alignof__ (TYPE)); */
- if (__alignof__ (TYPE) > 4)
- (AP).__va_ndx = (((AP).__va_ndx + 7) & -8)
- */
+ orig_ndx = get_initialized_tmp_var (ndx, pre_p, NULL);
if (TYPE_ALIGN (type) > BITS_PER_WORD)
{
- tmp = build (PLUS_EXPR, integer_type_node, ndx,
- build_int_2 ((2 * UNITS_PER_WORD) - 1, 0));
- tmp = build (BIT_AND_EXPR, integer_type_node, tmp,
- build_int_2 (-2 * UNITS_PER_WORD, -1));
- tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
- TREE_SIDE_EFFECTS (tmp) = 1;
- expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_UNIT;
+
+ t = build2 (PLUS_EXPR, integer_type_node, unshare_expr (orig_ndx),
+ build_int_cst (integer_type_node, align - 1));
+ t = build2 (BIT_AND_EXPR, integer_type_node, t,
+ build_int_cst (integer_type_node, -align));
+ gimplify_assign (unshare_expr (orig_ndx), t, pre_p);
}
/* Increment __va_ndx to point past the argument:
- orig_ndx = (AP).__va_ndx;
- (AP).__va_ndx += __va_size (TYPE);
- */
-
- orig_ndx = gen_reg_rtx (SImode);
- r = expand_expr (ndx, orig_ndx, SImode, EXPAND_NORMAL);
- if (r != orig_ndx)
- emit_move_insn (orig_ndx, r);
+ (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
- tmp = build (PLUS_EXPR, integer_type_node, ndx,
- make_tree (intSI_type_node, va_size));
- tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
- TREE_SIDE_EFFECTS (tmp) = 1;
- expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ t = fold_convert (integer_type_node, va_size);
+ t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, t);
+ gimplify_assign (unshare_expr (ndx), t, pre_p);
/* Check if the argument is in registers:
if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
- && !MUST_PASS_IN_STACK (type))
- __array = (AP).__va_reg;
- */
+ && !must_pass_in_stack (type))
+ __array = (AP).__va_reg; */
- array = gen_reg_rtx (Pmode);
+ array = create_tmp_var (ptr_type_node, NULL);
- lab_over = NULL_RTX;
- if (!MUST_PASS_IN_STACK (VOIDmode, type))
+ lab_over = NULL;
+ if (!targetm.calls.must_pass_in_stack (TYPE_MODE (type), type))
{
- lab_false = gen_label_rtx ();
- lab_over = gen_label_rtx ();
+ lab_false = create_artificial_label ();
+ lab_over = create_artificial_label ();
+
+ t = build2 (GT_EXPR, boolean_type_node, unshare_expr (ndx),
+ build_int_cst (integer_type_node,
+ MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD));
+ t = build3 (COND_EXPR, void_type_node, t,
+ build1 (GOTO_EXPR, void_type_node, lab_false),
+ NULL_TREE);
+ gimplify_and_add (t, pre_p);
- emit_cmp_and_jump_insns (expand_expr (ndx, NULL_RTX, SImode,
- EXPAND_NORMAL),
- GEN_INT (MAX_ARGS_IN_REGISTERS
- * UNITS_PER_WORD),
- GT, const1_rtx, SImode, 0, lab_false);
+ gimplify_assign (unshare_expr (array), reg, pre_p);
- r = expand_expr (reg, array, Pmode, EXPAND_NORMAL);
- if (r != array)
- emit_move_insn (array, r);
+ t = build1 (GOTO_EXPR, void_type_node, lab_over);
+ gimplify_and_add (t, pre_p);
- emit_jump_insn (gen_jump (lab_over));
- emit_barrier ();
- emit_label (lab_false);
+ t = build1 (LABEL_EXPR, void_type_node, lab_false);
+ gimplify_and_add (t, pre_p);
}
+
/* ...otherwise, the argument is on the stack (never split between
registers and the stack -- change __va_ndx if necessary):
else
{
- if (orig_ndx < __MAX_ARGS_IN_REGISTERS * 4)
- (AP).__va_ndx = __MAX_ARGS_IN_REGISTERS * 4 + __va_size (TYPE);
+ if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
+ (AP).__va_ndx = 32 + __va_size (TYPE);
__array = (AP).__va_stk;
- }
- */
+ } */
+
+ lab_false2 = create_artificial_label ();
- lab_false2 = gen_label_rtx ();
- emit_cmp_and_jump_insns (orig_ndx,
- GEN_INT (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD),
- GE, const1_rtx, SImode, 0, lab_false2);
+ t = build2 (GT_EXPR, boolean_type_node, unshare_expr (orig_ndx),
+ build_int_cst (integer_type_node,
+ MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD));
+ t = build3 (COND_EXPR, void_type_node, t,
+ build1 (GOTO_EXPR, void_type_node, lab_false2),
+ NULL_TREE);
+ gimplify_and_add (t, pre_p);
- tmp = build (PLUS_EXPR, sizetype, make_tree (intSI_type_node, va_size),
- build_int_2 (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD, 0));
- tmp = build (MODIFY_EXPR, integer_type_node, ndx, tmp);
- TREE_SIDE_EFFECTS (tmp) = 1;
- expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ t = size_binop (PLUS_EXPR, unshare_expr (va_size), size_int (32));
+ t = fold_convert (integer_type_node, t);
+ gimplify_assign (unshare_expr (ndx), t, pre_p);
- emit_label (lab_false2);
+ t = build1 (LABEL_EXPR, void_type_node, lab_false2);
+ gimplify_and_add (t, pre_p);
- r = expand_expr (stk, array, Pmode, EXPAND_NORMAL);
- if (r != array)
- emit_move_insn (array, r);
+ gimplify_assign (array, stk, pre_p);
- if (lab_over != NULL_RTX)
- emit_label (lab_over);
+ if (lab_over)
+ {
+ t = build1 (LABEL_EXPR, void_type_node, lab_over);
+ gimplify_and_add (t, pre_p);
+ }
/* Given the base array pointer (__array) and index to the subsequent
@@ -2615,44 +2914,147 @@ xtensa_va_arg (valist, type)
: __va_size (TYPE))
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)
+ {
+ t = fold_build2 (GE_EXPR, boolean_type_node, unshare_expr (type_size),
+ size_int (PARM_BOUNDARY / BITS_PER_UNIT));
+ t = fold_build3 (COND_EXPR, sizetype, t, unshare_expr (va_size),
+ unshare_expr (type_size));
+ size = t;
+ }
+ else
+ size = unshare_expr (va_size);
+
+ t = fold_convert (sizetype, unshare_expr (ndx));
+ t = build2 (MINUS_EXPR, sizetype, t, size);
+ addr = build2 (POINTER_PLUS_EXPR, ptr_type_node, unshare_expr (array), t);
+
+ addr = fold_convert (build_pointer_type (type), addr);
+ if (indirect)
+ addr = build_va_arg_indirect_ref (addr);
+ return build_va_arg_indirect_ref (addr);
+}
+
+
+/* Builtins. */
+
+enum xtensa_builtin
+{
+ XTENSA_BUILTIN_UMULSIDI3,
+ XTENSA_BUILTIN_THREAD_POINTER,
+ XTENSA_BUILTIN_SET_THREAD_POINTER,
+ XTENSA_BUILTIN_max
+};
+
+
+static void
+xtensa_init_builtins (void)
+{
+ tree ftype, decl;
+
+ ftype = build_function_type_list (unsigned_intDI_type_node,
+ unsigned_intSI_type_node,
+ unsigned_intSI_type_node, NULL_TREE);
- size = gen_reg_rtx (SImode);
- emit_move_insn (size, va_size);
-
- if (BYTES_BIG_ENDIAN)
+ decl = add_builtin_function ("__builtin_umulsidi3", ftype,
+ XTENSA_BUILTIN_UMULSIDI3, BUILT_IN_MD,
+ "__umulsidi3", NULL_TREE);
+ TREE_NOTHROW (decl) = 1;
+ TREE_READONLY (decl) = 1;
+
+ if (TARGET_THREADPTR)
{
- rtx lab_use_va_size = gen_label_rtx ();
+ ftype = build_function_type (ptr_type_node, void_list_node);
+ decl = add_builtin_function ("__builtin_thread_pointer", ftype,
+ XTENSA_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+ NULL, NULL_TREE);
+ TREE_READONLY (decl) = 1;
+ TREE_NOTHROW (decl) = 1;
+
+ ftype = build_function_type_list (void_type_node, ptr_type_node,
+ NULL_TREE);
+ decl = add_builtin_function ("__builtin_set_thread_pointer", ftype,
+ XTENSA_BUILTIN_SET_THREAD_POINTER,
+ BUILT_IN_MD, NULL, NULL_TREE);
+ TREE_NOTHROW (decl) = 1;
+ }
+}
+
+
+static tree
+xtensa_fold_builtin (tree fndecl, tree arglist, bool ignore ATTRIBUTE_UNUSED)
+{
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ tree arg0, arg1;
- emit_cmp_and_jump_insns (expand_expr (type_size, NULL_RTX, SImode,
- EXPAND_NORMAL),
- GEN_INT (PARM_BOUNDARY / BITS_PER_UNIT),
- GE, const1_rtx, SImode, 0, lab_use_va_size);
+ switch (fcode)
+ {
+ case XTENSA_BUILTIN_UMULSIDI3:
+ arg0 = TREE_VALUE (arglist);
+ arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+ if ((TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+ || TARGET_MUL32_HIGH)
+ return fold_build2 (MULT_EXPR, unsigned_intDI_type_node,
+ fold_convert (unsigned_intDI_type_node, arg0),
+ fold_convert (unsigned_intDI_type_node, arg1));
+ break;
- r = expand_expr (type_size, size, SImode, EXPAND_NORMAL);
- if (r != size)
- emit_move_insn (size, r);
+ case XTENSA_BUILTIN_THREAD_POINTER:
+ case XTENSA_BUILTIN_SET_THREAD_POINTER:
+ break;
- emit_label (lab_use_va_size);
+ default:
+ internal_error ("bad builtin code");
+ break;
}
- addr_tree = build (PLUS_EXPR, ptr_type_node,
- make_tree (ptr_type_node, array),
- ndx);
- addr_tree = build (MINUS_EXPR, ptr_type_node, addr_tree,
- make_tree (intSI_type_node, size));
- addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
- addr = copy_to_reg (addr);
- return addr;
+ return NULL;
+}
+
+
+static rtx
+xtensa_expand_builtin (tree exp, rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ rtx arg;
+
+ switch (fcode)
+ {
+ case XTENSA_BUILTIN_UMULSIDI3:
+ /* The umulsidi3 builtin is just a mechanism to avoid calling the real
+ __umulsidi3 function when the Xtensa configuration can directly
+ implement it. If not, just call the function. */
+ return expand_call (exp, target, ignore);
+
+ case XTENSA_BUILTIN_THREAD_POINTER:
+ if (!target || !register_operand (target, Pmode))
+ target = gen_reg_rtx (Pmode);
+ emit_insn (gen_load_tp (target));
+ return target;
+
+ case XTENSA_BUILTIN_SET_THREAD_POINTER:
+ arg = expand_normal (CALL_EXPR_ARG (exp, 0));
+ if (!register_operand (arg, Pmode))
+ arg = copy_to_mode_reg (Pmode, arg);
+ emit_insn (gen_set_tp (arg));
+ return const0_rtx;
+
+ default:
+ internal_error ("bad builtin code");
+ }
+ return NULL_RTX;
}
enum reg_class
-xtensa_preferred_reload_class (x, class, isoutput)
- rtx x;
- enum reg_class class;
- int isoutput;
+xtensa_preferred_reload_class (rtx x, enum reg_class rclass, int isoutput)
{
if (!isoutput && CONSTANT_P (x) && GET_CODE (x) == CONST_DOUBLE)
return NO_REGS;
@@ -2663,35 +3065,34 @@ xtensa_preferred_reload_class (x, class, isoutput)
won't know that it is live because the hard frame pointer is
treated specially. */
- if (class == AR_REGS || class == GR_REGS)
+ if (rclass == AR_REGS || rclass == GR_REGS)
return RL_REGS;
- return class;
+ return rclass;
}
enum reg_class
-xtensa_secondary_reload_class (class, mode, x, isoutput)
- enum reg_class class;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx x;
- int isoutput;
+xtensa_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
+ enum machine_mode mode, secondary_reload_info *sri)
{
int regno;
- if (GET_CODE (x) == SIGN_EXTEND)
- x = XEXP (x, 0);
- regno = xt_true_regnum (x);
-
- if (!isoutput)
+ if (in_p && constantpool_mem_p (x))
{
- if (class == FP_REGS && constantpool_mem_p (x))
+ if (rclass == FP_REGS)
return RL_REGS;
+
+ if (mode == QImode)
+ sri->icode = CODE_FOR_reloadqi_literal;
+ else if (mode == HImode)
+ sri->icode = CODE_FOR_reloadhi_literal;
}
+ regno = xt_true_regnum (x);
if (ACC_REG_P (regno))
- return ((class == GR_REGS || class == RL_REGS) ? NO_REGS : RL_REGS);
- if (class == ACC_REG)
+ return ((rclass == GR_REGS || rclass == RL_REGS) ? NO_REGS : RL_REGS);
+ if (rclass == ACC_REG)
return (GP_REG_P (regno) ? NO_REGS : RL_REGS);
return NO_REGS;
@@ -2699,7 +3100,7 @@ xtensa_secondary_reload_class (class, mode, x, isoutput)
void
-order_regs_for_local_alloc ()
+order_regs_for_local_alloc (void)
{
if (!leaf_function_p ())
{
@@ -2711,9 +3112,9 @@ order_regs_for_local_alloc ()
int i, num_arg_regs;
int nxt = 0;
- /* use the AR registers in increasing order (skipping a0 and a1)
- but save the incoming argument registers for a last resort */
- num_arg_regs = current_function_args_info.arg_words;
+ /* Use the AR registers in increasing order (skipping a0 and a1)
+ but save the incoming argument registers for a last resort. */
+ num_arg_regs = crtl->args.info.arg_words;
if (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++)
@@ -2721,11 +3122,11 @@ order_regs_for_local_alloc ()
for (i = 0; i < num_arg_regs; 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++)
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++)
reg_alloc_order[nxt++] = FP_REG_FIRST + i;
@@ -2740,64 +3141,11 @@ order_regs_for_local_alloc ()
}
-/* A customized version of reg_overlap_mentioned_p that only looks for
- references to a7 (as opposed to hard_frame_pointer_rtx). */
-
-int
-a7_overlap_mentioned_p (x)
- rtx x;
-{
- int i, j;
- unsigned int x_regno;
- const char *fmt;
-
- if (GET_CODE (x) == REG)
- {
- x_regno = REGNO (x);
- return (x != hard_frame_pointer_rtx
- && x_regno < A7_REG + 1
- && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
- }
-
- if (GET_CODE (x) == SUBREG
- && GET_CODE (SUBREG_REG (x)) == REG
- && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER)
- {
- x_regno = subreg_regno (x);
- return (SUBREG_REG (x) != hard_frame_pointer_rtx
- && x_regno < A7_REG + 1
- && x_regno + HARD_REGNO_NREGS (A7_REG, GET_MODE (x)) > A7_REG);
- }
-
- /* X does not match, so try its subexpressions. */
- fmt = GET_RTX_FORMAT (GET_CODE (x));
- for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- {
- if (a7_overlap_mentioned_p (XEXP (x, i)))
- return 1;
- }
- else if (fmt[i] == 'E')
- {
- for (j = XVECLEN (x, i) - 1; j >=0; j--)
- if (a7_overlap_mentioned_p (XVECEXP (x, i, j)))
- return 1;
- }
- }
-
- return 0;
-}
-
-
/* Some Xtensa targets support multiple bss sections. If the section
name ends with ".bss", add SECTION_BSS to the flags. */
static unsigned int
-xtensa_multibss_section_type_flags (decl, name, reloc)
- 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);
const char *suffix;
@@ -2809,7 +3157,7 @@ xtensa_multibss_section_type_flags (decl, name, reloc)
&& DECL_INITIAL (decl) == NULL_TREE))
flags |= SECTION_BSS; /* @nobits */
else
- warning ("only uninitialized variables can be placed in a "
+ warning (0, "only uninitialized variables can be placed in a "
".bss section");
}
@@ -2819,24 +3167,22 @@ xtensa_multibss_section_type_flags (decl, name, reloc)
/* The literal pool stays with the function. */
-static void
-xtensa_select_rtx_section (mode, x, align)
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx x ATTRIBUTE_UNUSED;
- unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+static section *
+xtensa_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED,
+ unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
- function_section (current_function_decl);
+ return function_section (current_function_decl);
}
+
/* Compute a (partial) cost for rtx X. Return true if the complete
cost has been computed, and false if subexpressions should be
scanned. In either case, *TOTAL contains the cost result. */
static bool
-xtensa_rtx_costs (x, code, outer_code, total)
- rtx x;
- int code, outer_code;
- int *total;
+xtensa_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed ATTRIBUTE_UNUSED)
{
switch (code)
{
@@ -2877,11 +3223,13 @@ xtensa_rtx_costs (x, code, outer_code, total)
case LSHIFTRT:
case ROTATE:
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;
}
if (xtensa_simm12b (INTVAL (x)))
*total = 5;
+ else if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (2);
else
*total = 6;
return true;
@@ -2889,11 +3237,17 @@ xtensa_rtx_costs (x, code, outer_code, total)
case CONST:
case LABEL_REF:
case SYMBOL_REF:
- *total = 5;
+ if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = 5;
return true;
case CONST_DOUBLE:
- *total = 7;
+ if (TARGET_CONST16)
+ *total = COSTS_N_INSNS (4);
+ else
+ *total = 7;
return true;
case MEM:
@@ -2909,9 +3263,14 @@ xtensa_rtx_costs (x, code, outer_code, total)
}
case FFS:
+ case CTZ:
*total = COSTS_N_INSNS (TARGET_NSA ? 5 : 50);
return true;
+ case CLZ:
+ *total = COSTS_N_INSNS (TARGET_NSA ? 1 : 50);
+ return true;
+
case NOT:
*total = COSTS_N_INSNS ((GET_MODE (x) == DImode) ? 3 : 2);
return true;
@@ -2968,8 +3327,10 @@ xtensa_rtx_costs (x, code, outer_code, total)
enum machine_mode xmode = GET_MODE (x);
if (xmode == SFmode)
*total = COSTS_N_INSNS (TARGET_HARD_FLOAT ? 4 : 50);
- else if (xmode == DFmode || xmode == DImode)
+ else if (xmode == DFmode)
*total = COSTS_N_INSNS (50);
+ else if (xmode == DImode)
+ *total = COSTS_N_INSNS (TARGET_MUL32_HIGH ? 10 : 50);
else if (TARGET_MUL32)
*total = COSTS_N_INSNS (4);
else if (TARGET_MAC16)
@@ -2996,7 +3357,7 @@ xtensa_rtx_costs (x, code, outer_code, total)
return true;
}
}
- /* fall through */
+ /* Fall through. */
case UDIV:
case UMOD:
@@ -3040,4 +3401,115 @@ xtensa_rtx_costs (x, code, outer_code, total)
}
}
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+xtensa_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+{
+ return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)
+ > 4 * UNITS_PER_WORD);
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE. */
+
+rtx
+xtensa_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED,
+ bool outgoing)
+{
+ return gen_rtx_REG ((INTEGRAL_TYPE_P (valtype)
+ && TYPE_PRECISION (valtype) < BITS_PER_WORD)
+ ? SImode : TYPE_MODE (valtype),
+ outgoing ? GP_OUTGOING_RETURN : GP_RETURN);
+}
+
+/* TRAMPOLINE_TEMPLATE: For Xtensa, the trampoline must perform an ENTRY
+ instruction with a minimal stack frame in order to get some free
+ registers. Once the actual call target is known, the proper stack frame
+ size is extracted from the ENTRY instruction at the target and the
+ current frame is adjusted to match. The trampoline then transfers
+ control to the instruction following the ENTRY at the target. Note:
+ this assumes that the target begins with an ENTRY instruction. */
+
+void
+xtensa_trampoline_template (FILE *stream)
+{
+ bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS);
+
+ fprintf (stream, "\t.begin no-transform\n");
+ fprintf (stream, "\tentry\tsp, %d\n", MIN_FRAME_SIZE);
+
+ if (use_call0)
+ {
+ /* Save the return address. */
+ fprintf (stream, "\tmov\ta10, a0\n");
+
+ /* Use a CALL0 instruction to skip past the constants and in the
+ process get the PC into A0. This allows PC-relative access to
+ the constants without relying on L32R. */
+ fprintf (stream, "\tcall0\t.Lskipconsts\n");
+ }
+ else
+ fprintf (stream, "\tj\t.Lskipconsts\n");
+
+ fprintf (stream, "\t.align\t4\n");
+ fprintf (stream, ".Lchainval:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lfnaddr:%s0\n", integer_asm_op (4, TRUE));
+ fprintf (stream, ".Lskipconsts:\n");
+
+ /* Load the static chain and function address from the trampoline. */
+ if (use_call0)
+ {
+ fprintf (stream, "\taddi\ta0, a0, 3\n");
+ fprintf (stream, "\tl32i\ta9, a0, 0\n");
+ fprintf (stream, "\tl32i\ta8, a0, 4\n");
+ }
+ else
+ {
+ fprintf (stream, "\tl32r\ta9, .Lchainval\n");
+ fprintf (stream, "\tl32r\ta8, .Lfnaddr\n");
+ }
+
+ /* Store the static chain. */
+ fprintf (stream, "\ts32i\ta9, sp, %d\n", MIN_FRAME_SIZE - 20);
+
+ /* Set the proper stack pointer value. */
+ fprintf (stream, "\tl32i\ta9, a8, 0\n");
+ fprintf (stream, "\textui\ta9, a9, %d, 12\n",
+ TARGET_BIG_ENDIAN ? 8 : 12);
+ fprintf (stream, "\tslli\ta9, a9, 3\n");
+ fprintf (stream, "\taddi\ta9, a9, %d\n", -MIN_FRAME_SIZE);
+ fprintf (stream, "\tsub\ta9, sp, a9\n");
+ fprintf (stream, "\tmovsp\tsp, a9\n");
+
+ if (use_call0)
+ /* Restore the return address. */
+ fprintf (stream, "\tmov\ta0, a10\n");
+
+ /* Jump to the instruction following the ENTRY. */
+ fprintf (stream, "\taddi\ta8, a8, 3\n");
+ fprintf (stream, "\tjx\ta8\n");
+
+ /* Pad size to a multiple of TRAMPOLINE_ALIGNMENT. */
+ if (use_call0)
+ fprintf (stream, "\t.byte\t0\n");
+ else
+ fprintf (stream, "\tnop\n");
+
+ fprintf (stream, "\t.end no-transform\n");
+}
+
+
+void
+xtensa_initialize_trampoline (rtx addr, rtx func, rtx chain)
+{
+ bool use_call0 = (TARGET_CONST16 || TARGET_ABSOLUTE_LITERALS);
+ int chain_off = use_call0 ? 12 : 8;
+ int func_off = use_call0 ? 16 : 12;
+ emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, chain_off)), chain);
+ emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, func_off)), func);
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_sync_caches"),
+ 0, VOIDmode, 1, addr, Pmode);
+}
+
+
#include "gt-xtensa.h"