OSDN Git Service

* configure.ac: Add HAVE_AS_TLS check for Xtensa.
[pf3gnuchains/gcc-fork.git] / gcc / config / xtensa / xtensa.c
index 4fea001..4e7edef 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines for insn-output.c for Tensilica's Xtensa architecture.
-   Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
    Free Software Foundation, Inc.
    Contributed by Bob Wilson (bwilson@tensilica.com) at Tensilica.
 
@@ -48,7 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "target-def.h"
 #include "langhooks.h"
-#include "tree-gimple.h"
+#include "gimple.h"
 #include "df.h"
 
 
@@ -131,6 +131,7 @@ static rtx gen_float_relational (enum rtx_code, rtx, rtx);
 static rtx gen_conditional_move (rtx);
 static rtx fixup_subreg_mem (rtx);
 static struct machine_function * xtensa_init_machine_status (void);
+static rtx xtensa_legitimize_tls_address (rtx);
 static bool xtensa_return_in_msb (const_tree);
 static void printx (FILE *, signed int);
 static void xtensa_function_epilogue (FILE *, HOST_WIDE_INT);
@@ -139,13 +140,16 @@ static unsigned int xtensa_multibss_section_type_flags (tree, const char *,
                                                        int) ATTRIBUTE_UNUSED;
 static section *xtensa_select_rtx_section (enum machine_mode, rtx,
                                           unsigned HOST_WIDE_INT);
-static bool xtensa_rtx_costs (rtx, int, int, int *);
+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, tree *, 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 const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
   REG_ALLOC_ORDER;
@@ -174,11 +178,14 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
 #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
@@ -188,6 +195,8 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
 
 #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
@@ -208,6 +217,15 @@ static const int reg_nonleaf_alloc_order[FIRST_PSEUDO_REGISTER] =
 #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
@@ -458,6 +476,18 @@ constantpool_mem_p (rtx op)
 }
 
 
+/* Return TRUE if X is a thread-local symbol.  */
+
+static bool
+xtensa_tls_symbol_p (rtx x)
+{
+  if (! TARGET_HAVE_TLS)
+    return false;
+
+  return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
+}
+
+
 void
 xtensa_extend_reg (rtx dst, rtx src)
 {
@@ -648,6 +678,16 @@ 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_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
       reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */
@@ -889,12 +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]) != 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
@@ -902,16 +968,16 @@ 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;
            }
        }
     }
@@ -1012,8 +1078,10 @@ xtensa_copy_incoming_a7 (rtx opnd)
     {
     case DFmode:
     case DImode:
-      emit_insn (gen_movsi_internal (gen_rtx_SUBREG (SImode, tmp, 0),
-                                    gen_rtx_REG (SImode, A7_REG - 1)));
+      /* 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;
@@ -1034,6 +1102,11 @@ xtensa_copy_incoming_a7 (rtx opnd)
     }
 
   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 ();
 
@@ -1194,7 +1267,7 @@ xtensa_expand_nonlocal_goto (rtx *operands)
 static struct machine_function *
 xtensa_init_machine_status (void)
 {
-  return ggc_alloc_cleared (sizeof (struct machine_function));
+  return GGC_CNEW (struct machine_function);
 }
 
 
@@ -1292,10 +1365,10 @@ init_alignment_context (struct alignment_context *ac, rtx mem)
 
 /* 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 the value to set if CMP == MEM.  */
+   and NEW_RTX the value to set if CMP == MEM.  */
 
 void
-xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new)
+xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new_rtx)
 {
   enum machine_mode mode = GET_MODE (mem);
   struct alignment_context ac;
@@ -1310,7 +1383,7 @@ xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new)
   if (ac.shift != NULL_RTX)
     {
       cmp = xtensa_expand_mask_and_shift (cmp, mode, ac.shift);
-      new = xtensa_expand_mask_and_shift (new, mode, ac.shift);
+      new_rtx = xtensa_expand_mask_and_shift (new_rtx, mode, ac.shift);
     }
 
   /* Load the surrounding word into VAL with the MEM value masked out.  */
