OSDN Git Service

* configure.ac: Add HAVE_AS_TLS check for Xtensa.
[pf3gnuchains/gcc-fork.git] / gcc / config / xtensa / xtensa.c
index 23798b0..4e7edef 100644 (file)
@@ -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
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -48,6 +48,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #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
@@ -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(())
 {
   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,92 +125,32 @@ 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 int b4const_or_zero (int);
 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 enum machine_mode xtensa_find_mode_for_size (unsigned);
 static struct machine_function * xtensa_init_machine_status (void);
+static rtx xtensa_legitimize_tls_address (rtx);
+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 void xtensa_select_rtx_section (enum machine_mode, rtx,
-                                      unsigned HOST_WIDE_INT);
-static bool xtensa_rtx_costs (rtx, int, int, int *);
+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 current_function_arg_words;
 static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
   REG_ALLOC_ORDER;
 \f
@@ -228,25 +172,114 @@ 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_RTX_COSTS
 #define TARGET_RTX_COSTS xtensa_rtx_costs
 #undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_0
+#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;
+
 \f
+/* 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 (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:
@@ -261,36 +294,28 @@ xtensa_b4constu (int v)
     case 64:
     case 128:
     case 256:
-      return 1;
+      return true;
     }
-  return 0;
+  return false;
 }
 
-int
-xtensa_simm8x256 (int v)
-{
-  return (v & 255) == 0 && (v >= -32768 && v <= 32512);
-}
 
-int
-xtensa_ai4const (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 (int v)
-{
-  return v >= -32 && v <= 95;
-}
 
-int
-xtensa_b4const (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:
@@ -305,51 +330,28 @@ xtensa_b4const (int v)
     case 64:
     case 128:
     case 256:
-      return 1;
+      return true;
     }
-  return 0;
-}
-
-int
-xtensa_simm8 (int v)
-{
-  return v >= -128 && v <= 127;
-}
-
-int
-xtensa_tp7 (int v)
-{
-  return (v >= 7 && v <= 22);
-}
-
-int
-xtensa_lsi4x4 (int v)
-{
-  return (v & 3) == 0 && (v >= 0 && v <= 60);
+  return false;
 }
 
-int
-xtensa_simm12b (int v)
-{
-  return v >= -2048 && v <= 2047;
-}
 
-int
-xtensa_uimm8 (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 (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 (int v)
-{
-  return (v & 3) == 0 && (v >= 0 && v <= 1020);
+  return false;
 }
 
 
@@ -380,52 +382,6 @@ xt_true_regnum (rtx x)
 
 
 int
-add_operand (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 (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 (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 (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 (enum machine_mode mode, rtx *operands)
 {
   /* Either the destination or source must be a register, and the
@@ -455,184 +411,24 @@ xtensa_valid_move (enum machine_mode mode, rtx *operands)
 
 
 int
-mask_operand (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 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return ((GET_CODE (op) == CONST_INT)
-         && xtensa_mask_immediate ((1 << INTVAL (op)) - 1));
-}
-
-
-int
-sext_operand (rtx op, enum machine_mode mode)
-{
-  if (TARGET_SEXT)
-    return nonimmed_operand (op, mode);
-  return mem_operand (op, mode);
-}
-
-
-int
-sext_fldsz_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return ((GET_CODE (op) == CONST_INT) && xtensa_tp7 (INTVAL (op) - 1));
-}
-
-
-int
-lsbitnum_operand (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 (int v)
-{
-  if (v == 0)
-    return TRUE;
-  return xtensa_b4const (v);
-}
-
-
-int
-branch_operand (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 (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 (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.  */
-      if (flag_pic)
-       {
-         tree callee, callee_sec, caller_sec;
-
-         if (GET_CODE (op) != SYMBOL_REF || !SYMBOL_REF_LOCAL_P (op))
-           return FALSE;
-
-         /* Don't attempt a direct call if the callee is known to be in
-            a different section, since there's a good chance it will be
-            out of range.  */
-
-         if (flag_function_sections
-             || DECL_ONE_ONLY (current_function_decl))
-           return FALSE;
-         caller_sec = DECL_SECTION_NAME (current_function_decl);
-         callee = SYMBOL_REF_DECL (op);
-         if (callee)
-           {
-             if (DECL_ONE_ONLY (callee))
-               return FALSE;
-             callee_sec = DECL_SECTION_NAME (callee);
-             if (((caller_sec == NULL_TREE) ^ (callee_sec == NULL_TREE))
-                 || (caller_sec != NULL_TREE
-                     && strcmp (TREE_STRING_POINTER (caller_sec),
-                                TREE_STRING_POINTER (callee_sec)) != 0))
-               return FALSE;
-           }
-         else if (caller_sec != NULL_TREE)
-           return FALSE;
-       }
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-
-int
-move_operand (rtx op, enum machine_mode mode)
-{
-  if (register_operand (op, mode)
-      || memory_operand (op, mode))
-    return TRUE;
-
-  switch (mode)
-    {
-    case DFmode:
-    case SFmode:
-      return TARGET_CONST16 && CONSTANT_P (op);
-
-    case DImode:
-    case SImode:
-      if (TARGET_CONST16)
-       return CONSTANT_P (op);
-      /* fall through */
-
-    case HImode:
-    case QImode:
-      /* 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 && xtensa_simm12b (INTVAL (op)))
-       return TRUE;
-      break;
-
-    default:
-      break;
-    }
-
-  return FALSE;
-}
-
-
-int
 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;
@@ -648,12 +444,12 @@ constantpool_address_p (rtx 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))
@@ -672,49 +468,23 @@ constantpool_address_p (rtx addr)
 int
 constantpool_mem_p (rtx op)
 {
+  if (GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
   if (GET_CODE (op) == MEM)
     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 (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 ((GET_CODE (op) != CONST_DOUBLE)
-      || (mode != GET_MODE (op))
-      || (mode != DFmode && mode != SFmode))
-    return FALSE;
-
-  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+  if (! TARGET_HAVE_TLS)
+    return false;
 
-  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 (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;
 }
 
 
@@ -724,7 +494,7 @@ 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);
 
@@ -733,85 +503,10 @@ xtensa_extend_reg (rtx dst, rtx src)
 }
 
 
-int
-branch_operator (rtx x, enum machine_mode mode)
+bool
+xtensa_mem_offset (unsigned v, 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 (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 (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 (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 (unsigned v, enum machine_mode mode)
-{
-  switch (mode)
+  switch (mode)
     {
     case BLKmode:
       /* Handle the worst case for block moves.  See xtensa_expand_block_move
@@ -877,7 +572,7 @@ gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
   struct cmp_info
   {
     enum rtx_code test_code;   /* test code to use in insn */
-    int (*const_range_p) (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 */
@@ -887,13 +582,13 @@ gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
 
   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 */
@@ -906,8 +601,7 @@ gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
   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 ];
 
@@ -960,7 +654,7 @@ gen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
       cmp1 = temp;
     }
 
-  return gen_rtx (p_info->test_code, VOIDmode, cmp0, cmp1);
+  return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);
 }
 
 
@@ -984,8 +678,18 @@ gen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
     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;
+    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 (test_code, VOIDmode, cmp0, cmp1));
+      fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
       reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
     }
 
@@ -999,7 +703,7 @@ gen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */
   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);
 }
 
 
@@ -1017,7 +721,7 @@ xtensa_expand_conditional_branch (rtx *operands, enum rtx_code 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;
@@ -1026,7 +730,8 @@ xtensa_expand_conditional_branch (rtx *operands, enum rtx_code 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;
@@ -1072,18 +777,18 @@ gen_conditional_move (rtx 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,
@@ -1093,7 +798,7 @@ gen_conditional_move (rtx 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;
@@ -1103,7 +808,7 @@ gen_conditional_move (rtx cmp)
                {
                case LT: code = GE; break;
                case GE: code = LT; break;
-               default: abort ();
+               default: gcc_unreachable ();
                }
            }
 
@@ -1113,7 +818,7 @@ gen_conditional_move (rtx 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))
@@ -1195,7 +900,7 @@ xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 
   switch (GET_CODE (operands[0]))
@@ -1211,7 +916,7 @@ xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -1224,13 +929,38 @@ xtensa_split_operand_pair (rtx operands[4], enum machine_mode mode)
 int
 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]))))
+  rtx src = operands[1];
+
+  if (CONSTANT_P (src)
+      && (GET_CODE (src) != CONST_INT || ! xtensa_simm12b (INTVAL (src))))
     {
-      if (!TARGET_CONST16)
-       operands[1] = force_const_mem (SImode, 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 (! 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
@@ -1238,28 +968,25 @@ xtensa_emit_move_sequence (rtx *operands, enum machine_mode mode)
 
       if (mode != SImode)
        {
-         if (register_operand (operands[0], mode))
+         if (register_operand (dst, mode))
            {
-             operands[0] = simplify_gen_subreg (SImode, operands[0], mode, 0);
-             emit_move_insn (operands[0], operands[1]);
+             emit_move_insn (simplify_gen_subreg (SImode, dst, mode, 0), src);
              return 1;
            }
          else
            {
-             operands[1] = force_reg (SImode, operands[1]);
-             operands[1] = gen_lowpart_SUBREG (mode, operands[1]);
+             src = force_reg (SImode, src);
+             src = gen_lowpart_SUBREG (mode, src);
+             operands[1] = src;
            }
        }
     }
 
-  if (!(reload_in_progress | reload_completed))
-    {
-      if (!xtensa_valid_move (mode, operands))
-       operands[1] = force_reg (mode, operands[1]);
+  if (!(reload_in_progress | reload_completed)
+      && !xtensa_valid_move (mode, operands))
+    operands[1] = force_reg (mode, operands[1]);
 
-      if (xtensa_copy_incoming_a7 (operands, mode))
-       return 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
@@ -1290,76 +1017,128 @@ fixup_subreg_mem (rtx 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 (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;
 
-      /* 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.  */
+  /* 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.  */
 
-      cfun->machine->incoming_a7_copied = true;
+  start_sequence ();
+  tmp = gen_reg_rtx (mode);
 
-      return 1;
+  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 ();
     }
 
-  return 0;
+  cfun->machine->set_frame_ptr_insn = emit_insn (gen_set_frame_ptr ());
+
+  /* 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 ();
+
+  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 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
@@ -1369,280 +1148,772 @@ xtensa_copy_incoming_a7 (rtx *operands, enum machine_mode mode)
 int
 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]);
 
-  emit_insn (gen_movstrsi_internal (operands[0], operands[1],
-                                   operands[2], operands[3]));
   return 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 */
+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 meminsnbuf
+struct alignment_context
 {
-  char template[30];
-  rtx operands[2];
+  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));
+
+  ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
+}
+
+
+/* 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 (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);
+
+  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);
+    }
 
-  ldinsns = (struct meminsnbuf *)
-    alloca (chunk_size * sizeof (struct meminsnbuf));
-  stinsns = (struct meminsnbuf *)
-    alloca (chunk_size * sizeof (struct meminsnbuf));
+  /* 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);
 
-  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];
+  /* 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));
 
-  while (bytes > 0)
+  /* 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);
+
+  /* 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);
+
+  /* Loop internal if so.  */
+  emit_cmp_and_jump_insns (oldval, val, NE, const0_rtx, SImode, true, csloop);
+
+  emit_label (csend);
+
+  /* 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);
+}
+
+
+/* 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.  */
+
+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)
     {
-      int n;
+    case PLUS:
+    case MINUS:
+      orig = gen_reg_rtx (SImode);
+      convert_move (orig, val, 1);
+      break;
 
-      for (n = 0; n < chunk_size; n++)
+    case SET:
+    case IOR:
+    case XOR:
+      break;
+
+    case MULT: /* NAND */
+    case AND:
+      /* val = "11..1<val>11..1" */
+      val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* Load full word.  Subsequent loads are performed by S32C1I.  */
+  cmp = force_reg (SImode, ac.memsi);
+
+  emit_label (csloop);
+  emit_move_insn (old, cmp);
+
+  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;
+
+    case AND:
+    case IOR:
+    case XOR:
+      tmp = expand_simple_binop (SImode, code, old, val,
+                                new_rtx, 1, OPTAB_DIRECT);
+      break;
+
+    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;
+
+    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);
+
+  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 (void)
+{
+  /* Set flag to cause FRAME_POINTER_REQUIRED to be set.  */
+  cfun->machine->accesses_prev_frame = 1;
+
+  emit_library_call
+    (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
+     0, VOIDmode, 0);
+}
+
+
+/* Emit the assembly for the end of a zero-cost loop.  Normally we just emit
+   a comment showing where the end of the loop is.  However, if there is a
+   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
+   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 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 (rtx insn, rtx *operands)
+{
+  char done = 0;
+
+  for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
+    {
+      switch (GET_CODE (insn))
        {
-         rtx addr, mem;
+       case NOTE:
+       case BARRIER:
+         break;
 
-         if (bytes == 0)
-           {
-             chunk_size = n;
-             break;
-           }
+       case CODE_LABEL:
+         output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
+         done = 1;
+         break;
 
-         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];
-           }
+       default:
+         {
+           rtx body = PATTERN (insn);
 
-         /* 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;
-       }
+           if (GET_CODE (body) == JUMP_INSN)
+             {
+               output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
+               done = 1;
+             }
+           else if ((GET_CODE (body) != USE)
+                    && (GET_CODE (body) != CLOBBER))
+             done = 1;
+         }
+         break;
+        }
+    }
+
+  output_asm_insn ("# loop end for %0", operands);
+}
+
+
+char *
+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;
 
-      /* 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);
+  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];
+
+  if (GET_CODE (tgt) == CONST_INT)
+    sprintf (result, "call8\t0x%lx", INTVAL (tgt));
+  else if (register_operand (tgt, VOIDmode))
+    sprintf (result, "callx8\t%%%d", callop);
+  else
+    sprintf (result, "call8\t%%%d", callop);
+
+  return result;
 }
 
 
-static enum machine_mode
-xtensa_find_mode_for_size (unsigned item_size)
+bool
+xtensa_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
 {
-  enum machine_mode mode, tmode;
+  /* 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);
 
-  while (1)
+  /* 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)
     {
-      mode = VOIDmode;
+      rtx xplus0 = XEXP (addr, 0);
+      rtx xplus1 = XEXP (addr, 1);
+      enum rtx_code code0;
+      enum rtx_code code1;
 
-      /* 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 ();
+      while (GET_CODE (xplus0) == SUBREG)
+       xplus0 = SUBREG_REG (xplus0);
+      code0 = GET_CODE (xplus0);
 
-      item_size = GET_MODE_SIZE (mode);
+      while (GET_CODE (xplus1) == SUBREG)
+       xplus1 = SUBREG_REG (xplus1);
+      code1 = GET_CODE (xplus1);
 
-      if (xtensa_ld_opcodes[(int) mode]
-         && xtensa_st_opcodes[(int) mode])
-       break;
+      /* 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);
+       }
 
-      /* cannot load & store this mode; try something smaller */
-      item_size -= 1;
+      if (code0 == REG && BASE_REG_P (xplus0, strict)
+         && code1 == CONST_INT
+         && xtensa_mem_offset (INTVAL (xplus1), mode))
+       return true;
     }
 