@@ -1319,10 +1392,10 @@ xtensa_expand_compare_and_swap (rtx target, rtx mem, rtx cmp, rtx new)
                                                OPTAB_DIRECT));
   emit_label (csloop);
 
-  /* Patch CMP and NEW into VAL at correct position.  */
+  /* 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, val,
+  newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new_rtx, val,
                                                 NULL_RTX, 1, OPTAB_DIRECT));
 
   /* Jump to end if we're done.  */
@@ -1366,7 +1439,7 @@ xtensa_expand_atomic (enum rtx_code code, rtx target, rtx mem, rtx val,
   rtx csloop = gen_label_rtx ();
   rtx cmp, tmp;
   rtx old = gen_reg_rtx (SImode);
-  rtx new = gen_reg_rtx (SImode);
+  rtx new_rtx = gen_reg_rtx (SImode);
   rtx orig = NULL_RTX;
 
   init_alignment_context (&ac, mem);
@@ -1417,35 +1490,35 @@ xtensa_expand_atomic (enum rtx_code code, rtx target, rtx mem, rtx val,
       tmp = expand_simple_binop (SImode, AND, old, ac.modemaski,
                                 NULL_RTX, 1, OPTAB_DIRECT);
       tmp = expand_simple_binop (SImode, IOR, tmp, val,
-                                new, 1, OPTAB_DIRECT);
+                                new_rtx, 1, OPTAB_DIRECT);
       break;
 
     case AND:
     case IOR:
     case XOR:
       tmp = expand_simple_binop (SImode, code, old, val,
-                                new, 1, OPTAB_DIRECT);
+                                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, 1, OPTAB_DIRECT);
+                                new_rtx, 1, OPTAB_DIRECT);
       break;
 
     default:
       gcc_unreachable ();
     }
 
-  if (tmp != new)
-    emit_move_insn (new, tmp);
-  emit_insn (gen_sync_compare_and_swapsi (cmp, ac.memsi, old, new));
+  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 : cmp);
+      tmp = (after ? new_rtx : cmp);
       convert_move (target,
                    (ac.shift == NULL_RTX ? tmp
                     : expand_simple_binop (SImode, LSHIFTRT, tmp, ac.shift,
@@ -1636,7 +1709,8 @@ xtensa_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
 {
   /* Allow constant pool addresses.  */
   if (mode != BLKmode && GET_MODE_SIZE (mode) >= UNITS_PER_WORD
-      && ! TARGET_CONST16 && constantpool_address_p (addr))
+      && ! TARGET_CONST16 && constantpool_address_p (addr)
+      && ! xtensa_tls_referenced_p (addr))
     return true;
 
   while (GET_CODE (addr) == SUBREG)
@@ -1681,11 +1755,97 @@ xtensa_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
 }
 
 
+/* Construct the SYMBOL_REF for the _TLS_MODULE_BASE_ symbol.  */
+
+static GTY(()) rtx xtensa_tls_module_base_symbol;
+
+static rtx
+xtensa_tls_module_base (void)
+{
+  if (! xtensa_tls_module_base_symbol)
+    {
+      xtensa_tls_module_base_symbol =
+       gen_rtx_SYMBOL_REF (Pmode, "_TLS_MODULE_BASE_");
+      SYMBOL_REF_FLAGS (xtensa_tls_module_base_symbol)
+        |= TLS_MODEL_GLOBAL_DYNAMIC << SYMBOL_FLAG_TLS_SHIFT;
+    }
+
+  return xtensa_tls_module_base_symbol;
+}
+
+
+static rtx
+xtensa_call_tls_desc (rtx sym, rtx *retp)
+{
+  rtx fn, arg, a10, call_insn, insns;
+
+  start_sequence ();
+  fn = gen_reg_rtx (Pmode);
+  arg = gen_reg_rtx (Pmode);
+  a10 = gen_rtx_REG (Pmode, 10);
+
+  emit_insn (gen_tls_func (fn, sym));
+  emit_insn (gen_tls_arg (arg, sym));
+  emit_move_insn (a10, arg);
+  call_insn = emit_call_insn (gen_tls_call (a10, fn, sym, const1_rtx));
+  CALL_INSN_FUNCTION_USAGE (call_insn)
+    = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, a10),
+                        CALL_INSN_FUNCTION_USAGE (call_insn));
+  insns = get_insns ();
+  end_sequence ();
+
+  *retp = a10;
+  return insns;
+}
+
+
+static rtx
+xtensa_legitimize_tls_address (rtx x)
+{
+  unsigned int model = SYMBOL_REF_TLS_MODEL (x);
+  rtx dest, tp, ret, modbase, base, addend, insns;
+
+  dest = gen_reg_rtx (Pmode);
+  switch (model)
+    {
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+      insns = xtensa_call_tls_desc (x, &ret);
+      emit_libcall_block (insns, dest, ret, x);
+      break;
+
+    case TLS_MODEL_LOCAL_DYNAMIC:
+      base = gen_reg_rtx (Pmode);
+      modbase = xtensa_tls_module_base ();
+      insns = xtensa_call_tls_desc (modbase, &ret);
+      emit_libcall_block (insns, base, ret, modbase);
+      addend = force_reg (SImode, gen_sym_DTPOFF (x));
+      emit_insn (gen_addsi3 (dest, base, addend));
+      break;
+
+    case TLS_MODEL_INITIAL_EXEC:
+    case TLS_MODEL_LOCAL_EXEC:
+      tp = gen_reg_rtx (SImode);
+      emit_insn (gen_load_tp (tp));
+      addend = force_reg (SImode, gen_sym_TPOFF (x));
+      emit_insn (gen_addsi3 (dest, tp, addend));
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return dest;
+}
+
+
 rtx
 xtensa_legitimize_address (rtx x,
                           rtx oldx ATTRIBUTE_UNUSED,
                           enum machine_mode mode)
 {
+  if (xtensa_tls_symbol_p (x))
+    return xtensa_legitimize_tls_address (x);
+
   if (GET_CODE (x) == PLUS)
     {
       rtx plus0 = XEXP (x, 0);
@@ -1717,6 +1877,46 @@ xtensa_legitimize_address (rtx x,
 }
 
 
+/* Helper for xtensa_tls_referenced_p.  */
+
+static int
+xtensa_tls_referenced_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (*x) == SYMBOL_REF)
+    return SYMBOL_REF_TLS_MODEL (*x) != 0;
+
+  /* Ignore TLS references that have already been legitimized.  */
+  if (GET_CODE (*x) == UNSPEC)
+    {
+      switch (XINT (*x, 1))
+       {
+       case UNSPEC_TPOFF:
+       case UNSPEC_DTPOFF:
+       case UNSPEC_TLS_FUNC:
+       case UNSPEC_TLS_ARG:
+       case UNSPEC_TLS_CALL:
+         return -1;
+       default:
+         break;
+       }
+    }
+
+  return 0;
+}
+
+
+/* Return TRUE if X contains any TLS symbol references.  */
+
+bool
+xtensa_tls_referenced_p (rtx x)
+{
+  if (! TARGET_HAVE_TLS)
+    return false;
+
+  return for_each_rtx (&x, xtensa_tls_referenced_p_1, NULL);
+}
+
+
 /* Return the debugger register number to use for 'regno'.  */
 
 int
@@ -1866,7 +2066,7 @@ override_options (void)
        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++)
        {
@@ -1874,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))
@@ -1904,6 +2104,8 @@ 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).
@@ -1985,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
@@ -2174,6 +2376,14 @@ xtensa_output_addr_const_extra (FILE *fp, rtx x)
     {
       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)
            {
@@ -2276,7 +2486,7 @@ compute_frame_size (int size)
 
   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;
 }
@@ -2297,6 +2507,10 @@ 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)
 {
@@ -2308,12 +2522,12 @@ xtensa_expand_prologue (void)
   size_rtx = GEN_INT (total_size);
 
   if (total_size < (1 << (12+3)))
-    insn = 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));
       insn = emit_insn (gen_movsi (stack_pointer_rtx, tmp_reg));
@@ -2375,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);
@@ -2389,10 +2603,25 @@ 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;
 }
 