-  return mode;
+  return false;
 }
 
 
-void
-xtensa_expand_nonlocal_goto (rtx *operands)
+/* 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)
 {
-  rtx goto_handler = operands[1];
-  rtx containing_fp = operands[3];
+  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;
+    }
 
-  /* generate a call to "__xtensa_nonlocal_goto" (in libgcc); the code
-     is too big to generate in-line */
+  return xtensa_tls_module_base_symbol;
+}
 
-  if (GET_CODE (containing_fp) != REG)
-    containing_fp = force_reg (Pmode, containing_fp);
 
-  goto_handler = replace_rtx (copy_rtx (goto_handler),
-                             virtual_stack_vars_rtx,
-                             containing_fp);
+static rtx
+xtensa_call_tls_desc (rtx sym, rtx *retp)
+{
+  rtx fn, arg, a10, call_insn, insns;
 
-  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_nonlocal_goto"),
-                    0, VOIDmode, 2,
-                    containing_fp, Pmode,
-                    goto_handler, Pmode);
+  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 struct machine_function *
-xtensa_init_machine_status (void)
+static rtx
+xtensa_legitimize_tls_address (rtx x)
 {
-  return ggc_alloc_cleared (sizeof (struct machine_function));
+  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;
 }
 
 
-void
-xtensa_setup_frame_addresses (void)
+rtx
+xtensa_legitimize_address (rtx x,
+                          rtx oldx ATTRIBUTE_UNUSED,
+                          enum machine_mode mode)
 {
-  /* Set flag to cause FRAME_POINTER_REQUIRED to be set.  */
-  cfun->machine->accesses_prev_frame = 1;
+  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);
+       }
 
-  emit_library_call
-    (gen_rtx_SYMBOL_REF (Pmode, "__xtensa_libgcc_window_spill"),
-     0, VOIDmode, 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 NULL_RTX;
 }
 
 
-/* Emit the assembly for the end of a zero-cost loop.  Normally we just emit
-   a comment showing where the end of the loop is.  However, if there is a
-   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
-   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
-   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.  */
+/* Helper for xtensa_tls_referenced_p.  */
 
-void
-xtensa_emit_loop_end (rtx insn, rtx *operands)
+static int
+xtensa_tls_referenced_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
-  char done = 0;
+  if (GET_CODE (*x) == SYMBOL_REF)
+    return SYMBOL_REF_TLS_MODEL (*x) != 0;
 
-  for (insn = PREV_INSN (insn); insn && !done; insn = PREV_INSN (insn))
+  /* Ignore TLS references that have already been legitimized.  */
+  if (GET_CODE (*x) == UNSPEC)
     {
-      switch (GET_CODE (insn))
+      switch (XINT (*x, 1))
        {
-       case NOTE:
-       case BARRIER:
-         break;
-
-       case CODE_LABEL:
-         output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
-         done = 1;
-         break;
-
+       case UNSPEC_TPOFF:
+       case UNSPEC_DTPOFF:
+       case UNSPEC_TLS_FUNC:
+       case UNSPEC_TLS_ARG:
+       case UNSPEC_TLS_CALL:
+         return -1;
        default:
-         {
-           rtx body = PATTERN (insn);
-
-           if (GET_CODE (body) == JUMP_INSN)
-             {
-               output_asm_insn (TARGET_DENSITY ? "nop.n" : "nop", operands);
-               done = 1;
-             }
-           else if ((GET_CODE (body) != USE)
-                    && (GET_CODE (body) != CLOBBER))
-             done = 1;
-         }
          break;
-        }
+       }
     }
 