@@ -2450,7 +2679,7 @@ static rtx
 xtensa_builtin_saveregs (void)
 {
   rtx gp_regs;
-  int arg_words = current_function_args_info.arg_words;
+  int arg_words = crtl->args.info.arg_words;
   int gp_left = MAX_ARGS_IN_REGISTERS - arg_words;
 
   if (gp_left <= 0)
@@ -2478,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;
@@ -2487,27 +2716,29 @@ 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 = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
-  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
-  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, 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 */
   u = make_tree (sizetype, expand_builtin_saveregs ());
   u = fold_convert (ptr_type_node, u);
-  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, reg, 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 - 32).  */
   u = make_tree (ptr_type_node, virtual_incoming_args_rtx);
   u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u, size_int (-32));
-  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, stk, u);
+  t = build2 (MODIFY_EXPR, ptr_type_node, stk, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -2516,8 +2747,8 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
      alignment offset for __va_stk.  */
   if (arg_words >= MAX_ARGS_IN_REGISTERS)
     arg_words += 2;
-  t = build2 (GIMPLE_MODIFY_STMT, integer_type_node, ndx,
-             size_int (arg_words * UNITS_PER_WORD));
+  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);
 }
@@ -2526,8 +2757,8 @@ xtensa_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 /* Implement `va_arg'.  */
 
 static tree
-xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
-                            tree *post_p ATTRIBUTE_UNUSED)
+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;
@@ -2549,7 +2780,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
                                               pre_p, NULL);
       real_part = get_initialized_tmp_var (real_part, pre_p, NULL);
 
-      imag_part = xtensa_gimplify_va_arg_expr (valist, TREE_TYPE (type),
+      imag_part = xtensa_gimplify_va_arg_expr (unshare_expr (valist),
+                                              TREE_TYPE (type),
                                               pre_p, NULL);
       imag_part = get_initialized_tmp_var (imag_part, pre_p, NULL);
 
@@ -2560,9 +2792,12 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
   f_reg = TREE_CHAIN (f_stk);
   f_ndx = TREE_CHAIN (f_reg);
 
-  stk = build3 (COMPONENT_REF, TREE_TYPE (f_stk), valist, f_stk, NULL_TREE);
-  reg = build3 (COMPONENT_REF, TREE_TYPE (f_reg), valist, f_reg, NULL_TREE);
-  ndx = build3 (COMPONENT_REF, TREE_TYPE (f_ndx), valist, f_ndx, NULL_TREE);
+  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 = size_in_bytes (type);
   va_size = round_up (type_size, UNITS_PER_WORD);
@@ -2582,10 +2817,11 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
     {
       int align = MIN (TYPE_ALIGN (type), STACK_BOUNDARY) / BITS_PER_UNIT;
 
-      t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, size_int (align - 1));
-      t = build2 (BIT_AND_EXPR, integer_type_node, t, size_int (-align));
-      t = build2 (GIMPLE_MODIFY_STMT, integer_type_node, orig_ndx, t);
-      gimplify_and_add (t, pre_p);
+      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);
     }
 
 
@@ -2595,8 +2831,7 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
 
   t = fold_convert (integer_type_node, va_size);
   t = build2 (PLUS_EXPR, integer_type_node, orig_ndx, t);