-  output_asm_insn ("# loop end for %0", operands);
+  return 0;
 }
 
 
-char *
-xtensa_emit_call (int callop, rtx *operands)
-{
-  static char result[64];
-  rtx tgt = operands[callop];
+/* Return TRUE if X contains any TLS symbol references.  */
 
-  if (GET_CODE (tgt) == CONST_INT)
-    sprintf (result, "call8\t0x%lx", INTVAL (tgt));
-  else if (register_operand (tgt, VOIDmode))
-    sprintf (result, "callx8\t%%%d", callop);
-  else
-    sprintf (result, "call8\t%%%d", callop);
+bool
+xtensa_tls_referenced_p (rtx x)
+{
+  if (! TARGET_HAVE_TLS)
+    return false;
 
-  return result;
+  return for_each_rtx (&x, xtensa_tls_referenced_p_1, NULL);
 }
 
 
@@ -1688,11 +1959,10 @@ xtensa_dbx_register_number (int regno)
 /* Initialize CUMULATIVE_ARGS for a function.  */
 
 void
-init_cumulative_args (CUMULATIVE_ARGS *cum,
-                     tree fntype ATTRIBUTE_UNUSED,
-                     rtx libname ATTRIBUTE_UNUSED)
+init_cumulative_args (CUMULATIVE_ARGS *cum, int incoming)
 {
   cum->arg_words = 0;
+  cum->incoming = incoming;
 }
 
 
@@ -1711,7 +1981,9 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree 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;
@@ -1729,7 +2001,6 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
   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);