-  t = build2 (GIMPLE_MODIFY_STMT, integer_type_node, ndx, t);
-  gimplify_and_add (t, pre_p);
+  gimplify_assign (unshare_expr (ndx), t, pre_p);
 
 
   /* Check if the argument is in registers:
@@ -2613,15 +2848,15 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
       lab_false = create_artificial_label ();
       lab_over = create_artificial_label ();
 
-      t = build2 (GT_EXPR, boolean_type_node, ndx,
-                 size_int (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD));
+      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);
 
-      t = build2 (GIMPLE_MODIFY_STMT, void_type_node, array, reg);
-      gimplify_and_add (t, pre_p);
+      gimplify_assign (unshare_expr (array), reg, pre_p);
 
       t = build1 (GOTO_EXPR, void_type_node, lab_over);
       gimplify_and_add (t, pre_p);
@@ -2643,23 +2878,22 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
 
   lab_false2 = create_artificial_label ();
 
-  t = build2 (GT_EXPR, boolean_type_node, orig_ndx,
-             size_int (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD));
+  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);
 
-  t = size_binop (PLUS_EXPR, va_size, size_int (32));
+  t = size_binop (PLUS_EXPR, unshare_expr (va_size), size_int (32));
   t = fold_convert (integer_type_node, t);
-  t = build2 (GIMPLE_MODIFY_STMT, integer_type_node, ndx, t);
-  gimplify_and_add (t, pre_p);
+  gimplify_assign (unshare_expr (ndx), t, pre_p);
 
   t = build1 (LABEL_EXPR, void_type_node, lab_false2);
   gimplify_and_add (t, pre_p);
 
-  t = build2 (GIMPLE_MODIFY_STMT, void_type_node, array, stk);
-  gimplify_and_add (t, pre_p);
+  gimplify_assign (array, stk, pre_p);
 
   if (lab_over)
     {
@@ -2681,16 +2915,18 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
 
   if (BYTES_BIG_ENDIAN && TREE_CODE (type_size) == INTEGER_CST)
     {
-      t = fold_build2 (GE_EXPR, boolean_type_node, type_size,
+      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, va_size, type_size);
+      t = fold_build3 (COND_EXPR, sizetype, t, unshare_expr (va_size),
+                      unshare_expr (type_size));
       size = t;
     }
   else
-    size = va_size;
+    size = unshare_expr (va_size);
 
-  t = build2 (MINUS_EXPR, sizetype, ndx, size);
-  addr = build2 (POINTER_PLUS_EXPR, ptr_type_node, array, t);
+  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)
@@ -2704,6 +2940,8 @@ xtensa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
 enum xtensa_builtin
 {
   XTENSA_BUILTIN_UMULSIDI3,
+  XTENSA_BUILTIN_THREAD_POINTER,
+  XTENSA_BUILTIN_SET_THREAD_POINTER,
   XTENSA_BUILTIN_max
 };
 
@@ -2711,15 +2949,34 @@ enum xtensa_builtin
 static void
 xtensa_init_builtins (void)
 {
-  tree ftype;
+  tree ftype, decl;
 
   ftype = build_function_type_list (unsigned_intDI_type_node,
                                    unsigned_intSI_type_node,
                                    unsigned_intSI_type_node, NULL_TREE);
 
-  add_builtin_function ("__builtin_umulsidi3", ftype,
-                       XTENSA_BUILTIN_UMULSIDI3, BUILT_IN_MD,
-                       "__umulsidi3", 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)
+    {
+      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;
+    }
 }
 
 
@@ -2729,8 +2986,9 @@ xtensa_fold_builtin (tree fndecl, tree arglist, bool ignore ATTRIBUTE_UNUSED)
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
   tree arg0, arg1;
 
-  if (fcode == XTENSA_BUILTIN_UMULSIDI3)
+  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)
@@ -2738,11 +2996,17 @@ xtensa_fold_builtin (tree fndecl, tree arglist, bool ignore ATTRIBUTE_UNUSED)
        return fold_build2 (MULT_EXPR, unsigned_intDI_type_node,
                            fold_convert (unsigned_intDI_type_node, arg0),
                            fold_convert (unsigned_intDI_type_node, arg1));
-      else
-       return NULL;
+      break;
+
+    case XTENSA_BUILTIN_THREAD_POINTER:
+    case XTENSA_BUILTIN_SET_THREAD_POINTER:
+      break;
+
+    default:
+      internal_error ("bad builtin code");
+      break;
     }
 
-  internal_error ("bad builtin code");
   return NULL;
 }
 
@@ -2755,20 +3019,38 @@ xtensa_expand_builtin (tree exp, rtx target,
 {
   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);
 
-  /* 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.  */
-  if (fcode == XTENSA_BUILTIN_UMULSIDI3)
-    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;
 
-  internal_error ("bad builtin code");
+    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;
@@ -2779,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;
@@ -2827,7 +3110,7 @@ order_regs_for_local_alloc (void)
 
       /* 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;
+      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++)
@@ -2894,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)
     {
@@ -3122,4 +3406,106 @@ xtensa_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
          > 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"