@@ -1740,39 +2011,43 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
            : 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);
 }
 
 
@@ -1785,32 +2060,13 @@ override_options (void)
   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);
-  xtensa_char_to_class['W'] = ((TARGET_CONST16) ? GR_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++)
        {
@@ -1818,7 +2074,7 @@ override_options (void)
 
          if (ACC_REG_P (regno))
            temp = (TARGET_MAC16
-                   && (class == MODE_INT) && (size <= UNITS_PER_WORD));
+                   && (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))
@@ -1848,6 +2104,17 @@ override_options (void)
   /* 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;
+    }
 }
 
 
@@ -1920,7 +2187,7 @@ print_operand (FILE *file, rtx x, int letter)
        {
          /* For a volatile memory reference, emit a MEMW before the
             load or store.  */
-         if (MEM_VOLATILE_P (x))
+         if (MEM_VOLATILE_P (x) && TARGET_SERIALIZE_VOLATILE)
            fprintf (file, "memw\n\t");
        }
       else
@@ -2102,37 +2369,75 @@ print_operand_address (FILE *file, rtx 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 *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;
@@ -2140,24 +2445,28 @@ xtensa_output_literal (FILE *file, rtx x, enum machine_mode mode, int 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 ();
     }
 }
 
@@ -2172,12 +2481,12 @@ long
 compute_frame_size (int size)
 {
   /* Add space for the incoming static chain value.  */
-  if (current_function_needs_context)
+  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;
 }
@@ -2198,75 +2507,72 @@ xtensa_frame_pointer_required (void)
 }
 
 
+/* 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)
+
 void
 xtensa_expand_prologue (void)
 {
   HOST_WIDE_INT total_size;
   rtx size_rtx;
+  rtx insn, note_rtx;
 
   total_size = compute_frame_size (get_frame_size ());
   size_rtx = GEN_INT (total_size);
 
   if (total_size < (1 << (12+3)))
-    emit_insn (gen_entry (size_rtx, size_rtx));
+    insn = emit_insn (gen_entry (size_rtx));
   else
     {
       /* Use a8 as a temporary since a0-a7 may be live.  */
       rtx tmp_reg = gen_rtx_REG (Pmode, A8_REG);
-      emit_insn (gen_entry (size_rtx, GEN_INT (MIN_FRAME_SIZE)));
+      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));
-      emit_move_insn (stack_pointer_rtx, tmp_reg);
+      insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg));
     }
 
   if (frame_pointer_needed)
     {
-      rtx first, insn, set_frame_ptr_insn = 0;
-
-      push_topmost_sequence ();
-      first = get_insns ();
-      pop_topmost_sequence ();
-
-      /* 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.  */
-
-      for (insn = first; insn; insn = NEXT_INSN (insn))
+      if (cfun->machine->set_frame_ptr_insn)
        {
-         rtx pat;
+         rtx first;
 
-         if (!INSN_P (insn))
-           continue;
+         push_topmost_sequence ();
+         first = get_insns ();
+         pop_topmost_sequence ();
 
-         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;
-           }
-       }
-
-      if (set_frame_ptr_insn)
-       {
          /* 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 != 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);
+               {
+                 PATTERN (insn) = replace_rtx (copy_rtx (PATTERN (insn)),
+                                               hard_frame_pointer_rtx,
+                                               stack_pointer_rtx);
+                 df_insn_rescan (insn);
+               }
            }
        }
       else
-       emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+       insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
+                                    stack_pointer_rtx));
     }
+
+  /* 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));
 }
 
 
@@ -2283,7 +2589,7 @@ xtensa_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
 rtx
 xtensa_return_addr (int count, rtx frame)
 {
-  rtx result, retaddr;
+  rtx result, retaddr, curaddr, label;
 
   if (count == -1)
     retaddr = gen_rtx_REG (Pmode, A0_REG);
@@ -2297,29 +2603,47 @@ xtensa_return_addr (int count, rtx 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 (void)
+
+   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;
 
@@ -2351,36 +2675,30 @@ xtensa_build_va_list (void)
 /* Save the incoming argument registers on the stack.  Returns the
    address of the saved registers.  */
 
-rtx
+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);
 }
@@ -2389,7 +2707,7 @@ xtensa_builtin_saveregs (void)
 /* Implement `va_start' for varargs and stdarg.  We look at the
    current function to fill in an initial va_list.  */
 
-void
+static void
 xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 {
   tree f_stk, stk;
@@ -2398,34 +2716,39 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
   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);
 }
@@ -2433,131 +2756,150 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 
 /* Implement `va_arg'.  */
 
-rtx
-xtensa_va_arg (tree valist, tree 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))
+         && !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 ();
 
-      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);
+      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);
 
-      r = expand_expr (reg, array, Pmode, EXPAND_NORMAL);
-      if (r != array)
-       emit_move_insn (array, r);
+      gimplify_assign (unshare_expr (array), reg, pre_p);
 
-      emit_jump_insn (gen_jump (lab_over));
-      emit_barrier ();
-      emit_label (lab_false);
+      t = build1 (GOTO_EXPR, void_type_node, lab_over);
+      gimplify_and_add (t, pre_p);
+
+      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 = 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);
+  lab_false2 = create_artificial_label ();
 
-  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 = 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);
 
-  emit_label (lab_false2);
+  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);
 
-  r = expand_expr (stk, array, Pmode, EXPAND_NORMAL);
-  if (r != array)
-    emit_move_insn (array, r);
+  t = build1 (LABEL_EXPR, void_type_node, lab_false2);
+  gimplify_and_add (t, pre_p);
 
-  if (lab_over != NULL_RTX)
-    emit_label (lab_over);
+  gimplify_assign (array, stk, pre_p);
+
+  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
@@ -2570,38 +2912,145 @@ xtensa_va_arg (tree valist, tree type)
      The results are endian-dependent because values smaller than one word
      are aligned differently.  */
 
-  size = gen_reg_rtx (SImode);
-  emit_move_insn (size, va_size);
 
-  if (BYTES_BIG_ENDIAN)
+  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);
+
+  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;
+    }
+}
+
 
-      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);
+static tree
+xtensa_fold_builtin (tree fndecl, tree arglist, bool ignore ATTRIBUTE_UNUSED)
+{
+  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+  tree arg0, arg1;
+
+  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 (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;
@@ -2612,33 +3061,34 @@ xtensa_preferred_reload_class (rtx x, enum reg_class class, int 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 (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;
@@ -2658,9 +3108,9 @@ order_regs_for_local_alloc (void)
       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++)
@@ -2668,11 +3118,11 @@ order_regs_for_local_alloc (void)
       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;
 
@@ -2687,55 +3137,6 @@ order_regs_for_local_alloc (void)
 }
 
 
-/* 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 (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.  */
 
@@ -2752,7 +3153,7 @@ xtensa_multibss_section_type_flags (tree decl, const char *name, int 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");
     }
 
@@ -2762,12 +3163,12 @@ xtensa_multibss_section_type_flags (tree decl, const char *name, int reloc)
 
 /* The literal pool stays with the function.  */
 
-static void
+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);
 }
 
 
@@ -2776,7 +3177,8 @@ xtensa_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
    scanned.  In either case, *TOTAL contains the cost result.  */
 
 static bool
-xtensa_rtx_costs (rtx x, int code, int outer_code, int *total)
+xtensa_rtx_costs (rtx x, int code, int outer_code, int *total,
+                 bool speed ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2817,7 +3219,7 @@ xtensa_rtx_costs (rtx x, int code, int outer_code, int *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)))
@@ -2857,9 +3259,14 @@ xtensa_rtx_costs (rtx x, int code, int outer_code, int *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;
@@ -2916,8 +3323,10 @@ xtensa_rtx_costs (rtx x, int code, int outer_code, int *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)
@@ -2944,7 +3353,7 @@ xtensa_rtx_costs (rtx x, int code, int outer_code, int *total)
            return true;
          }
       }
-      /* fall through */
+      /* Fall through.  */
 
     case UDIV:
     case UMOD:
@@ -2988,4 +3397,115 @@ xtensa_rtx_costs (rtx x, int code, int outer_code, int *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"