]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/config/s390/s390.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / config / s390 / s390.c
index 4c96dce089b91f6f45209cb23b935c7f57780b13..557838bc44da11592148590d2f90188e02807f69 100644 (file)
@@ -1,27 +1,30 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+   2007, 2008 Free Software Foundation, Inc.
    Contributed by Hartmut Penner (hpenner@de.ibm.com) and
-                  Ulrich Weigand (uweigand@de.ibm.com).
+                  Ulrich Weigand (uweigand@de.ibm.com) and
+                  Andreas Krebbel (Andreas.Krebbel@de.ibm.com).
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC 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 version.
+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 3, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+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 GNU CC; 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"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
 #include "tm_p.h"
@@ -45,51 +48,193 @@ Boston, MA 02111-1307, USA.  */
 #include "target.h"
 #include "target-def.h"
 #include "debug.h"
+#include "langhooks.h"
+#include "optabs.h"
+#include "gimple.h"
+#include "df.h"
+#include "params.h"
 
 
-static bool s390_assemble_integer PARAMS ((rtx, unsigned int, int));
-static int s390_adjust_cost PARAMS ((rtx, rtx, rtx, int));
-static int s390_adjust_priority PARAMS ((rtx, int));
+/* Define the specific costs for a given cpu.  */
 
-#undef  TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
-#undef  TARGET_ASM_ALIGNED_DI_OP
-#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
-#undef  TARGET_ASM_INTEGER
-#define TARGET_ASM_INTEGER s390_assemble_integer
-
-#undef  TARGET_ASM_FUNCTION_PROLOGUE 
-#define TARGET_ASM_FUNCTION_PROLOGUE s390_function_prologue
-
-#undef  TARGET_ASM_FUNCTION_EPILOGUE 
-#define TARGET_ASM_FUNCTION_EPILOGUE s390_function_epilogue
+struct processor_costs 
+{
+  /* multiplication */
+  const int m;        /* cost of an M instruction.  */
+  const int mghi;     /* cost of an MGHI instruction.  */
+  const int mh;       /* cost of an MH instruction.  */
+  const int mhi;      /* cost of an MHI instruction.  */
+  const int ml;       /* cost of an ML instruction.  */
+  const int mr;       /* cost of an MR instruction.  */
+  const int ms;       /* cost of an MS instruction.  */
+  const int msg;      /* cost of an MSG instruction.  */
+  const int msgf;     /* cost of an MSGF instruction.  */
+  const int msgfr;    /* cost of an MSGFR instruction.  */
+  const int msgr;     /* cost of an MSGR instruction.  */
+  const int msr;      /* cost of an MSR instruction.  */
+  const int mult_df;  /* cost of multiplication in DFmode.  */
+  const int mxbr;
+  /* square root */
+  const int sqxbr;    /* cost of square root in TFmode.  */
+  const int sqdbr;    /* cost of square root in DFmode.  */
+  const int sqebr;    /* cost of square root in SFmode.  */
+  /* multiply and add */
+  const int madbr;    /* cost of multiply and add in DFmode.  */
+  const int maebr;    /* cost of multiply and add in SFmode.  */
+  /* division */
+  const int dxbr;
+  const int ddbr;
+  const int debr;
+  const int dlgr;
+  const int dlr;
+  const int dr;
+  const int dsgfr;
+  const int dsgr;
+};
 
-#undef  TARGET_ASM_OPEN_PAREN
-#define TARGET_ASM_OPEN_PAREN ""
+const struct processor_costs *s390_cost;
 
-#undef  TARGET_ASM_CLOSE_PAREN
-#define TARGET_ASM_CLOSE_PAREN ""
+static const
+struct processor_costs z900_cost = 
+{
+  COSTS_N_INSNS (5),     /* M     */
+  COSTS_N_INSNS (10),    /* MGHI  */
+  COSTS_N_INSNS (5),     /* MH    */
+  COSTS_N_INSNS (4),     /* MHI   */
+  COSTS_N_INSNS (5),     /* ML    */
+  COSTS_N_INSNS (5),     /* MR    */
+  COSTS_N_INSNS (4),     /* MS    */
+  COSTS_N_INSNS (15),    /* MSG   */
+  COSTS_N_INSNS (7),     /* MSGF  */
+  COSTS_N_INSNS (7),     /* MSGFR */
+  COSTS_N_INSNS (10),    /* MSGR  */
+  COSTS_N_INSNS (4),     /* MSR   */
+  COSTS_N_INSNS (7),     /* multiplication in DFmode */
+  COSTS_N_INSNS (13),    /* MXBR */
+  COSTS_N_INSNS (136),   /* SQXBR */
+  COSTS_N_INSNS (44),    /* SQDBR */
+  COSTS_N_INSNS (35),    /* SQEBR */
+  COSTS_N_INSNS (18),    /* MADBR */
+  COSTS_N_INSNS (13),    /* MAEBR */
+  COSTS_N_INSNS (134),   /* DXBR */
+  COSTS_N_INSNS (30),    /* DDBR */
+  COSTS_N_INSNS (27),    /* DEBR */
+  COSTS_N_INSNS (220),   /* DLGR */
+  COSTS_N_INSNS (34),    /* DLR */
+  COSTS_N_INSNS (34),    /* DR */
+  COSTS_N_INSNS (32),    /* DSGFR */
+  COSTS_N_INSNS (32),    /* DSGR */
+};
 
-#undef  TARGET_SCHED_ADJUST_COST
-#define TARGET_SCHED_ADJUST_COST s390_adjust_cost
+static const
+struct processor_costs z990_cost = 
+{
+  COSTS_N_INSNS (4),     /* M     */
+  COSTS_N_INSNS (2),     /* MGHI  */
+  COSTS_N_INSNS (2),     /* MH    */
+  COSTS_N_INSNS (2),     /* MHI   */
+  COSTS_N_INSNS (4),     /* ML    */
+  COSTS_N_INSNS (4),     /* MR    */
+  COSTS_N_INSNS (5),     /* MS    */
+  COSTS_N_INSNS (6),     /* MSG   */
+  COSTS_N_INSNS (4),     /* MSGF  */
+  COSTS_N_INSNS (4),     /* MSGFR */
+  COSTS_N_INSNS (4),     /* MSGR  */
+  COSTS_N_INSNS (4),     /* MSR   */
+  COSTS_N_INSNS (1),     /* multiplication in DFmode */
+  COSTS_N_INSNS (28),    /* MXBR */
+  COSTS_N_INSNS (130),   /* SQXBR */
+  COSTS_N_INSNS (66),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (60),    /* DXBR */
+  COSTS_N_INSNS (40),    /* DDBR */
+  COSTS_N_INSNS (26),    /* DEBR */
+  COSTS_N_INSNS (176),   /* DLGR */
+  COSTS_N_INSNS (31),    /* DLR */
+  COSTS_N_INSNS (31),    /* DR */
+  COSTS_N_INSNS (31),    /* DSGFR */
+  COSTS_N_INSNS (31),    /* DSGR */
+};
 
-#undef  TARGET_SCHED_ADJUST_PRIORITY
-#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
+static const
+struct processor_costs z9_109_cost = 
+{
+  COSTS_N_INSNS (4),     /* M     */
+  COSTS_N_INSNS (2),     /* MGHI  */
+  COSTS_N_INSNS (2),     /* MH    */
+  COSTS_N_INSNS (2),     /* MHI   */
+  COSTS_N_INSNS (4),     /* ML    */
+  COSTS_N_INSNS (4),     /* MR    */
+  COSTS_N_INSNS (5),     /* MS    */
+  COSTS_N_INSNS (6),     /* MSG   */
+  COSTS_N_INSNS (4),     /* MSGF  */
+  COSTS_N_INSNS (4),     /* MSGFR */
+  COSTS_N_INSNS (4),     /* MSGR  */
+  COSTS_N_INSNS (4),     /* MSR   */
+  COSTS_N_INSNS (1),     /* multiplication in DFmode */
+  COSTS_N_INSNS (28),    /* MXBR */
+  COSTS_N_INSNS (130),   /* SQXBR */
+  COSTS_N_INSNS (66),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (60),    /* DXBR */
+  COSTS_N_INSNS (40),    /* DDBR */
+  COSTS_N_INSNS (26),    /* DEBR */
+  COSTS_N_INSNS (30),    /* DLGR */
+  COSTS_N_INSNS (23),    /* DLR */
+  COSTS_N_INSNS (23),    /* DR */
+  COSTS_N_INSNS (24),    /* DSGFR */
+  COSTS_N_INSNS (24),    /* DSGR */
+};
 
-struct gcc_target targetm = TARGET_INITIALIZER;
+static const
+struct processor_costs z10_cost =
+{
+  COSTS_N_INSNS (10),    /* M     */
+  COSTS_N_INSNS (10),    /* MGHI  */
+  COSTS_N_INSNS (10),    /* MH    */
+  COSTS_N_INSNS (10),    /* MHI   */
+  COSTS_N_INSNS (10),    /* ML    */
+  COSTS_N_INSNS (10),    /* MR    */
+  COSTS_N_INSNS (10),    /* MS    */
+  COSTS_N_INSNS (10),    /* MSG   */
+  COSTS_N_INSNS (10),    /* MSGF  */
+  COSTS_N_INSNS (10),    /* MSGFR */
+  COSTS_N_INSNS (10),    /* MSGR  */
+  COSTS_N_INSNS (10),    /* MSR   */
+  COSTS_N_INSNS (1) ,    /* multiplication in DFmode */
+  COSTS_N_INSNS (50),    /* MXBR */
+  COSTS_N_INSNS (120),   /* SQXBR */
+  COSTS_N_INSNS (52),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (111),   /* DXBR */
+  COSTS_N_INSNS (39),    /* DDBR */
+  COSTS_N_INSNS (32),    /* DEBR */
+  COSTS_N_INSNS (160),   /* DLGR */
+  COSTS_N_INSNS (71),    /* DLR */
+  COSTS_N_INSNS (71),    /* DR */
+  COSTS_N_INSNS (71),    /* DSGFR */
+  COSTS_N_INSNS (71),    /* DSGR */
+};
 
 extern int reload_completed;
 
-/* The alias set for prologue/epilogue register save/restore.  */
-static int s390_sr_alias_set = 0;
-
-/* Function count for creating unique internal labels in a compile unit.  */
-int  s390_function_count = 0;
+/* Kept up to date using the SCHED_VARIABLE_ISSUE hook.  */
+static rtx last_scheduled_insn;
 
 /* Save information from a "cmpxx" operation until the branch or scc is
    emitted.  */
 rtx s390_compare_op0, s390_compare_op1;
 
+/* Save the result of a compare_and_swap  until the branch or scc is
+   emitted.  */
+rtx s390_compare_emitted = NULL_RTX;
+
 /* Structure used to hold the components of a S/390 memory
    address.  A legitimate address on S/390 is of the general
    form
@@ -104,57 +249,192 @@ struct s390_address
   rtx base;
   rtx indx;
   rtx disp;
-  int pointer;
+  bool pointer;
+  bool literal_pool;
 };
 
-/* Structure containing information for prologue and epilogue.  */ 
+/* Which cpu are we tuning for.  */
+enum processor_type s390_tune = PROCESSOR_max;
+enum processor_flags s390_tune_flags;
+/* Which instruction set architecture to use.  */
+enum processor_type s390_arch;
+enum processor_flags s390_arch_flags;
 
-struct s390_frame
+HOST_WIDE_INT s390_warn_framesize = 0;
+HOST_WIDE_INT s390_stack_size = 0;
+HOST_WIDE_INT s390_stack_guard = 0;
+
+/* The following structure is embedded in the machine 
+   specific part of struct function.  */
+
+struct s390_frame_layout GTY (())
 {
-  int frame_pointer_p;
-  int return_reg_saved_p;
-  int save_fprs_p;
+  /* Offset within stack frame.  */
+  HOST_WIDE_INT gprs_offset;
+  HOST_WIDE_INT f0_offset;
+  HOST_WIDE_INT f4_offset;
+  HOST_WIDE_INT f8_offset;
+  HOST_WIDE_INT backchain_offset;
+
+  /* Number of first and last gpr where slots in the register
+     save area are reserved for.  */
+  int first_save_gpr_slot;
+  int last_save_gpr_slot;
+
+  /* Number of first and last gpr to be saved, restored.  */
   int first_save_gpr;
   int first_restore_gpr;
   int last_save_gpr;
-  int arg_frame_offset;
-
+  int last_restore_gpr;
+
+  /* Bits standing for floating point registers. Set, if the 
+     respective register has to be saved. Starting with reg 16 (f0) 
+     at the rightmost bit.
+     Bit 15 -  8  7  6  5  4  3  2  1  0
+     fpr 15 -  8  7  5  3  1  6  4  2  0
+     reg 31 - 24 23 22 21 20 19 18 17 16  */
+  unsigned int fpr_bitmap;
+
+  /* Number of floating point registers f8-f15 which must be saved.  */
+  int high_fprs;
+
+  /* Set if return address needs to be saved.
+     This flag is set by s390_return_addr_rtx if it could not use
+     the initial value of r14 and therefore depends on r14 saved
+     to the stack.  */
+  bool save_return_addr_p;
+
+  /* Size of stack frame.  */
   HOST_WIDE_INT frame_size;
 };
 
-static int s390_match_ccmode_set PARAMS ((rtx, enum machine_mode));
-static int s390_branch_condition_mask PARAMS ((rtx));
-static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int));
-static int check_mode PARAMS ((rtx, enum machine_mode *));
-static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
-static int s390_decompose_address PARAMS ((rtx, struct s390_address *, int));
-static int reg_used_in_mem_p PARAMS ((int, rtx));
-static int addr_generation_dependency_p PARAMS ((rtx, rtx));
-static void s390_split_branches PARAMS ((void));
-static void find_constant_pool_ref PARAMS ((rtx, rtx *));
-static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx));
-static void s390_chunkify_pool PARAMS ((void));
-static int save_fprs_p PARAMS ((void));
-static int find_unused_clobbered_reg PARAMS ((void));
-static void s390_frame_info PARAMS ((struct s390_frame *));
-static rtx save_fpr PARAMS ((rtx, int, int));
-static rtx restore_fpr PARAMS ((rtx, int, int));
-static int s390_function_arg_size PARAMS ((enum machine_mode, tree));
+/* Define the structure for the machine field in struct function.  */
+
+struct machine_function GTY(())
+{
+  struct s390_frame_layout frame_layout;
+
+  /* Literal pool base register.  */
+  rtx base_reg;
+
+  /* True if we may need to perform branch splitting.  */
+  bool split_branches_pending_p;
+
+  /* Some local-dynamic TLS symbol name.  */
+  const char *some_ld_name;
+
+  bool has_landing_pad_p;
+};
+
+/* Few accessor macros for struct cfun->machine->s390_frame_layout.  */
+
+#define cfun_frame_layout (cfun->machine->frame_layout)
+#define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs)
+#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot -           \
+  cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD)
+#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |=    \
+  (1 << (BITNUM)))
+#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap &    \
+  (1 << (BITNUM))))
+
+/* Number of GPRs and FPRs used for argument passing.  */
+#define GP_ARG_NUM_REG 5
+#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2)
+
+/* A couple of shortcuts.  */
+#define CONST_OK_FOR_J(x) \
+       CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J")
+#define CONST_OK_FOR_K(x) \
+       CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K")
+#define CONST_OK_FOR_Os(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os")
+#define CONST_OK_FOR_Op(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op")
+#define CONST_OK_FOR_On(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On")
+
+#define REGNO_PAIR_OK(REGNO, MODE)                               \
+  (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
+
+/* That's the read ahead of the dynamic branch prediction unit in
+   bytes on a z10 CPU.  */
+#define Z10_PREDICT_DISTANCE 384
+
+static enum machine_mode
+s390_libgcc_cmp_return_mode (void)
+{
+  return TARGET_64BIT ? DImode : SImode;
+}
+
+static enum machine_mode
+s390_libgcc_shift_count_mode (void)
+{
+  return TARGET_64BIT ? DImode : SImode;
+}
+
+/* Return true if the back end supports mode MODE.  */
+static bool
+s390_scalar_mode_supported_p (enum machine_mode mode)
+{
+  if (DECIMAL_FLOAT_MODE_P (mode))
+    return true;
+  else
+    return default_scalar_mode_supported_p (mode);
+}
+
+/* Set the has_landing_pad_p flag in struct machine_function to VALUE.  */
+
+void
+s390_set_has_landing_pad_p (bool value)
+{
+  cfun->machine->has_landing_pad_p = value;
+}
+
+/* If two condition code modes are compatible, return a condition code
+   mode which is compatible with both.  Otherwise, return
+   VOIDmode.  */
+
+static enum machine_mode
+s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
+{
+  if (m1 == m2)
+    return m1;
+
+  switch (m1)
+    {
+    case CCZmode:
+      if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode
+         || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
+        return m2;
+      return VOIDmode;
+
+    case CCSmode:
+    case CCUmode:
+    case CCTmode:
+    case CCSRmode:
+    case CCURmode:
+    case CCZ1mode:
+      if (m2 == CCZmode)
+       return m1;
+      
+      return VOIDmode;
+
+    default:
+      return VOIDmode;
+    }
+  return VOIDmode;
+}
 
 /* Return true if SET either doesn't set the CC register, or else
-   the source and destination have matching CC modes and that 
+   the source and destination have matching CC modes and that
    CC mode is at least as constrained as REQ_MODE.  */
-static int
-s390_match_ccmode_set (set, req_mode)
-     rtx set;
-     enum machine_mode req_mode;
+
+static bool
+s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
 {
   enum machine_mode set_mode;
 
-  if (GET_CODE (set) != SET)
-    abort ();
+  gcc_assert (GET_CODE (set) == SET);
 
   if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
     return 1;
@@ -163,40 +443,53 @@ s390_match_ccmode_set (set, req_mode)
   switch (set_mode)
     {
     case CCSmode:
-      if (req_mode != CCSmode)
-        return 0;
-      break;
+    case CCSRmode:
     case CCUmode:
-      if (req_mode != CCUmode)
-        return 0;
-      break;
+    case CCURmode:
     case CCLmode:
-      if (req_mode != CCLmode)
+    case CCL1mode:
+    case CCL2mode:
+    case CCL3mode:
+    case CCT1mode:
+    case CCT2mode:
+    case CCT3mode:
+      if (req_mode != set_mode)
         return 0;
       break;
+
     case CCZmode:
-      if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode)
+      if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode
+         && req_mode != CCSRmode && req_mode != CCURmode)
         return 0;
       break;
+
+    case CCAPmode:
+    case CCANmode:
+      if (req_mode != CCAmode)
+        return 0;
+      break;
+
     default:
-      abort ();
+      gcc_unreachable ();
     }
+
   return (GET_MODE (SET_SRC (set)) == set_mode);
 }
 
-/* Return true if every SET in INSN that sets the CC register 
-   has source and destination with matching CC modes and that 
-   CC mode is at least as constrained as REQ_MODE.  */
-int
-s390_match_ccmode (insn, req_mode)
-     rtx insn;
-     enum machine_mode req_mode;
+/* Return true if every SET in INSN that sets the CC register
+   has source and destination with matching CC modes and that
+   CC mode is at least as constrained as REQ_MODE.
+   If REQ_MODE is VOIDmode, always return false.  */
+
+bool
+s390_match_ccmode (rtx insn, enum machine_mode req_mode)
 {
   int i;
 
+  /* s390_tm_ccmode returns VOIDmode to indicate failure.  */
+  if (req_mode == VOIDmode)
+    return false;
+
   if (GET_CODE (PATTERN (insn)) == SET)
     return s390_match_ccmode_set (PATTERN (insn), req_mode);
 
@@ -206,36 +499,125 @@ s390_match_ccmode (insn, req_mode)
           rtx set = XVECEXP (PATTERN (insn), 0, i);
           if (GET_CODE (set) == SET)
             if (!s390_match_ccmode_set (set, req_mode))
-              return 0;
+              return false;
         }
 
-  return 1;
+  return true;
+}
+
+/* If a test-under-mask instruction can be used to implement
+   (compare (and ... OP1) OP2), return the CC mode required
+   to do that.  Otherwise, return VOIDmode.
+   MIXED is true if the instruction can distinguish between
+   CC1 and CC2 for mixed selected bits (TMxx), it is false
+   if the instruction cannot (TM).  */
+
+enum machine_mode
+s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
+{
+  int bit0, bit1;
+
+  /* ??? Fixme: should work on CONST_DOUBLE as well.  */
+  if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT)
+    return VOIDmode;
+
+  /* Selected bits all zero: CC0.
+     e.g.: int a; if ((a & (16 + 128)) == 0) */
+  if (INTVAL (op2) == 0)
+    return CCTmode;
+
+  /* Selected bits all one: CC3. 
+     e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */
+  if (INTVAL (op2) == INTVAL (op1))
+    return CCT3mode;
+
+  /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.:
+     int a;
+     if ((a & (16 + 128)) == 16)         -> CCT1
+     if ((a & (16 + 128)) == 128)        -> CCT2  */
+  if (mixed)
+    {
+      bit1 = exact_log2 (INTVAL (op2));
+      bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2));
+      if (bit0 != -1 && bit1 != -1)
+        return bit0 > bit1 ? CCT1mode : CCT2mode;
+    }
+
+  return VOIDmode;
 }
 
-/* Given a comparison code OP (EQ, NE, etc.) and the operands 
-   OP0 and OP1 of a COMPARE, return the mode to be used for the 
+/* Given a comparison code OP (EQ, NE, etc.) and the operands
+   OP0 and OP1 of a COMPARE, return the mode to be used for the
    comparison.  */
 
 enum machine_mode
-s390_select_ccmode (code, op0, op1) 
-     enum rtx_code code;
-     rtx op0;
-     rtx op1;
+s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
 {
   switch (code)
     {
       case EQ:
       case NE:
-       if (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
-           || GET_CODE (op1) == NEG)
+       if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
+         return CCAPmode;
+       if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
+           && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
+         return CCAPmode;
+       if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
+            || GET_CODE (op1) == NEG)
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
          return CCLmode;
 
+       if (GET_CODE (op0) == AND)
+         {
+           /* Check whether we can potentially do it via TM.  */
+           enum machine_mode ccmode;
+           ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1);
+           if (ccmode != VOIDmode)
+             {
+               /* Relax CCTmode to CCZmode to allow fall-back to AND
+                  if that turns out to be beneficial.  */
+               return ccmode == CCTmode ? CCZmode : ccmode;
+             }
+         }
+
+       if (register_operand (op0, HImode)
+           && GET_CODE (op1) == CONST_INT
+           && (INTVAL (op1) == -1 || INTVAL (op1) == 65535))
+         return CCT3mode;
+       if (register_operand (op0, QImode)
+           && GET_CODE (op1) == CONST_INT
+           && (INTVAL (op1) == -1 || INTVAL (op1) == 255))
+         return CCT3mode;
+
        return CCZmode;
 
       case LE:
       case LT:
       case GE:
       case GT:
+       /* The only overflow condition of NEG and ABS happens when
+          -INT_MAX is used as parameter, which stays negative. So
+          we have an overflow from a positive value to a negative. 
+          Using CCAP mode the resulting cc can be used for comparisons.  */
+       if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
+         return CCAPmode;
+
+       /* If constants are involved in an add instruction it is possible to use
+          the resulting cc for comparisons with zero. Knowing the sign of the
+          constant the overflow behavior gets predictable. e.g.:
+            int a, b; if ((b = a + c) > 0)  
+          with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP  */
+       if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
+           && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
+         {
+           if (INTVAL (XEXP((op0), 1)) < 0)
+             return CCANmode;
+           else
+             return CCAPmode;
+         }
+       /* Fall through.  */
       case UNORDERED:
       case ORDERED:
       case UNEQ:
@@ -244,45 +626,306 @@ s390_select_ccmode (code, op0, op1)
       case UNGE:
       case UNGT:
       case LTGT:
+       if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
+           && GET_CODE (op1) != CONST_INT)
+         return CCSRmode;
        return CCSmode;
 
-      case LEU:
       case LTU:
       case GEU:
+       if (GET_CODE (op0) == PLUS
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
+         return CCL1mode;
+
+       if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
+           && GET_CODE (op1) != CONST_INT)
+         return CCURmode;
+       return CCUmode;
+
+      case LEU:
       case GTU:
+       if (GET_CODE (op0) == MINUS
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
+         return CCL2mode;
+
+       if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
+           && GET_CODE (op1) != CONST_INT)
+         return CCURmode;
        return CCUmode;
 
       default:
-       abort ();
+       gcc_unreachable ();
     }
 }
 
-/* Return branch condition mask to implement a branch 
-   specified by CODE.  */
+/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
+   that we can implement more efficiently.  */
 
-static int
-s390_branch_condition_mask (code)
-    rtx code;
-{ 
+void
+s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
+{
+  /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
+  if ((*code == EQ || *code == NE)
+      && *op1 == const0_rtx
+      && GET_CODE (*op0) == ZERO_EXTRACT
+      && GET_CODE (XEXP (*op0, 1)) == CONST_INT
+      && GET_CODE (XEXP (*op0, 2)) == CONST_INT
+      && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
+    {
+      rtx inner = XEXP (*op0, 0);
+      HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner));
+      HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1));
+      HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2));
+
+      if (len > 0 && len < modesize
+         && pos >= 0 && pos + len <= modesize
+         && modesize <= HOST_BITS_PER_WIDE_INT)
+       {
+         unsigned HOST_WIDE_INT block;
+         block = ((unsigned HOST_WIDE_INT) 1 << len) - 1;
+         block <<= modesize - pos - len;
+
+         *op0 = gen_rtx_AND (GET_MODE (inner), inner,
+                             gen_int_mode (block, GET_MODE (inner)));
+       }
+    }
+
+  /* Narrow AND of memory against immediate to enable TM.  */
+  if ((*code == EQ || *code == NE)
+      && *op1 == const0_rtx
+      && GET_CODE (*op0) == AND
+      && GET_CODE (XEXP (*op0, 1)) == CONST_INT
+      && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
+    {
+      rtx inner = XEXP (*op0, 0);
+      rtx mask = XEXP (*op0, 1);
+
+      /* Ignore paradoxical SUBREGs if all extra bits are masked out.  */
+      if (GET_CODE (inner) == SUBREG
+         && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner)))
+         && (GET_MODE_SIZE (GET_MODE (inner))
+             >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+         && ((INTVAL (mask)
+               & GET_MODE_MASK (GET_MODE (inner))
+               & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner))))
+             == 0))
+       inner = SUBREG_REG (inner);
+
+      /* Do not change volatile MEMs.  */
+      if (MEM_P (inner) && !MEM_VOLATILE_P (inner))
+       {
+         int part = s390_single_part (XEXP (*op0, 1),
+                                      GET_MODE (inner), QImode, 0);
+         if (part >= 0)
+           {
+             mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode);
+             inner = adjust_address_nv (inner, QImode, part);
+             *op0 = gen_rtx_AND (QImode, inner, mask);
+           }
+       }
+    }
+
+  /* Narrow comparisons against 0xffff to HImode if possible.  */
+  if ((*code == EQ || *code == NE)
+      && GET_CODE (*op1) == CONST_INT
+      && INTVAL (*op1) == 0xffff
+      && SCALAR_INT_MODE_P (GET_MODE (*op0))
+      && (nonzero_bits (*op0, GET_MODE (*op0)) 
+         & ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
+    {
+      *op0 = gen_lowpart (HImode, *op0);
+      *op1 = constm1_rtx;
+    }
+
+  /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible.  */
+  if (GET_CODE (*op0) == UNSPEC
+      && XINT (*op0, 1) == UNSPEC_CCU_TO_INT
+      && XVECLEN (*op0, 0) == 1
+      && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
+      && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
+      && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+      && *op1 == const0_rtx)
+    {
+      enum rtx_code new_code = UNKNOWN;
+      switch (*code)
+       {
+         case EQ: new_code = EQ;  break;
+         case NE: new_code = NE;  break;
+         case LT: new_code = GTU; break;
+         case GT: new_code = LTU; break;
+         case LE: new_code = GEU; break;
+         case GE: new_code = LEU; break;
+         default: break;
+       }
+
+      if (new_code != UNKNOWN)
+       {
+         *op0 = XVECEXP (*op0, 0, 0);
+         *code = new_code;
+       }
+    }
+
+  /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible.  */
+  if (GET_CODE (*op0) == UNSPEC
+      && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT
+      && XVECLEN (*op0, 0) == 1
+      && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode
+      && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
+      && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+      && *op1 == const0_rtx)
+    {
+      enum rtx_code new_code = UNKNOWN;
+      switch (*code)
+       {
+         case EQ: new_code = EQ;  break;
+         case NE: new_code = NE;  break;
+         default: break;
+       }
+
+      if (new_code != UNKNOWN)
+       {
+         *op0 = XVECEXP (*op0, 0, 0);
+         *code = new_code;
+       }
+    }
+
+  /* Simplify cascaded EQ, NE with const0_rtx.  */
+  if ((*code == NE || *code == EQ)
+      && (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE)
+      && GET_MODE (*op0) == SImode
+      && GET_MODE (XEXP (*op0, 0)) == CCZ1mode
+      && REG_P (XEXP (*op0, 0))
+      && XEXP (*op0, 1) == const0_rtx
+      && *op1 == const0_rtx)
+    {
+      if ((*code == EQ && GET_CODE (*op0) == NE)
+          || (*code == NE && GET_CODE (*op0) == EQ))
+       *code = EQ;
+      else
+       *code = NE;
+      *op0 = XEXP (*op0, 0);
+    }
+
+  /* Prefer register over memory as first operand.  */
+  if (MEM_P (*op0) && REG_P (*op1))
+    {
+      rtx tem = *op0; *op0 = *op1; *op1 = tem;
+      *code = swap_condition (*code);
+    }
+}
+
+/* Emit a compare instruction suitable to implement the comparison
+   OP0 CODE OP1.  Return the correct condition RTL to be placed in
+   the IF_THEN_ELSE of the conditional branch testing the result.  */
+
+rtx
+s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
+{
+  enum machine_mode mode = s390_select_ccmode (code, op0, op1);
+  rtx ret = NULL_RTX;
+
+  /* Do not output a redundant compare instruction if a compare_and_swap
+     pattern already computed the result and the machine modes are compatible.  */
+  if (s390_compare_emitted 
+      && (s390_cc_modes_compatible (GET_MODE (s390_compare_emitted), mode)
+         == GET_MODE (s390_compare_emitted)))
+    ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx); 
+  else
+    {
+      rtx cc = gen_rtx_REG (mode, CC_REGNUM);
+      
+      emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
+      ret = gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); 
+    }
+  s390_compare_emitted = NULL_RTX;
+  return ret;
+}
+
+/* Emit a SImode compare and swap instruction setting MEM to NEW_RTX if OLD
+   matches CMP.
+   Return the correct condition RTL to be placed in the IF_THEN_ELSE of the
+   conditional branch testing the result.  */
+
+static rtx
+s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new_rtx)
+{
+  rtx ret;
+
+  emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new_rtx));
+  ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
+
+  s390_compare_emitted = NULL_RTX;
+
+  return ret;
+}
+
+/* Emit a jump instruction to TARGET.  If COND is NULL_RTX, emit an
+   unconditional jump, else a conditional jump under condition COND.  */
+
+void
+s390_emit_jump (rtx target, rtx cond)
+{
+  rtx insn;
+
+  target = gen_rtx_LABEL_REF (VOIDmode, target);
+  if (cond)
+    target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx);
+
+  insn = gen_rtx_SET (VOIDmode, pc_rtx, target);
+  emit_jump_insn (insn);
+}
+
+/* Return branch condition mask to implement a branch
+   specified by CODE.  Return -1 for invalid comparisons.  */
+
+int
+s390_branch_condition_mask (rtx code)
+{
   const int CC0 = 1 << 3;
   const int CC1 = 1 << 2;
   const int CC2 = 1 << 1;
   const int CC3 = 1 << 0;
 
-  if (GET_CODE (XEXP (code, 0)) != REG
-      || REGNO (XEXP (code, 0)) != CC_REGNUM
-      || XEXP (code, 1) != const0_rtx)
-    abort ();
+  gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
+  gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
+  gcc_assert (XEXP (code, 1) == const0_rtx);
 
   switch (GET_MODE (XEXP (code, 0)))
     {
     case CCZmode:
+    case CCZ1mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
        case NE:        return CC1 | CC2 | CC3;
-       default:
-         abort ();
+       default:        return -1;
+        }
+      break;
+
+    case CCT1mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC1;
+       case NE:        return CC0 | CC2 | CC3;
+       default:        return -1;
+        }
+      break;
+
+    case CCT2mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC2;
+       case NE:        return CC0 | CC1 | CC3;
+       default:        return -1;
+        }
+      break;
+
+    case CCT3mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC3;
+       case NE:        return CC0 | CC1 | CC2;
+       default:        return -1;
         }
       break;
 
@@ -291,13 +934,40 @@ s390_branch_condition_mask (code)
         {
         case EQ:       return CC0 | CC2;
        case NE:        return CC1 | CC3;
-       case UNORDERED: return CC2 | CC3;  /* carry */
-       case ORDERED:   return CC0 | CC1;  /* no carry */
-       default:
-         abort ();
+       default:        return -1;
+        }
+      break;
+
+    case CCL1mode:
+      switch (GET_CODE (code))
+        {
+       case LTU:       return CC2 | CC3;  /* carry */
+       case GEU:       return CC0 | CC1;  /* no carry */
+       default:        return -1;
+        }
+      break;
+
+    case CCL2mode:
+      switch (GET_CODE (code))
+        {
+       case GTU:       return CC0 | CC1;  /* borrow */
+       case LEU:       return CC2 | CC3;  /* no borrow */
+       default:        return -1;
         }
       break;
 
+    case CCL3mode:
+      switch (GET_CODE (code))
+       {
+       case EQ:        return CC0 | CC2;
+       case NE:        return CC1 | CC3;
+       case LTU:       return CC1;
+       case GTU:       return CC3;
+       case LEU:       return CC1 | CC2;
+       case GEU:       return CC2 | CC3;
+       default:        return -1;
+       }
+
     case CCUmode:
       switch (GET_CODE (code))
         {
@@ -307,8 +977,46 @@ s390_branch_condition_mask (code)
         case GTU:      return CC2;
         case LEU:      return CC0 | CC1;
         case GEU:      return CC0 | CC2;
-       default:
-         abort ();
+       default:        return -1;
+        }
+      break;
+
+    case CCURmode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0;
+        case NE:       return CC2 | CC1 | CC3;
+        case LTU:      return CC2;
+        case GTU:      return CC1;
+        case LEU:      return CC0 | CC2;
+        case GEU:      return CC0 | CC1;
+       default:        return -1;
+        }
+      break;
+
+    case CCAPmode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0;
+        case NE:       return CC1 | CC2 | CC3;
+        case LT:       return CC1 | CC3;
+        case GT:       return CC2;
+        case LE:       return CC0 | CC1 | CC3;
+        case GE:       return CC0 | CC2;
+       default:        return -1;
+        }
+      break;
+
+    case CCANmode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0;
+        case NE:       return CC1 | CC2 | CC3;
+        case LT:       return CC1;
+        case GT:       return CC2 | CC3;
+        case LE:       return CC0 | CC1;
+        case GE:       return CC0 | CC2 | CC3;
+       default:        return -1;
         }
       break;
 
@@ -329,25 +1037,81 @@ s390_branch_condition_mask (code)
         case UNLE:     return CC0 | CC1 | CC3;
         case UNGE:     return CC0 | CC2 | CC3;
        case LTGT:      return CC1 | CC2;
-       default:
-         abort ();
+       default:        return -1;
+        }
+      break;
+
+    case CCSRmode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0;
+        case NE:       return CC2 | CC1 | CC3;
+        case LT:       return CC2;
+        case GT:       return CC1;
+        case LE:       return CC0 | CC2;
+        case GE:       return CC0 | CC1;
+       case UNORDERED: return CC3;
+       case ORDERED:   return CC0 | CC2 | CC1;
+       case UNEQ:      return CC0 | CC3;
+        case UNLT:     return CC2 | CC3;
+        case UNGT:     return CC1 | CC3;
+        case UNLE:     return CC0 | CC2 | CC3;
+        case UNGE:     return CC0 | CC1 | CC3;
+       case LTGT:      return CC2 | CC1;
+       default:        return -1;
         }
+      break;
 
     default:
-      abort ();
+      return -1;
     }
 }
 
-/* If INV is false, return assembler mnemonic string to implement 
-   a branch specified by CODE.  If INV is true, return mnemonic 
-   for the corresponding inverted branch.  */
 
-static const char *
-s390_branch_condition_mnemonic (code, inv)
-     rtx code;
-     int inv;
+/* Return branch condition mask to implement a compare and branch
+   specified by CODE.  Return -1 for invalid comparisons.  */
+
+int
+s390_compare_and_branch_condition_mask (rtx code)
 {
-  static const char *mnemonic[16] =
+  const int CC0 = 1 << 3;
+  const int CC1 = 1 << 2;
+  const int CC2 = 1 << 1;
+
+  switch (GET_CODE (code))
+    {
+    case EQ:
+      return CC0;
+    case NE:
+      return CC1 | CC2;
+    case LT:
+    case LTU:
+      return CC1;
+    case GT:
+    case GTU:
+      return CC2;
+    case LE:
+    case LEU:
+      return CC0 | CC1;
+    case GE:
+    case GEU:
+      return CC0 | CC2;
+    default:
+      gcc_unreachable ();
+    }
+  return -1;
+}
+
+/* If INV is false, return assembler mnemonic string to implement
+   a branch specified by CODE.  If INV is true, return mnemonic
+   for the corresponding inverted branch.  */
+
+static const char *
+s390_branch_condition_mnemonic (rtx code, int inv)
+{
+  int mask;
+
+  static const char *const mnemonic[16] =
     {
       NULL, "o", "h", "nle",
       "l", "nhe", "lh", "ne",
@@ -355,909 +1119,605 @@ s390_branch_condition_mnemonic (code, inv)
       "le", "nh", "no", NULL
     };
 
-  int mask = s390_branch_condition_mask (code);
+  if (GET_CODE (XEXP (code, 0)) == REG
+      && REGNO (XEXP (code, 0)) == CC_REGNUM
+      && XEXP (code, 1) == const0_rtx)
+    mask = s390_branch_condition_mask (code);
+  else
+    mask = s390_compare_and_branch_condition_mask (code);
+
+  gcc_assert (mask >= 0);
 
   if (inv)
     mask ^= 15;
 
-  if (mask < 1 || mask > 14)
-    abort ();
+  gcc_assert (mask >= 1 && mask <= 14);
 
   return mnemonic[mask];
 }
 
-/* If OP is an integer constant of mode MODE with exactly one
-   HImode subpart unequal to DEF, return the number of that 
-   subpart.  As a special case, all HImode subparts of OP are
-   equal to DEF, return zero.  Otherwise, return -1.  */
+/* Return the part of op which has a value different from def.
+   The size of the part is determined by mode.
+   Use this function only if you already know that op really
+   contains such a part.  */
 
-int
-s390_single_hi (op, mode, def)
-     rtx op;
-     enum machine_mode mode;
-     int def;
+unsigned HOST_WIDE_INT
+s390_extract_part (rtx op, enum machine_mode mode, int def)
 {
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value;
-      int n_parts = GET_MODE_SIZE (mode) / 2;
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) INTVAL (op);
-          else
-            value >>= 16;
-
-          if ((value & 0xffff) != (unsigned)(def & 0xffff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
-
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
+  unsigned HOST_WIDE_INT value = 0;
+  int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode);
+  int part_bits = GET_MODE_BITSIZE (mode);
+  unsigned HOST_WIDE_INT part_mask
+    = ((unsigned HOST_WIDE_INT)1 << part_bits) - 1;
+  int i;
 
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
+  for (i = 0; i < max_parts; i++)
     {
-      unsigned HOST_WIDE_INT value;
-      int n_parts = GET_MODE_SIZE (mode) / 2;
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-          else if (i == HOST_BITS_PER_WIDE_INT / 16)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
-          else
-            value >>= 16;
-
-          if ((value & 0xffff) != (unsigned)(def & 0xffff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
+      if (i == 0)
+       value = (unsigned HOST_WIDE_INT) INTVAL (op);
+      else
+       value >>= part_bits;
 
-      return part == -1 ? 0 : (n_parts - 1 - part);
+      if ((value & part_mask) != (def & part_mask))
+       return value & part_mask;
     }
 
-  return -1;      
+  gcc_unreachable ();
 }
 
-/* Extract the HImode part number PART from integer 
-   constant OP of mode MODE.  */
+/* If OP is an integer constant of mode MODE with exactly one
+   part of mode PART_MODE unequal to DEF, return the number of that
+   part. Otherwise, return -1.  */
 
 int
-s390_extract_hi (op, mode, part)
-    rtx op;
-    enum machine_mode mode;
-    int part;
-{
-  int n_parts = GET_MODE_SIZE (mode) / 2;
-  if (part < 0 || part >= n_parts)
-    abort();
-  else
-    part = n_parts - 1 - part;
+s390_single_part (rtx op,
+                 enum machine_mode mode,
+                 enum machine_mode part_mode,
+                 int def)
+{
+  unsigned HOST_WIDE_INT value = 0;
+  int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode);
+  unsigned HOST_WIDE_INT part_mask
+    = ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1;
+  int i, part = -1;
 
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
-      return ((value >> (16 * part)) & 0xffff);
-    }
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
+  if (GET_CODE (op) != CONST_INT)
+    return -1;
+
+  for (i = 0; i < n_parts; i++)
     {
-      unsigned HOST_WIDE_INT value;
-      if (part < HOST_BITS_PER_WIDE_INT / 16)
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
+      if (i == 0)
+       value = (unsigned HOST_WIDE_INT) INTVAL (op);
       else
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
-        part -= HOST_BITS_PER_WIDE_INT / 16;
+       value >>= GET_MODE_BITSIZE (part_mode);
 
-      return ((value >> (16 * part)) & 0xffff); 
+      if ((value & part_mask) != (def & part_mask))
+       {
+         if (part != -1)
+           return -1;
+         else
+           part = i;
+       }
     }
-
-  abort ();
+  return part == -1 ? -1 : n_parts - 1 - part;
 }
 
-/* If OP is an integer constant of mode MODE with exactly one
-   QImode subpart unequal to DEF, return the number of that 
-   subpart.  As a special case, all QImode subparts of OP are
-   equal to DEF, return zero.  Otherwise, return -1.  */
+/* Return true if IN contains a contiguous bitfield in the lower SIZE
+   bits and no other bits are set in IN.  POS and LENGTH can be used
+   to obtain the start position and the length of the bitfield.
 
-int
-s390_single_qi (op, mode, def)
-     rtx op;
-     enum machine_mode mode;
-     int def;
+   POS gives the position of the first bit of the bitfield counting
+   from the lowest order bit starting with zero.  In order to use this
+   value for S/390 instructions this has to be converted to "bits big
+   endian" style.  */
+
+bool
+s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size,
+                          int *pos, int *length)
 {
-  if (GET_CODE (op) == CONST_INT)
+  int tmp_pos = 0;
+  int tmp_length = 0;
+  int i;
+  unsigned HOST_WIDE_INT mask = 1ULL;
+  bool contiguous = false;
+
+  for (i = 0; i < size; mask <<= 1, i++)
     {
-      unsigned HOST_WIDE_INT value;
-      int n_parts = GET_MODE_SIZE (mode);
-      int i, part = -1;
+      if (contiguous)
+       {
+         if (mask & in)
+           tmp_length++;
+         else
+           break;
+       }
+      else
+       {
+         if (mask & in)
+           {
+             contiguous = true;
+             tmp_length++;
+           }
+         else
+           tmp_pos++;
+       }
+    }
 
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) INTVAL (op);
-          else
-            value >>= 8;
+  if (!tmp_length)
+    return false;
 
-          if ((value & 0xff) != (unsigned)(def & 0xff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
+  /* Calculate a mask for all bits beyond the contiguous bits.  */
+  mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1));
 
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
+  if (mask & in)
+    return false;
 
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
-    {
-      unsigned HOST_WIDE_INT value;
-      int n_parts = GET_MODE_SIZE (mode);
-      int i, part = -1;
+  if (tmp_length + tmp_pos - 1 > size)
+    return false;
 
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-          else if (i == HOST_BITS_PER_WIDE_INT / 8)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
-          else
-            value >>= 8;
-
-          if ((value & 0xff) != (unsigned)(def & 0xff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
+  if (length)
+    *length = tmp_length;
 
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
+  if (pos)
+    *pos = tmp_pos;
 
-  return -1;      
+  return true;
 }
 
-/* Extract the QImode part number PART from integer 
-   constant OP of mode MODE.  */
-
-int
-s390_extract_qi (op, mode, part)
-    rtx op;
-    enum machine_mode mode;
-    int part;
-{
-  int n_parts = GET_MODE_SIZE (mode);
-  if (part < 0 || part >= n_parts)
-    abort();
-  else
-    part = n_parts - 1 - part;
+/* Check whether we can (and want to) split a double-word
+   move in mode MODE from SRC to DST into two single-word
+   moves, moving the subword FIRST_SUBWORD first.  */
 
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
-      return ((value >> (8 * part)) & 0xff);
-    }
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
+bool
+s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
+{
+  /* Floating point registers cannot be split.  */
+  if (FP_REG_P (src) || FP_REG_P (dst))
+    return false;
+
+  /* We don't need to split if operands are directly accessible.  */
+  if (s_operand (src, mode) || s_operand (dst, mode))
+    return false;
+
+  /* Non-offsettable memory references cannot be split.  */
+  if ((GET_CODE (src) == MEM && !offsettable_memref_p (src))
+      || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst)))
+    return false;
+
+  /* Moving the first subword must not clobber a register
+     needed to move the second subword.  */
+  if (register_operand (dst, mode))
     {
-      unsigned HOST_WIDE_INT value;
-      if (part < HOST_BITS_PER_WIDE_INT / 8)
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-      else
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
-        part -= HOST_BITS_PER_WIDE_INT / 8;
-
-      return ((value >> (8 * part)) & 0xff); 
+      rtx subreg = operand_subword (dst, first_subword, 0, mode);
+      if (reg_overlap_mentioned_p (subreg, src))
+        return false;
     }
 
-  abort ();
+  return true;
 }
 
+/* Return true if it can be proven that [MEM1, MEM1 + SIZE]
+   and [MEM2, MEM2 + SIZE] do overlap and false
+   otherwise.  */
 
-/* Change optimizations to be performed, depending on the 
-   optimization level.
-
-   LEVEL is the optimization level specified; 2 if `-O2' is
-   specified, 1 if `-O' is specified, and 0 if neither is specified.
+bool
+s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size)
+{
+  rtx addr1, addr2, addr_delta;
+  HOST_WIDE_INT delta;
 
-   SIZE is non-zero if `-Os' is specified and zero otherwise.  */
+  if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
+    return true;
 
-void
-optimization_options (level, size)
-     int level ATTRIBUTE_UNUSED;
-     int size ATTRIBUTE_UNUSED;
-{
-#ifdef HAVE_decrement_and_branch_on_count
-  /* When optimizing, enable use of BRCT instruction.  */
-  if (level >= 1)
-      flag_branch_on_count_reg = 1;
-#endif
-}
+  if (size == 0)
+    return false;
 
-void
-override_options ()
-{
-  /* Acquire a unique set number for our register saves and restores.  */
-  s390_sr_alias_set = new_alias_set ();
-}
+  addr1 = XEXP (mem1, 0);
+  addr2 = XEXP (mem2, 0);
 
+  addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
 
-/* Map for smallest class containing reg regno.  */
+  /* This overlapping check is used by peepholes merging memory block operations.
+     Overlapping operations would otherwise be recognized by the S/390 hardware
+     and would fall back to a slower implementation. Allowing overlapping 
+     operations would lead to slow code but not to wrong code. Therefore we are
+     somewhat optimistic if we cannot prove that the memory blocks are 
+     overlapping.
+     That's why we return false here although this may accept operations on
+     overlapping memory areas.  */
+  if (!addr_delta || GET_CODE (addr_delta) != CONST_INT)
+    return false;
 
-enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
-{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
-  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
-  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
-  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
-  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  ADDR_REGS,    NO_REGS,   ADDR_REGS 
-};
+  delta = INTVAL (addr_delta);
 
+  if (delta == 0
+      || (delta > 0 && delta < size)
+      || (delta < 0 && -delta < size))
+    return true;
 
-/* Return true if OP a (const_int 0) operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-int
-const0_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  return op == CONST0_RTX (mode);
+  return false;
 }
 
-/* Return true if OP is constant.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Check whether the address of memory reference MEM2 equals exactly
+   the address of memory reference MEM1 plus DELTA.  Return true if
+   we can prove this to be the case, false otherwise.  */
 
-int
-consttable_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+bool
+s390_offset_p (rtx mem1, rtx mem2, rtx delta)
 {
-  return CONSTANT_P (op);
-}
+  rtx addr1, addr2, addr_delta;
 
-/* Return true if the mode of operand OP matches MODE.
-   If MODE is set to VOIDmode, set it to the mode of OP.  */ 
+  if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
+    return false;
 
-static int
-check_mode (op, mode)
-     register rtx op;
-     enum machine_mode *mode;
-{
-  if (*mode == VOIDmode)
-      *mode = GET_MODE (op);
-  else
-  {
-    if (GET_MODE (op) != VOIDmode && GET_MODE (op) != *mode)
-       return 0;
-  }
-  return 1;
-}
-
-/* Return true if OP a valid operand for the LARL instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+  addr1 = XEXP (mem1, 0);
+  addr2 = XEXP (mem2, 0);
 
-int
-larl_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  if (! check_mode (op, &mode))
-    return 0;
+  addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
+  if (!addr_delta || !rtx_equal_p (addr_delta, delta))
+    return false;
 
-  /* Allow labels and local symbols.  */
-  if (GET_CODE (op) == LABEL_REF)
-    return 1;
-  if (GET_CODE (op) == SYMBOL_REF
-      && (!flag_pic || SYMBOL_REF_FLAG (op) 
-          || CONSTANT_POOL_ADDRESS_P (op)))
-    return 1;
+  return true;
+}
 
-  /* Everything else must have a CONST, so strip it.  */
-  if (GET_CODE (op) != CONST)
-    return 0;
-  op = XEXP (op, 0);
+/* Expand logical operator CODE in mode MODE with operands OPERANDS.  */
 
-  /* Allow adding *even* constants.  */
-  if (GET_CODE (op) == PLUS)
+void
+s390_expand_logical_operator (enum rtx_code code, enum machine_mode mode,
+                             rtx *operands)
+{
+  enum machine_mode wmode = mode;
+  rtx dst = operands[0];
+  rtx src1 = operands[1];
+  rtx src2 = operands[2];
+  rtx op, clob, tem;
+
+  /* If we cannot handle the operation directly, use a temp register.  */
+  if (!s390_logical_operator_ok_p (operands))
+    dst = gen_reg_rtx (mode);
+
+  /* QImode and HImode patterns make sense only if we have a destination
+     in memory.  Otherwise perform the operation in SImode.  */
+  if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM)
+    wmode = SImode;
+
+  /* Widen operands if required.  */
+  if (mode != wmode)
     {
-      if (GET_CODE (XEXP (op, 1)) != CONST_INT
-          || (INTVAL (XEXP (op, 1)) & 1) != 0)
-        return 0;
-      op = XEXP (op, 0);
+      if (GET_CODE (dst) == SUBREG
+         && (tem = simplify_subreg (wmode, dst, mode, 0)) != 0)
+       dst = tem;
+      else if (REG_P (dst))
+       dst = gen_rtx_SUBREG (wmode, dst, 0);
+      else
+        dst = gen_reg_rtx (wmode);
+
+      if (GET_CODE (src1) == SUBREG
+         && (tem = simplify_subreg (wmode, src1, mode, 0)) != 0)
+       src1 = tem;
+      else if (GET_MODE (src1) != VOIDmode)
+       src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0);
+
+      if (GET_CODE (src2) == SUBREG
+         && (tem = simplify_subreg (wmode, src2, mode, 0)) != 0)
+       src2 = tem;
+      else if (GET_MODE (src2) != VOIDmode)
+       src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0);
     }
 
-  /* Labels and local symbols allowed here as well.  */
-  if (GET_CODE (op) == LABEL_REF)
-    return 1;
-  if (GET_CODE (op) == SYMBOL_REF
-      && (!flag_pic || SYMBOL_REF_FLAG (op)
-          || CONSTANT_POOL_ADDRESS_P (op)))
-    return 1;
-
-  /* Now we must have a @GOTENT offset or @PLT stub.  */
-  if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == 111)
-    return 1;
-  if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == 113)
-    return 1;
+  /* Emit the instruction.  */
+  op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2));
+  clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+  emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob)));
 
-  return 0;
+  /* Fix up the destination if needed.  */
+  if (dst != operands[0])
+    emit_move_insn (operands[0], gen_lowpart (mode, dst));
 }
 
-/* Return true if OP is a valid FP-Register.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR).  */
 
-int
-fp_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+bool
+s390_logical_operator_ok_p (rtx *operands)
 {
-  register enum rtx_code code = GET_CODE (op);
-  if (! check_mode (op, &mode))
-    return 0;
-  if (code == REG && REGNO_OK_FOR_FP_P (REGNO (op)))
-    return 1;
-  else
-    return 0;
+  /* If the destination operand is in memory, it needs to coincide
+     with one of the source operands.  After reload, it has to be
+     the first source operand.  */
+  if (GET_CODE (operands[0]) == MEM)
+    return rtx_equal_p (operands[0], operands[1])
+          || (!reload_completed && rtx_equal_p (operands[0], operands[2]));
+
+  return true;
 }
 
-/* Helper routine to implement s_operand and s_imm_operand.
-   OP is the current operation.
-   MODE is the current operation mode.
-   ALLOW_IMMEDIATE specifies whether immediate operands should
-   be accepted or not.  */
+/* Narrow logical operation CODE of memory operand MEMOP with immediate
+   operand IMMOP to switch from SS to SI type instructions.  */
 
-static int
-general_s_operand (op, mode, allow_immediate)
-     register rtx op;
-     enum machine_mode mode;
-     int allow_immediate;
+void
+s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
 {
-  struct s390_address addr;
+  int def = code == AND ? -1 : 0;
+  HOST_WIDE_INT mask;
+  int part;
 
-  /* Call general_operand first, so that we don't have to
-     check for many special cases.  */
-  if (!general_operand (op, mode))
-    return 0;
+  gcc_assert (GET_CODE (*memop) == MEM);
+  gcc_assert (!MEM_VOLATILE_P (*memop));
 
-  /* Just like memory_operand, allow (subreg (mem ...))
-     after reload.  */
-  if (reload_completed 
-      && GET_CODE (op) == SUBREG 
-      && GET_CODE (SUBREG_REG (op)) == MEM)
-    op = SUBREG_REG (op);
+  mask = s390_extract_part (*immop, QImode, def);
+  part = s390_single_part (*immop, GET_MODE (*memop), QImode, def);
+  gcc_assert (part >= 0);
 
-  switch (GET_CODE (op))
-    {
-      /* Constants that we are sure will be forced to the
-         literal pool in reload are OK as s-operand.  Note
-        that we cannot call s390_preferred_reload_class here
-        because it might not be known yet at this point 
-        whether the current function is a leaf or not.  */
-      case CONST_INT:
-      case CONST_DOUBLE:
-       if (!allow_immediate || reload_completed)
-         break;
-       if (!legitimate_reload_constant_p (op))
-         return 1;
-       if (!TARGET_64BIT)
-         return 1;
-       break;
+  *memop = adjust_address (*memop, QImode, part);
+  *immop = gen_int_mode (mask, QImode);
+}
 
-      /* Memory operands are OK unless they already use an
-        index register.  */
-      case MEM:
-       if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
-         return 1;
-       if (s390_decompose_address (XEXP (op, 0), &addr, FALSE) 
-           && !addr.indx)
-         return 1;
-       break;
 
-      default:
-       break;
-    }
+/* How to allocate a 'struct machine_function'.  */
 
-  return 0;
+static struct machine_function *
+s390_init_machine_status (void)
+{
+  return GGC_CNEW (struct machine_function);
 }
 
-/* Return true if OP is a valid S-type operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Change optimizations to be performed, depending on the
+   optimization level.
+
+   LEVEL is the optimization level specified; 2 if `-O2' is
+   specified, 1 if `-O' is specified, and 0 if neither is specified.
 
-int
-s_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+   SIZE is nonzero if `-Os' is specified and zero otherwise.  */
+
+void
+optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
 {
-  return general_s_operand (op, mode, 0);
-}
+  /* ??? There are apparently still problems with -fcaller-saves.  */
+  flag_caller_saves = 0;
 
-/* Return true if OP is a valid S-type operand or an immediate 
-   operand that can be addressed as S-type operand by forcing 
-   it into the literal pool.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+  /* By default, always emit DWARF-2 unwind info.  This allows debugging
+     without maintaining a stack frame back-chain.  */
+  flag_asynchronous_unwind_tables = 1;
 
-int
-s_imm_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  return general_s_operand (op, mode, 1);
+  /* Use MVCLE instructions to decrease code size if requested.  */
+  if (size != 0)
+    target_flags |= MASK_MVCLE;
 }
 
-/* Return true if OP is a valid operand for the BRAS instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Return true if ARG is the name of a processor.  Set *TYPE and *FLAGS
+   to the associated processor_type and processor_flags if so.  */
 
-int
-bras_sym_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+s390_handle_arch_option (const char *arg,
+                        enum processor_type *type,
+                        enum processor_flags *flags)
 {
-  register enum rtx_code code = GET_CODE (op);
-
-  /* Allow SYMBOL_REFs.  */
-  if (code == SYMBOL_REF)
-    return 1;
+  static struct pta
+    {
+      const char *const name;          /* processor name or nickname.  */
+      const enum processor_type processor;
+      const enum processor_flags flags;
+    }
+  const processor_alias_table[] =
+    {
+      {"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT},
+      {"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT},
+      {"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
+      {"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
+                                   | PF_LONG_DISPLACEMENT},
+      {"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
+                                       | PF_LONG_DISPLACEMENT | PF_EXTIMM},
+      {"z9-ec", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
+                             | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP },
+      {"z10", PROCESSOR_2097_Z10, PF_IEEE_FLOAT | PF_ZARCH
+                             | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP | PF_Z10},
+    };
+  size_t i;
 
-  /* Allow @PLT stubs.  */
-  if (code == CONST
-      && GET_CODE (XEXP (op, 0)) == UNSPEC
-      && XINT (XEXP (op, 0), 1) == 113)
-    return 1;
-  return 0;
+  for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++)
+    if (strcmp (arg, processor_alias_table[i].name) == 0)
+      {
+       *type = processor_alias_table[i].processor;
+       *flags = processor_alias_table[i].flags;
+       return true;
+      }
+  return false;
 }
 
-\f
-/* Return true if OP is a load multiple operation.  It is known to be a
-   PARALLEL and the first section will be tested. 
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Implement TARGET_HANDLE_OPTION.  */
 
-int
-load_multiple_operation (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
 {
-  int count = XVECLEN (op, 0);
-  unsigned int dest_regno;
-  rtx src_addr;
-  int i, off;
+  switch (code)
+    {
+    case OPT_march_:
+      return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags);
+
+    case OPT_mstack_guard_:
+      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1)
+       return false;
+      if (exact_log2 (s390_stack_guard) == -1)
+       error ("stack guard value must be an exact power of 2");
+      return true;
+
+    case OPT_mstack_size_:
+      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1)
+       return false;
+      if (exact_log2 (s390_stack_size) == -1)
+       error ("stack size must be an exact power of 2");
+      return true;
 
+    case OPT_mtune_:
+      return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
 
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
-    return 0;
+    case OPT_mwarn_framesize_:
+      return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
 
-  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
-  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+    default:
+      return true;
+    }
+}
 
-  /* Check, is base, or base + displacement.  */
+void
+override_options (void)
+{
+  /* Set up function hooks.  */
+  init_machine_status = s390_init_machine_status;
 
-  if (GET_CODE (src_addr) == REG)
-    off = 0;
-  else if (GET_CODE (src_addr) == PLUS
-          && GET_CODE (XEXP (src_addr, 0)) == REG 
-          && GET_CODE (XEXP (src_addr, 1)) == CONST_INT)
+  /* Architecture mode defaults according to ABI.  */
+  if (!(target_flags_explicit & MASK_ZARCH))
     {
-      off = INTVAL (XEXP (src_addr, 1));
-      src_addr = XEXP (src_addr, 0);
+      if (TARGET_64BIT)
+       target_flags |= MASK_ZARCH;
+      else
+       target_flags &= ~MASK_ZARCH;
     }
-  else
-    return 0;
 
-  if (src_addr == frame_pointer_rtx || src_addr == arg_pointer_rtx)
-    return 0;
-
-  for (i = 1; i < count; i++)
-    {
-      rtx elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_DEST (elt)) != REG
-         || GET_MODE (SET_DEST (elt)) != Pmode
-         || REGNO (SET_DEST (elt)) != dest_regno + i
-         || GET_CODE (SET_SRC (elt)) != MEM
-         || GET_MODE (SET_SRC (elt)) != Pmode
-         || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
-         || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
-         || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
-         || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1))
-            != off + i * UNITS_PER_WORD)
-       return 0;
+  /* Determine processor architectural level.  */
+  if (!s390_arch_string)
+    {
+      s390_arch_string = TARGET_ZARCH? "z900" : "g5";
+      s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags);
     }
 
-  return 1;
-}
+  /* Determine processor to tune for.  */
+  if (s390_tune == PROCESSOR_max)
+    {
+      s390_tune = s390_arch;
+      s390_tune_flags = s390_arch_flags;
+    }
 
-/* Return true if OP is a store multiple operation.  It is known to be a
-   PARALLEL and the first section will be tested. 
-   OP is the current operation.
-   MODE is the current operation mode.  */
+  /* Sanity checks.  */
+  if (TARGET_ZARCH && !TARGET_CPU_ZARCH)
+    error ("z/Architecture mode not supported on %s", s390_arch_string);
+  if (TARGET_64BIT && !TARGET_ZARCH)
+    error ("64-bit ABI not supported in ESA/390 mode");
 
-int
-store_multiple_operation (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  int count = XVECLEN (op, 0);
-  unsigned int src_regno;
-  rtx dest_addr;
-  int i, off;
-
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
-    return 0;
+  if (TARGET_HARD_DFP && !TARGET_DFP)
+    {
+      if (target_flags_explicit & MASK_HARD_DFP)
+       {
+         if (!TARGET_CPU_DFP)
+           error ("Hardware decimal floating point instructions"
+                  " not available on %s", s390_arch_string);
+         if (!TARGET_ZARCH)
+           error ("Hardware decimal floating point instructions"
+                  " not available in ESA/390 mode");
+       }
+      else
+       target_flags &= ~MASK_HARD_DFP;
+    }
 
-  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
-  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+  if ((target_flags_explicit & MASK_SOFT_FLOAT) && TARGET_SOFT_FLOAT)
+    {
+      if ((target_flags_explicit & MASK_HARD_DFP) && TARGET_HARD_DFP)
+       error ("-mhard-dfp can't be used in conjunction with -msoft-float");
 
-  /* Check, is base, or base + displacement.  */
+      target_flags &= ~MASK_HARD_DFP;
+    }
 
-  if (GET_CODE (dest_addr) == REG)
-    off = 0;
-  else if (GET_CODE (dest_addr) == PLUS
-          && GET_CODE (XEXP (dest_addr, 0)) == REG 
-          && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT)
+  /* Set processor cost function.  */
+  switch (s390_tune)
     {
-      off = INTVAL (XEXP (dest_addr, 1));
-      dest_addr = XEXP (dest_addr, 0);
+    case PROCESSOR_2084_Z990:
+      s390_cost = &z990_cost;
+      break;
+    case PROCESSOR_2094_Z9_109:
+      s390_cost = &z9_109_cost;
+      break;
+    case PROCESSOR_2097_Z10:
+      s390_cost = &z10_cost;
+      break;
+    default:
+      s390_cost = &z900_cost;
     }
-  else
-    return 0;
 
-  if (dest_addr == frame_pointer_rtx || dest_addr == arg_pointer_rtx)
-    return 0;
+  if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT)
+    error ("-mbackchain -mpacked-stack -mhard-float are not supported "
+          "in combination");
 
-  for (i = 1; i < count; i++)
-    {
-      rtx elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_SRC (elt)) != REG
-         || GET_MODE (SET_SRC (elt)) != Pmode
-         || REGNO (SET_SRC (elt)) != src_regno + i
-         || GET_CODE (SET_DEST (elt)) != MEM
-         || GET_MODE (SET_DEST (elt)) != Pmode
-         || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
-         || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
-         || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
-         || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1))
-            != off + i * UNITS_PER_WORD)
-       return 0;
+  if (s390_stack_size)
+    {
+      if (s390_stack_guard >= s390_stack_size)
+       error ("stack size must be greater than the stack guard value");
+      else if (s390_stack_size > 1 << 16)
+       error ("stack size must not be greater than 64k");
     }
-  return 1;
+  else if (s390_stack_guard)
+    error ("-mstack-guard implies use of -mstack-size"); 
+
+#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
+  if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
+    target_flags |= MASK_LONG_DOUBLE_128;
+#endif
+
+  if (s390_tune == PROCESSOR_2097_Z10
+      && !PARAM_SET_P (PARAM_MAX_UNROLLED_INSNS))
+    set_param_value ("max-unrolled-insns", 100);
 }
 
+/* Map for smallest class containing reg regno.  */
 
-/* Return true if OP contains a symbol reference */
+const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
+{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS,
+  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
+  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
+  ADDR_REGS,    ADDR_REGS, ADDR_REGS, ADDR_REGS,
+  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
+  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
+  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
+  FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
+  ADDR_REGS,    CC_REGS,   ADDR_REGS, ADDR_REGS,
+  ACCESS_REGS, ACCESS_REGS
+};
 
-int
-symbolic_reference_mentioned_p (op)
-     rtx op;
+/* Return attribute type of insn.  */
+
+static enum attr_type
+s390_safe_attr_type (rtx insn)
 {
-  register const char *fmt;
-  register int i;
+  if (recog_memoized (insn) >= 0)
+    return get_attr_type (insn);
+  else
+    return TYPE_NONE;
+}
 
-  if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
-    return 1;
-
-  fmt = GET_RTX_FORMAT (GET_CODE (op));
-  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'E')
-       {
-         register int j;
-
-         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
-           if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
-             return 1;
-       }
-
-      else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
-       return 1;
-    }
-
-  return 0;
-}
-
-
-/* Return true if OP is a legitimate general operand when 
-   generating PIC code.  It is given that flag_pic is on 
-   and that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
-
-int
-legitimate_pic_operand_p (op)
-     register rtx op;
-{
-  /* Accept all non-symbolic constants.  */
-  if (!SYMBOLIC_CONST (op))
-    return 1;
-
-  /* Reject everything else; must be handled 
-     via emit_pic_move.  */
-  return 0;
-}
-
-/* Returns true if the constant value OP is a legitimate general operand.
-   It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
-
-int
-legitimate_constant_p (op)
-     register rtx op;
-{
-  /* Accept all non-symbolic constants.  */
-  if (!SYMBOLIC_CONST (op))
-    return 1;
-
-  /* In the PIC case, symbolic constants must *not* be
-     forced into the literal pool.  We accept them here,
-     so that they will be handled by emit_pic_move.  */
-  if (flag_pic)
-    return 1;
-
-  /* Even in the non-PIC case, we can accept immediate
-     LARL operands here.  */
-  if (TARGET_64BIT)
-    return larl_operand (op, VOIDmode);
-
-  /* All remaining non-PIC symbolic constants are
-     forced into the literal pool.  */
-  return 0;
-}
-
-/* Returns true if the constant value OP is a legitimate general
-   operand during and after reload.  The difference to 
-   legitimate_constant_p is that this function will not accept
-   a constant that would need to be forced to the literal pool
-   before it can be used as operand.  */
-
-int
-legitimate_reload_constant_p (op)
-     register rtx op;
-{
-  /* Accept l(g)hi operands.  */
-  if (GET_CODE (op) == CONST_INT
-      && CONST_OK_FOR_LETTER_P (INTVAL (op), 'K'))
-    return 1;
-
-  /* Accept lliXX operands.  */
-  if (TARGET_64BIT
-      && s390_single_hi (op, DImode, 0) >= 0)
-  return 1;
-
-  /* Accept larl operands.  */
-  if (TARGET_64BIT
-      && larl_operand (op, VOIDmode))
-    return 1;
-
-  /* If reload is completed, and we do not already have a
-     literal pool, and OP must be forced to the literal 
-     pool, then something must have gone wrong earlier.
-     We *cannot* force the constant any more, because the
-     prolog generation already decided we don't need to 
-     set up the base register.  */
-  if (reload_completed && !regs_ever_live[BASE_REGISTER])
-    abort ();
-
-  /* Everything else cannot be handled without reload.  */
-  return 0;
-}
-
-/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
-   return the class of reg to actually use.  */
-
-enum reg_class
-s390_preferred_reload_class (op, class)
-     rtx op;
-     enum reg_class class;
-{
-  /* This can happen if a floating point constant is being
-     reloaded into an integer register.  Leave well alone.  */
-  if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
-      && class != FP_REGS)
-    return class;
-
-  switch (GET_CODE (op))
-    {
-      /* Constants we cannot reload must be forced into the
-        literal pool.  For constants we *could* handle directly,
-        it might still be preferable to put them in the pool and
-        use a memory-to-memory instruction.
-
-        However, try to avoid needlessly allocating a literal
-        pool in a routine that wouldn't otherwise need any.
-        Heuristically, we assume that 64-bit leaf functions
-        typically don't need a literal pool, all others do.  */
-      case CONST_DOUBLE:
-      case CONST_INT:
-       if (!legitimate_reload_constant_p (op))
-         return NO_REGS;
-
-       if (TARGET_64BIT && current_function_is_leaf)
-         return class;
-
-       return NO_REGS;
-
-      /* If a symbolic constant or a PLUS is reloaded,
-        it is most likely being used as an address, so
-        prefer ADDR_REGS.  If 'class' is not a superset
-        of ADDR_REGS, e.g. FP_REGS, reject this reload.  */
-      case PLUS:
-      case LABEL_REF:
-      case SYMBOL_REF:
-      case CONST:
-       if (reg_class_subset_p (ADDR_REGS, class))
-          return ADDR_REGS;
-       else
-         return NO_REGS;
-
-      default:
-       break;
-    }
-
-  return class;
-}
-
-/* Return the register class of a scratch register needed to
-   load IN into a register of class CLASS in MODE.
-
-   We need a temporary when loading a PLUS expression which
-   is not a legitimate operand of the LOAD ADDRESS instruction.  */
-
-enum reg_class
-s390_secondary_input_reload_class (class, mode, in)
-     enum reg_class class ATTRIBUTE_UNUSED;
-     enum machine_mode mode;
-     rtx in;
-{
-  if (s390_plus_operand (in, mode))
-    return ADDR_REGS;
-
-  return NO_REGS;
-}
-
-/* Return true if OP is a PLUS that is not a legitimate
-   operand for the LA instruction. 
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-s390_plus_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  if (!check_mode (op, &mode) || mode != Pmode)
-    return FALSE;
-
-  if (GET_CODE (op) != PLUS)
-    return FALSE;
-
-  if (legitimate_la_operand_p (op))
-    return FALSE;
-
-  return TRUE;
-}
-
-/* Generate code to load SRC, which is PLUS that is not a
-   legitimate operand for the LA instruction, into TARGET.
-   SCRATCH may be used as scratch register.  */
-
-void
-s390_expand_plus_operand (target, src, scratch_in)
-     register rtx target;
-     register rtx src;
-     register rtx scratch_in;
-{
-  rtx sum1, sum2, scratch;
-
-  /* ??? reload apparently does not ensure that the scratch register
-     and the target do not overlap.  We absolutely require this to be
-     the case, however.  Therefore the reload_in[sd]i patterns ask for
-     a double-sized scratch register, and if one part happens to be
-     equal to the target, we use the other one.  */
-  scratch = gen_rtx_REG (Pmode, REGNO (scratch_in));
-  if (rtx_equal_p (scratch, target))
-    scratch = gen_rtx_REG (Pmode, REGNO (scratch_in) + 1);
-
-  /* src must be a PLUS; get its two operands.  */
-  if (GET_CODE (src) != PLUS || GET_MODE (src) != Pmode)
-    abort ();
-
-  /* Check if any of the two operands is already scheduled
-     for replacement by reload.  This can happen e.g. when
-     float registers occur in an address.  */
-  sum1 = find_replacement (&XEXP (src, 0));
-  sum2 = find_replacement (&XEXP (src, 1));
-
-  /* If one of the two operands is equal to the target,
-     make it the first one.  If one is a constant, make
-     it the second one.  */
-  if (rtx_equal_p (target, sum2)
-      || GET_CODE (sum1) == CONST_INT)
-    {
-      rtx tem = sum2;
-      sum2 = sum1;
-      sum1 = tem;
-    }
-
-  /* If the first operand is not an address register,
-     we reload it into the target.  */
-  if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
-    {
-      emit_move_insn (target, sum1);
-      sum1 = target;
-    }
-
-  /* Likewise for the second operand.  However, take
-     care not to clobber the target if we already used
-     it for the first operand.  Use the scratch instead.
-     Also, allow an immediate offset if it is in range.  */
-  if ((true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
-      && !(GET_CODE (sum2) == CONST_INT
-           && INTVAL (sum2) >= 0 && INTVAL (sum2) < 4096))
-    {
-      if (!rtx_equal_p (target, sum1))
-        {
-          emit_move_insn (target, sum2);
-          sum2 = target;
-        }
-      else
-        {
-          emit_move_insn (scratch, sum2);
-          sum2 = scratch;
-        }
-    }
-
-  /* Emit the LOAD ADDRESS pattern.  Note that reload of PLUS
-     is only ever performed on addresses, so we can mark the
-     sum as legitimate for LA in any case.  */
-  src = gen_rtx_PLUS (Pmode, sum1, sum2);
-  src = legitimize_la_operand (src);
-  emit_insn (gen_rtx_SET (VOIDmode, target, src));
-}
+/* Return true if DISP is a valid short displacement.  */
 
+static bool
+s390_short_displacement (rtx disp)
+{
+  /* No displacement is OK.  */
+  if (!disp)
+    return true;
+
+  /* Without the long displacement facility we don't need to
+     distingiush between long and short displacement.  */
+  if (!TARGET_LONG_DISPLACEMENT)
+    return true;
+
+  /* Integer displacement in range.  */
+  if (GET_CODE (disp) == CONST_INT)
+    return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
+
+  /* GOT offset is not OK, the GOT can be large.  */
+  if (GET_CODE (disp) == CONST
+      && GET_CODE (XEXP (disp, 0)) == UNSPEC
+      && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
+          || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
+    return false;
+
+  /* All other symbolic constants are literal pool references,
+     which are OK as the literal pool must be small.  */
+  if (GET_CODE (disp) == CONST)
+    return true;
+
+  return false;
+}
 
 /* Decompose a RTL expression ADDR for a memory address into
-   its components, returned in OUT.  The boolean STRICT 
-   specifies whether strict register checking applies.
-   Returns 0 if ADDR is not a valid memory address, nonzero
+   its components, returned in OUT.
+
+   Returns false if ADDR is not a valid memory address, true
    otherwise.  If OUT is NULL, don't return the components,
    but check for validity only.
 
@@ -1266,15 +1726,24 @@ s390_expand_plus_operand (target, src, scratch_in)
    canonical form so that they will be recognized.  */
 
 static int
-s390_decompose_address (addr, out, strict)
-     register rtx addr;
-     struct s390_address *out;
-     int strict;
+s390_decompose_address (rtx addr, struct s390_address *out)
 {
+  HOST_WIDE_INT offset = 0;
   rtx base = NULL_RTX;
   rtx indx = NULL_RTX;
   rtx disp = NULL_RTX;
-  int pointer = FALSE;
+  rtx orig_disp;
+  bool pointer = false;
+  bool base_ptr = false;
+  bool indx_ptr = false;
+  bool literal_pool = false;
+
+  /* We may need to substitute the literal pool base register into the address
+     below.  However, at this point we do not know which register is going to
+     be used as base, so we substitute the arg pointer register.  This is going
+     to be treated as holding a pointer below -- it shouldn't be used for any
+     other purpose.  */
+  rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM);
 
   /* Decompose address into base + index + displacement.  */
 
@@ -1312,3035 +1781,8434 @@ s390_decompose_address (addr, out, strict)
 
       else
        {
-         return FALSE;
+         return false;
        }
     }
 
   else
     disp = addr;               /* displacement */
 
+  /* Extract integer part of displacement.  */
+  orig_disp = disp;
+  if (disp)
+    {
+      if (GET_CODE (disp) == CONST_INT)
+       {
+         offset = INTVAL (disp);
+         disp = NULL_RTX;
+       }
+      else if (GET_CODE (disp) == CONST
+              && GET_CODE (XEXP (disp, 0)) == PLUS
+              && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
+       {
+         offset = INTVAL (XEXP (XEXP (disp, 0), 1));
+         disp = XEXP (XEXP (disp, 0), 0);
+       }
+    }
+
+  /* Strip off CONST here to avoid special case tests later.  */
+  if (disp && GET_CODE (disp) == CONST)
+    disp = XEXP (disp, 0);
+
+  /* We can convert literal pool addresses to
+     displacements by basing them off the base register.  */
+  if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
+    {
+      /* Either base or index must be free to hold the base register.  */
+      if (!base)
+        base = fake_pool_base, literal_pool = true;
+      else if (!indx)
+        indx = fake_pool_base, literal_pool = true;
+      else
+        return false;
+
+      /* Mark up the displacement.  */
+      disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
+                            UNSPEC_LTREL_OFFSET);
+    }
 
   /* Validate base register.  */
   if (base)
     {
       if (GET_CODE (base) == UNSPEC)
-        {
-          if (XVECLEN (base, 0) != 1 || XINT (base, 1) != 101)
-             return FALSE;
-         base = XVECEXP (base, 0, 0);
-         pointer = TRUE;
-       }
+       switch (XINT (base, 1))
+         {
+         case UNSPEC_LTREF:
+           if (!disp)
+             disp = gen_rtx_UNSPEC (Pmode, 
+                                    gen_rtvec (1, XVECEXP (base, 0, 0)),
+                                    UNSPEC_LTREL_OFFSET);
+           else
+             return false;
+
+           base = XVECEXP (base, 0, 1);
+           break;
+
+         case UNSPEC_LTREL_BASE:
+           if (XVECLEN (base, 0) == 1)
+             base = fake_pool_base, literal_pool = true;
+           else
+             base = XVECEXP (base, 0, 1);
+           break;
 
-      if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
-         return FALSE;
+         default:
+           return false;
+         }
+
+      if (!REG_P (base) 
+         || (GET_MODE (base) != SImode 
+             && GET_MODE (base) != Pmode))
+       return false;
 
-      if ((strict && ! REG_OK_FOR_BASE_STRICT_P (base))
-         || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (base)))
-         return FALSE;
-    
-      if (REGNO (base) == BASE_REGISTER
-         || REGNO (base) == STACK_POINTER_REGNUM
+      if (REGNO (base) == STACK_POINTER_REGNUM
          || REGNO (base) == FRAME_POINTER_REGNUM
          || ((reload_completed || reload_in_progress)
              && frame_pointer_needed
              && REGNO (base) == HARD_FRAME_POINTER_REGNUM)
+         || REGNO (base) == ARG_POINTER_REGNUM
           || (flag_pic
               && REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = TRUE;
+        pointer = base_ptr = true;
+
+      if ((reload_completed || reload_in_progress)
+         && base == cfun->machine->base_reg)
+        pointer = base_ptr = literal_pool = true;
     }
 
   /* Validate index register.  */
   if (indx)
     {
       if (GET_CODE (indx) == UNSPEC)
-        {
-          if (XVECLEN (indx, 0) != 1 || XINT (indx, 1) != 101)
-             return FALSE;
-         indx = XVECEXP (indx, 0, 0);
-         pointer = TRUE;
-       }
+       switch (XINT (indx, 1))
+         {
+         case UNSPEC_LTREF:
+           if (!disp)
+             disp = gen_rtx_UNSPEC (Pmode, 
+                                    gen_rtvec (1, XVECEXP (indx, 0, 0)),
+                                    UNSPEC_LTREL_OFFSET);
+           else
+             return false;
+
+           indx = XVECEXP (indx, 0, 1);
+           break;
+
+         case UNSPEC_LTREL_BASE:
+           if (XVECLEN (indx, 0) == 1)
+             indx = fake_pool_base, literal_pool = true;
+           else
+             indx = XVECEXP (indx, 0, 1);
+           break;
+
+         default:
+           return false;
+         }
 
-      if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
-         return FALSE;
+      if (!REG_P (indx) 
+         || (GET_MODE (indx) != SImode
+             && GET_MODE (indx) != Pmode))
+       return false;
 
-      if ((strict && ! REG_OK_FOR_BASE_STRICT_P (indx))
-         || (! strict && ! REG_OK_FOR_BASE_NONSTRICT_P (indx)))
-         return FALSE;
-    
-      if (REGNO (indx) == BASE_REGISTER
-         || REGNO (indx) == STACK_POINTER_REGNUM
+      if (REGNO (indx) == STACK_POINTER_REGNUM
          || REGNO (indx) == FRAME_POINTER_REGNUM
          || ((reload_completed || reload_in_progress)
              && frame_pointer_needed
              && REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
+         || REGNO (indx) == ARG_POINTER_REGNUM
           || (flag_pic
               && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = TRUE;
+        pointer = indx_ptr = true;
+
+      if ((reload_completed || reload_in_progress)
+         && indx == cfun->machine->base_reg)
+        pointer = indx_ptr = literal_pool = true;
+    }
+
+  /* Prefer to use pointer as base, not index.  */
+  if (base && indx && !base_ptr
+      && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
+    {
+      rtx tmp = base;
+      base = indx;
+      indx = tmp;
     }
 
   /* Validate displacement.  */
-  if (disp)
+  if (!disp)
     {
-      /* Allow integer constant in range.  */
-      if (GET_CODE (disp) == CONST_INT)
+      /* If virtual registers are involved, the displacement will change later 
+        anyway as the virtual registers get eliminated.  This could make a 
+        valid displacement invalid, but it is more likely to make an invalid 
+        displacement valid, because we sometimes access the register save area 
+        via negative offsets to one of those registers.
+        Thus we don't check the displacement for validity here.  If after
+        elimination the displacement turns out to be invalid after all,
+        this is fixed up by reload in any case.  */
+      if (base != arg_pointer_rtx 
+         && indx != arg_pointer_rtx 
+         && base != return_address_pointer_rtx 
+         && indx != return_address_pointer_rtx
+         && base != frame_pointer_rtx 
+         && indx != frame_pointer_rtx
+         && base != virtual_stack_vars_rtx 
+         && indx != virtual_stack_vars_rtx)
+       if (!DISP_IN_RANGE (offset))
+         return false;
+    }
+  else
+    {
+      /* All the special cases are pointers.  */
+      pointer = true;
+
+      /* In the small-PIC case, the linker converts @GOT
+         and @GOTNTPOFF offsets to possible displacements.  */
+      if (GET_CODE (disp) == UNSPEC
+          && (XINT (disp, 1) == UNSPEC_GOT
+             || XINT (disp, 1) == UNSPEC_GOTNTPOFF)
+         && flag_pic == 1)
         {
-          if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096)
-              return FALSE;
+         ;
         }
 
-      /* In the small-PIC case, the linker converts @GOT12 
-         offsets to possible displacements.  */
-      else if (GET_CODE (disp) == CONST
-               && GET_CODE (XEXP (disp, 0)) == UNSPEC
-               && XINT (XEXP (disp, 0), 1) == 110)
-        {
-          if (flag_pic != 1)
-            return FALSE;
-
-         pointer = TRUE;
-        }
+      /* Accept pool label offsets.  */
+      else if (GET_CODE (disp) == UNSPEC
+              && XINT (disp, 1) == UNSPEC_POOL_OFFSET)
+       ;
 
-      /* Accept chunkfied literal pool symbol references.  */
-      else if (GET_CODE (disp) == CONST
-               && GET_CODE (XEXP (disp, 0)) == MINUS
-               && GET_CODE (XEXP (XEXP (disp, 0), 0)) == LABEL_REF
-               && GET_CODE (XEXP (XEXP (disp, 0), 1)) == LABEL_REF)
-        {
-         pointer = TRUE;
-        }
-      /* Likewise if a constant offset is present.  */
-      else if (GET_CODE (disp) == CONST
-               && GET_CODE (XEXP (disp, 0)) == PLUS
-               && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT
-               && GET_CODE (XEXP (XEXP (disp, 0), 0)) == MINUS
-               && GET_CODE (XEXP (XEXP (XEXP (disp, 0), 0), 0)) == LABEL_REF
-               && GET_CODE (XEXP (XEXP (XEXP (disp, 0), 0), 1)) == LABEL_REF)
+      /* Accept literal pool references.  */
+      else if (GET_CODE (disp) == UNSPEC
+              && XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
         {
-         pointer = TRUE;
+         orig_disp = gen_rtx_CONST (Pmode, disp);
+         if (offset)
+           {
+             /* If we have an offset, make sure it does not
+                exceed the size of the constant pool entry.  */
+             rtx sym = XVECEXP (disp, 0, 0);
+             if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
+               return false;
+
+              orig_disp = plus_constant (orig_disp, offset);
+           }
         }
 
-      /* We can convert literal pool addresses to 
-         displacements by basing them off the base register.  */
       else
-        {
-          /* In some cases, we can accept an additional
-             small constant offset.  Split these off here.  */
-
-          unsigned int offset = 0;
-
-          if (GET_CODE (disp) == CONST
-              && GET_CODE (XEXP (disp, 0)) == PLUS
-              && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
-            {
-              offset = INTVAL (XEXP (XEXP (disp, 0), 1));
-              disp = XEXP (XEXP (disp, 0), 0);
-            }
-
-          /* Now we must have a literal pool address.  */
-          if (GET_CODE (disp) != SYMBOL_REF
-              || !CONSTANT_POOL_ADDRESS_P (disp))
-            return FALSE;
-
-          /* In 64-bit PIC mode we cannot accept symbolic 
-             constants in the constant pool.  */
-          if (TARGET_64BIT && flag_pic
-              && SYMBOLIC_CONST (get_pool_constant (disp)))
-            return FALSE;
-
-          /* If we have an offset, make sure it does not
-             exceed the size of the constant pool entry.  */
-          if (offset && offset >= GET_MODE_SIZE (get_pool_mode (disp)))
-            return FALSE;
-
-          /* Either base or index must be free to 
-             hold the base register.  */
-          if (base && indx)
-            return FALSE;
-
-          /* Convert the address.  */
-          if (base)
-            indx = gen_rtx_REG (Pmode, BASE_REGISTER);
-          else
-            base = gen_rtx_REG (Pmode, BASE_REGISTER);
-
-          disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), 100);
-          disp = gen_rtx_CONST (Pmode, disp);
-
-          if (offset)
-            disp = plus_constant (disp, offset);
-
-         pointer = TRUE;
-        }
+       return false;
     }
 
   if (!base && !indx)
-    pointer = TRUE;
-   
+    pointer = true;
+
   if (out)
     {
       out->base = base;
       out->indx = indx;
-      out->disp = disp;
+      out->disp = orig_disp;
       out->pointer = pointer;
+      out->literal_pool = literal_pool;
     }
 
-  return TRUE;
+  return true;
 }
 
-/* Return nonzero if ADDR is a valid memory address.
-   STRICT specifies whether strict register checking applies.  */
+/* Decompose a RTL expression OP for a shift count into its components,
+   and return the base register in BASE and the offset in OFFSET.
 
-int
-legitimate_address_p (mode, addr, strict)
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-     register rtx addr;
-     int strict;
+   Return true if OP is a valid shift count, false if not.  */
+
+bool
+s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset)
 {
-  return s390_decompose_address (addr, NULL, strict);
+  HOST_WIDE_INT off = 0;
+
+  /* We can have an integer constant, an address register,
+     or a sum of the two.  */
+  if (GET_CODE (op) == CONST_INT)
+    {
+      off = INTVAL (op);
+      op = NULL_RTX;
+    }
+  if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
+    {
+      off = INTVAL (XEXP (op, 1));
+      op = XEXP (op, 0);
+    }
+  while (op && GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+
+  if (op && GET_CODE (op) != REG)
+    return false;
+
+  if (offset)
+    *offset = off;
+  if (base)
+    *base = op;
+
+   return true;
 }
 
-/* Return 1 if OP is a valid operand for the LA instruction.
-   In 31-bit, we need to prove that the result is used as an
-   address, as LA performs only a 31-bit addition.  */
 
-int
-legitimate_la_operand_p (op)
-     register rtx op;
+/* Return true if CODE is a valid address without index.  */
+
+bool
+s390_legitimate_address_without_index_p (rtx op)
 {
   struct s390_address addr;
-  if (!s390_decompose_address (op, &addr, FALSE))
-    return FALSE;
 
-  if (TARGET_64BIT || addr.pointer)
-    return TRUE;
+  if (!s390_decompose_address (XEXP (op, 0), &addr))
+    return false;
+  if (addr.indx)
+    return false;
 
-  return FALSE;
+  return true;
 }
 
-/* Return a modified variant of OP that is guaranteed to
-   be accepted by legitimate_la_operand_p.  */
 
-rtx
-legitimize_la_operand (op)
-     register rtx op;
+/* Return true if ADDR is of kind symbol_ref or symbol_ref + const_int
+   and return these parts in SYMREF and ADDEND.  You can pass NULL in
+   SYMREF and/or ADDEND if you are not interested in these values.  */
+
+static bool
+s390_symref_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
 {
-  struct s390_address addr;
-  if (!s390_decompose_address (op, &addr, FALSE))
-    abort ();
+  HOST_WIDE_INT tmpaddend = 0;
 
-  if (TARGET_64BIT || addr.pointer)
-    return op;
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
 
-  if (!addr.base)
-    abort ();
+  if (GET_CODE (addr) == PLUS)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == SYMBOL_REF
+         && CONST_INT_P (XEXP (addr, 1)))
+       {
+         tmpaddend = INTVAL (XEXP (addr, 1));
+         addr = XEXP (addr, 0);
+       }
+      else
+       return false;
+    }
+  else
+    if (GET_CODE (addr) != SYMBOL_REF)
+       return false;
 
-  op = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr.base), 101);
-  if (addr.indx)
-    op = gen_rtx_PLUS (Pmode, op, addr.indx);
-  if (addr.disp)
-    op = gen_rtx_PLUS (Pmode, op, addr.disp);
+  if (symref)
+    *symref = addr;
+  if (addend)
+    *addend = tmpaddend;
 
-  return op; 
+  return true;
 }
 
-/* Return a legitimate reference for ORIG (an address) using the
-   register REG.  If REG is 0, a new pseudo is generated.
 
-   There are two types of references that must be handled:
+/* Return true if the address in OP is valid for constraint letter C
+   if wrapped in a MEM rtx.  Set LIT_POOL_OK to true if it literal
+   pool MEMs should be accepted.  Only the Q, R, S, T constraint
+   letters are allowed for C.  */
 
-   1. Global data references must load the address from the GOT, via
-      the PIC reg.  An insn is emitted to do this load, and the reg is
-      returned.
-
-   2. Static data references, constant pool addresses, and code labels
-      compute the address as an offset from the GOT, whose base is in
-      the PIC reg.  Static data objects have SYMBOL_REF_FLAG set to
-      differentiate them from global data objects.  The returned
-      address is the PIC reg + an unspec constant.
-
-   GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
-   reg also appears in the address.  */
-
-rtx
-legitimize_pic_address (orig, reg)
-     rtx orig;
-     rtx reg;
+static int
+s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
 {
-  rtx addr = orig;
-  rtx new = orig;
-  rtx base;
+  struct s390_address addr;
+  bool decomposed = false;
 
-  if (GET_CODE (addr) == LABEL_REF
-      || (GET_CODE (addr) == SYMBOL_REF
-         && (SYMBOL_REF_FLAG (addr) 
-              || CONSTANT_POOL_ADDRESS_P (addr))))
+  /* This check makes sure that no symbolic address (except literal
+     pool references) are accepted by the R or T constraints.  */
+  if (s390_symref_operand_p (op, NULL, NULL))
     {
-      /* This is a local symbol.  */
-      if (TARGET_64BIT)
-        {
-          /* Access local symbols PC-relative via LARL.  
-             This is the same as in the non-PIC case, so it is 
-             handled automatically ...  */
-        }
-      else
-        {
-          /* Access local symbols relative to the literal pool.  */
-
-          rtx temp = reg? reg : gen_reg_rtx (Pmode);
-
-          addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 100);
-          addr = gen_rtx_CONST (SImode, addr);
-          addr = force_const_mem (SImode, addr);
-         emit_move_insn (temp, addr);
-
-          base = gen_rtx_REG (Pmode, BASE_REGISTER);
-          base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-          new = gen_rtx_PLUS (Pmode, base, temp);
-
-          if (reg != 0)
-            {
-              emit_move_insn (reg, new);
-              new = reg;
-            }
-        }
+      if (!lit_pool_ok)
+       return 0;
+      if (!s390_decompose_address (op, &addr))
+       return 0;
+      if (!addr.literal_pool)
+       return 0;
+      decomposed = true;
     }
-  else if (GET_CODE (addr) == SYMBOL_REF)
-    {
-      if (reg == 0)
-        reg = gen_reg_rtx (Pmode);
-
-      if (flag_pic == 1)
-        {
-          /* Assume GOT offset < 4k.  This is handled the same way
-             in both 31- and 64-bit code (@GOT12).  */
-
-          current_function_uses_pic_offset_table = 1;
 
-          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 110);
-          new = gen_rtx_CONST (Pmode, new);
-          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
-          new = gen_rtx_MEM (Pmode, new);
-          RTX_UNCHANGING_P (new) = 1;
-          emit_move_insn (reg, new);
-          new = reg;
-        }
-      else if (TARGET_64BIT)
-        {
-          /* If the GOT offset might be >= 4k, we determine the position
-             of the GOT entry via a PC-relative LARL (@GOTENT).  */
+  switch (c)
+    {
+    case 'Q': /* no index short displacement */
+      if (!decomposed && !s390_decompose_address (op, &addr))
+       return 0;
+      if (addr.indx)
+       return 0;
+      if (!s390_short_displacement (addr.disp))
+       return 0;
+      break;
 
-          rtx temp = gen_reg_rtx (Pmode);
+    case 'R': /* with index short displacement */
+      if (TARGET_LONG_DISPLACEMENT)
+       {
+         if (!decomposed && !s390_decompose_address (op, &addr))
+           return 0;
+         if (!s390_short_displacement (addr.disp))
+           return 0;
+       }
+      /* Any invalid address here will be fixed up by reload,
+        so accept it for the most generic constraint.  */
+      break;
 
-          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 111);
-          new = gen_rtx_CONST (Pmode, new);
-          emit_move_insn (temp, new);
+    case 'S': /* no index long displacement */
+      if (!TARGET_LONG_DISPLACEMENT)
+       return 0;
+      if (!decomposed && !s390_decompose_address (op, &addr))
+       return 0;
+      if (addr.indx)
+       return 0;
+      if (s390_short_displacement (addr.disp))
+       return 0;
+      break;
 
-          new = gen_rtx_MEM (Pmode, temp);
-          RTX_UNCHANGING_P (new) = 1;
-          emit_move_insn (reg, new);
-          new = reg;
-        }
-      else
-        {
-          /* If the GOT offset might be >= 4k, we have to load it 
-             from the literal pool (@GOT).  */
+    case 'T': /* with index long displacement */
+      if (!TARGET_LONG_DISPLACEMENT)
+       return 0;
+      /* Any invalid address here will be fixed up by reload,
+        so accept it for the most generic constraint.  */
+      if ((decomposed || s390_decompose_address (op, &addr))
+         && s390_short_displacement (addr.disp))
+       return 0;
+      break;
+    default:
+      return 0;
+    }
+  return 1;
+}
 
-          rtx temp = gen_reg_rtx (Pmode);
 
-          current_function_uses_pic_offset_table = 1;
+/* Evaluates constraint strings described by the regular expression
+   ([A|B|Z](Q|R|S|T))|U|W|Y and returns 1 if OP is a valid operand for
+   the constraint given in STR, or 0 else.  */
 
-          addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 112);
-          addr = gen_rtx_CONST (SImode, addr);
-          addr = force_const_mem (SImode, addr);
-          emit_move_insn (temp, addr);
+int
+s390_mem_constraint (const char *str, rtx op)
+{
+  char c = str[0];
 
-          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-          new = gen_rtx_MEM (Pmode, new);
-          RTX_UNCHANGING_P (new) = 1;
-          emit_move_insn (reg, new);
-          new = reg;
-        }
-    }      
-  else
+  switch (c)
     {
-      if (GET_CODE (addr) == CONST)
-       {
-         addr = XEXP (addr, 0);
-         if (GET_CODE (addr) == UNSPEC)
-           {
-             if (XVECLEN (addr, 0) != 1)
-                abort ();
-              switch (XINT (addr, 1))
-                {
-                  /* If someone moved an @GOT or lt-relative UNSPEC
-                     out of the literal pool, force them back in.  */
-                  case 100:
-                  case 112:
-                  case 114:
-                    new = force_const_mem (SImode, orig);
-                    break;
+    case 'A':
+      /* Check for offsettable variants of memory constraints.  */
+      if (!MEM_P (op) || MEM_VOLATILE_P (op))
+       return 0;
+      if ((reload_completed || reload_in_progress)
+         ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
+       return 0;
+      return s390_check_qrst_address (str[1], XEXP (op, 0), true);
+    case 'B':
+      /* Check for non-literal-pool variants of memory constraints.  */
+      if (!MEM_P (op))
+       return 0;
+      return s390_check_qrst_address (str[1], XEXP (op, 0), false);
+    case 'Q':
+    case 'R':
+    case 'S':
+    case 'T':
+      if (GET_CODE (op) != MEM)
+       return 0;
+      return s390_check_qrst_address (c, XEXP (op, 0), true);
+    case 'U':
+      return (s390_check_qrst_address ('Q', op, true)
+             || s390_check_qrst_address ('R', op, true));
+    case 'W':
+      return (s390_check_qrst_address ('S', op, true)
+             || s390_check_qrst_address ('T', op, true));
+    case 'Y':
+      /* Simply check for the basic form of a shift count.  Reload will
+        take care of making sure we have a proper base register.  */
+      if (!s390_decompose_shift_count (op, NULL, NULL))
+       return 0;
+      break;
+    case 'Z':
+      return s390_check_qrst_address (str[1], op, true);
+    default:
+      return 0;
+    }
+  return 1;
+}
 
-                  /* @GOTENT is OK as is.  */
-                  case 111:
-                    break;
 
-                  /* @PLT is OK as is on 64-bit, must be converted to
-                     lt-relative PLT on 31-bit.  */
-                  case 113:
-                    if (!TARGET_64BIT)
-                      {
-                        rtx temp = reg? reg : gen_reg_rtx (Pmode);
+/* Evaluates constraint strings starting with letter O.  Input
+   parameter C is the second letter following the "O" in the constraint
+   string. Returns 1 if VALUE meets the respective constraint and 0
+   otherwise.  */
 
-                        addr = XVECEXP (addr, 0, 0);
-                        addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 114);
-                        addr = gen_rtx_CONST (SImode, addr);
-                        addr = force_const_mem (SImode, addr);
-                       emit_move_insn (temp, addr);
+int
+s390_O_constraint_str (const char c, HOST_WIDE_INT value)
+{
+  if (!TARGET_EXTIMM)
+    return 0;
 
-                        base = gen_rtx_REG (Pmode, BASE_REGISTER);
-                        base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-                        new = gen_rtx_PLUS (Pmode, base, temp);
+  switch (c)
+    {
+    case 's':
+      return trunc_int_for_mode (value, SImode) == value;
 
-                        if (reg != 0)
-                          {
-                            emit_move_insn (reg, new);
-                            new = reg;
-                          }
-                      }
-                    break;
+    case 'p':
+      return value == 0
+       || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
 
-                  /* Everything else cannot happen.  */
-                  default:
-                    abort ();
-                }
-           }
-         else if (GET_CODE (addr) != PLUS)
-           abort ();
-       }
-      if (GET_CODE (addr) == PLUS)
-       {
-         rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
-         /* Check first to see if this is a constant offset 
-             from a local symbol reference.  */
-         if ((GET_CODE (op0) == LABEL_REF
-               || (GET_CODE (op0) == SYMBOL_REF
-                   && (SYMBOL_REF_FLAG (op0)
-                        || CONSTANT_POOL_ADDRESS_P (op0))))
-             && GET_CODE (op1) == CONST_INT)
-           {
-              if (TARGET_64BIT)
-                {
-                  if (INTVAL (op1) & 1)
-                    {
-                      /* LARL can't handle odd offsets, so emit a 
-                         pair of LARL and LA.  */
-                      rtx temp = reg? reg : gen_reg_rtx (Pmode);
+    case 'n':
+      return s390_single_part (GEN_INT (value - 1), DImode, SImode, -1) == 1;
 
-                      if (INTVAL (op1) < 0 || INTVAL (op1) >= 4096)
-                        {
-                          int even = INTVAL (op1) - 1;
-                          op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
-                         op0 = gen_rtx_CONST (Pmode, op0);
-                          op1 = GEN_INT (1);
-                        }
+    default:
+      gcc_unreachable ();
+    }
+}
 
-                      emit_move_insn (temp, op0);
-                      new = gen_rtx_PLUS (Pmode, temp, op1);
 
-                      if (reg != 0)
-                        {
-                          emit_move_insn (reg, new);
-                          new = reg;
-                        }
-                    }
-                  else
-                    {
-                      /* If the offset is even, we can just use LARL.
-                         This will happen automatically.  */
-                    }
-                }
-              else
-                {
-                  /* Access local symbols relative to the literal pool.  */
+/* Evaluates constraint strings starting with letter N.  Parameter STR
+   contains the letters following letter "N" in the constraint string.
+   Returns true if VALUE matches the constraint.  */
 
-                  rtx temp = reg? reg : gen_reg_rtx (Pmode);
+int
+s390_N_constraint_str (const char *str, HOST_WIDE_INT value)
+{
+  enum machine_mode mode, part_mode;
+  int def;
+  int part, part_goal;
 
-                  addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), 100);
-                  addr = gen_rtx_PLUS (SImode, addr, op1);
-                  addr = gen_rtx_CONST (SImode, addr);
-                  addr = force_const_mem (SImode, addr);
-                 emit_move_insn (temp, addr);
 
-                  base = gen_rtx_REG (Pmode, BASE_REGISTER);
-                  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-                  new = gen_rtx_PLUS (Pmode, base, temp);
+  if (str[0] == 'x')
+    part_goal = -1;
+  else
+    part_goal = str[0] - '0';
 
-                  if (reg != 0)
-                    {
-                      emit_move_insn (reg, new);
-                      new = reg;
-                    }
-                }
-           }
+  switch (str[1])
+    {
+    case 'Q':
+      part_mode = QImode;
+      break;
+    case 'H':
+      part_mode = HImode;
+      break;
+    case 'S':
+      part_mode = SImode;
+      break;
+    default:
+      return 0;
+    }
 
-          /* Now, check whether it is an LT-relative symbol plus offset
-             that was pulled out of the literal pool.  Force it back in.  */
+  switch (str[2])
+    {
+    case 'H':
+      mode = HImode;
+      break;
+    case 'S':
+      mode = SImode;
+      break;
+    case 'D':
+      mode = DImode;
+      break;
+    default:
+      return 0;
+    }
 
-         else if (GET_CODE (op0) == UNSPEC
-                  && GET_CODE (op1) == CONST_INT)
-            {
-             if (XVECLEN (op0, 0) != 1)
-                abort ();
-              if (XINT (op0, 1) != 100)
-                abort ();
+  switch (str[3])
+    {
+    case '0':
+      def = 0;
+      break;
+    case 'F':
+      def = -1;
+      break;
+    default:
+      return 0;
+    }
 
-              new = force_const_mem (SImode, orig);
-            }
+  if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
+    return 0;
 
-          /* Otherwise, compute the sum.  */
-         else
-           {
-             base = legitimize_pic_address (XEXP (addr, 0), reg);
-             new  = legitimize_pic_address (XEXP (addr, 1),
-                                            base == reg ? NULL_RTX : reg);
-             if (GET_CODE (new) == CONST_INT)
-               new = plus_constant (base, INTVAL (new));
-             else
-               {
-                 if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
-                   {
-                     base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
-                     new = XEXP (new, 1);
-                   }
-                 new = gen_rtx_PLUS (Pmode, base, new);
-               }
+  part = s390_single_part (GEN_INT (value), mode, part_mode, def);
+  if (part < 0)
+    return 0;
+  if (part_goal != -1 && part_goal != part)
+    return 0;
 
-             if (GET_CODE (new) == CONST)
-               new = XEXP (new, 0);
-              new = force_operand (new, 0);
-           }
-       }
-    }
-  return new;
+  return 1;
 }
 
-/* Emit insns to move operands[1] into operands[0].  */
 
-void
-emit_pic_move (operands, mode)
-     rtx *operands;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+/* Returns true if the input parameter VALUE is a float zero.  */
 
-  if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
-    operands[1] = force_reg (Pmode, operands[1]);
-  else
-    operands[1] = legitimize_pic_address (operands[1], temp);
+int
+s390_float_const_zero_p (rtx value)
+{
+  return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
+         && value == CONST0_RTX (GET_MODE (value)));
 }
 
-/* Try machine-dependent ways of modifying an illegitimate address X
-   to be legitimate.  If we find one, return the new, valid address.
-
-   OLDX is the address as it was before break_out_memory_refs was called.
-   In some cases it is useful to look at this to decide what needs to be done.
-
-   MODE is the mode of the operand pointed to by X.
 
-   When -fpic is used, special handling is needed for symbolic references.
-   See comments by legitimize_pic_address for details.  */
+/* Compute a (partial) cost for rtx X.  Return true if the complete
+   cost has been computed, and false if subexpressions should be
+   scanned.  In either case, *TOTAL contains the cost result.  
+   CODE contains GET_CODE (x), OUTER_CODE contains the code 
+   of the superexpression of x.  */
 
-rtx
-legitimize_address (x, oldx, mode)
-     register rtx x;
-     register rtx oldx ATTRIBUTE_UNUSED;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+s390_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed ATTRIBUTE_UNUSED)
 {
-  rtx constant_term = const0_rtx;
-
-  if (flag_pic)
+  switch (code)
     {
-      if (SYMBOLIC_CONST (x)
-          || (GET_CODE (x) == PLUS 
-              && (SYMBOLIC_CONST (XEXP (x, 0)) 
-                  || SYMBOLIC_CONST (XEXP (x, 1)))))
-         x = legitimize_pic_address (x, 0);
-
-      if (legitimate_address_p (mode, x, FALSE))
-       return x;
-    }
+    case CONST:
+    case CONST_INT:
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_DOUBLE:
+    case MEM:
+      *total = 0;
+      return true;
 
-  x = eliminate_constant_term (x, &constant_term);
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ROTATE:
+    case ROTATERT:
+    case AND:
+    case IOR:
+    case XOR:
+    case NEG:
+    case NOT:
+      *total = COSTS_N_INSNS (1);
+      return false;
 
-  if (GET_CODE (x) == PLUS)
-    {
-      if (GET_CODE (XEXP (x, 0)) == REG)
+    case PLUS:
+    case MINUS:
+      /* Check for multiply and add.  */
+      if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode)
+         && GET_CODE (XEXP (x, 0)) == MULT
+         && TARGET_HARD_FLOAT && TARGET_FUSED_MADD)
        {
-         register rtx temp = gen_reg_rtx (Pmode);
-         register rtx val  = force_operand (XEXP (x, 1), temp);
-         if (val != temp)
-           emit_move_insn (temp, val);
-
-         x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp);
+         /* This is the multiply and add case.  */
+         if (GET_MODE (x) == DFmode)
+           *total = s390_cost->madbr;
+         else
+           *total = s390_cost->maebr;
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), MULT, speed) 
+           + rtx_cost (XEXP (XEXP (x, 0), 1), MULT, speed) 
+           + rtx_cost (XEXP (x, 1), code, speed);
+         return true;  /* Do not do an additional recursive descent.  */
        }
+      *total = COSTS_N_INSNS (1);
+      return false;
 
-      else if (GET_CODE (XEXP (x, 1)) == REG)
+    case MULT:      
+      switch (GET_MODE (x))
        {
-         register rtx temp = gen_reg_rtx (Pmode);
-         register rtx val  = force_operand (XEXP (x, 0), temp);
-         if (val != temp)
-           emit_move_insn (temp, val);
-
-         x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1));
+       case SImode:
+         {
+           rtx left = XEXP (x, 0);
+           rtx right = XEXP (x, 1);
+           if (GET_CODE (right) == CONST_INT
+               && CONST_OK_FOR_K (INTVAL (right)))
+             *total = s390_cost->mhi;
+           else if (GET_CODE (left) == SIGN_EXTEND)
+             *total = s390_cost->mh;
+           else
+             *total = s390_cost->ms;  /* msr, ms, msy */
+           break;
+         }
+       case DImode:
+         {
+           rtx left = XEXP (x, 0);
+           rtx right = XEXP (x, 1);
+           if (TARGET_64BIT)
+             {
+               if (GET_CODE (right) == CONST_INT
+                   && CONST_OK_FOR_K (INTVAL (right)))
+                 *total = s390_cost->mghi;
+               else if (GET_CODE (left) == SIGN_EXTEND)
+                 *total = s390_cost->msgf;
+               else
+                 *total = s390_cost->msg;  /* msgr, msg */
+             }
+           else /* TARGET_31BIT */
+             {
+               if (GET_CODE (left) == SIGN_EXTEND
+                   && GET_CODE (right) == SIGN_EXTEND)
+                 /* mulsidi case: mr, m */
+                 *total = s390_cost->m;
+               else if (GET_CODE (left) == ZERO_EXTEND
+                        && GET_CODE (right) == ZERO_EXTEND
+                        && TARGET_CPU_ZARCH)
+                 /* umulsidi case: ml, mlr */
+                 *total = s390_cost->ml;
+               else
+                 /* Complex calculation is required.  */
+                 *total = COSTS_N_INSNS (40);
+             }
+           break;
+         }
+       case SFmode:
+       case DFmode:
+         *total = s390_cost->mult_df;
+         break;
+       case TFmode:
+         *total = s390_cost->mxbr;
+         break;
+       default:
+         return false;
+       }
+      return false;
+
+    case UDIV:
+    case UMOD:
+      if (GET_MODE (x) == TImode)             /* 128 bit division */
+       *total = s390_cost->dlgr;
+      else if (GET_MODE (x) == DImode)
+       {
+         rtx right = XEXP (x, 1);
+         if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
+           *total = s390_cost->dlr;
+         else                                 /* 64 by 64 bit division */
+           *total = s390_cost->dlgr;
+       }
+      else if (GET_MODE (x) == SImode)         /* 32 bit division */
+       *total = s390_cost->dlr;
+      return false;
+
+    case DIV:
+    case MOD:
+      if (GET_MODE (x) == DImode)
+       {
+         rtx right = XEXP (x, 1);
+         if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
+           if (TARGET_64BIT)
+             *total = s390_cost->dsgfr;
+           else
+             *total = s390_cost->dr;
+         else                                 /* 64 by 64 bit division */
+           *total = s390_cost->dsgr;
+       }
+      else if (GET_MODE (x) == SImode)         /* 32 bit division */
+       *total = s390_cost->dlr;
+      else if (GET_MODE (x) == SFmode)
+       {
+         *total = s390_cost->debr;
+       }
+      else if (GET_MODE (x) == DFmode)
+       {
+         *total = s390_cost->ddbr;
+       }
+      else if (GET_MODE (x) == TFmode)
+       {
+         *total = s390_cost->dxbr;
+       }
+      return false;
+
+    case SQRT:
+      if (GET_MODE (x) == SFmode)
+       *total = s390_cost->sqebr;
+      else if (GET_MODE (x) == DFmode)
+       *total = s390_cost->sqdbr;
+      else /* TFmode */
+       *total = s390_cost->sqxbr;
+      return false;
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      if (outer_code == MULT || outer_code == DIV || outer_code == MOD
+         || outer_code == PLUS || outer_code == MINUS
+         || outer_code == COMPARE)
+       *total = 0;
+      return false;
+
+    case COMPARE:
+      *total = COSTS_N_INSNS (1);
+      if (GET_CODE (XEXP (x, 0)) == AND
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+       {
+         rtx op0 = XEXP (XEXP (x, 0), 0);
+         rtx op1 = XEXP (XEXP (x, 0), 1);
+         rtx op2 = XEXP (x, 1);
+
+         if (memory_operand (op0, GET_MODE (op0))
+             && s390_tm_ccmode (op1, op2, 0) != VOIDmode)
+           return true;
+         if (register_operand (op0, GET_MODE (op0))
+             && s390_tm_ccmode (op1, op2, 1) != VOIDmode)
+           return true;
        }
+      return false;
+
+    default:
+      return false;
     }
+}
 
-  if (constant_term != const0_rtx)
-    x = gen_rtx_PLUS (Pmode, x, constant_term);
+/* Return the cost of an address rtx ADDR.  */
 
-  return x;
+static int
+s390_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+  struct s390_address ad;
+  if (!s390_decompose_address (addr, &ad))
+    return 1000;
+
+  return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
 }
 
-/* In the name of slightly smaller debug output, and to cater to
-   general assembler losage, recognize various UNSPEC sequences
-   and turn them back into a direct symbol reference.  */
+/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode,
+   otherwise return 0.  */
 
-rtx
-s390_simplify_dwarf_addr (orig_x)
-     rtx orig_x;
+int
+tls_symbolic_operand (rtx op)
 {
-  rtx x = orig_x, y;
+  if (GET_CODE (op) != SYMBOL_REF)
+    return 0;
+  return SYMBOL_REF_TLS_MODEL (op);
+}
+\f
+/* Split DImode access register reference REG (on 64-bit) into its constituent
+   low and high parts, and store them into LO and HI.  Note that gen_lowpart/
+   gen_highpart cannot be used as they assume all registers are word-sized,
+   while our access registers have only half that size.  */
 
-  if (GET_CODE (x) != MEM)
-    return orig_x;
+void
+s390_split_access_reg (rtx reg, rtx *lo, rtx *hi)
+{
+  gcc_assert (TARGET_64BIT);
+  gcc_assert (ACCESS_REG_P (reg));
+  gcc_assert (GET_MODE (reg) == DImode);
+  gcc_assert (!(REGNO (reg) & 1));
 
-  x = XEXP (x, 0);
-  if (GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 1)) == CONST
-      && GET_CODE (XEXP (x, 0)) == REG
-      && REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM)
+  *lo = gen_rtx_REG (SImode, REGNO (reg) + 1);
+  *hi = gen_rtx_REG (SImode, REGNO (reg));
+}
+
+/* Return true if OP contains a symbol reference */
+
+bool
+symbolic_reference_mentioned_p (rtx op)
+{
+  const char *fmt;
+  int i;
+
+  if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+    return 1;
+
+  fmt = GET_RTX_FORMAT (GET_CODE (op));
+  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
     {
-      y = XEXP (XEXP (x, 1), 0);
-      if (GET_CODE (y) == UNSPEC
-         && XINT (y, 1) == 110)
-       return XVECEXP (y, 0, 0);
-      return orig_x;
+      if (fmt[i] == 'E')
+       {
+         int j;
+
+         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+           if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+             return 1;
+       }
+
+      else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+       return 1;
     }
 
-  if (GET_CODE (x) == CONST)
+  return 0;
+}
+
+/* Return true if OP contains a reference to a thread-local symbol.  */
+
+bool
+tls_symbolic_reference_mentioned_p (rtx op)
+{
+  const char *fmt;
+  int i;
+
+  if (GET_CODE (op) == SYMBOL_REF)
+    return tls_symbolic_operand (op);
+
+  fmt = GET_RTX_FORMAT (GET_CODE (op));
+  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
     {
-      y = XEXP (x, 0);
-      if (GET_CODE (y) == UNSPEC
-         && XINT (y, 1) == 111)
-       return XVECEXP (y, 0, 0);
-      return orig_x;
+      if (fmt[i] == 'E')
+       {
+         int j;
+
+         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+           if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+             return true;
+       }
+
+      else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i)))
+       return true;
     }
 
-  return orig_x;      
+  return false;
 }
 
-/* Output symbolic constant X in assembler syntax to 
-   stdio stream FILE.  */
 
-void
-s390_output_symbolic_const (file, x)
-     FILE *file;
-     rtx x;
+/* Return true if OP is a legitimate general operand when
+   generating PIC code.  It is given that flag_pic is on
+   and that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
+
+int
+legitimate_pic_operand_p (rtx op)
 {
-  switch (GET_CODE (x))
-    {
-    case CONST:
-    case ZERO_EXTEND:
-    case SIGN_EXTEND:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      break;
+  /* Accept all non-symbolic constants.  */
+  if (!SYMBOLIC_CONST (op))
+    return 1;
 
-    case PLUS:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      fprintf (file, "+");
-      s390_output_symbolic_const (file, XEXP (x, 1));
-      break;
+  /* Reject everything else; must be handled
+     via emit_symbolic_move.  */
+  return 0;
+}
 
-    case MINUS:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      fprintf (file, "-");
-      s390_output_symbolic_const (file, XEXP (x, 1));
-      break;
+/* Returns true if the constant value OP is a legitimate general operand.
+   It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
+
+int
+legitimate_constant_p (rtx op)
+{
+  /* Accept all non-symbolic constants.  */
+  if (!SYMBOLIC_CONST (op))
+    return 1;
+
+  /* Accept immediate LARL operands.  */
+  if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode))
+    return 1;
+
+  /* Thread-local symbols are never legal constants.  This is
+     so that emit_call knows that computing such addresses
+     might require a function call.  */
+  if (TLS_SYMBOLIC_CONST (op))
+    return 0;
+
+  /* In the PIC case, symbolic constants must *not* be
+     forced into the literal pool.  We accept them here,
+     so that they will be handled by emit_symbolic_move.  */
+  if (flag_pic)
+    return 1;
+
+  /* All remaining non-PIC symbolic constants are
+     forced into the literal pool.  */
+  return 0;
+}
 
+/* Determine if it's legal to put X into the constant pool.  This
+   is not possible if X contains the address of a symbol that is
+   not constant (TLS) or not known at final link time (PIC).  */
+
+static bool
+s390_cannot_force_const_mem (rtx x)
+{
+  switch (GET_CODE (x))
+    {
     case CONST_INT:
+    case CONST_DOUBLE:
+      /* Accept all non-symbolic constants.  */
+      return false;
+
     case LABEL_REF:
-    case CODE_LABEL:
+      /* Labels are OK iff we are non-PIC.  */
+      return flag_pic != 0;
+
     case SYMBOL_REF:
-      output_addr_const (file, x);
-      break;
+      /* 'Naked' TLS symbol references are never OK,
+         non-TLS symbols are OK iff we are non-PIC.  */
+      if (tls_symbolic_operand (x))
+       return true;
+      else
+       return flag_pic != 0;
+
+    case CONST:
+      return s390_cannot_force_const_mem (XEXP (x, 0));
+    case PLUS:
+    case MINUS:
+      return s390_cannot_force_const_mem (XEXP (x, 0))
+            || s390_cannot_force_const_mem (XEXP (x, 1));
 
     case UNSPEC:
-      if (XVECLEN (x, 0) != 1)
-        output_operand_lossage ("invalid UNSPEC as operand (1)");
       switch (XINT (x, 1))
-        {
-        case 100:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-          fprintf (file, "-.LT%X", s390_function_count);
-         break;
-       case 110:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOT12");
-         break;
-       case 111:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOTENT");
-         break;
-       case 112:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOT");
-         break;
-       case 113:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@PLT");
-         break;
-       case 114:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-          fprintf (file, "@PLT-.LT%X", s390_function_count);
-         break;
+       {
+       /* Only lt-relative or GOT-relative UNSPECs are OK.  */
+       case UNSPEC_LTREL_OFFSET:
+       case UNSPEC_GOT:
+       case UNSPEC_GOTOFF:
+       case UNSPEC_PLTOFF:
+       case UNSPEC_TLSGD:
+       case UNSPEC_TLSLDM:
+       case UNSPEC_NTPOFF:
+       case UNSPEC_DTPOFF:
+       case UNSPEC_GOTNTPOFF:
+       case UNSPEC_INDNTPOFF:
+         return false;
+
+       /* If the literal pool shares the code section, be put
+          execute template placeholders into the pool as well.  */
+       case UNSPEC_INSN:
+         return TARGET_CPU_ZARCH;
+
        default:
-         output_operand_lossage ("invalid UNSPEC as operand (2)");
-         break;
-        }
+         return true;
+       }
       break;
 
     default:
-      fatal_insn ("UNKNOWN in s390_output_symbolic_const !?", x);
-      break;
+      gcc_unreachable ();
     }
 }
 
-/* Output address operand ADDR in assembler syntax to 
-   stdio stream FILE.  */
+/* Returns true if the constant value OP is a legitimate general
+   operand during and after reload.  The difference to
+   legitimate_constant_p is that this function will not accept
+   a constant that would need to be forced to the literal pool
+   before it can be used as operand.  */
 
-void
-print_operand_address (file, addr)
-     FILE *file;
-     rtx addr;
+bool
+legitimate_reload_constant_p (rtx op)
 {
-  struct s390_address ad;
+  /* Accept la(y) operands.  */
+  if (GET_CODE (op) == CONST_INT
+      && DISP_IN_RANGE (INTVAL (op)))
+    return true;
 
-  if (!s390_decompose_address (addr, &ad, TRUE))
-    output_operand_lossage ("Cannot decompose address.");
-  if (ad.disp)
-    s390_output_symbolic_const (file, ad.disp);
-  else
-    fprintf (file, "0");
+  /* Accept l(g)hi/l(g)fi operands.  */
+  if (GET_CODE (op) == CONST_INT
+      && (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op))))
+    return true;
 
-  if (ad.base && ad.indx)
-    fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)],
-                              reg_names[REGNO (ad.base)]);
-  else if (ad.base)
-    fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
-}
+  /* Accept lliXX operands.  */
+  if (TARGET_ZARCH
+      && GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
+      && s390_single_part (op, word_mode, HImode, 0) >= 0)
+  return true;
+
+  if (TARGET_EXTIMM
+      && GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
+      && s390_single_part (op, word_mode, SImode, 0) >= 0)
+    return true;
 
-/* Output operand X in assembler syntax to stdio stream FILE.  
-   CODE specified the format flag.  The following format flags 
-   are recognized:
+  /* Accept larl operands.  */
+  if (TARGET_CPU_ZARCH
+      && larl_operand (op, VOIDmode))
+    return true;
 
-    'C': print opcode suffix for branch condition.
-    'D': print opcode suffix for inverse branch condition.
-    'O': print only the displacement of a memory reference.
-    'R': print only the base register of a memory reference.
-    'N': print the second word of a DImode operand.
-    'M': print the second word of a TImode operand.
+  /* Accept lzXX operands.  */
+  if (GET_CODE (op) == CONST_DOUBLE
+      && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
+    return true;
 
-    'b': print integer X as if it's an unsigned byte.
-    'x': print integer X as if it's an unsigned word.
-    'h': print integer X as if it's a signed word.  */
+  /* Accept double-word operands that can be split.  */
+  if (GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op))
+    {
+      enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode;
+      rtx hi = operand_subword (op, 0, 0, dword_mode);
+      rtx lo = operand_subword (op, 1, 0, dword_mode);
+      return legitimate_reload_constant_p (hi)
+            && legitimate_reload_constant_p (lo);
+    }
 
-void
-print_operand (file, x, code)
-     FILE *file;
-     rtx x;
-     int code;
+  /* Everything else cannot be handled without reload.  */
+  return false;
+}
+
+/* Given an rtx OP being reloaded into a reg required to be in class RCLASS,
+   return the class of reg to actually use.  */
+
+enum reg_class
+s390_preferred_reload_class (rtx op, enum reg_class rclass)
 {
-  switch (code)
+  switch (GET_CODE (op))
     {
-    case 'C':
-      fprintf (file, s390_branch_condition_mnemonic (x, FALSE));
-      return;
+      /* Constants we cannot reload must be forced into the
+        literal pool.  */
 
-    case 'D':
-      fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
-      return;
+      case CONST_DOUBLE:
+      case CONST_INT:
+       if (legitimate_reload_constant_p (op))
+         return rclass;
+       else
+         return NO_REGS;
 
-    case 'O':
-      {
-        struct s390_address ad;
+      /* If a symbolic constant or a PLUS is reloaded,
+        it is most likely being used as an address, so
+        prefer ADDR_REGS.  If 'class' is not a superset
+        of ADDR_REGS, e.g. FP_REGS, reject this reload.  */
+      case PLUS:
+      case LABEL_REF:
+      case SYMBOL_REF:
+      case CONST:
+       if (reg_class_subset_p (ADDR_REGS, rclass))
+          return ADDR_REGS;
+       else
+         return NO_REGS;
 
-        if (GET_CODE (x) != MEM
-            || !s390_decompose_address (XEXP (x, 0), &ad, TRUE)
-            || ad.indx)
-          abort ();
+      default:
+       break;
+    }
 
-        if (ad.disp)
-          s390_output_symbolic_const (file, ad.disp);
-        else
-          fprintf (file, "0");
-      }
-      return;
-
-    case 'R':
-      {
-        struct s390_address ad;
-
-        if (GET_CODE (x) != MEM
-            || !s390_decompose_address (XEXP (x, 0), &ad, TRUE)
-            || ad.indx)
-          abort ();
+  return rclass;
+}
 
-        if (ad.base)
-          fprintf (file, "%s", reg_names[REGNO (ad.base)]);
-        else
-          fprintf (file, "0");
-      }
-      return;
+/* Return true if ADDR is SYMBOL_REF + addend with addend being a
+   multiple of ALIGNMENT and the SYMBOL_REF being naturally
+   aligned.  */
 
-    case 'N':
-      if (GET_CODE (x) == REG)
-       x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
-      else if (GET_CODE (x) == MEM)
-       x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
-      else
-        abort ();
-      break;
+bool
+s390_check_symref_alignment (rtx addr, HOST_WIDE_INT alignment)
+{
+  HOST_WIDE_INT addend;
+  rtx symref;
 
-    case 'M':
-      if (GET_CODE (x) == REG)
-       x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
-      else if (GET_CODE (x) == MEM)
-       x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
-      else
-        abort ();
-      break;
-    }
+  if (!s390_symref_operand_p (addr, &symref, &addend))
+    return false;
 
-  switch (GET_CODE (x))
-    {
-    case REG:
-      fprintf (file, "%s", reg_names[REGNO (x)]);
-      break;
+  return (!SYMBOL_REF_NOT_NATURALLY_ALIGNED_P (symref)
+         && !(addend & (alignment - 1)));
+}
 
-    case MEM:
-      output_address (XEXP (x, 0));
-      break;
+/* ADDR is moved into REG using larl.  If ADDR isn't a valid larl
+   operand SCRATCH is used to reload the even part of the address and
+   adding one.  */
 
-    case CONST:
-    case CODE_LABEL:
-    case LABEL_REF:
-    case SYMBOL_REF:
-      s390_output_symbolic_const (file, x);
-      break;
+void
+s390_reload_larl_operand (rtx reg, rtx addr, rtx scratch)
+{
+  HOST_WIDE_INT addend;
+  rtx symref;
 
-    case CONST_INT:
-      if (code == 'b')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
-      else if (code == 'x')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
-      else if (code == 'h')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
-      else
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
-      break;
+  if (!s390_symref_operand_p (addr, &symref, &addend))
+    gcc_unreachable ();
 
-    case CONST_DOUBLE:
-      if (GET_MODE (x) != VOIDmode)
-        abort ();
-      if (code == 'b')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
-      else if (code == 'x')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
-      else if (code == 'h')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
+  if (!(addend & 1))
+    /* Easy case.  The addend is even so larl will do fine.  */
+    emit_move_insn (reg, addr);
+  else
+    {
+      /* We can leave the scratch register untouched if the target
+        register is a valid base register.  */
+      if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+         && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS)
+       scratch = reg;
+
+      gcc_assert (REGNO (scratch) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO_REG_CLASS (REGNO (scratch)) == ADDR_REGS);
+
+      if (addend != 1)
+       emit_move_insn (scratch,
+                       gen_rtx_CONST (Pmode,
+                                      gen_rtx_PLUS (Pmode, symref,
+                                                    GEN_INT (addend - 1))));
       else
-        abort ();
-      break;
+       emit_move_insn (scratch, symref);
 
-    default:
-      fatal_insn ("UNKNOWN in print_operand !?", x);
-      break;
-    }
-}
-
-/* Target hook for assembling integer objects.  We need to define it
-   here to work a round a bug in some versions of GAS, which couldn't
-   handle values smaller than INT_MIN when printed in decimal.  */
-
-static bool
-s390_assemble_integer (x, size, aligned_p)
-     rtx x;
-     unsigned int size;
-     int aligned_p;
-{
-  if (size == 8 && aligned_p
-      && GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
-    {
-      fputs ("\t.quad\t", asm_out_file);
-      fprintf (asm_out_file, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
-      putc ('\n', asm_out_file);
-      return true;
+      /* Increment the address using la in order to avoid clobbering cc.  */
+      emit_move_insn (reg, gen_rtx_PLUS (Pmode, scratch, const1_rtx));
     }
-  return default_assemble_integer (x, size, aligned_p);
 }
 
+/* Generate what is necessary to move between REG and MEM using
+   SCRATCH.  The direction is given by TOMEM.  */
 
-#define DEBUG_SCHED 0
-
-/* Returns true if register REGNO is used  for forming 
-   a memory address in expression X.  */
-
-static int
-reg_used_in_mem_p (regno, x)
-     int regno;
-     rtx x;
+void
+s390_reload_symref_address (rtx reg, rtx mem, rtx scratch, bool tomem)
 {
-  enum rtx_code code = GET_CODE (x);
-  int i, j;
-  const char *fmt;
-  
-  if (code == MEM)
-    {
-      if (refers_to_regno_p (regno, regno+1,
-                            XEXP (x, 0), 0))
-       return 1;
-    }
-  else if (code == SET 
-          && GET_CODE (SET_DEST (x)) == PC)
-    {
-      if (refers_to_regno_p (regno, regno+1,
-                            SET_SRC (x), 0))
-       return 1;
-    }
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'e'
-         && reg_used_in_mem_p (regno, XEXP (x, i)))
-       return 1;
-      
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (x, i); j++)
-         if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
-           return 1;
-    }
-  return 0;
+  /* Reload might have pulled a constant out of the literal pool.
+     Force it back in.  */
+  if (CONST_INT_P (mem) || GET_CODE (mem) == CONST_DOUBLE
+      || GET_CODE (mem) == CONST)
+    mem = force_const_mem (GET_MODE (reg), mem);
+
+  gcc_assert (MEM_P (mem));
+
+  /* For a load from memory we can leave the scratch register
+     untouched if the target register is a valid base register.  */
+  if (!tomem
+      && REGNO (reg) < FIRST_PSEUDO_REGISTER
+      && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS
+      && GET_MODE (reg) == GET_MODE (scratch))
+    scratch = reg;
+
+  /* Load address into scratch register.  Since we can't have a
+     secondary reload for a secondary reload we have to cover the case
+     where larl would need a secondary reload here as well.  */
+  s390_reload_larl_operand (scratch, XEXP (mem, 0), scratch);
+
+  /* Now we can use a standard load/store to do the move.  */
+  if (tomem)
+    emit_move_insn (replace_equiv_address (mem, scratch), reg);
+  else
+    emit_move_insn (reg, replace_equiv_address (mem, scratch));
 }
 
-/* Returns true if expression DEP_RTX sets an address register
-   used by instruction INSN to address memory.  */
+/* Inform reload about cases where moving X with a mode MODE to a register in
+   RCLASS requires an extra scratch or immediate register.  Return the class
+   needed for the immediate register.  */
 
-static int 
-addr_generation_dependency_p (dep_rtx, insn)
-     rtx dep_rtx; 
-     rtx insn;
+static enum reg_class
+s390_secondary_reload (bool in_p, rtx x, enum reg_class rclass,
+                      enum machine_mode mode, secondary_reload_info *sri)
 {
-  rtx target, pat;
+  /* Intermediate register needed.  */
+  if (reg_classes_intersect_p (CC_REGS, rclass))
+    return GENERAL_REGS;
 
-  if (GET_CODE (dep_rtx) == SET)
+  if (TARGET_Z10)
     {
-      target = SET_DEST (dep_rtx);
-      
-      if (GET_CODE (target) == REG)
+      /* On z10 several optimizer steps may generate larl operands with
+        an odd addend.  */
+      if (in_p
+         && s390_symref_operand_p (x, NULL, NULL)
+         && mode == Pmode
+         && !s390_check_symref_alignment (x, 2))
+       sri->icode = ((mode == DImode) ? CODE_FOR_reloaddi_larl_odd_addend_z10
+                     : CODE_FOR_reloadsi_larl_odd_addend_z10);
+
+      /* On z10 we need a scratch register when moving QI, TI or floating
+        point mode values from or to a memory location with a SYMBOL_REF
+        or if the symref addend of a SI or DI move is not aligned to the
+        width of the access.  */
+      if (MEM_P (x)
+         && s390_symref_operand_p (XEXP (x, 0), NULL, NULL)
+         && (mode == QImode || mode == TImode || FLOAT_MODE_P (mode)
+             || (!TARGET_64BIT && mode == DImode)
+             || ((mode == HImode || mode == SImode || mode == DImode)
+                 && (!s390_check_symref_alignment (XEXP (x, 0),
+                                                   GET_MODE_SIZE (mode))))))
        {
-         int regno = REGNO (target);
+#define __SECONDARY_RELOAD_CASE(M,m)                                   \
+         case M##mode:                                                 \
+           if (TARGET_64BIT)                                           \
+             sri->icode = in_p ? CODE_FOR_reload##m##di_toreg_z10 :    \
+                                  CODE_FOR_reload##m##di_tomem_z10;    \
+           else                                                        \
+             sri->icode = in_p ? CODE_FOR_reload##m##si_toreg_z10 :    \
+                                  CODE_FOR_reload##m##si_tomem_z10;    \
+         break;
 
-         if (get_attr_type (insn) == TYPE_LA)
+         switch (GET_MODE (x))
            {
-             pat = PATTERN (insn);
-             if (GET_CODE (pat) == PARALLEL)
-               {
-                 if (XVECLEN (pat, 0) != 2)
-                   abort();
-                 pat = XVECEXP (pat, 0, 0);
-               }
-             if (GET_CODE (pat) == SET)
-               return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
-             else
-               abort();
+             __SECONDARY_RELOAD_CASE (QI, qi);
+             __SECONDARY_RELOAD_CASE (HI, hi);
+             __SECONDARY_RELOAD_CASE (SI, si);
+             __SECONDARY_RELOAD_CASE (DI, di);
+             __SECONDARY_RELOAD_CASE (TI, ti);
+             __SECONDARY_RELOAD_CASE (SF, sf);
+             __SECONDARY_RELOAD_CASE (DF, df);
+             __SECONDARY_RELOAD_CASE (TF, tf);
+             __SECONDARY_RELOAD_CASE (SD, sd);
+             __SECONDARY_RELOAD_CASE (DD, dd);
+             __SECONDARY_RELOAD_CASE (TD, td);
+
+           default:
+             gcc_unreachable ();
            }
-         else if (get_attr_atype (insn) == ATYPE_MEM)
-           return reg_used_in_mem_p (regno, PATTERN (insn));
+#undef __SECONDARY_RELOAD_CASE
        }
     }
-  return 0;
-}
-
 
-/* Return the modified cost of the dependency of instruction INSN
-   on instruction DEP_INSN through the link LINK.  COST is the 
-   default cost of that dependency.
+  /* We need a scratch register when loading a PLUS expression which
+     is not a legitimate operand of the LOAD ADDRESS instruction.  */
+  if (in_p && s390_plus_operand (x, mode))
+    sri->icode = (TARGET_64BIT ?
+                 CODE_FOR_reloaddi_plus : CODE_FOR_reloadsi_plus);
+
+  /* Performing a multiword move from or to memory we have to make sure the
+     second chunk in memory is addressable without causing a displacement
+     overflow.  If that would be the case we calculate the address in
+     a scratch register.  */
+  if (MEM_P (x)
+      && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (x, 0), 1))
+                        + GET_MODE_SIZE (mode) - 1))
+    {
+      /* For GENERAL_REGS a displacement overflow is no problem if occurring
+        in a s_operand address since we may fallback to lm/stm.  So we only
+        have to care about overflows in the b+i+d case.  */
+      if ((reg_classes_intersect_p (GENERAL_REGS, rclass)
+          && s390_class_max_nregs (GENERAL_REGS, mode) > 1
+          && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS)
+         /* For FP_REGS no lm/stm is available so this check is triggered
+            for displacement overflows in b+i+d and b+d like addresses.  */
+         || (reg_classes_intersect_p (FP_REGS, rclass)
+             && s390_class_max_nregs (FP_REGS, mode) > 1))
+       {
+         if (in_p)
+           sri->icode = (TARGET_64BIT ?
+                         CODE_FOR_reloaddi_nonoffmem_in :
+                         CODE_FOR_reloadsi_nonoffmem_in);
+         else
+           sri->icode = (TARGET_64BIT ?
+                         CODE_FOR_reloaddi_nonoffmem_out :
+                         CODE_FOR_reloadsi_nonoffmem_out);
+       }
+    }
 
-   Data dependencies are all handled without delay.  However, if a
-   register is modified and subsequently used as base or index 
-   register of a memory reference, at least 4 cycles need to pass
-   between setting and using the register to avoid pipeline stalls.  
-   An exception is the LA instruction. An address generated by LA can
-   be used by introducing only a one cycle stall on the pipeline.  */
+  /* A scratch address register is needed when a symbolic constant is
+     copied to r0 compiling with -fPIC.  In other cases the target
+     register might be used as temporary (see legitimize_pic_address).  */
+  if (in_p && SYMBOLIC_CONST (x) && flag_pic == 2 && rclass != ADDR_REGS)
+    sri->icode = (TARGET_64BIT ?
+                 CODE_FOR_reloaddi_PIC_addr :
+                 CODE_FOR_reloadsi_PIC_addr);
 
-static int
-s390_adjust_cost (insn, link, dep_insn, cost)
-     rtx insn;
-     rtx link;
-     rtx dep_insn;
-     int cost;
-{
-  rtx dep_rtx;
-  int i;
+  /* Either scratch or no register needed.  */
+  return NO_REGS;
+}
 
-  /* If the dependence is an anti-dependence, there is no cost.  For an
-     output dependence, there is sometimes a cost, but it doesn't seem
-     worth handling those few cases.  */
+/* Generate code to load SRC, which is PLUS that is not a
+   legitimate operand for the LA instruction, into TARGET.
+   SCRATCH may be used as scratch register.  */
 
-  if (REG_NOTE_KIND (link) != 0)
-    return 0;
+void
+s390_expand_plus_operand (rtx target, rtx src,
+                         rtx scratch)
+{
+  rtx sum1, sum2;
+  struct s390_address ad;
 
-  /* If we can't recognize the insns, we can't really do anything.  */
-  if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
-    return cost;
+  /* src must be a PLUS; get its two operands.  */
+  gcc_assert (GET_CODE (src) == PLUS);
+  gcc_assert (GET_MODE (src) == Pmode);
 
-  dep_rtx = PATTERN (dep_insn);
+  /* Check if any of the two operands is already scheduled
+     for replacement by reload.  This can happen e.g. when
+     float registers occur in an address.  */
+  sum1 = find_replacement (&XEXP (src, 0));
+  sum2 = find_replacement (&XEXP (src, 1));
+  src = gen_rtx_PLUS (Pmode, sum1, sum2);
 
-  if (GET_CODE (dep_rtx) == SET)
+  /* If the address is already strictly valid, there's nothing to do.  */
+  if (!s390_decompose_address (src, &ad)
+      || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+      || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
     {
-      if (addr_generation_dependency_p (dep_rtx, insn))
+      /* Otherwise, one of the operands cannot be an address register;
+         we reload its value into the scratch register.  */
+      if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
        {
-         cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;  
-         if (DEBUG_SCHED)
-           {
-             fprintf (stderr, "\n\nAddress dependency detected: cost %d\n",
-                      cost);
-             debug_rtx (dep_insn);
-             debug_rtx (insn);
-           }
+         emit_move_insn (scratch, sum1);
+         sum1 = scratch;
        }
-    }
-  else if (GET_CODE (dep_rtx) == PARALLEL)
-    {
-      for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
+      if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
        {
-         if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i),
-                                           insn))
-           {
-             cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;  
-             if (DEBUG_SCHED)
-               {
-                 fprintf (stderr, "\n\nAddress dependency detected: cost %d\n"
-                          ,cost);
-                 debug_rtx (dep_insn);
-                 debug_rtx (insn);
-               }
-           }
+         emit_move_insn (scratch, sum2);
+         sum2 = scratch;
+       }
+
+      /* According to the way these invalid addresses are generated
+         in reload.c, it should never happen (at least on s390) that
+         *neither* of the PLUS components, after find_replacements
+         was applied, is an address register.  */
+      if (sum1 == scratch && sum2 == scratch)
+       {
+         debug_rtx (src);
+         gcc_unreachable ();
        }
+
+      src = gen_rtx_PLUS (Pmode, sum1, sum2);
     }
 
-  return cost;
+  /* Emit the LOAD ADDRESS pattern.  Note that reload of PLUS
+     is only ever performed on addresses, so we can mark the
+     sum as legitimate for LA in any case.  */
+  s390_load_address (target, src);
 }
 
 
-/* A C statement (sans semicolon) to update the integer scheduling priority
-   INSN_PRIORITY (INSN).  Reduce the priority to execute the INSN earlier,
-   increase the priority to execute INSN later.  Do not define this macro if
-   you do not need to adjust the scheduling priorities of insns. 
-
-   A LA instruction maybe scheduled later, since the pipeline bypasses the
-   calculated value.  */
+/* Return true if ADDR is a valid memory address.
+   STRICT specifies whether strict register checking applies.  */
 
-static int
-s390_adjust_priority (insn, priority)
-     rtx insn ATTRIBUTE_UNUSED;
-     int priority;
+bool
+legitimate_address_p (enum machine_mode mode, rtx addr, int strict)
 {
-  if (! INSN_P (insn))
-    return priority;
+  struct s390_address ad;
 
-  if (GET_CODE (PATTERN (insn)) == USE 
-      || GET_CODE (PATTERN (insn)) == CLOBBER)
-    return priority;
-  
-  switch (get_attr_type (insn))
+  if (TARGET_Z10
+      && larl_operand (addr, VOIDmode)
+      && (mode == VOIDmode
+         || s390_check_symref_alignment (addr, GET_MODE_SIZE (mode))))
+    return true;
+
+  if (!s390_decompose_address (addr, &ad))
+    return false;
+
+  if (strict)
     {
-    default:
-      break;
+      if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+       return false;
+
+      if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))
+       return false;
+    }
+  else
+    {
+      if (ad.base 
+         && !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER
+              || REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS))
+       return false;
       
-    case TYPE_LA:
-      if (priority >= 0 && priority < 0x01000000)
-       priority <<= 3;
-      break;
-    case TYPE_LM:
-      /* LM in epilogue should never be scheduled. This
-        is due to literal access done in function body.
-        The usage of register 13 is not mentioned explicitly,
-        leading to scheduling 'LM' accross this instructions.  
-      */ 
-      priority = 0x7fffffff;
-      break;
+      if (ad.indx
+         && !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER
+              || REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS))
+         return false;
     }
-  
-  return priority;
+  return true;
 }
 
+/* Return true if OP is a valid operand for the LA instruction.
+   In 31-bit, we need to prove that the result is used as an
+   address, as LA performs only a 31-bit addition.  */
 
-/* Split all branches that exceed the maximum distance.  */
-
-static void 
-s390_split_branches (void)
+bool
+legitimate_la_operand_p (rtx op)
 {
-  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  rtx insn, pat, label, target, jump, tmp;
-
-  /* In 64-bit mode we can jump +- 4GB.  */
+  struct s390_address addr;
+  if (!s390_decompose_address (op, &addr))
+    return false;
 
-  if (TARGET_64BIT)
-    return;
+  return (TARGET_64BIT || addr.pointer);
+}
 
-  /* Find all branches that exceed 64KB, and split them.  */
+/* Return true if it is valid *and* preferable to use LA to
+   compute the sum of OP1 and OP2.  */
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      if (GET_CODE (insn) != JUMP_INSN)
-       continue;
+bool
+preferred_la_operand_p (rtx op1, rtx op2)
+{
+  struct s390_address addr;
 
-      pat = PATTERN (insn);
-      if (GET_CODE (pat) != SET)
-       continue;
+  if (op2 != const0_rtx)
+    op1 = gen_rtx_PLUS (Pmode, op1, op2);
 
-      if (GET_CODE (SET_SRC (pat)) == LABEL_REF) 
-       {
-         label = SET_SRC (pat);
-       } 
-      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) 
-       {
-         if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) 
-           label = XEXP (SET_SRC (pat), 1);
-          else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) 
-            label = XEXP (SET_SRC (pat), 2);
-         else
-           continue;
-        }
-      else
-       continue;
+  if (!s390_decompose_address (op1, &addr))
+    return false;
+  if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base)))
+    return false;
+  if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx)))
+    return false;
 
-      if (get_attr_length (insn) == 4)
-       continue;
+  if (!TARGET_64BIT && !addr.pointer)
+    return false;
 
-      if (flag_pic)
-       {
-         target = gen_rtx_UNSPEC (SImode, gen_rtvec (1, label), 100);
-         target = gen_rtx_CONST (SImode, target);
-         target = force_const_mem (SImode, target);
-         jump = gen_rtx_REG (Pmode, BASE_REGISTER);
-         jump = gen_rtx_PLUS (Pmode, jump, temp_reg);
-       }
-      else
-       {
-         target = force_const_mem (Pmode, label);
-         jump = temp_reg;
-       }
+  if (addr.pointer)
+    return true;
 
-      if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
-       {
-         if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
-           jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
-                                        jump, pc_rtx);
-         else
-           jump = gen_rtx_IF_THEN_ELSE (VOIDmode, XEXP (SET_SRC (pat), 0),
-                                        pc_rtx, jump);
-       }
+  if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
+      || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
+    return true;
 
-      tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
-      INSN_ADDRESSES_NEW (tmp, -1);
+  return false;
+}
 
-      tmp = emit_jump_insn_before (gen_rtx_SET (VOIDmode, pc_rtx, jump), insn);
-      INSN_ADDRESSES_NEW (tmp, -1);
+/* Emit a forced load-address operation to load SRC into DST.
+   This will use the LOAD ADDRESS instruction even in situations
+   where legitimate_la_operand_p (SRC) returns false.  */
 
-      remove_insn (insn);
-      insn = tmp;
-    }
+void
+s390_load_address (rtx dst, rtx src)
+{
+  if (TARGET_64BIT)
+    emit_move_insn (dst, src);
+  else
+    emit_insn (gen_force_la_31 (dst, src));
 }
 
+/* Return a legitimate reference for ORIG (an address) using the
+   register REG.  If REG is 0, a new pseudo is generated.
 
-/* Find a literal pool symbol referenced in RTX X, and store 
-   it at REF.  Will abort if X contains references to more than 
-   one such pool symbol; multiple references to the same symbol
-   are allowed, however. 
+   There are two types of references that must be handled:
 
-   The rtx pointed to by REF must be initialized to NULL_RTX 
-   by the caller before calling this routine.  */
+   1. Global data references must load the address from the GOT, via
+      the PIC reg.  An insn is emitted to do this load, and the reg is
+      returned.
 
-static void
-find_constant_pool_ref (x, ref)
-     rtx x;
-     rtx *ref;
-{
-  int i, j;
-  const char *fmt;
+   2. Static data references, constant pool addresses, and code labels
+      compute the address as an offset from the GOT, whose base is in
+      the PIC reg.  Static data objects have SYMBOL_FLAG_LOCAL set to
+      differentiate them from global data objects.  The returned
+      address is the PIC reg + an unspec constant.
 
-  if (GET_CODE (x) == SYMBOL_REF
-      && CONSTANT_POOL_ADDRESS_P (x))
-    {
-      if (*ref == NULL_RTX)
-        *ref = x;
-      else if (*ref != x)
-        abort();
-    }
+   GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
+   reg also appears in the address.  */
 
-  fmt = GET_RTX_FORMAT (GET_CODE (x));
-  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+rtx
+legitimize_pic_address (rtx orig, rtx reg)
+{
+  rtx addr = orig;
+  rtx new_rtx = orig;
+  rtx base;
+
+  gcc_assert (!TLS_SYMBOLIC_CONST (addr));
+
+  if (GET_CODE (addr) == LABEL_REF
+      || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
     {
-      if (fmt[i] == 'e')
+      /* This is a local symbol.  */
+      if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
         {
-          find_constant_pool_ref (XEXP (x, i), ref);
+          /* Access local symbols PC-relative via LARL.
+             This is the same as in the non-PIC case, so it is
+             handled automatically ...  */
         }
-      else if (fmt[i] == 'E')
+      else
         {
-          for (j = 0; j < XVECLEN (x, i); j++)
-            find_constant_pool_ref (XVECEXP (x, i, j), ref);
-        }
-    }
-}
+          /* Access local symbols relative to the GOT.  */
 
-/* Replace every reference to the literal pool symbol REF
-   in X by the address ADDR.  Fix up MEMs as required.  */
+          rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-static void
-replace_constant_pool_ref (x, ref, addr)
-     rtx *x;
-     rtx ref;
-     rtx addr;
-{
-  int i, j;
-  const char *fmt;
+         if (reload_in_progress || reload_completed)
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
-  if (*x == ref)
-    abort ();
+          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
+          addr = gen_rtx_CONST (Pmode, addr);
+          addr = force_const_mem (Pmode, addr);
+         emit_move_insn (temp, addr);
 
-  /* Literal pool references can only occur inside a MEM ...  */
-  if (GET_CODE (*x) == MEM)
+          new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+          if (reg != 0)
+            {
+              s390_load_address (reg, new_rtx);
+              new_rtx = reg;
+            }
+        }
+    }
+  else if (GET_CODE (addr) == SYMBOL_REF)
     {
-      rtx memref = XEXP (*x, 0);
+      if (reg == 0)
+        reg = gen_reg_rtx (Pmode);
 
-      if (memref == ref)
-       {
-         *x = replace_equiv_address (*x, addr);
-         return;
-       }
+      if (flag_pic == 1)
+        {
+          /* Assume GOT offset < 4k.  This is handled the same way
+             in both 31- and 64-bit code (@GOT).  */
 
-      if (GET_CODE (memref) == CONST
-         && GET_CODE (XEXP (memref, 0)) == PLUS
-         && GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT
-         && XEXP (XEXP (memref, 0), 0) == ref)
-       {
-         HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1));
-         *x = replace_equiv_address (*x, plus_constant (addr, off));
-         return;
-       }
-    }
+         if (reload_in_progress || reload_completed)
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
-  /* ... or a load-address type pattern.  */
-  if (GET_CODE (*x) == SET)
-    {
-      rtx addrref = SET_SRC (*x);
+          new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
+          new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+          new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx);
+          new_rtx = gen_const_mem (Pmode, new_rtx);
+          emit_move_insn (reg, new_rtx);
+          new_rtx = reg;
+        }
+      else if (TARGET_CPU_ZARCH)
+        {
+          /* If the GOT offset might be >= 4k, we determine the position
+             of the GOT entry via a PC-relative LARL (@GOTENT).  */
 
-      if (addrref == ref)
-       {
-         SET_SRC (*x) = addr;
-         return;
-       }
+          rtx temp = reg ? reg : gen_reg_rtx (Pmode);
 
-      if (GET_CODE (addrref) == CONST
-         && GET_CODE (XEXP (addrref, 0)) == PLUS
-         && GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT
-         && XEXP (XEXP (addrref, 0), 0) == ref)
-       {
-         HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1));
-         SET_SRC (*x) = plus_constant (addr, off);
-         return;
-       }
-    }
+         gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER
+                     || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS);
 
-  fmt = GET_RTX_FORMAT (GET_CODE (*x));
-  for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-        {
-          replace_constant_pool_ref (&XEXP (*x, i), ref, addr);
+          new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
+          new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+          emit_move_insn (temp, new_rtx);
+
+          new_rtx = gen_const_mem (Pmode, temp);
+          emit_move_insn (reg, new_rtx);
+          new_rtx = reg;
         }
-      else if (fmt[i] == 'E')
+      else
         {
-          for (j = 0; j < XVECLEN (*x, i); j++)
-            replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, addr);
+          /* If the GOT offset might be >= 4k, we have to load it
+             from the literal pool (@GOT).  */
+
+          rtx temp = reg ? reg : gen_reg_rtx (Pmode);
+
+         gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER
+                     || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS);
+
+         if (reload_in_progress || reload_completed)
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
+          addr = gen_rtx_CONST (Pmode, addr);
+          addr = force_const_mem (Pmode, addr);
+          emit_move_insn (temp, addr);
+
+          new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+          new_rtx = gen_const_mem (Pmode, new_rtx);
+          emit_move_insn (reg, new_rtx);
+          new_rtx = reg;
         }
     }
-}
+  else
+    {
+      if (GET_CODE (addr) == CONST)
+       {
+         addr = XEXP (addr, 0);
+         if (GET_CODE (addr) == UNSPEC)
+           {
+             gcc_assert (XVECLEN (addr, 0) == 1);
+              switch (XINT (addr, 1))
+                {
+                  /* If someone moved a GOT-relative UNSPEC
+                     out of the literal pool, force them back in.  */
+                  case UNSPEC_GOTOFF:
+                  case UNSPEC_PLTOFF:
+                    new_rtx = force_const_mem (Pmode, orig);
+                    break;
 
-/* We keep a list of constants we which we have to add to internal
-   constant tables in the middle of large functions.  */
+                  /* @GOT is OK as is if small.  */
+                 case UNSPEC_GOT:
+                   if (flag_pic == 2)
+                     new_rtx = force_const_mem (Pmode, orig);
+                   break;
 
-#define NR_C_MODES 6
-enum machine_mode constant_modes[NR_C_MODES] = 
-{
-  DFmode, DImode,
-  SFmode, SImode,
-  HImode,
-  QImode
-};
+                  /* @GOTENT is OK as is.  */
+                  case UNSPEC_GOTENT:
+                    break;
 
-rtx (*gen_consttable[NR_C_MODES])(rtx) =
-{
-  gen_consttable_df, gen_consttable_di,
-  gen_consttable_sf, gen_consttable_si,
-  gen_consttable_hi,
-  gen_consttable_qi
-};
+                  /* @PLT is OK as is on 64-bit, must be converted to
+                     GOT-relative @PLTOFF on 31-bit.  */
+                  case UNSPEC_PLT:
+                    if (!TARGET_CPU_ZARCH)
+                      {
+                        rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-struct constant
-{
-  struct constant *next;
-  rtx value;
-  rtx label;
-};
+                       if (reload_in_progress || reload_completed)
+                         df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
-struct constant_pool
-{
-  struct constant_pool *next;
-  rtx first_insn;
-  rtx last_insn;
+                        addr = XVECEXP (addr, 0, 0);
+                        addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+                                              UNSPEC_PLTOFF);
+                        addr = gen_rtx_CONST (Pmode, addr);
+                        addr = force_const_mem (Pmode, addr);
+                       emit_move_insn (temp, addr);
 
-  struct constant *constants[NR_C_MODES];
-  rtx label;
-  int size;
-};
+                        new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+                        if (reg != 0)
+                          {
+                            s390_load_address (reg, new_rtx);
+                            new_rtx = reg;
+                          }
+                      }
+                    break;
 
-static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx));
-static void s390_end_pool PARAMS ((struct constant_pool *, rtx));
-static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx));
-static rtx s390_add_pool PARAMS ((struct constant_pool *, rtx, enum machine_mode));
-static rtx s390_dump_pool PARAMS ((struct constant_pool *));
-static void s390_free_pool PARAMS ((struct constant_pool *));
+                  /* Everything else cannot happen.  */
+                  default:
+                    gcc_unreachable ();
+                }
+           }
+         else 
+           gcc_assert (GET_CODE (addr) == PLUS);
+       }
+      if (GET_CODE (addr) == PLUS)
+       {
+         rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
 
-/* Create new constant pool covering instructions starting at INSN
-   and chain it to the end of POOL_LIST.  */
+         gcc_assert (!TLS_SYMBOLIC_CONST (op0));
+         gcc_assert (!TLS_SYMBOLIC_CONST (op1));
 
-static struct constant_pool *
-s390_start_pool (pool_list, insn)
-     struct constant_pool **pool_list;
-     rtx insn;
-{
-  struct constant_pool *pool, **prev;
-  int i;
+         /* Check first to see if this is a constant offset
+             from a local symbol reference.  */
+         if ((GET_CODE (op0) == LABEL_REF
+               || (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0)))
+             && GET_CODE (op1) == CONST_INT)
+           {
+              if (TARGET_CPU_ZARCH
+                 && larl_operand (op0, VOIDmode)
+                 && INTVAL (op1) < (HOST_WIDE_INT)1 << 31
+                 && INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31))
+                {
+                  if (INTVAL (op1) & 1)
+                    {
+                      /* LARL can't handle odd offsets, so emit a
+                         pair of LARL and LA.  */
+                      rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-  pool = (struct constant_pool *) xmalloc (sizeof *pool);
-  pool->next = NULL;
-  for (i = 0; i < NR_C_MODES; i++)
-    pool->constants[i] = NULL;
+                      if (!DISP_IN_RANGE (INTVAL (op1)))
+                        {
+                          HOST_WIDE_INT even = INTVAL (op1) - 1;
+                          op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
+                         op0 = gen_rtx_CONST (Pmode, op0);
+                          op1 = const1_rtx;
+                        }
 
-  pool->label = gen_label_rtx ();
-  pool->first_insn = insn;
-  pool->last_insn = NULL_RTX;
-  pool->size = 0;
-  for (prev = pool_list; *prev; prev = &(*prev)->next)
-    ;
-  *prev = pool;
+                      emit_move_insn (temp, op0);
+                      new_rtx = gen_rtx_PLUS (Pmode, temp, op1);
 
-  return pool;
+                      if (reg != 0)
+                        {
+                          s390_load_address (reg, new_rtx);
+                          new_rtx = reg;
+                        }
+                    }
+                  else
+                    {
+                      /* If the offset is even, we can just use LARL.
+                         This will happen automatically.  */
+                    }
+                }
+              else
+                {
+                  /* Access local symbols relative to the GOT.  */
+
+                  rtx temp = reg? reg : gen_reg_rtx (Pmode);
+
+                 if (reload_in_progress || reload_completed)
+                   df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+                  addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0),
+                                        UNSPEC_GOTOFF);
+                  addr = gen_rtx_PLUS (Pmode, addr, op1);
+                  addr = gen_rtx_CONST (Pmode, addr);
+                  addr = force_const_mem (Pmode, addr);
+                 emit_move_insn (temp, addr);
+
+                  new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+                  if (reg != 0)
+                    {
+                      s390_load_address (reg, new_rtx);
+                      new_rtx = reg;
+                    }
+                }
+           }
+
+          /* Now, check whether it is a GOT relative symbol plus offset
+             that was pulled out of the literal pool.  Force it back in.  */
+
+         else if (GET_CODE (op0) == UNSPEC
+                  && GET_CODE (op1) == CONST_INT
+                  && XINT (op0, 1) == UNSPEC_GOTOFF)
+            {
+             gcc_assert (XVECLEN (op0, 0) == 1);
+
+              new_rtx = force_const_mem (Pmode, orig);
+            }
+
+          /* Otherwise, compute the sum.  */
+         else
+           {
+             base = legitimize_pic_address (XEXP (addr, 0), reg);
+             new_rtx  = legitimize_pic_address (XEXP (addr, 1),
+                                            base == reg ? NULL_RTX : reg);
+             if (GET_CODE (new_rtx) == CONST_INT)
+               new_rtx = plus_constant (base, INTVAL (new_rtx));
+             else
+               {
+                 if (GET_CODE (new_rtx) == PLUS && CONSTANT_P (XEXP (new_rtx, 1)))
+                   {
+                     base = gen_rtx_PLUS (Pmode, base, XEXP (new_rtx, 0));
+                     new_rtx = XEXP (new_rtx, 1);
+                   }
+                 new_rtx = gen_rtx_PLUS (Pmode, base, new_rtx);
+               }
+
+             if (GET_CODE (new_rtx) == CONST)
+               new_rtx = XEXP (new_rtx, 0);
+              new_rtx = force_operand (new_rtx, 0);
+           }
+       }
+    }
+  return new_rtx;
 }
 
-/* End range of instructions covered by POOL at INSN.  */
+/* Load the thread pointer into a register.  */
 
-static void
-s390_end_pool (pool, insn)
-     struct constant_pool *pool;
-     rtx insn;
+rtx
+s390_get_thread_pointer (void)
 {
-  pool->last_insn = insn;
+  rtx tp = gen_reg_rtx (Pmode);
+
+  emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM));
+  mark_reg_pointer (tp, BITS_PER_WORD);
+
+  return tp;
 }
 
-/* Return pool out of POOL_LIST that covers INSN.  */
+/* Emit a tls call insn. The call target is the SYMBOL_REF stored
+   in s390_tls_symbol which always refers to __tls_get_offset.
+   The returned offset is written to RESULT_REG and an USE rtx is
+   generated for TLS_CALL.  */
 
-static struct constant_pool *
-s390_find_pool (pool_list, insn)
-     struct constant_pool *pool_list;
-     rtx insn;
+static GTY(()) rtx s390_tls_symbol;
+
+static void
+s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
 {
-  int addr = INSN_ADDRESSES (INSN_UID (insn));
-  struct constant_pool *pool;
+  rtx insn;
 
-  if (addr == -1)
-    return NULL;
+  gcc_assert (flag_pic);
 
-  for (pool = pool_list; pool; pool = pool->next)
-    if (INSN_ADDRESSES (INSN_UID (pool->first_insn)) <= addr
-        && (pool->last_insn == NULL_RTX
-            || INSN_ADDRESSES (INSN_UID (pool->last_insn)) > addr))
-      break;
+  if (!s390_tls_symbol)
+    s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
 
-  return pool;
+  insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg,
+                        gen_rtx_REG (Pmode, RETURN_REGNUM));
+
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg);
+  RTL_CONST_CALL_P (insn) = 1;
 }
 
-/* Add constant VAL of mode MODE to the constant pool POOL.
-   Return an RTX describing the distance from the start of
-   the pool to the location of the new constant.  */
+/* ADDR contains a thread-local SYMBOL_REF.  Generate code to compute
+   this (thread-local) address.  REG may be used as temporary.  */
 
 static rtx
-s390_add_pool (pool, val, mode)
-     struct constant_pool *pool;
-     rtx val;
-     enum machine_mode mode;
+legitimize_tls_address (rtx addr, rtx reg)
 {
-  struct constant *c;
-  rtx offset;
-  int i;
+  rtx new_rtx, tls_call, temp, base, r2, insn;
 
-  for (i = 0; i < NR_C_MODES; i++)
-    if (constant_modes[i] == mode)
-      break;
-  if (i == NR_C_MODES)
-    abort ();
+  if (GET_CODE (addr) == SYMBOL_REF)
+    switch (tls_symbolic_operand (addr))
+      {
+      case TLS_MODEL_GLOBAL_DYNAMIC:
+       start_sequence ();
+       r2 = gen_rtx_REG (Pmode, 2);
+       tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD);
+       new_rtx = gen_rtx_CONST (Pmode, tls_call);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+       emit_move_insn (r2, new_rtx);
+       s390_emit_tls_call_insn (r2, tls_call);
+       insn = get_insns ();
+       end_sequence ();
+
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       temp = gen_reg_rtx (Pmode);
+       emit_libcall_block (insn, temp, r2, new_rtx);
+
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
+         }
+       break;
 
-  for (c = pool->constants[i]; c != NULL; c = c->next)
-    if (rtx_equal_p (val, c->value))
-      break;
+      case TLS_MODEL_LOCAL_DYNAMIC:
+       start_sequence ();
+       r2 = gen_rtx_REG (Pmode, 2);
+       tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM);
+       new_rtx = gen_rtx_CONST (Pmode, tls_call);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+       emit_move_insn (r2, new_rtx);
+       s390_emit_tls_call_insn (r2, tls_call);
+       insn = get_insns ();
+       end_sequence ();
+
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
+       temp = gen_reg_rtx (Pmode);
+       emit_libcall_block (insn, temp, r2, new_rtx);
+
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       base = gen_reg_rtx (Pmode);
+       s390_load_address (base, new_rtx);
+
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+       temp = gen_reg_rtx (Pmode);
+       emit_move_insn (temp, new_rtx);
+
+       new_rtx = gen_rtx_PLUS (Pmode, base, temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
+         }
+       break;
 
-  if (c == NULL)
-    {
-      c = (struct constant *) xmalloc (sizeof *c);
-      c->value = val;
-      c->label = gen_label_rtx ();
-      c->next = pool->constants[i];
-      pool->constants[i] = c;
-      pool->size += GET_MODE_SIZE (mode);
-    }
-
-  offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label), 
-                                gen_rtx_LABEL_REF (Pmode, pool->label));
-  offset = gen_rtx_CONST (Pmode, offset);
-  return offset;
-}
-
-/* Dump out the constants in POOL.  */
+      case TLS_MODEL_INITIAL_EXEC:
+       if (flag_pic == 1)
+         {
+           /* Assume GOT offset < 4k.  This is handled the same way
+              in both 31- and 64-bit code.  */
+
+           if (reload_in_progress || reload_completed)
+             df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx);
+           new_rtx = gen_const_mem (Pmode, new_rtx);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new_rtx);
+         }
+       else if (TARGET_CPU_ZARCH)
+         {
+           /* If the GOT offset might be >= 4k, we determine the position
+              of the GOT entry via a PC-relative LARL.  */
 
-static rtx
-s390_dump_pool (pool)
-     struct constant_pool *pool;
-{
-  struct constant *c;
-  rtx insn;
-  int i;
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new_rtx);
 
-  /* Select location to put literal pool.  */
-  if (TARGET_64BIT)
-    insn = get_last_insn ();
-  else
-    insn = pool->last_insn? pool->last_insn : get_last_insn ();
+           new_rtx = gen_const_mem (Pmode, temp);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new_rtx);
+         }
+       else if (flag_pic)
+         {
+           /* If the GOT offset might be >= 4k, we have to load it
+              from the literal pool.  */
 
-  /* Pool start insn switches to proper section 
-     and guarantees necessary alignment.  */
-  if (TARGET_64BIT)
-    insn = emit_insn_after (gen_pool_start_64 (), insn);
-  else
-    insn = emit_insn_after (gen_pool_start_31 (), insn);
-  INSN_ADDRESSES_NEW (insn, -1);
+           if (reload_in_progress || reload_completed)
+             df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
-  insn = emit_label_after (pool->label, insn);
-  INSN_ADDRESSES_NEW (insn, -1);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = force_const_mem (Pmode, new_rtx);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new_rtx);
 
-  /* Dump constants in descending alignment requirement order,
-     ensuring proper alignment for every constant.  */
-  for (i = 0; i < NR_C_MODES; i++)
-    for (c = pool->constants[i]; c; c = c->next)
-      {
-       insn = emit_label_after (c->label, insn);
-       INSN_ADDRESSES_NEW (insn, -1);
-       insn = emit_insn_after (gen_consttable[i] (c->value), insn);
-       INSN_ADDRESSES_NEW (insn, -1);
-      }
+            new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+           new_rtx = gen_const_mem (Pmode, new_rtx);
 
-  /* Pool end insn switches back to previous section 
-     and guarantees necessary alignment.  */
-  if (TARGET_64BIT)
-    insn = emit_insn_after (gen_pool_end_64 (), insn);
-  else
-    insn = emit_insn_after (gen_pool_end_31 (), insn);
-  INSN_ADDRESSES_NEW (insn, -1);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD);
+           temp = gen_reg_rtx (Pmode);
+           emit_insn (gen_rtx_SET (Pmode, temp, new_rtx));
+         }
+       else
+         {
+           /* In position-dependent code, load the absolute address of
+              the GOT entry from the literal pool.  */
+
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = force_const_mem (Pmode, new_rtx);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new_rtx);
+
+           new_rtx = temp;
+           new_rtx = gen_const_mem (Pmode, new_rtx);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD);
+           temp = gen_reg_rtx (Pmode);
+           emit_insn (gen_rtx_SET (Pmode, temp, new_rtx));
+         }
 
-  insn = emit_barrier_after (insn);
-  INSN_ADDRESSES_NEW (insn, -1);
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
+         }
+       break;
 
-  return insn;
-}
+      case TLS_MODEL_LOCAL_EXEC:
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+        temp = gen_reg_rtx (Pmode);
+       emit_move_insn (temp, new_rtx);
 
-/* Free all memory used by POOL.  */
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
+         }
+       break;
 
-static void
-s390_free_pool (pool)
-     struct constant_pool *pool;
-{
-  int i;
+      default:
+       gcc_unreachable ();
+      }
 
-  for (i = 0; i < NR_C_MODES; i++)
+  else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC)
     {
-      struct constant *c = pool->constants[i];
-      while (c != NULL)
+      switch (XINT (XEXP (addr, 0), 1))
        {
-         struct constant *next = c->next;
-         free (c);
-         c = next;
+       case UNSPEC_INDNTPOFF:
+         gcc_assert (TARGET_CPU_ZARCH);
+         new_rtx = addr;
+         break;
+
+       default:
+         gcc_unreachable ();
        }
     }
 
-  free (pool);
-} 
+  else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
+          && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
+    {
+      new_rtx = XEXP (XEXP (addr, 0), 0);
+      if (GET_CODE (new_rtx) != SYMBOL_REF)
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
 
-/* Used in s390.md for branch length calculation.  */
-int s390_pool_overflow = 0;
+      new_rtx = legitimize_tls_address (new_rtx, reg);
+      new_rtx = plus_constant (new_rtx, INTVAL (XEXP (XEXP (addr, 0), 1)));
+      new_rtx = force_operand (new_rtx, 0);
+    }
 
-/* Chunkify the literal pool if required.  */
+  else
+    gcc_unreachable ();  /* for now ... */
 
-#define S390_POOL_CHUNK_MIN    0xc00
-#define S390_POOL_CHUNK_MAX    0xe00
+  return new_rtx;
+}
+
+/* Emit insns making the address in operands[1] valid for a standard
+   move to operands[0].  operands[1] is replaced by an address which
+   should be used instead of the former RTX to emit the move
+   pattern.  */
 
-static void 
-s390_chunkify_pool (void)
+void
+emit_symbolic_move (rtx *operands)
 {
-  rtx base_reg = gen_rtx_REG (Pmode, 
-                             TARGET_64BIT? BASE_REGISTER : RETURN_REGNUM);
+  rtx temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
 
-  struct constant_pool *curr_pool = NULL, *pool_list = NULL;
-  int extra_size = 0;
-  bitmap far_labels;
-  rtx insn;
+  if (GET_CODE (operands[0]) == MEM)
+    operands[1] = force_reg (Pmode, operands[1]);
+  else if (TLS_SYMBOLIC_CONST (operands[1]))
+    operands[1] = legitimize_tls_address (operands[1], temp);
+  else if (flag_pic)
+    operands[1] = legitimize_pic_address (operands[1], temp);
+}
+
+/* Try machine-dependent ways of modifying an illegitimate address X
+   to be legitimate.  If we find one, return the new, valid address.
 
-  /* Do we need to chunkify the literal pool?  */
+   OLDX is the address as it was before break_out_memory_refs was called.
+   In some cases it is useful to look at this to decide what needs to be done.
 
-  if (get_pool_size () < S390_POOL_CHUNK_MAX)
-    return;
+   MODE is the mode of the operand pointed to by X.
 
-  /* Scan all insns and move literals to pool chunks.
-     Replace all occurrances of literal pool references
-     by explicit references to pool chunk entries.  */
+   When -fpic is used, special handling is needed for symbolic references.
+   See comments by legitimize_pic_address for details.  */
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+rtx
+legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+                   enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  rtx constant_term = const0_rtx;
+
+  if (TLS_SYMBOLIC_CONST (x))
     {
-      if (GET_CODE (insn) == INSN)
-       {
-         rtx addr, pool_ref = NULL_RTX;
-         find_constant_pool_ref (PATTERN (insn), &pool_ref);
-         if (pool_ref)
-           {
-             if (!curr_pool)
-               curr_pool = s390_start_pool (&pool_list, insn);
+      x = legitimize_tls_address (x, 0);
 
-             addr = s390_add_pool (curr_pool, get_pool_constant (pool_ref), 
-                                              get_pool_mode (pool_ref));
+      if (legitimate_address_p (mode, x, FALSE))
+       return x;
+    }
+  else if (GET_CODE (x) == PLUS
+          && (TLS_SYMBOLIC_CONST (XEXP (x, 0)) 
+              || TLS_SYMBOLIC_CONST (XEXP (x, 1))))
+    {
+      return x;
+    }
+  else if (flag_pic)
+    {
+      if (SYMBOLIC_CONST (x)
+          || (GET_CODE (x) == PLUS
+              && (SYMBOLIC_CONST (XEXP (x, 0))
+                  || SYMBOLIC_CONST (XEXP (x, 1)))))
+         x = legitimize_pic_address (x, 0);
 
-             addr = gen_rtx_PLUS (Pmode, base_reg, addr);
-             replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
-             INSN_CODE (insn) = -1;
-           }
-       }
+      if (legitimate_address_p (mode, x, FALSE))
+       return x;
+    }
 
-      if (!curr_pool 
-         || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
-          || INSN_ADDRESSES (INSN_UID (insn)) == -1)
-       continue;
+  x = eliminate_constant_term (x, &constant_term);
 
-      if (TARGET_64BIT)
-       {
-         if (curr_pool->size < S390_POOL_CHUNK_MAX)
-           continue;
+  /* Optimize loading of large displacements by splitting them
+     into the multiple of 4K and the rest; this allows the
+     former to be CSE'd if possible.
 
-         s390_end_pool (curr_pool, insn);
-         curr_pool = NULL;
-       }
-      else
-       {
-          int chunk_size = INSN_ADDRESSES (INSN_UID (insn))
-                          - INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
-                        + extra_size;
+     Don't do this if the displacement is added to a register
+     pointing into the stack frame, as the offsets will
+     change later anyway.  */
 
-         /* We will later have to insert base register reload insns.
-            Those will have an effect on code size, which we need to
-            consider here.  This calculation makes rather pessimistic
-            worst-case assumptions.  */
-         if (GET_CODE (insn) == CODE_LABEL
-             || GET_CODE (insn) == JUMP_INSN)
-           extra_size += 6;
-         else if (GET_CODE (insn) == CALL_INSN)
-           extra_size += 4;
+  if (GET_CODE (constant_term) == CONST_INT
+      && !TARGET_LONG_DISPLACEMENT
+      && !DISP_IN_RANGE (INTVAL (constant_term))
+      && !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x))))
+    {
+      HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff;
+      HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower;
 
-         if (chunk_size < S390_POOL_CHUNK_MIN
-             && curr_pool->size < S390_POOL_CHUNK_MIN)
-           continue;
+      rtx temp = gen_reg_rtx (Pmode);
+      rtx val  = force_operand (GEN_INT (upper), temp);
+      if (val != temp)
+       emit_move_insn (temp, val);
 
-         /* Pool chunks can only be inserted after BARRIERs ...  */
-         if (GET_CODE (insn) == BARRIER)
-           {
-             s390_end_pool (curr_pool, insn);
-             curr_pool = NULL;
-             extra_size = 0;
-           }
+      x = gen_rtx_PLUS (Pmode, x, temp);
+      constant_term = GEN_INT (lower);
+    }
 
-         /* ... so if we don't find one in time, create one.  */
-          else if ((chunk_size > S390_POOL_CHUNK_MAX
-                  || curr_pool->size > S390_POOL_CHUNK_MAX)
-                 && (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN))
-           {
-             int addr = INSN_ADDRESSES (INSN_UID (insn));
-              rtx label, jump, barrier;
+  if (GET_CODE (x) == PLUS)
+    {
+      if (GET_CODE (XEXP (x, 0)) == REG)
+       {
+         rtx temp = gen_reg_rtx (Pmode);
+         rtx val  = force_operand (XEXP (x, 1), temp);
+         if (val != temp)
+           emit_move_insn (temp, val);
 
-             label = gen_label_rtx ();
-             jump = emit_jump_insn_after (gen_jump (label), insn);
-             barrier = emit_barrier_after (jump);
-             insn = emit_label_after (label, barrier);
-             JUMP_LABEL (jump) = label;
-             LABEL_NUSES (label) = 1;
+         x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp);
+       }
 
-             INSN_ADDRESSES_NEW (jump, addr+1);
-             INSN_ADDRESSES_NEW (barrier, addr+1);
-             INSN_ADDRESSES_NEW (insn, -1);
+      else if (GET_CODE (XEXP (x, 1)) == REG)
+       {
+         rtx temp = gen_reg_rtx (Pmode);
+         rtx val  = force_operand (XEXP (x, 0), temp);
+         if (val != temp)
+           emit_move_insn (temp, val);
 
-             s390_end_pool (curr_pool, barrier);
-             curr_pool = NULL;
-             extra_size = 0;
-           }
+         x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1));
        }
     }
 
-  /* Dump out all literal pools.  */
+  if (constant_term != const0_rtx)
+    x = gen_rtx_PLUS (Pmode, x, constant_term);
 
-  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    s390_dump_pool (curr_pool);
+  return x;
+}
 
+/* Try a machine-dependent way of reloading an illegitimate address AD
+   operand.  If we find one, push the reload and and return the new address.
 
-  /* Find all labels that are branched into 
-     from an insn belonging to a different chunk.  */
+   MODE is the mode of the enclosing MEM.  OPNUM is the operand number
+   and TYPE is the reload type of the current reload.  */
 
-  far_labels = BITMAP_XMALLOC ();
+rtx 
+legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
+                          int opnum, int type)
+{
+  if (!optimize || TARGET_LONG_DISPLACEMENT)
+    return NULL_RTX;
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  if (GET_CODE (ad) == PLUS)
     {
-      /* Labels marked with LABEL_PRESERVE_P can be target
-        of non-local jumps, so we have to mark them.
-        The same holds for named labels.
+      rtx tem = simplify_binary_operation (PLUS, Pmode,
+                                          XEXP (ad, 0), XEXP (ad, 1));
+      if (tem)
+       ad = tem;
+    }
 
-        Don't do that, however, if it is the label before
-        a jump table.  */
+  if (GET_CODE (ad) == PLUS
+      && GET_CODE (XEXP (ad, 0)) == REG
+      && GET_CODE (XEXP (ad, 1)) == CONST_INT
+      && !DISP_IN_RANGE (INTVAL (XEXP (ad, 1))))
+    {
+      HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff;
+      HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower;
+      rtx cst, tem, new_rtx;
 
-      if (GET_CODE (insn) == CODE_LABEL 
-         && (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
-       {
-         rtx vec_insn = next_real_insn (insn);
-         rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? 
-                       PATTERN (vec_insn) : NULL_RTX;
-         if (!vec_pat
-             || !(GET_CODE (vec_pat) == ADDR_VEC
-                  || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
-           bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn));
-       }
+      cst = GEN_INT (upper);
+      if (!legitimate_reload_constant_p (cst))
+       cst = force_const_mem (Pmode, cst);
 
-      /* If we have a direct jump (conditional or unconditional)
-        or a casesi jump, check all potential targets.  */
-      else if (GET_CODE (insn) == JUMP_INSN) 
-       {
-          rtx pat = PATTERN (insn);
-          if (GET_CODE (pat) == SET) 
-            {
-             rtx label = 0;
-
-              if (GET_CODE (SET_SRC (pat)) == LABEL_REF) 
-               {
-                 label = XEXP (SET_SRC (pat), 0);
-               } 
-              else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) 
-               {
-                 if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) 
-                   label = XEXP (XEXP (SET_SRC (pat), 1), 0);
-                 else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) 
-                   label = XEXP (XEXP (SET_SRC (pat), 2), 0);
-               }
+      tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst);
+      new_rtx = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower));
 
-             if (label)
-               {
-                 if (s390_find_pool (pool_list, label) 
-                     != s390_find_pool (pool_list, insn))
-                   bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
-               }
-            } 
-         else if (GET_CODE (pat) == PARALLEL
-                  && XVECLEN (pat, 0) == 2
-                  && GET_CODE (XVECEXP (pat, 0, 0)) == SET
-                  && GET_CODE (XVECEXP (pat, 0, 1)) == USE
-                  && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF)
-           {
-             /* Find the jump table used by this casesi jump.  */
-             rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
-             rtx vec_insn = next_real_insn (vec_label);
-             rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? 
-                           PATTERN (vec_insn) : NULL_RTX;
-             if (vec_pat
-                 && (GET_CODE (vec_pat) == ADDR_VEC
-                     || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
-               {
-                 int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
+      push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0,
+                  BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, 
+                  opnum, (enum reload_type) type);
+      return new_rtx;
+    }
 
-                 for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
-                   {
-                     rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
+  return NULL_RTX;
+}
 
-                     if (s390_find_pool (pool_list, label) 
-                         != s390_find_pool (pool_list, insn))
-                       bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
-                   }
-               }
-           }
-        }
+/* Emit code to move LEN bytes from DST to SRC.  */
+
+void
+s390_expand_movmem (rtx dst, rtx src, rtx len)
+{
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
+    {
+      if (INTVAL (len) > 0)
+        emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1)));
     }
 
-  /* Insert base register reload insns before every pool.  */
+  else if (TARGET_MVCLE)
+    {
+      emit_insn (gen_movmem_long (dst, src, convert_to_mode (Pmode, len, 1)));
+    }
 
-  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    if (TARGET_64BIT)
-      {
-       rtx pool_ref = gen_rtx_LABEL_REF (Pmode, curr_pool->label);
-       rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
-       rtx insn = curr_pool->first_insn;
-        INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
-      }
-    else
-      {
-       rtx new_insn = gen_reload_base (base_reg, curr_pool->label);
-       rtx insn = curr_pool->first_insn;
-        INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
-      }
+  else
+    {
+      rtx dst_addr, src_addr, count, blocks, temp;
+      rtx loop_start_label = gen_label_rtx ();
+      rtx loop_end_label = gen_label_rtx ();
+      rtx end_label = gen_label_rtx ();
+      enum machine_mode mode;
+
+      mode = GET_MODE (len);
+      if (mode == VOIDmode)
+        mode = Pmode;
+
+      dst_addr = gen_reg_rtx (Pmode);
+      src_addr = gen_reg_rtx (Pmode);
+      count = gen_reg_rtx (mode);
+      blocks = gen_reg_rtx (mode);
+
+      convert_move (count, len, 1);
+      emit_cmp_and_jump_insns (count, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, end_label);
+
+      emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
+      emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX));
+      dst = change_address (dst, VOIDmode, dst_addr);
+      src = change_address (src, VOIDmode, src_addr);
+
+      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      if (temp != count)
+        emit_move_insn (count, temp);
+
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
+
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_label (loop_start_label);
+
+      emit_insn (gen_movmem_short (dst, src, GEN_INT (255)));
+      s390_load_address (dst_addr,
+                        gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
+      s390_load_address (src_addr,
+                        gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256)));
+
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
+
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_jump (loop_start_label);
+      emit_label (loop_end_label);
+
+      emit_insn (gen_movmem_short (dst, src,
+                                  convert_to_mode (Pmode, count, 1)));
+      emit_label (end_label);
+    }
+}
 
-  /* Insert base register reload insns at every far label.  */
+/* Emit code to set LEN bytes at DST to VAL.
+   Make use of clrmem if VAL is zero.  */
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    if (GET_CODE (insn) == CODE_LABEL 
-        && bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
-      {
-       struct constant_pool *pool = s390_find_pool (pool_list, insn);
-       if (pool)
-         {
-           if (TARGET_64BIT)
-             {
-               rtx pool_ref = gen_rtx_LABEL_REF (Pmode, pool->label);
-               rtx new_insn = gen_rtx_SET (Pmode, base_reg, pool_ref);
-               INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-             }
-           else
-             {
-               rtx new_insn = gen_reload_base (base_reg, pool->label);
-               INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-             }
-         }
-      }
+void
+s390_expand_setmem (rtx dst, rtx len, rtx val)
+{
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0)
+    return;
 
-  /* Insert base register reload insns after every call if necessary.  */
+  gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
+  
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257)
+    {
+      if (val == const0_rtx && INTVAL (len) <= 256)
+        emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
+      else
+       {
+         /* Initialize memory by storing the first byte.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         
+         if (INTVAL (len) > 1)
+           {
+             /* Initiate 1 byte overlap move.
+                The first byte of DST is propagated through DSTP1.
+                Prepare a movmem for:  DST+1 = DST (length = LEN - 1).
+                DST is set to size 1 so the rest of the memory location
+                does not count as source operand.  */
+             rtx dstp1 = adjust_address (dst, VOIDmode, 1);
+             set_mem_size (dst, const1_rtx);
+
+             emit_insn (gen_movmem_short (dstp1, dst, 
+                                          GEN_INT (INTVAL (len) - 2)));
+           }
+       }
+    }
 
-  if (REGNO (base_reg) == RETURN_REGNUM)
-    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) == CALL_INSN)
-        {
-          struct constant_pool *pool = s390_find_pool (pool_list, insn);
-          if (pool)
-            {
-              rtx new_insn = gen_reload_base2 (base_reg, pool->label);
-              INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
-            }
-         }
+  else if (TARGET_MVCLE)
+    {
+      val = force_not_mem (convert_modes (Pmode, QImode, val, 1));
+      emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val));
+    }
 
+  else
+    {
+      rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX;
+      rtx loop_start_label = gen_label_rtx ();
+      rtx loop_end_label = gen_label_rtx ();
+      rtx end_label = gen_label_rtx ();
+      enum machine_mode mode;
+
+      mode = GET_MODE (len);
+      if (mode == VOIDmode)
+        mode = Pmode;
+
+      dst_addr = gen_reg_rtx (Pmode);
+      src_addr = gen_reg_rtx (Pmode);
+      count = gen_reg_rtx (mode);
+      blocks = gen_reg_rtx (mode);
+
+      convert_move (count, len, 1);
+      emit_cmp_and_jump_insns (count, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, end_label);
+
+      emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
+      dst = change_address (dst, VOIDmode, dst_addr);
+
+      if (val == const0_rtx)
+        temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      else
+       {
+         dstp1 = adjust_address (dst, VOIDmode, 1);
+         set_mem_size (dst, const1_rtx);
 
-  /* Recompute insn addresses.  */
+         /* Initialize memory by storing the first byte.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         
+         /* If count is 1 we are done.  */
+         emit_cmp_and_jump_insns (count, const1_rtx,
+                                  EQ, NULL_RTX, mode, 1, end_label);
 
-  s390_pool_overflow = 1;
-  init_insn_lengths ();
-  shorten_branches (get_insns ());
-  s390_pool_overflow = 0;
+         temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, 0);
+       }
+      if (temp != count)
+        emit_move_insn (count, temp);
 
-  /* Insert base register reload insns after far branches.  */
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
 
-  if (!TARGET_64BIT)
-    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-      if (GET_CODE (insn) == JUMP_INSN
-         && GET_CODE (PATTERN (insn)) == SET
-         && get_attr_length (insn) >= 12)
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_label (loop_start_label);
+
+      if (val == const0_rtx)
+       emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
+      else
+       emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255)));
+      s390_load_address (dst_addr,
+                        gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
+
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
+
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_jump (loop_start_label);
+      emit_label (loop_end_label);
+
+      if (val == const0_rtx)
+        emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
+      else
+        emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1)));
+      emit_label (end_label);
+    }
+}
+
+/* Emit code to compare LEN bytes at OP0 with those at OP1,
+   and return the result in TARGET.  */
+
+void
+s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
+{
+  rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM);
+  rtx tmp;
+
+  /* As the result of CMPINT is inverted compared to what we need,
+     we have to swap the operands.  */
+  tmp = op0; op0 = op1; op1 = tmp;
+
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
+    {
+      if (INTVAL (len) > 0)
+        {
+          emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1)));
+          emit_insn (gen_cmpint (target, ccreg));
+        }
+      else
+        emit_move_insn (target, const0_rtx);
+    }
+  else if (TARGET_MVCLE)
+    {
+      emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1)));
+      emit_insn (gen_cmpint (target, ccreg));
+    }
+  else
+    {
+      rtx addr0, addr1, count, blocks, temp;
+      rtx loop_start_label = gen_label_rtx ();
+      rtx loop_end_label = gen_label_rtx ();
+      rtx end_label = gen_label_rtx ();
+      enum machine_mode mode;
+
+      mode = GET_MODE (len);
+      if (mode == VOIDmode)
+        mode = Pmode;
+
+      addr0 = gen_reg_rtx (Pmode);
+      addr1 = gen_reg_rtx (Pmode);
+      count = gen_reg_rtx (mode);
+      blocks = gen_reg_rtx (mode);
+
+      convert_move (count, len, 1);
+      emit_cmp_and_jump_insns (count, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, end_label);
+
+      emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX));
+      emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX));
+      op0 = change_address (op0, VOIDmode, addr0);
+      op1 = change_address (op1, VOIDmode, addr1);
+
+      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      if (temp != count)
+        emit_move_insn (count, temp);
+
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
+
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_label (loop_start_label);
+
+      emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
+      temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
+      temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
+                       gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx);
+      temp = gen_rtx_SET (VOIDmode, pc_rtx, temp);
+      emit_jump_insn (temp);
+
+      s390_load_address (addr0,
+                        gen_rtx_PLUS (Pmode, addr0, GEN_INT (256)));
+      s390_load_address (addr1,
+                        gen_rtx_PLUS (Pmode, addr1, GEN_INT (256)));
+
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      if (temp != blocks)
+        emit_move_insn (blocks, temp);
+
+      emit_cmp_and_jump_insns (blocks, const0_rtx,
+                              EQ, NULL_RTX, mode, 1, loop_end_label);
+
+      emit_jump (loop_start_label);
+      emit_label (loop_end_label);
+
+      emit_insn (gen_cmpmem_short (op0, op1,
+                                  convert_to_mode (Pmode, count, 1)));
+      emit_label (end_label);
+
+      emit_insn (gen_cmpint (target, ccreg));
+    }
+}
+
+
+/* Expand conditional increment or decrement using alc/slb instructions.
+   Should generate code setting DST to either SRC or SRC + INCREMENT,
+   depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1.
+   Returns true if successful, false otherwise.
+
+   That makes it possible to implement some if-constructs without jumps e.g.:
+   (borrow = CC0 | CC1 and carry = CC2 | CC3)
+   unsigned int a, b, c;
+   if (a < b)  c++; -> CCU  b > a  -> CC2;    c += carry;
+   if (a < b)  c--; -> CCL3 a - b  -> borrow; c -= borrow;
+   if (a <= b) c++; -> CCL3 b - a  -> borrow; c += carry;
+   if (a <= b) c--; -> CCU  a <= b -> borrow; c -= borrow;
+
+   Checks for EQ and NE with a nonzero value need an additional xor e.g.:
+   if (a == b) c++; -> CCL3 a ^= b; 0 - a  -> borrow;    c += carry;
+   if (a == b) c--; -> CCU  a ^= b; a <= 0 -> CC0 | CC1; c -= borrow;
+   if (a != b) c++; -> CCU  a ^= b; a > 0  -> CC2;       c += carry;
+   if (a != b) c--; -> CCL3 a ^= b; 0 - a  -> borrow;    c -= borrow; */
+
+bool
+s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
+                  rtx dst, rtx src, rtx increment)
+{
+  enum machine_mode cmp_mode;
+  enum machine_mode cc_mode;
+  rtx op_res;
+  rtx insn;
+  rtvec p;
+  int ret;
+
+  if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode)
+      && (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode))
+    cmp_mode = SImode;
+  else if ((GET_MODE (cmp_op0) == DImode || GET_MODE (cmp_op0) == VOIDmode)
+          && (GET_MODE (cmp_op1) == DImode || GET_MODE (cmp_op1) == VOIDmode))
+    cmp_mode = DImode;
+  else
+    return false;
+
+  /* Try ADD LOGICAL WITH CARRY.  */
+  if (increment == const1_rtx)
+    {
+      /* Determine CC mode to use.  */
+      if (cmp_code == EQ || cmp_code == NE)
        {
-         struct constant_pool *pool = s390_find_pool (pool_list, insn);
-         if (pool)
+         if (cmp_op1 != const0_rtx)
            {
-             rtx new_insn = gen_reload_base (base_reg, pool->label);
-             INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
+             cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
+                                            NULL_RTX, 0, OPTAB_WIDEN);
+             cmp_op1 = const0_rtx;
            }
+
+         cmp_code = cmp_code == EQ ? LEU : GTU;
        }
 
+      if (cmp_code == LTU || cmp_code == LEU)
+       {
+         rtx tem = cmp_op0;
+         cmp_op0 = cmp_op1;
+         cmp_op1 = tem;
+         cmp_code = swap_condition (cmp_code);
+       }
 
-  /* Free all memory.  */
+      switch (cmp_code)
+       {
+         case GTU:
+           cc_mode = CCUmode;
+           break;
 
-  while (pool_list)
+         case GEU:
+           cc_mode = CCL3mode;
+           break;
+
+         default:
+           return false;
+       }
+
+      /* Emit comparison instruction pattern. */
+      if (!register_operand (cmp_op0, cmp_mode))
+       cmp_op0 = force_reg (cmp_mode, cmp_op0);
+
+      insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
+                         gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
+      /* We use insn_invalid_p here to add clobbers if required.  */
+      ret = insn_invalid_p (emit_insn (insn));
+      gcc_assert (!ret);
+
+      /* Emit ALC instruction pattern.  */
+      op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
+                              gen_rtx_REG (cc_mode, CC_REGNUM),
+                              const0_rtx);
+
+      if (src != const0_rtx)
+       {
+         if (!register_operand (src, GET_MODE (dst)))
+           src = force_reg (GET_MODE (dst), src);
+
+         op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, src);
+         op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, const0_rtx);
+       }
+
+      p = rtvec_alloc (2);
+      RTVEC_ELT (p, 0) = 
+        gen_rtx_SET (VOIDmode, dst, op_res);
+      RTVEC_ELT (p, 1) = 
+       gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+
+      return true;
+    }
+
+  /* Try SUBTRACT LOGICAL WITH BORROW.  */
+  if (increment == constm1_rtx)
     {
-      struct constant_pool *next = pool_list->next;
-      s390_free_pool (pool_list);
-      pool_list = next;
+      /* Determine CC mode to use.  */
+      if (cmp_code == EQ || cmp_code == NE)
+       {
+         if (cmp_op1 != const0_rtx)
+           {
+             cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1,
+                                            NULL_RTX, 0, OPTAB_WIDEN);
+             cmp_op1 = const0_rtx;
+           }
+
+         cmp_code = cmp_code == EQ ? LEU : GTU;
+       }
+
+      if (cmp_code == GTU || cmp_code == GEU)
+       {
+         rtx tem = cmp_op0;
+         cmp_op0 = cmp_op1;
+         cmp_op1 = tem;
+         cmp_code = swap_condition (cmp_code);
+       }
+
+      switch (cmp_code)
+       {
+         case LEU:
+           cc_mode = CCUmode;
+           break;
+
+         case LTU:
+           cc_mode = CCL3mode;
+           break;
+
+         default:
+           return false;
+       }
+
+      /* Emit comparison instruction pattern. */
+      if (!register_operand (cmp_op0, cmp_mode))
+       cmp_op0 = force_reg (cmp_mode, cmp_op0);
+
+      insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
+                         gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
+      /* We use insn_invalid_p here to add clobbers if required.  */
+      ret = insn_invalid_p (emit_insn (insn));
+      gcc_assert (!ret);
+
+      /* Emit SLB instruction pattern.  */
+      if (!register_operand (src, GET_MODE (dst)))
+       src = force_reg (GET_MODE (dst), src);
+
+      op_res = gen_rtx_MINUS (GET_MODE (dst), 
+                             gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx), 
+                             gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), 
+                                             gen_rtx_REG (cc_mode, CC_REGNUM), 
+                                             const0_rtx));
+      p = rtvec_alloc (2);
+      RTVEC_ELT (p, 0) = 
+        gen_rtx_SET (VOIDmode, dst, op_res);
+      RTVEC_ELT (p, 1) = 
+       gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
+
+      return true;
     }
 
-  BITMAP_XFREE (far_labels);
+  return false;
 }
 
+/* Expand code for the insv template. Return true if successful.  */
 
-/* Index of constant pool chunk that is currently being processed.
-   Set to -1 before function output has started.  */
-int s390_pool_count = -1;
+bool
+s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
+{
+  int bitsize = INTVAL (op1);
+  int bitpos = INTVAL (op2);
 
-/* Number of elements of current constant pool.  */
-int s390_nr_constants;
+  /* On z10 we can use the risbg instruction to implement insv.  */
+  if (TARGET_Z10
+      && ((GET_MODE (dest) == DImode && GET_MODE (src) == DImode)
+         || (GET_MODE (dest) == SImode && GET_MODE (src) == SImode)))
+    {
+      rtx op;
+      rtx clobber;
 
-/* Output main constant pool to stdio stream FILE.  */ 
+      op = gen_rtx_SET (GET_MODE(src),
+                       gen_rtx_ZERO_EXTRACT (GET_MODE (dest), dest, op1, op2),
+                       src);
+      clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clobber)));
 
-void
-s390_output_constant_pool (file)
-     FILE *file;
-{
-  /* Output constant pool.  */
-  if (s390_nr_constants)
+      return true;
+    }
+
+  /* We need byte alignment.  */
+  if (bitsize % BITS_PER_UNIT)
+    return false;
+
+  if (bitpos == 0
+      && memory_operand (dest, VOIDmode)
+      && (register_operand (src, word_mode)
+         || const_int_operand (src, VOIDmode)))
     {
-      if (TARGET_64BIT)
+      /* Emit standard pattern if possible.  */
+      enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT);
+      if (GET_MODE_BITSIZE (mode) == bitsize)
+       emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src));
+
+      /* (set (ze (mem)) (const_int)).  */
+      else if (const_int_operand (src, VOIDmode))
+       {
+         int size = bitsize / BITS_PER_UNIT;
+         rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode,
+                                       GET_MODE_SIZE (word_mode) - size);
+
+         dest = adjust_address (dest, BLKmode, 0);
+         set_mem_size (dest, GEN_INT (size));
+         s390_expand_movmem (dest, src_mem, GEN_INT (size));
+       }
+         
+      /* (set (ze (mem)) (reg)).  */
+      else if (register_operand (src, word_mode))
        {
-         fprintf (file, "\tlarl\t%s,.LT%X\n", reg_names[BASE_REGISTER],
-                  s390_function_count);
-         readonly_data_section ();
-         ASM_OUTPUT_ALIGN (file, 3);
+         if (bitsize <= GET_MODE_BITSIZE (SImode))
+           emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1,
+                                                 const0_rtx), src);
+         else
+           {
+             /* Emit st,stcmh sequence.  */
+             int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode);
+             int size = stcmh_width / BITS_PER_UNIT;
+
+             emit_move_insn (adjust_address (dest, SImode, size), 
+                             gen_lowpart (SImode, src));
+             set_mem_size (dest, GEN_INT (size));
+             emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT
+                                                   (stcmh_width), const0_rtx),
+                             gen_rtx_LSHIFTRT (word_mode, src, GEN_INT
+                                               (GET_MODE_BITSIZE (SImode))));
+           }
        }
       else
+       return false;
+
+      return true;
+    }
+
+  /* (set (ze (reg)) (const_int)).  */
+  if (TARGET_ZARCH
+      && register_operand (dest, word_mode) 
+      && (bitpos % 16) == 0
+      && (bitsize % 16) == 0
+      && const_int_operand (src, VOIDmode))
+    {
+      HOST_WIDE_INT val = INTVAL (src);
+      int regpos = bitpos + bitsize;
+
+      while (regpos > bitpos)
        {
-         fprintf (file, "\tbras\t%s,.LTN%X\n", reg_names[BASE_REGISTER],
-                  s390_function_count);
+         enum machine_mode putmode;
+         int putsize;
+
+         if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32))
+           putmode = SImode;
+         else
+           putmode = HImode;
+
+         putsize = GET_MODE_BITSIZE (putmode);
+         regpos -= putsize;
+         emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, 
+                                               GEN_INT (putsize),
+                                               GEN_INT (regpos)), 
+                         gen_int_mode (val, putmode));
+         val >>= putsize;
        }
-      fprintf (file, ".LT%X:\n", s390_function_count);
+      gcc_assert (regpos == bitpos);
+      return true;
+    }
+
+  return false;
+}
 
-      s390_pool_count = 0;
-      output_constant_pool (current_function_name, current_function_decl);
-      s390_pool_count = -1;
+/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a
+   register that holds VAL of mode MODE shifted by COUNT bits.  */
 
-      if (TARGET_64BIT)
-       function_section (current_function_decl);
+static inline rtx
+s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
+{
+  val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
+                            NULL_RTX, 1, OPTAB_DIRECT);
+  return expand_simple_binop (SImode, ASHIFT, val, count, 
+                             NULL_RTX, 1, OPTAB_DIRECT);
+}
+
+/* Structure to hold the initial parameters for a compare_and_swap operation
+   in HImode and QImode.  */ 
+
+struct alignment_context
+{
+  rtx memsi;     /* SI aligned memory location.  */ 
+  rtx shift;     /* Bit offset with regard to lsb.  */
+  rtx modemask;          /* Mask of the HQImode shifted by SHIFT bits.  */
+  rtx modemaski;  /* ~modemask */
+  bool aligned;          /* True if memory is aligned, false else.  */
+};
+
+/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize
+   structure AC for transparent simplifying, if the memory alignment is known
+   to be at least 32bit.  MEM is the memory location for the actual operation
+   and MODE its mode.  */
+
+static void
+init_alignment_context (struct alignment_context *ac, rtx mem,
+                       enum machine_mode mode)
+{
+  ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
+  ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
+
+  if (ac->aligned)
+    ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned.  */
+  else
+    {
+      /* Alignment is unknown.  */
+      rtx byteoffset, 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));
+
+      /* Calculate shiftcount.  */
+      byteoffset = expand_simple_binop (Pmode, AND, addr,
+                                       GEN_INT (GET_MODE_SIZE (SImode) - 1),
+                                       NULL_RTX, 1, OPTAB_DIRECT);
+      /* As we already have some offset, evaluate the remaining distance.  */
+      ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
+                                     NULL_RTX, 1, OPTAB_DIRECT);
+
+    }
+  /* 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);
+  /* Calculate masks.  */
+  ac->modemask = expand_simple_binop (SImode, ASHIFT, 
+                                    GEN_INT (GET_MODE_MASK (mode)), ac->shift,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+  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.
+   CMP is never in memory for compare_and_swap_cc because
+   expand_bool_compare_and_swap puts it into a register for later compare.  */
+
+void
+s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new_rtx)
+{
+  struct alignment_context ac;
+  rtx cmpv, newv, val, resv, cc;
+  rtx res = gen_reg_rtx (SImode);
+  rtx csloop = gen_label_rtx ();
+  rtx csend = gen_label_rtx ();
+
+  gcc_assert (register_operand (target, VOIDmode));
+  gcc_assert (MEM_P (mem));
+
+  init_alignment_context (&ac, mem, mode);
+
+  /* Shift the values to the correct bit positions.  */
+  if (!(ac.aligned && MEM_P (cmp)))
+    cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift);
+  if (!(ac.aligned && MEM_P (new_rtx)))
+    new_rtx = s390_expand_mask_and_shift (new_rtx, mode, ac.shift);
+
+  /* Load full word.  Subsequent loads are performed by CS.  */
+  val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski,
+                            NULL_RTX, 1, OPTAB_DIRECT);
+
+  /* Start CS loop.  */
+  emit_label (csloop);
+  /* val = "<mem>00..0<mem>" 
+   * cmp = "00..0<cmp>00..0"
+   * new = "00..0<new>00..0" 
+   */
+
+  /* Patch cmp and new with val at correct position.  */
+  if (ac.aligned && MEM_P (cmp))
+    {
+      cmpv = force_reg (SImode, val);
+      store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp);
+    }
+  else
+    cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
+                                                  NULL_RTX, 1, OPTAB_DIRECT));
+  if (ac.aligned && MEM_P (new_rtx))
+    {
+      newv = force_reg (SImode, val);
+      store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new_rtx);
+    }
+  else
+    newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new_rtx, val,
+                                                  NULL_RTX, 1, OPTAB_DIRECT));
+
+  /* Jump to end if we're done (likely?).  */
+  s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi,
+                                                    cmpv, newv));
+
+  /* Check for changes outside mode.  */
+  resv = expand_simple_binop (SImode, AND, res, ac.modemaski, 
+                             NULL_RTX, 1, OPTAB_DIRECT);
+  cc = s390_emit_compare (NE, resv, val); 
+  emit_move_insn (val, resv);
+  /* Loop internal if so.  */
+  s390_emit_jump (csloop, cc);
+
+  emit_label (csend);
+  
+  /* Return the correct part of the bitfield.  */
+  convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift, 
+                                            NULL_RTX, 1, OPTAB_DIRECT), 1);
+}
+
+/* Expand an atomic operation CODE of mode MODE.  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
+s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
+                   rtx target, rtx mem, rtx val, bool after)
+{
+  struct alignment_context ac;
+  rtx cmp;
+  rtx new_rtx = gen_reg_rtx (SImode);
+  rtx orig = gen_reg_rtx (SImode);
+  rtx csloop = gen_label_rtx ();
+
+  gcc_assert (!target || register_operand (target, VOIDmode));
+  gcc_assert (MEM_P (mem));
+
+  init_alignment_context (&ac, mem, mode);
+
+  /* Shift val to the correct bit positions.
+     Preserve "icm", but prevent "ex icm".  */
+  if (!(ac.aligned && code == SET && MEM_P (val)))
+    val = s390_expand_mask_and_shift (val, mode, ac.shift);
+
+  /* Further preparation insns.  */
+  if (code == PLUS || code == MINUS)
+    emit_move_insn (orig, val);
+  else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */
+    val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
+                              NULL_RTX, 1, OPTAB_DIRECT);
+
+  /* Load full word.  Subsequent loads are performed by CS.  */
+  cmp = force_reg (SImode, ac.memsi);
+
+  /* Start CS loop.  */
+  emit_label (csloop);
+  emit_move_insn (new_rtx, cmp);
+
+  /* Patch new with val at correct position.  */
+  switch (code)
+    {
+    case PLUS:
+    case MINUS:
+      val = expand_simple_binop (SImode, code, new_rtx, orig,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      val = expand_simple_binop (SImode, AND, val, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      /* FALLTHRU */
+    case SET: 
+      if (ac.aligned && MEM_P (val))
+       store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0, SImode, val);
       else
-        fprintf (file, ".LTN%X:\n", s390_function_count);
+       {
+         new_rtx = expand_simple_binop (SImode, AND, new_rtx, ac.modemaski,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+         new_rtx = expand_simple_binop (SImode, IOR, new_rtx, val,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+       }
+      break;
+    case AND:
+    case IOR:
+    case XOR:
+      new_rtx = expand_simple_binop (SImode, code, new_rtx, val,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+    case MULT: /* NAND */
+      new_rtx = expand_simple_binop (SImode, AND, new_rtx, val,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      new_rtx = expand_simple_binop (SImode, XOR, new_rtx, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
+                                                     ac.memsi, cmp, new_rtx));
+
+  /* Return the correct part of the bitfield.  */
+  if (target)
+    convert_move (target, expand_simple_binop (SImode, LSHIFTRT,
+                                              after ? new_rtx : cmp, ac.shift,
+                                              NULL_RTX, 1, OPTAB_DIRECT), 1);
+}
+
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
+   We need to emit DTP-relative relocations.  */
+
+static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
+
+static void
+s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  switch (size)
+    {
+    case 4:
+      fputs ("\t.long\t", file);
+      break;
+    case 8:
+      fputs ("\t.quad\t", file);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  output_addr_const (file, x);
+  fputs ("@DTPOFF", file);
+}
+
+#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
+/* Implement TARGET_MANGLE_TYPE.  */
+
+static const char *
+s390_mangle_type (const_tree type)
+{
+  if (TYPE_MAIN_VARIANT (type) == long_double_type_node
+      && TARGET_LONG_DOUBLE_128)
+    return "g";
+
+  /* For all other types, use normal C++ mangling.  */
+  return NULL;
+}
+#endif
+
+/* In the name of slightly smaller debug output, and to cater to
+   general assembler lossage, recognize various UNSPEC sequences
+   and turn them back into a direct symbol reference.  */
+
+static rtx
+s390_delegitimize_address (rtx orig_x)
+{
+  rtx x = orig_x, y;
+
+  if (GET_CODE (x) != MEM)
+    return orig_x;
+
+  x = XEXP (x, 0);
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 1)) == CONST
+      && GET_CODE (XEXP (x, 0)) == REG
+      && REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM)
+    {
+      y = XEXP (XEXP (x, 1), 0);
+      if (GET_CODE (y) == UNSPEC
+         && XINT (y, 1) == UNSPEC_GOT)
+       return XVECEXP (y, 0, 0);
+      return orig_x;
+    }
+
+  if (GET_CODE (x) == CONST)
+    {
+      y = XEXP (x, 0);
+      if (GET_CODE (y) == UNSPEC
+         && XINT (y, 1) == UNSPEC_GOTENT)
+       return XVECEXP (y, 0, 0);
+      return orig_x;
+    }
+
+  return orig_x;
+}
+
+/* Output operand OP to stdio stream FILE.
+   OP is an address (register + offset) which is not used to address data;
+   instead the rightmost bits are interpreted as the value.  */
+
+static void
+print_shift_count_operand (FILE *file, rtx op)
+{
+  HOST_WIDE_INT offset;
+  rtx base;
+
+  /* Extract base register and offset.  */
+  if (!s390_decompose_shift_count (op, &base, &offset))
+    gcc_unreachable ();
+
+  /* Sanity check.  */
+  if (base)
+    {
+      gcc_assert (GET_CODE (base) == REG);
+      gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS);
     }
+
+  /* Offsets are constricted to twelve bits.  */
+  fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1));
+  if (base)
+    fprintf (file, "(%s)", reg_names[REGNO (base)]);
 }
 
+/* See 'get_some_local_dynamic_name'.  */
+
+static int
+get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
+{
+  rtx x = *px;
+
+  if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+    {
+      x = get_pool_constant (x);
+      return for_each_rtx (&x, get_some_local_dynamic_name_1, 0);
+    }
+
+  if (GET_CODE (x) == SYMBOL_REF
+      && tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC)
+    {
+      cfun->machine->some_ld_name = XSTR (x, 0);
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Locate some local-dynamic symbol still in use by this function
+   so that we can print its name in local-dynamic base patterns.  */
+
+static const char *
+get_some_local_dynamic_name (void)
+{
+  rtx insn;
+
+  if (cfun->machine->some_ld_name)
+    return cfun->machine->some_ld_name;
+
+  for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+        && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
+      return cfun->machine->some_ld_name;
+
+  gcc_unreachable ();
+}
+
+/* Output machine-dependent UNSPECs occurring in address constant X
+   in assembler syntax to stdio stream FILE.  Returns true if the
+   constant X could be recognized, false otherwise.  */
+
+bool
+s390_output_addr_const_extra (FILE *file, rtx x)
+{
+  if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
+    switch (XINT (x, 1))
+      {
+      case UNSPEC_GOTENT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTENT");
+       return true;
+      case UNSPEC_GOT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOT");
+       return true;
+      case UNSPEC_GOTOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTOFF");
+       return true;
+      case UNSPEC_PLT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@PLT");
+       return true;
+      case UNSPEC_PLTOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@PLTOFF");
+       return true;
+      case UNSPEC_TLSGD:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@TLSGD");
+       return true;
+      case UNSPEC_TLSLDM:
+       assemble_name (file, get_some_local_dynamic_name ());
+       fprintf (file, "@TLSLDM");
+       return true;
+      case UNSPEC_DTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@DTPOFF");
+       return true;
+      case UNSPEC_NTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@NTPOFF");
+       return true;
+      case UNSPEC_GOTNTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTNTPOFF");
+       return true;
+      case UNSPEC_INDNTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@INDNTPOFF");
+       return true;
+      }
+
+  if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 2)
+    switch (XINT (x, 1))
+      {
+      case UNSPEC_POOL_OFFSET:
+       x = gen_rtx_MINUS (GET_MODE (x), XVECEXP (x, 0, 0), XVECEXP (x, 0, 1));
+       output_addr_const (file, x);
+       return true;
+      }
+  return false;
+}
+
+/* Output address operand ADDR in assembler syntax to
+   stdio stream FILE.  */
+
+void
+print_operand_address (FILE *file, rtx addr)
+{
+  struct s390_address ad;
+
+  if (s390_symref_operand_p (addr, NULL, NULL))
+    {
+      gcc_assert (TARGET_Z10);
+      output_addr_const (file, addr);
+      return;
+    }
+
+  if (!s390_decompose_address (addr, &ad)
+      || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+      || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
+    output_operand_lossage ("cannot decompose address");
+
+  if (ad.disp)
+    output_addr_const (file, ad.disp);
+  else
+    fprintf (file, "0");
+
+  if (ad.base && ad.indx)
+    fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)],
+                              reg_names[REGNO (ad.base)]);
+  else if (ad.base)
+    fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
+}
+
+/* Output operand X in assembler syntax to stdio stream FILE.
+   CODE specified the format flag.  The following format flags
+   are recognized:
+
+    'C': print opcode suffix for branch condition.
+    'D': print opcode suffix for inverse branch condition.
+    'J': print tls_load/tls_gdcall/tls_ldcall suffix
+    'G': print the size of the operand in bytes.
+    'O': print only the displacement of a memory reference.
+    'R': print only the base register of a memory reference.
+    'S': print S-type memory reference (base+displacement).
+    'N': print the second word of a DImode operand.
+    'M': print the second word of a TImode operand.
+    'Y': print shift count operand.
+
+    'b': print integer X as if it's an unsigned byte.
+    'c': print integer X as if it's an signed byte.
+    'x': print integer X as if it's an unsigned halfword.
+    'h': print integer X as if it's a signed halfword.
+    'i': print the first nonzero HImode part of X.
+    'j': print the first HImode part unequal to -1 of X.
+    'k': print the first nonzero SImode part of X.
+    'm': print the first SImode part unequal to -1 of X.
+    'o': print integer X as if it's an unsigned 32bit word.  */
+
+void
+print_operand (FILE *file, rtx x, int code)
+{
+  switch (code)
+    {
+    case 'C':
+      fprintf (file, s390_branch_condition_mnemonic (x, FALSE));
+      return;
+
+    case 'D':
+      fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
+      return;
+
+    case 'J':
+      if (GET_CODE (x) == SYMBOL_REF)
+       {
+         fprintf (file, "%s", ":tls_load:");
+         output_addr_const (file, x);
+       }
+      else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
+       {
+         fprintf (file, "%s", ":tls_gdcall:");
+         output_addr_const (file, XVECEXP (x, 0, 0));
+       }
+      else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM)
+       {
+         fprintf (file, "%s", ":tls_ldcall:");
+         assemble_name (file, get_some_local_dynamic_name ());
+       }
+      else
+       gcc_unreachable ();
+      return;
+
+    case 'G':
+      fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x)));
+      return;
+
+    case 'O':
+      {
+        struct s390_address ad;
+       int ret;
+
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
+
+        if (ad.disp)
+          output_addr_const (file, ad.disp);
+        else
+          fprintf (file, "0");
+      }
+      return;
+
+    case 'R':
+      {
+        struct s390_address ad;
+       int ret;
+
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
+
+        if (ad.base)
+          fprintf (file, "%s", reg_names[REGNO (ad.base)]);
+        else
+          fprintf (file, "0");
+      }
+      return;
+
+    case 'S':
+      {
+       struct s390_address ad;
+       int ret;
+
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
+
+       if (ad.disp)
+         output_addr_const (file, ad.disp);
+       else
+         fprintf (file, "0");
+
+       if (ad.base)
+         fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
+      }
+      return;
+
+    case 'N':
+      if (GET_CODE (x) == REG)
+       x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
+      else if (GET_CODE (x) == MEM)
+       x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
+      else
+        gcc_unreachable ();
+      break;
+
+    case 'M':
+      if (GET_CODE (x) == REG)
+       x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
+      else if (GET_CODE (x) == MEM)
+       x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
+      else
+        gcc_unreachable ();
+      break;
+
+    case 'Y':
+      print_shift_count_operand (file, x);
+      return;
+    }
+
+  switch (GET_CODE (x))
+    {
+    case REG:
+      fprintf (file, "%s", reg_names[REGNO (x)]);
+      break;
+
+    case MEM:
+      output_address (XEXP (x, 0));
+      break;
+
+    case CONST:
+    case CODE_LABEL:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      output_addr_const (file, x);
+      break;
+
+    case CONST_INT:
+      if (code == 'b')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
+      else if (code == 'c')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xff) ^ 0x80) - 0x80);
+      else if (code == 'x')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
+      else if (code == 'h')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
+      else if (code == 'i')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, HImode, 0));
+      else if (code == 'j')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, HImode, -1));
+      else if (code == 'k')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, SImode, 0));
+      else if (code == 'm')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, SImode, -1));
+      else if (code == 'o')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff);
+      else
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
+      break;
+
+    case CONST_DOUBLE:
+      gcc_assert (GET_MODE (x) == VOIDmode);
+      if (code == 'b')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
+      else if (code == 'x')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
+      else if (code == 'h')
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
+      else
+        gcc_unreachable ();
+      break;
+
+    default:
+      fatal_insn ("UNKNOWN in print_operand !?", x);
+      break;
+    }
+}
+
+/* Target hook for assembling integer objects.  We need to define it
+   here to work a round a bug in some versions of GAS, which couldn't
+   handle values smaller than INT_MIN when printed in decimal.  */
+
+static bool
+s390_assemble_integer (rtx x, unsigned int size, int aligned_p)
+{
+  if (size == 8 && aligned_p
+      && GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
+    {
+      fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n",
+              INTVAL (x));
+      return true;
+    }
+  return default_assemble_integer (x, size, aligned_p);
+}
+
+/* Returns true if register REGNO is used  for forming
+   a memory address in expression X.  */
+
+static bool
+reg_used_in_mem_p (int regno, rtx x)
+{
+  enum rtx_code code = GET_CODE (x);
+  int i, j;
+  const char *fmt;
+
+  if (code == MEM)
+    {
+      if (refers_to_regno_p (regno, regno+1,
+                            XEXP (x, 0), 0))
+       return true;
+    }
+  else if (code == SET
+          && GET_CODE (SET_DEST (x)) == PC)
+    {
+      if (refers_to_regno_p (regno, regno+1,
+                            SET_SRC (x), 0))
+       return true;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e'
+         && reg_used_in_mem_p (regno, XEXP (x, i)))
+       return true;
+
+      else if (fmt[i] == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
+           return true;
+    }
+  return false;
+}
+
+/* Returns true if expression DEP_RTX sets an address register
+   used by instruction INSN to address memory.  */
+
+static bool
+addr_generation_dependency_p (rtx dep_rtx, rtx insn)
+{
+  rtx target, pat;
+
+  if (GET_CODE (dep_rtx) == INSN)
+      dep_rtx = PATTERN (dep_rtx);
+
+  if (GET_CODE (dep_rtx) == SET)
+    {
+      target = SET_DEST (dep_rtx);
+      if (GET_CODE (target) == STRICT_LOW_PART)
+       target = XEXP (target, 0);
+      while (GET_CODE (target) == SUBREG)
+       target = SUBREG_REG (target);
+
+      if (GET_CODE (target) == REG)
+       {
+         int regno = REGNO (target);
+
+         if (s390_safe_attr_type (insn) == TYPE_LA)
+           {
+             pat = PATTERN (insn);
+             if (GET_CODE (pat) == PARALLEL)
+               {
+                 gcc_assert (XVECLEN (pat, 0) == 2);
+                 pat = XVECEXP (pat, 0, 0);
+               }
+             gcc_assert (GET_CODE (pat) == SET);
+             return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
+           }
+         else if (get_attr_atype (insn) == ATYPE_AGEN)
+           return reg_used_in_mem_p (regno, PATTERN (insn));
+       }
+    }
+  return false;
+}
+
+/* Return 1, if dep_insn sets register used in insn in the agen unit.  */
+
+int
+s390_agen_dep_p (rtx dep_insn, rtx insn)
+{
+  rtx dep_rtx = PATTERN (dep_insn);
+  int i;
+
+  if (GET_CODE (dep_rtx) == SET
+      && addr_generation_dependency_p (dep_rtx, insn))
+    return 1;
+  else if (GET_CODE (dep_rtx) == PARALLEL)
+    {
+      for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
+       {
+         if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn))
+           return 1;
+       }
+    }
+  return 0;
+}
+
+
+/* A C statement (sans semicolon) to update the integer scheduling priority
+   INSN_PRIORITY (INSN).  Increase the priority to execute the INSN earlier,
+   reduce the priority to execute INSN later.  Do not define this macro if
+   you do not need to adjust the scheduling priorities of insns.
+
+   A STD instruction should be scheduled earlier,
+   in order to use the bypass.  */
+
+
+static int
+s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
+{
+  if (! INSN_P (insn))
+    return priority;
+
+  if (s390_tune != PROCESSOR_2084_Z990
+      && s390_tune != PROCESSOR_2094_Z9_109
+      && s390_tune != PROCESSOR_2097_Z10)
+    return priority;
+
+  switch (s390_safe_attr_type (insn))
+    {
+      case TYPE_FSTOREDF:
+      case TYPE_FSTORESF:
+       priority = priority << 3;
+       break;
+      case TYPE_STORE:
+      case TYPE_STM:
+       priority = priority << 1;
+       break;
+      default:
+        break;
+    }
+  return priority;
+}
+
+
+/* The number of instructions that can be issued per cycle.  */
+
+static int
+s390_issue_rate (void)
+{
+  switch (s390_tune)
+    {
+    case PROCESSOR_2084_Z990:
+    case PROCESSOR_2094_Z9_109:
+      return 3;
+    case PROCESSOR_2097_Z10:
+      return 2;
+    default:
+      return 1;
+    }
+}
+
+static int
+s390_first_cycle_multipass_dfa_lookahead (void)
+{
+  return 4;
+}
+
+
+/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
+   Fix up MEMs as required.  */
+
+static void
+annotate_constant_pool_refs (rtx *x)
+{
+  int i, j;
+  const char *fmt;
+
+  gcc_assert (GET_CODE (*x) != SYMBOL_REF
+             || !CONSTANT_POOL_ADDRESS_P (*x));
+
+  /* Literal pool references can only occur inside a MEM ...  */
+  if (GET_CODE (*x) == MEM)
+    {
+      rtx memref = XEXP (*x, 0);
+
+      if (GET_CODE (memref) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (memref))
+       {
+         rtx base = cfun->machine->base_reg;
+         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base),
+                                    UNSPEC_LTREF);
+
+         *x = replace_equiv_address (*x, addr);
+         return;
+       }
+
+      if (GET_CODE (memref) == CONST
+         && GET_CODE (XEXP (memref, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (memref, 0), 0)) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (memref, 0), 0)))
+       {
+         HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1));
+         rtx sym = XEXP (XEXP (memref, 0), 0);
+         rtx base = cfun->machine->base_reg;
+         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
+                                    UNSPEC_LTREF);
+
+         *x = replace_equiv_address (*x, plus_constant (addr, off));
+         return;
+       }
+    }
+
+  /* ... or a load-address type pattern.  */
+  if (GET_CODE (*x) == SET)
+    {
+      rtx addrref = SET_SRC (*x);
+
+      if (GET_CODE (addrref) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (addrref))
+       {
+         rtx base = cfun->machine->base_reg;
+         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addrref, base),
+                                    UNSPEC_LTREF);
+
+         SET_SRC (*x) = addr;
+         return;
+       }
+
+      if (GET_CODE (addrref) == CONST
+         && GET_CODE (XEXP (addrref, 0)) == PLUS
+         && GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (addrref, 0), 0)) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (addrref, 0), 0)))
+       {
+         HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1));
+         rtx sym = XEXP (XEXP (addrref, 0), 0);
+         rtx base = cfun->machine->base_reg;
+         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
+                                    UNSPEC_LTREF);
+
+         SET_SRC (*x) = plus_constant (addr, off);
+         return;
+       }
+    }
+
+  /* Annotate LTREL_BASE as well.  */
+  if (GET_CODE (*x) == UNSPEC
+      && XINT (*x, 1) == UNSPEC_LTREL_BASE)
+    {
+      rtx base = cfun->machine->base_reg;
+      *x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XVECEXP (*x, 0, 0), base),
+                                 UNSPEC_LTREL_BASE);
+      return;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (*x));
+  for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          annotate_constant_pool_refs (&XEXP (*x, i));
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (*x, i); j++)
+            annotate_constant_pool_refs (&XVECEXP (*x, i, j));
+        }
+    }
+}
+
+/* Split all branches that exceed the maximum distance.
+   Returns true if this created a new literal pool entry.  */
+
+static int
+s390_split_branches (void)
+{
+  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  int new_literal = 0, ret;
+  rtx insn, pat, tmp, target;
+  rtx *label;
+
+  /* We need correct insn addresses.  */
+
+  shorten_branches (get_insns ());
+
+  /* Find all branches that exceed 64KB, and split them.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) != JUMP_INSN)
+       continue;
+
+      pat = PATTERN (insn);
+      if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
+       pat = XVECEXP (pat, 0, 0);
+      if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
+       continue;
+
+      if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
+       {
+         label = &SET_SRC (pat);
+       }
+      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
+       {
+         if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
+           label = &XEXP (SET_SRC (pat), 1);
+          else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
+            label = &XEXP (SET_SRC (pat), 2);
+         else
+           continue;
+        }
+      else
+       continue;
+
+      if (get_attr_length (insn) <= 4)
+       continue;
+
+      /* We are going to use the return register as scratch register,
+        make sure it will be saved/restored by the prologue/epilogue.  */
+      cfun_frame_layout.save_return_addr_p = 1;
+
+      if (!flag_pic)
+       {
+         new_literal = 1;
+         tmp = force_const_mem (Pmode, *label);
+         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
+         INSN_ADDRESSES_NEW (tmp, -1);
+         annotate_constant_pool_refs (&PATTERN (tmp));
+
+         target = temp_reg;
+       }
+      else
+       {
+         new_literal = 1;
+         target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label),
+                                  UNSPEC_LTREL_OFFSET);
+         target = gen_rtx_CONST (Pmode, target);
+         target = force_const_mem (Pmode, target);
+         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
+         INSN_ADDRESSES_NEW (tmp, -1);
+         annotate_constant_pool_refs (&PATTERN (tmp));
+
+          target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0),
+                                                       cfun->machine->base_reg),
+                                  UNSPEC_LTREL_BASE);
+         target = gen_rtx_PLUS (Pmode, temp_reg, target);
+       }
+
+      ret = validate_change (insn, label, target, 0);
+      gcc_assert (ret);
+    }
+
+  return new_literal;
+}
+
+
+/* Find an annotated literal pool symbol referenced in RTX X, 
+   and store it at REF.  Will abort if X contains references to 
+   more than one such pool symbol; multiple references to the same
+   symbol are allowed, however.
+
+   The rtx pointed to by REF must be initialized to NULL_RTX
+   by the caller before calling this routine.  */
+
+static void
+find_constant_pool_ref (rtx x, rtx *ref)
+{
+  int i, j;
+  const char *fmt;
+
+  /* Ignore LTREL_BASE references.  */
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_LTREL_BASE)
+    return;
+  /* Likewise POOL_ENTRY insns.  */
+  if (GET_CODE (x) == UNSPEC_VOLATILE
+      && XINT (x, 1) == UNSPECV_POOL_ENTRY)
+    return;
+
+  gcc_assert (GET_CODE (x) != SYMBOL_REF
+              || !CONSTANT_POOL_ADDRESS_P (x));
+
+  if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF)
+    {
+      rtx sym = XVECEXP (x, 0, 0);
+      gcc_assert (GET_CODE (sym) == SYMBOL_REF
+                 && CONSTANT_POOL_ADDRESS_P (sym));
+
+      if (*ref == NULL_RTX)
+       *ref = sym;
+      else 
+       gcc_assert (*ref == sym);
+
+      return;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          find_constant_pool_ref (XEXP (x, i), ref);
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (x, i); j++)
+            find_constant_pool_ref (XVECEXP (x, i, j), ref);
+        }
+    }
+}
+
+/* Replace every reference to the annotated literal pool 
+   symbol REF in X by its base plus OFFSET.  */
+
+static void
+replace_constant_pool_ref (rtx *x, rtx ref, rtx offset)
+{
+  int i, j;
+  const char *fmt;
+
+  gcc_assert (*x != ref);
+
+  if (GET_CODE (*x) == UNSPEC
+      && XINT (*x, 1) == UNSPEC_LTREF
+      && XVECEXP (*x, 0, 0) == ref)
+    {
+      *x = gen_rtx_PLUS (Pmode, XVECEXP (*x, 0, 1), offset);
+      return;
+    }
+
+  if (GET_CODE (*x) == PLUS
+      && GET_CODE (XEXP (*x, 1)) == CONST_INT
+      && GET_CODE (XEXP (*x, 0)) == UNSPEC
+      && XINT (XEXP (*x, 0), 1) == UNSPEC_LTREF
+      && XVECEXP (XEXP (*x, 0), 0, 0) == ref)
+    {
+      rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset);
+      *x = plus_constant (addr, INTVAL (XEXP (*x, 1)));
+      return;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (*x));
+  for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          replace_constant_pool_ref (&XEXP (*x, i), ref, offset);
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (*x, i); j++)
+            replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, offset);
+        }
+    }
+}
+
+/* Check whether X contains an UNSPEC_LTREL_BASE.
+   Return its constant pool symbol if found, NULL_RTX otherwise.  */
+
+static rtx
+find_ltrel_base (rtx x)
+{
+  int i, j;
+  const char *fmt;
+
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_LTREL_BASE)
+    return XVECEXP (x, 0, 0);
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          rtx fnd = find_ltrel_base (XEXP (x, i));
+         if (fnd)
+           return fnd;
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (x, i); j++)
+           {
+              rtx fnd = find_ltrel_base (XVECEXP (x, i, j));
+             if (fnd)
+               return fnd;
+           }
+        }
+    }
+
+  return NULL_RTX;
+}
+
+/* Replace any occurrence of UNSPEC_LTREL_BASE in X with its base.  */
+
+static void
+replace_ltrel_base (rtx *x)
+{
+  int i, j;
+  const char *fmt;
+
+  if (GET_CODE (*x) == UNSPEC
+      && XINT (*x, 1) == UNSPEC_LTREL_BASE)
+    {
+      *x = XVECEXP (*x, 0, 1);
+      return;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (*x));
+  for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+        {
+          replace_ltrel_base (&XEXP (*x, i));
+        }
+      else if (fmt[i] == 'E')
+        {
+          for (j = 0; j < XVECLEN (*x, i); j++)
+            replace_ltrel_base (&XVECEXP (*x, i, j));
+        }
+    }
+}
+
+
+/* We keep a list of constants which we have to add to internal
+   constant tables in the middle of large functions.  */
+
+#define NR_C_MODES 11
+enum machine_mode constant_modes[NR_C_MODES] =
+{
+  TFmode, TImode, TDmode,
+  DFmode, DImode, DDmode,
+  SFmode, SImode, SDmode,
+  HImode,
+  QImode
+};
+
+struct constant
+{
+  struct constant *next;
+  rtx value;
+  rtx label;
+};
+
+struct constant_pool
+{
+  struct constant_pool *next;
+  rtx first_insn;
+  rtx pool_insn;
+  bitmap insns;
+  rtx emit_pool_after;
+
+  struct constant *constants[NR_C_MODES];
+  struct constant *execute;
+  rtx label;
+  int size;
+};
+
+/* Allocate new constant_pool structure.  */
+
+static struct constant_pool *
+s390_alloc_pool (void)
+{
+  struct constant_pool *pool;
+  int i;
+
+  pool = (struct constant_pool *) xmalloc (sizeof *pool);
+  pool->next = NULL;
+  for (i = 0; i < NR_C_MODES; i++)
+    pool->constants[i] = NULL;
+
+  pool->execute = NULL;
+  pool->label = gen_label_rtx ();
+  pool->first_insn = NULL_RTX;
+  pool->pool_insn = NULL_RTX;
+  pool->insns = BITMAP_ALLOC (NULL);
+  pool->size = 0;
+  pool->emit_pool_after = NULL_RTX;
+
+  return pool;
+}
+
+/* Create new constant pool covering instructions starting at INSN
+   and chain it to the end of POOL_LIST.  */
+
+static struct constant_pool *
+s390_start_pool (struct constant_pool **pool_list, rtx insn)
+{
+  struct constant_pool *pool, **prev;
+
+  pool = s390_alloc_pool ();
+  pool->first_insn = insn;
+
+  for (prev = pool_list; *prev; prev = &(*prev)->next)
+    ;
+  *prev = pool;
+
+  return pool;
+}
+
+/* End range of instructions covered by POOL at INSN and emit
+   placeholder insn representing the pool.  */
+
+static void
+s390_end_pool (struct constant_pool *pool, rtx insn)
+{
+  rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
+
+  if (!insn)
+    insn = get_last_insn ();
+
+  pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn);
+  INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+}
+
+/* Add INSN to the list of insns covered by POOL.  */
+
+static void
+s390_add_pool_insn (struct constant_pool *pool, rtx insn)
+{
+  bitmap_set_bit (pool->insns, INSN_UID (insn));
+}
+
+/* Return pool out of POOL_LIST that covers INSN.  */
+
+static struct constant_pool *
+s390_find_pool (struct constant_pool *pool_list, rtx insn)
+{
+  struct constant_pool *pool;
+
+  for (pool = pool_list; pool; pool = pool->next)
+    if (bitmap_bit_p (pool->insns, INSN_UID (insn)))
+      break;
+
+  return pool;
+}
+
+/* Add constant VAL of mode MODE to the constant pool POOL.  */
+
+static void
+s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
+{
+  struct constant *c;
+  int i;
+
+  for (i = 0; i < NR_C_MODES; i++)
+    if (constant_modes[i] == mode)
+      break;
+  gcc_assert (i != NR_C_MODES);
+
+  for (c = pool->constants[i]; c != NULL; c = c->next)
+    if (rtx_equal_p (val, c->value))
+      break;
+
+  if (c == NULL)
+    {
+      c = (struct constant *) xmalloc (sizeof *c);
+      c->value = val;
+      c->label = gen_label_rtx ();
+      c->next = pool->constants[i];
+      pool->constants[i] = c;
+      pool->size += GET_MODE_SIZE (mode);
+    }
+}
+
+/* Return an rtx that represents the offset of X from the start of
+   pool POOL.  */
+
+static rtx
+s390_pool_offset (struct constant_pool *pool, rtx x)
+{
+  rtx label;
+
+  label = gen_rtx_LABEL_REF (GET_MODE (x), pool->label);
+  x = gen_rtx_UNSPEC (GET_MODE (x), gen_rtvec (2, x, label),
+                     UNSPEC_POOL_OFFSET);
+  return gen_rtx_CONST (GET_MODE (x), x);
+}
+
+/* Find constant VAL of mode MODE in the constant pool POOL.
+   Return an RTX describing the distance from the start of
+   the pool to the location of the new constant.  */
+
+static rtx
+s390_find_constant (struct constant_pool *pool, rtx val,
+                   enum machine_mode mode)
+{
+  struct constant *c;
+  int i;
+
+  for (i = 0; i < NR_C_MODES; i++)
+    if (constant_modes[i] == mode)
+      break;
+  gcc_assert (i != NR_C_MODES);
+
+  for (c = pool->constants[i]; c != NULL; c = c->next)
+    if (rtx_equal_p (val, c->value))
+      break;
+
+  gcc_assert (c);
+
+  return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label));
+}
+
+/* Check whether INSN is an execute.  Return the label_ref to its
+   execute target template if so, NULL_RTX otherwise.  */
+
+static rtx
+s390_execute_label (rtx insn)
+{
+  if (GET_CODE (insn) == INSN
+      && GET_CODE (PATTERN (insn)) == PARALLEL
+      && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
+      && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
+    return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
+
+  return NULL_RTX;
+}
+
+/* Add execute target for INSN to the constant pool POOL.  */
+
+static void
+s390_add_execute (struct constant_pool *pool, rtx insn)
+{
+  struct constant *c;
+
+  for (c = pool->execute; c != NULL; c = c->next)
+    if (INSN_UID (insn) == INSN_UID (c->value))
+      break;
+
+  if (c == NULL)
+    {
+      c = (struct constant *) xmalloc (sizeof *c);
+      c->value = insn;
+      c->label = gen_label_rtx ();
+      c->next = pool->execute;
+      pool->execute = c;
+      pool->size += 6;
+    }
+}
+
+/* Find execute target for INSN in the constant pool POOL.
+   Return an RTX describing the distance from the start of
+   the pool to the location of the execute target.  */
+
+static rtx
+s390_find_execute (struct constant_pool *pool, rtx insn)
+{
+  struct constant *c;
+
+  for (c = pool->execute; c != NULL; c = c->next)
+    if (INSN_UID (insn) == INSN_UID (c->value))
+      break;
+
+  gcc_assert (c);
+
+  return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label));
+}
+
+/* For an execute INSN, extract the execute target template.  */
+
+static rtx
+s390_execute_target (rtx insn)
+{
+  rtx pattern = PATTERN (insn);
+  gcc_assert (s390_execute_label (insn));
+
+  if (XVECLEN (pattern, 0) == 2)
+    {
+      pattern = copy_rtx (XVECEXP (pattern, 0, 1));
+    }
+  else
+    {
+      rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1);
+      int i;
+
+      for (i = 0; i < XVECLEN (pattern, 0) - 1; i++)
+       RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1));
+
+      pattern = gen_rtx_PARALLEL (VOIDmode, vec);
+    }
+
+  return pattern;
+}
+
+/* Indicate that INSN cannot be duplicated.  This is the case for
+   execute insns that carry a unique label.  */
+
+static bool
+s390_cannot_copy_insn_p (rtx insn)
+{
+  rtx label = s390_execute_label (insn);
+  return label && label != const0_rtx;
+}
+
+/* Dump out the constants in POOL.  If REMOTE_LABEL is true,
+   do not emit the pool base label.  */
+
+static void
+s390_dump_pool (struct constant_pool *pool, bool remote_label)
+{
+  struct constant *c;
+  rtx insn = pool->pool_insn;
+  int i;
+
+  /* Switch to rodata section.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = emit_insn_after (gen_pool_section_start (), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  /* Ensure minimum pool alignment.  */
+  if (TARGET_CPU_ZARCH)
+    insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn);
+  else
+    insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn);
+  INSN_ADDRESSES_NEW (insn, -1);
+
+  /* Emit pool base label.  */
+  if (!remote_label)
+    {
+      insn = emit_label_after (pool->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  /* Dump constants in descending alignment requirement order,
+     ensuring proper alignment for every constant.  */
+  for (i = 0; i < NR_C_MODES; i++)
+    for (c = pool->constants[i]; c; c = c->next)
+      {
+       /* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references.  */
+       rtx value = copy_rtx (c->value);
+       if (GET_CODE (value) == CONST
+           && GET_CODE (XEXP (value, 0)) == UNSPEC
+           && XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET
+           && XVECLEN (XEXP (value, 0), 0) == 1)
+         value = s390_pool_offset (pool, XVECEXP (XEXP (value, 0), 0, 0));
+
+       insn = emit_label_after (c->label, insn);
+       INSN_ADDRESSES_NEW (insn, -1);
+
+       value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i],
+                                        gen_rtvec (1, value),
+                                        UNSPECV_POOL_ENTRY);
+       insn = emit_insn_after (value, insn);
+       INSN_ADDRESSES_NEW (insn, -1);
+      }
+
+  /* Ensure minimum alignment for instructions.  */
+  insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn);
+  INSN_ADDRESSES_NEW (insn, -1);
+
+  /* Output in-pool execute template insns.  */
+  for (c = pool->execute; c; c = c->next)
+    {
+      insn = emit_label_after (c->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      insn = emit_insn_after (s390_execute_target (c->value), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  /* Switch back to previous section.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = emit_insn_after (gen_pool_section_end (), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  insn = emit_barrier_after (insn);
+  INSN_ADDRESSES_NEW (insn, -1);
+
+  /* Remove placeholder insn.  */
+  remove_insn (pool->pool_insn);
+}
+
+/* Free all memory used by POOL.  */
+
+static void
+s390_free_pool (struct constant_pool *pool)
+{
+  struct constant *c, *next;
+  int i;
+
+  for (i = 0; i < NR_C_MODES; i++)
+    for (c = pool->constants[i]; c; c = next)
+      {
+       next = c->next;
+       free (c);
+      }
+
+  for (c = pool->execute; c; c = next)
+    {
+      next = c->next;
+      free (c);
+    }
+
+  BITMAP_FREE (pool->insns);
+  free (pool);
+}
+
+
+/* Collect main literal pool.  Return NULL on overflow.  */
+
+static struct constant_pool *
+s390_mainpool_start (void)
+{
+  struct constant_pool *pool;
+  rtx insn;
+
+  pool = s390_alloc_pool ();
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE
+         && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL)
+       {
+         gcc_assert (!pool->pool_insn);
+         pool->pool_insn = insn;
+       }
+
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
+       {
+         s390_add_execute (pool, insn);
+       }
+      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+       {
+         rtx pool_ref = NULL_RTX;
+         find_constant_pool_ref (PATTERN (insn), &pool_ref);
+         if (pool_ref)
+           {
+             rtx constant = get_pool_constant (pool_ref);
+             enum machine_mode mode = get_pool_mode (pool_ref);
+             s390_add_constant (pool, constant, mode);
+           }
+       }
+
+      /* If hot/cold partitioning is enabled we have to make sure that
+        the literal pool is emitted in the same section where the
+        initialization of the literal pool base pointer takes place.
+        emit_pool_after is only used in the non-overflow case on non
+        Z cpus where we can emit the literal pool at the end of the
+        function body within the text section.  */
+      if (NOTE_P (insn)
+         && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS
+         && !pool->emit_pool_after)
+       pool->emit_pool_after = PREV_INSN (insn);
+    }
+
+  gcc_assert (pool->pool_insn || pool->size == 0);
+
+  if (pool->size >= 4096)
+    {
+      /* We're going to chunkify the pool, so remove the main
+        pool placeholder insn.  */
+      remove_insn (pool->pool_insn);
+
+      s390_free_pool (pool);
+      pool = NULL;
+    }
+
+  /* If the functions ends with the section where the literal pool
+     should be emitted set the marker to its end.  */
+  if (pool && !pool->emit_pool_after)
+    pool->emit_pool_after = get_last_insn ();
+
+  return pool;
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+   Modify the current function to output the pool constants as well as
+   the pool register setup instruction.  */
+
+static void
+s390_mainpool_finish (struct constant_pool *pool)
+{
+  rtx base_reg = cfun->machine->base_reg;
+  rtx insn;
+
+  /* If the pool is empty, we're done.  */
+  if (pool->size == 0)
+    {
+      /* We don't actually need a base register after all.  */
+      cfun->machine->base_reg = NULL_RTX;
+
+      if (pool->pool_insn)
+       remove_insn (pool->pool_insn);
+      s390_free_pool (pool);
+      return;
+    }
+
+  /* We need correct insn addresses.  */
+  shorten_branches (get_insns ());
+
+  /* On zSeries, we use a LARL to load the pool register.  The pool is
+     located in the .rodata section, so we emit it after the function.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = gen_main_base_64 (base_reg, pool->label);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+
+      insn = get_last_insn ();
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      s390_dump_pool (pool, 0);
+    }
+
+  /* On S/390, if the total size of the function's code plus literal pool
+     does not exceed 4096 bytes, we use BASR to set up a function base
+     pointer, and emit the literal pool at the end of the function.  */
+  else if (INSN_ADDRESSES (INSN_UID (pool->emit_pool_after))
+          + pool->size + 8 /* alignment slop */ < 4096)
+    {
+      insn = gen_main_base_31_small (base_reg, pool->label);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+
+      insn = emit_label_after (pool->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      /* emit_pool_after will be set by s390_mainpool_start to the
+        last insn of the section where the literal pool should be
+        emitted.  */
+      insn = pool->emit_pool_after;
+
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      s390_dump_pool (pool, 1);
+    }
+
+  /* Otherwise, we emit an inline literal pool and use BASR to branch
+     over it, setting up the pool register at the same time.  */
+  else
+    {
+      rtx pool_end = gen_label_rtx ();
+
+      insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+
+      insn = emit_label_after (pool->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      insn = emit_label_after (pool_end, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      s390_dump_pool (pool, 1);
+    }
+
+
+  /* Replace all literal pool references.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (INSN_P (insn))
+       replace_ltrel_base (&PATTERN (insn));
+
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+        {
+          rtx addr, pool_ref = NULL_RTX;
+          find_constant_pool_ref (PATTERN (insn), &pool_ref);
+          if (pool_ref)
+            {
+             if (s390_execute_label (insn))
+               addr = s390_find_execute (pool, insn);
+             else
+               addr = s390_find_constant (pool, get_pool_constant (pool_ref),
+                                                get_pool_mode (pool_ref));
+
+              replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
+              INSN_CODE (insn) = -1;
+            }
+        }
+    }
+
+
+  /* Free the pool.  */
+  s390_free_pool (pool);
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+   We have decided we cannot use this pool, so revert all changes
+   to the current function that were done by s390_mainpool_start.  */
+static void
+s390_mainpool_cancel (struct constant_pool *pool)
+{
+  /* We didn't actually change the instruction stream, so simply
+     free the pool memory.  */
+  s390_free_pool (pool);
+}
+
+
+/* Chunkify the literal pool.  */
+
+#define S390_POOL_CHUNK_MIN    0xc00
+#define S390_POOL_CHUNK_MAX    0xe00
+
+static struct constant_pool *
+s390_chunkify_start (void)
+{
+  struct constant_pool *curr_pool = NULL, *pool_list = NULL;
+  int extra_size = 0;
+  bitmap far_labels;
+  rtx pending_ltrel = NULL_RTX;
+  rtx insn;
+
+  rtx (*gen_reload_base) (rtx, rtx) =
+    TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31;
+
+
+  /* We need correct insn addresses.  */
+
+  shorten_branches (get_insns ());
+
+  /* Scan all insns and move literals to pool chunks.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      bool section_switch_p = false;
+
+      /* Check for pending LTREL_BASE.  */
+      if (INSN_P (insn))
+       {
+         rtx ltrel_base = find_ltrel_base (PATTERN (insn));
+         if (ltrel_base)
+           {
+             gcc_assert (ltrel_base == pending_ltrel);
+             pending_ltrel = NULL_RTX;
+           }
+       }
+
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
+       {
+         if (!curr_pool)
+           curr_pool = s390_start_pool (&pool_list, insn);
+
+         s390_add_execute (curr_pool, insn);
+         s390_add_pool_insn (curr_pool, insn);
+       }
+      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+       {
+         rtx pool_ref = NULL_RTX;
+         find_constant_pool_ref (PATTERN (insn), &pool_ref);
+         if (pool_ref)
+           {
+             rtx constant = get_pool_constant (pool_ref);
+             enum machine_mode mode = get_pool_mode (pool_ref);
+
+             if (!curr_pool)
+               curr_pool = s390_start_pool (&pool_list, insn);
+
+             s390_add_constant (curr_pool, constant, mode);
+             s390_add_pool_insn (curr_pool, insn);
+
+             /* Don't split the pool chunk between a LTREL_OFFSET load
+                and the corresponding LTREL_BASE.  */
+             if (GET_CODE (constant) == CONST
+                 && GET_CODE (XEXP (constant, 0)) == UNSPEC
+                 && XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET)
+               {
+                 gcc_assert (!pending_ltrel);
+                 pending_ltrel = pool_ref;
+               }
+           }
+       }
+
+      if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
+       {
+         if (curr_pool)
+           s390_add_pool_insn (curr_pool, insn);
+         /* An LTREL_BASE must follow within the same basic block.  */
+         gcc_assert (!pending_ltrel);
+       }
+
+      if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
+       section_switch_p = true;
+
+      if (!curr_pool
+         || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
+          || INSN_ADDRESSES (INSN_UID (insn)) == -1)
+       continue;
+
+      if (TARGET_CPU_ZARCH)
+       {
+         if (curr_pool->size < S390_POOL_CHUNK_MAX)
+           continue;
+
+         s390_end_pool (curr_pool, NULL_RTX);
+         curr_pool = NULL;
+       }
+      else
+       {
+          int chunk_size = INSN_ADDRESSES (INSN_UID (insn))
+                          - INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
+                        + extra_size;
+
+         /* We will later have to insert base register reload insns.
+            Those will have an effect on code size, which we need to
+            consider here.  This calculation makes rather pessimistic
+            worst-case assumptions.  */
+         if (GET_CODE (insn) == CODE_LABEL)
+           extra_size += 6;
+
+         if (chunk_size < S390_POOL_CHUNK_MIN
+             && curr_pool->size < S390_POOL_CHUNK_MIN
+             && !section_switch_p)
+           continue;
+
+         /* Pool chunks can only be inserted after BARRIERs ...  */
+         if (GET_CODE (insn) == BARRIER)
+           {
+             s390_end_pool (curr_pool, insn);
+             curr_pool = NULL;
+             extra_size = 0;
+           }
+
+         /* ... so if we don't find one in time, create one.  */
+          else if (chunk_size > S390_POOL_CHUNK_MAX
+                  || curr_pool->size > S390_POOL_CHUNK_MAX
+                  || section_switch_p)
+           {
+              rtx label, jump, barrier;
+
+             if (!section_switch_p)
+               {
+                 /* We can insert the barrier only after a 'real' insn.  */
+                 if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
+                   continue;
+                 if (get_attr_length (insn) == 0)
+                   continue;
+                 /* Don't separate LTREL_BASE from the corresponding
+                LTREL_OFFSET load.  */
+                 if (pending_ltrel)
+                   continue;
+               }
+             else
+               {
+                 gcc_assert (!pending_ltrel);
+
+                 /* The old pool has to end before the section switch
+                    note in order to make it part of the current
+                    section.  */
+                 insn = PREV_INSN (insn);
+               }
+
+             label = gen_label_rtx ();
+             jump = emit_jump_insn_after (gen_jump (label), insn);
+             barrier = emit_barrier_after (jump);
+             insn = emit_label_after (label, barrier);
+             JUMP_LABEL (jump) = label;
+             LABEL_NUSES (label) = 1;
+
+             INSN_ADDRESSES_NEW (jump, -1);
+             INSN_ADDRESSES_NEW (barrier, -1);
+             INSN_ADDRESSES_NEW (insn, -1);
+
+             s390_end_pool (curr_pool, barrier);
+             curr_pool = NULL;
+             extra_size = 0;
+           }
+       }
+    }
+
+  if (curr_pool)
+    s390_end_pool (curr_pool, NULL_RTX);
+  gcc_assert (!pending_ltrel);
+
+  /* Find all labels that are branched into
+     from an insn belonging to a different chunk.  */
+
+  far_labels = BITMAP_ALLOC (NULL);
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      /* Labels marked with LABEL_PRESERVE_P can be target
+        of non-local jumps, so we have to mark them.
+        The same holds for named labels.
+
+        Don't do that, however, if it is the label before
+        a jump table.  */
+
+      if (GET_CODE (insn) == CODE_LABEL
+         && (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
+       {
+         rtx vec_insn = next_real_insn (insn);
+         rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
+                       PATTERN (vec_insn) : NULL_RTX;
+         if (!vec_pat
+             || !(GET_CODE (vec_pat) == ADDR_VEC
+                  || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
+           bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn));
+       }
+
+      /* If we have a direct jump (conditional or unconditional)
+        or a casesi jump, check all potential targets.  */
+      else if (GET_CODE (insn) == JUMP_INSN)
+       {
+          rtx pat = PATTERN (insn);
+         if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
+           pat = XVECEXP (pat, 0, 0);
+
+          if (GET_CODE (pat) == SET)
+            {
+             rtx label = JUMP_LABEL (insn);
+             if (label)
+               {
+                 if (s390_find_pool (pool_list, label)
+                     != s390_find_pool (pool_list, insn))
+                   bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
+               }
+            }
+         else if (GET_CODE (pat) == PARALLEL
+                  && XVECLEN (pat, 0) == 2
+                  && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+                  && GET_CODE (XVECEXP (pat, 0, 1)) == USE
+                  && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF)
+           {
+             /* Find the jump table used by this casesi jump.  */
+             rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
+             rtx vec_insn = next_real_insn (vec_label);
+             rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
+                           PATTERN (vec_insn) : NULL_RTX;
+             if (vec_pat
+                 && (GET_CODE (vec_pat) == ADDR_VEC
+                     || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
+               {
+                 int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
+
+                 for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
+                   {
+                     rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
+
+                     if (s390_find_pool (pool_list, label)
+                         != s390_find_pool (pool_list, insn))
+                       bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
+                   }
+               }
+           }
+        }
+    }
+
+  /* Insert base register reload insns before every pool.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    {
+      rtx new_insn = gen_reload_base (cfun->machine->base_reg, 
+                                     curr_pool->label);
+      rtx insn = curr_pool->first_insn;
+      INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
+    }
+
+  /* Insert base register reload insns at every far label.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (GET_CODE (insn) == CODE_LABEL
+        && bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
+      {
+       struct constant_pool *pool = s390_find_pool (pool_list, insn);
+       if (pool)
+         {
+           rtx new_insn = gen_reload_base (cfun->machine->base_reg, 
+                                           pool->label);
+           INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
+         }
+      }
+
+
+  BITMAP_FREE (far_labels);
+
+
+  /* Recompute insn addresses.  */
+
+  init_insn_lengths ();
+  shorten_branches (get_insns ());
+
+  return pool_list;
+}
+
+/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
+   After we have decided to use this list, finish implementing
+   all changes to the current function as required.  */
+
+static void
+s390_chunkify_finish (struct constant_pool *pool_list)
+{
+  struct constant_pool *curr_pool = NULL;
+  rtx insn;
+
+
+  /* Replace all literal pool references.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (INSN_P (insn))
+       replace_ltrel_base (&PATTERN (insn));
+
+      curr_pool = s390_find_pool (pool_list, insn);
+      if (!curr_pool)
+       continue;
+
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+        {
+          rtx addr, pool_ref = NULL_RTX;
+          find_constant_pool_ref (PATTERN (insn), &pool_ref);
+          if (pool_ref)
+            {
+             if (s390_execute_label (insn))
+               addr = s390_find_execute (curr_pool, insn);
+             else
+               addr = s390_find_constant (curr_pool,
+                                          get_pool_constant (pool_ref),
+                                          get_pool_mode (pool_ref));
+
+              replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
+              INSN_CODE (insn) = -1;
+            }
+        }
+    }
+
+  /* Dump out all literal pools.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    s390_dump_pool (curr_pool, 0);
+
+  /* Free pool list.  */
+
+  while (pool_list)
+    {
+      struct constant_pool *next = pool_list->next;
+      s390_free_pool (pool_list);
+      pool_list = next;
+    }
+}
+
+/* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
+   We have decided we cannot use this list, so revert all changes
+   to the current function that were done by s390_chunkify_start.  */
+
+static void
+s390_chunkify_cancel (struct constant_pool *pool_list)
+{
+  struct constant_pool *curr_pool = NULL;
+  rtx insn;
+
+  /* Remove all pool placeholder insns.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    {
+      /* Did we insert an extra barrier?  Remove it.  */
+      rtx barrier = PREV_INSN (curr_pool->pool_insn);
+      rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
+      rtx label = NEXT_INSN (curr_pool->pool_insn);
+
+      if (jump && GET_CODE (jump) == JUMP_INSN
+         && barrier && GET_CODE (barrier) == BARRIER
+         && label && GET_CODE (label) == CODE_LABEL
+         && GET_CODE (PATTERN (jump)) == SET
+         && SET_DEST (PATTERN (jump)) == pc_rtx
+         && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
+         && XEXP (SET_SRC (PATTERN (jump)), 0) == label)
+       {
+         remove_insn (jump);
+         remove_insn (barrier);
+         remove_insn (label);
+       }
+
+      remove_insn (curr_pool->pool_insn);
+    }
+
+  /* Remove all base register reload insns.  */
+
+  for (insn = get_insns (); insn; )
+    {
+      rtx next_insn = NEXT_INSN (insn);
+
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
+         && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
+       remove_insn (insn);
+
+      insn = next_insn;
+    }
+
+  /* Free pool list.  */
+
+  while (pool_list)
+    {
+      struct constant_pool *next = pool_list->next;
+      s390_free_pool (pool_list);
+      pool_list = next;
+    }
+}
+
+/* Output the constant pool entry EXP in mode MODE with alignment ALIGN.  */
+
+void
+s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
+{
+  REAL_VALUE_TYPE r;
+
+  switch (GET_MODE_CLASS (mode))
+    {
+    case MODE_FLOAT:
+    case MODE_DECIMAL_FLOAT:
+      gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
+
+      REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
+      assemble_real (r, mode, align);
+      break;
+
+    case MODE_INT:
+      assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
+      mark_symbol_refs_as_used (exp);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+
+/* Return an RTL expression representing the value of the return address
+   for the frame COUNT steps up from the current frame.  FRAME is the
+   frame pointer of that frame.  */
+
+rtx
+s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+  int offset;
+  rtx addr;
+
+  /* Without backchain, we fail for all but the current frame.  */
+
+  if (!TARGET_BACKCHAIN && count > 0)
+    return NULL_RTX;
+
+  /* For the current frame, we need to make sure the initial
+     value of RETURN_REGNUM is actually saved.  */
+
+  if (count == 0)
+    {
+      /* On non-z architectures branch splitting could overwrite r14.  */
+      if (TARGET_CPU_ZARCH)
+       return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
+      else
+       {
+         cfun_frame_layout.save_return_addr_p = true;
+         return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
+       }
+    }
+
+  if (TARGET_PACKED_STACK)
+    offset = -2 * UNITS_PER_WORD;
+  else
+    offset = RETURN_REGNUM * UNITS_PER_WORD;
+
+  addr = plus_constant (frame, offset);
+  addr = memory_address (Pmode, addr);
+  return gen_rtx_MEM (Pmode, addr);
+}
+
+/* Return an RTL expression representing the back chain stored in
+   the current stack frame.  */
+
+rtx
+s390_back_chain_rtx (void)
+{
+  rtx chain;
+
+  gcc_assert (TARGET_BACKCHAIN);
+
+  if (TARGET_PACKED_STACK)
+    chain = plus_constant (stack_pointer_rtx,
+                          STACK_POINTER_OFFSET - UNITS_PER_WORD);
+  else
+    chain = stack_pointer_rtx;
+
+  chain = gen_rtx_MEM (Pmode, chain);
+  return chain;
+}
+
+/* Find first call clobbered register unused in a function.
+   This could be used as base register in a leaf function
+   or for holding the return address before epilogue.  */
+
+static int
+find_unused_clobbered_reg (void)
+{
+  int i;
+  for (i = 0; i < 6; i++)
+    if (!df_regs_ever_live_p (i))
+      return i;
+  return 0;
+}
+
+
+/* Helper function for s390_regs_ever_clobbered.  Sets the fields in DATA for all 
+   clobbered hard regs in SETREG.  */
+
+static void
+s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *data)
+{
+  int *regs_ever_clobbered = (int *)data;
+  unsigned int i, regno;
+  enum machine_mode mode = GET_MODE (setreg);
+
+  if (GET_CODE (setreg) == SUBREG)
+    {
+      rtx inner = SUBREG_REG (setreg);
+      if (!GENERAL_REG_P (inner))
+       return;
+      regno = subreg_regno (setreg);
+    }
+  else if (GENERAL_REG_P (setreg))
+    regno = REGNO (setreg);
+  else
+    return;
+
+  for (i = regno;
+       i < regno + HARD_REGNO_NREGS (regno, mode);
+       i++)
+    regs_ever_clobbered[i] = 1;
+}
+
+/* Walks through all basic blocks of the current function looking
+   for clobbered hard regs using s390_reg_clobbered_rtx.  The fields
+   of the passed integer array REGS_EVER_CLOBBERED are set to one for
+   each of those regs.  */
+
+static void
+s390_regs_ever_clobbered (int *regs_ever_clobbered)
+{
+  basic_block cur_bb;
+  rtx cur_insn;
+  unsigned int i;
+
+  memset (regs_ever_clobbered, 0, 16 * sizeof (int));
+
+  /* For non-leaf functions we have to consider all call clobbered regs to be
+     clobbered.  */
+  if (!current_function_is_leaf)
+    {
+      for (i = 0; i < 16; i++)
+       regs_ever_clobbered[i] = call_really_used_regs[i];
+    }
+
+  /* Make the "magic" eh_return registers live if necessary.  For regs_ever_live
+     this work is done by liveness analysis (mark_regs_live_at_end).
+     Special care is needed for functions containing landing pads.  Landing pads
+     may use the eh registers, but the code which sets these registers is not
+     contained in that function.  Hence s390_regs_ever_clobbered is not able to
+     deal with this automatically.  */
+  if (crtl->calls_eh_return || cfun->machine->has_landing_pad_p)
+    for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++)
+      if (crtl->calls_eh_return 
+         || (cfun->machine->has_landing_pad_p 
+             && df_regs_ever_live_p (EH_RETURN_DATA_REGNO (i))))
+       regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1;
+
+  /* For nonlocal gotos all call-saved registers have to be saved.
+     This flag is also set for the unwinding code in libgcc.
+     See expand_builtin_unwind_init.  For regs_ever_live this is done by
+     reload.  */
+  if (cfun->has_nonlocal_label)
+    for (i = 0; i < 16; i++)
+      if (!call_really_used_regs[i])
+       regs_ever_clobbered[i] = 1;
+
+  FOR_EACH_BB (cur_bb)
+    {
+      FOR_BB_INSNS (cur_bb, cur_insn)
+       {
+         if (INSN_P (cur_insn))
+           note_stores (PATTERN (cur_insn),
+                        s390_reg_clobbered_rtx, 
+                        regs_ever_clobbered);
+       }
+    }
+}
+
+/* Determine the frame area which actually has to be accessed 
+   in the function epilogue. The values are stored at the 
+   given pointers AREA_BOTTOM (address of the lowest used stack
+   address) and AREA_TOP (address of the first item which does 
+   not belong to the stack frame).  */
+
+static void
+s390_frame_area (int *area_bottom, int *area_top)
+{
+  int b, t;
+  int i;
+
+  b = INT_MAX;
+  t = INT_MIN;
+
+  if (cfun_frame_layout.first_restore_gpr != -1)
+    {
+      b = (cfun_frame_layout.gprs_offset
+          + cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
+      t = b + (cfun_frame_layout.last_restore_gpr
+              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
+    }
+
+  if (TARGET_64BIT && cfun_save_high_fprs_p)
+    {
+      b = MIN (b, cfun_frame_layout.f8_offset);
+      t = MAX (t, (cfun_frame_layout.f8_offset
+                  + cfun_frame_layout.high_fprs * 8));
+    }
+
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (cfun_fpr_bit_p (i))
+       {
+         b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
+         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
+       }
+  
+  *area_bottom = b;
+  *area_top = t;
+}
+
+/* Fill cfun->machine with info about register usage of current function.
+   Return in CLOBBERED_REGS which GPRs are currently considered set.  */
+
+static void
+s390_register_info (int clobbered_regs[])
+{
+  int i, j;
+
+  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
+  cfun_frame_layout.fpr_bitmap = 0;
+  cfun_frame_layout.high_fprs = 0;
+  if (TARGET_64BIT)
+    for (i = 24; i < 32; i++)
+      if (df_regs_ever_live_p (i) && !global_regs[i])
+       {
+         cfun_set_fpr_bit (i - 16);
+         cfun_frame_layout.high_fprs++;
+       }
+
+  /* Find first and last gpr to be saved.  We trust regs_ever_live
+     data, except that we don't save and restore global registers.
+
+     Also, all registers with special meaning to the compiler need
+     to be handled extra.  */
+
+  s390_regs_ever_clobbered (clobbered_regs);
+
+  for (i = 0; i < 16; i++)
+    clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
+
+  if (frame_pointer_needed)
+    clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
+
+  if (flag_pic)
+    clobbered_regs[PIC_OFFSET_TABLE_REGNUM] 
+      |= df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
+
+  clobbered_regs[BASE_REGNUM] 
+    |= (cfun->machine->base_reg
+        && REGNO (cfun->machine->base_reg) == BASE_REGNUM);
+
+  clobbered_regs[RETURN_REGNUM]
+    |= (!current_function_is_leaf
+       || TARGET_TPF_PROFILING
+       || cfun->machine->split_branches_pending_p
+       || cfun_frame_layout.save_return_addr_p
+       || crtl->calls_eh_return
+       || cfun->stdarg);
+
+  clobbered_regs[STACK_POINTER_REGNUM]
+    |= (!current_function_is_leaf
+       || TARGET_TPF_PROFILING
+       || cfun_save_high_fprs_p
+       || get_frame_size () > 0
+       || cfun->calls_alloca
+       || cfun->stdarg);
+
+  for (i = 6; i < 16; i++)
+    if (df_regs_ever_live_p (i) || clobbered_regs[i])
+      break;
+  for (j = 15; j > i; j--)
+    if (df_regs_ever_live_p (j) || clobbered_regs[j])
+      break;
+
+  if (i == 16)
+    {
+      /* Nothing to save/restore.  */
+      cfun_frame_layout.first_save_gpr_slot = -1;
+      cfun_frame_layout.last_save_gpr_slot = -1;
+      cfun_frame_layout.first_save_gpr = -1;
+      cfun_frame_layout.first_restore_gpr = -1;
+      cfun_frame_layout.last_save_gpr = -1;
+      cfun_frame_layout.last_restore_gpr = -1;
+    }
+  else
+    {
+      /* Save slots for gprs from i to j.  */
+      cfun_frame_layout.first_save_gpr_slot = i;
+      cfun_frame_layout.last_save_gpr_slot = j;
+
+      for (i = cfun_frame_layout.first_save_gpr_slot; 
+          i < cfun_frame_layout.last_save_gpr_slot + 1; 
+          i++)
+       if (clobbered_regs[i])
+         break;
+
+      for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--)
+       if (clobbered_regs[j])
+         break;
+      
+      if (i == cfun_frame_layout.last_save_gpr_slot + 1)
+       {
+         /* Nothing to save/restore.  */
+         cfun_frame_layout.first_save_gpr = -1;
+         cfun_frame_layout.first_restore_gpr = -1;
+         cfun_frame_layout.last_save_gpr = -1;
+         cfun_frame_layout.last_restore_gpr = -1;
+       }
+      else
+       {
+         /* Save / Restore from gpr i to j.  */
+         cfun_frame_layout.first_save_gpr = i;
+         cfun_frame_layout.first_restore_gpr = i;
+         cfun_frame_layout.last_save_gpr = j;
+         cfun_frame_layout.last_restore_gpr = j;
+       }
+    }
+
+  if (cfun->stdarg)
+    {
+      /* Varargs functions need to save gprs 2 to 6.  */
+      if (cfun->va_list_gpr_size
+         && crtl->args.info.gprs < GP_ARG_NUM_REG)
+       {
+         int min_gpr = crtl->args.info.gprs;
+         int max_gpr = min_gpr + cfun->va_list_gpr_size;
+         if (max_gpr > GP_ARG_NUM_REG)
+           max_gpr = GP_ARG_NUM_REG;
+
+         if (cfun_frame_layout.first_save_gpr == -1
+             || cfun_frame_layout.first_save_gpr > 2 + min_gpr)
+           {
+             cfun_frame_layout.first_save_gpr = 2 + min_gpr;
+             cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr;
+           }
+
+         if (cfun_frame_layout.last_save_gpr == -1
+             || cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1)
+           {
+             cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1;
+             cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1;
+           }
+       }
+
+      /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved.  */
+      if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size
+         && crtl->args.info.fprs < FP_ARG_NUM_REG)
+       {
+         int min_fpr = crtl->args.info.fprs;
+         int max_fpr = min_fpr + cfun->va_list_fpr_size;
+         if (max_fpr > FP_ARG_NUM_REG)
+           max_fpr = FP_ARG_NUM_REG;
+
+         /* ??? This is currently required to ensure proper location
+            of the fpr save slots within the va_list save area.  */
+         if (TARGET_PACKED_STACK)
+           min_fpr = 0;
+
+         for (i = min_fpr; i < max_fpr; i++)
+           cfun_set_fpr_bit (i);
+       }
+    }
+
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (df_regs_ever_live_p (i + 16) && !global_regs[i + 16])
+       cfun_set_fpr_bit (i);
+}
+
+/* Fill cfun->machine with info about frame of current function.  */
+
+static void
+s390_frame_info (void)
+{
+  int i;
+
+  cfun_frame_layout.frame_size = get_frame_size ();
+  if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
+    fatal_error ("total size of local variables exceeds architecture limit");
+  
+  if (!TARGET_PACKED_STACK)
+    {
+      cfun_frame_layout.backchain_offset = 0;
+      cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
+      cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
+      cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
+      cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot
+                                      * UNITS_PER_WORD);
+    }
+  else if (TARGET_BACKCHAIN) /* kernel stack layout */
+    {
+      cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
+                                           - UNITS_PER_WORD);
+      cfun_frame_layout.gprs_offset 
+       = (cfun_frame_layout.backchain_offset 
+          - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1)
+          * UNITS_PER_WORD);
+         
+      if (TARGET_64BIT)
+       {
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.gprs_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+         
+         cfun_frame_layout.f0_offset 
+           = (cfun_frame_layout.f4_offset 
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+       }
+      else
+       {
+         /* On 31 bit we have to care about alignment of the
+            floating point regs to provide fastest access.  */
+         cfun_frame_layout.f0_offset 
+           = ((cfun_frame_layout.gprs_offset 
+               & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1))
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+         
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.f0_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+       }
+    }
+  else /* no backchain */
+    {
+      cfun_frame_layout.f4_offset 
+       = (STACK_POINTER_OFFSET
+          - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+      
+      cfun_frame_layout.f0_offset 
+       = (cfun_frame_layout.f4_offset
+          - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+      
+      cfun_frame_layout.gprs_offset 
+       = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
+    }
+
+  if (current_function_is_leaf
+      && !TARGET_TPF_PROFILING
+      && cfun_frame_layout.frame_size == 0
+      && !cfun_save_high_fprs_p
+      && !cfun->calls_alloca
+      && !cfun->stdarg)
+    return;
+
+  if (!TARGET_PACKED_STACK)
+    cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET
+                                    + crtl->outgoing_args_size
+                                    + cfun_frame_layout.high_fprs * 8);
+  else
+    {
+      if (TARGET_BACKCHAIN)
+       cfun_frame_layout.frame_size += UNITS_PER_WORD;
+
+      /* No alignment trouble here because f8-f15 are only saved under 
+        64 bit.  */
+      cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
+                                              cfun_frame_layout.f4_offset),
+                                         cfun_frame_layout.gprs_offset)
+                                    - cfun_frame_layout.high_fprs * 8);
+
+      cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
+
+      for (i = 0; i < 8; i++)
+       if (cfun_fpr_bit_p (i))
+         cfun_frame_layout.frame_size += 8;
+      
+      cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
+      
+      /* If under 31 bit an odd number of gprs has to be saved we have to adjust
+        the frame size to sustain 8 byte alignment of stack frames.  */
+      cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
+                                      STACK_BOUNDARY / BITS_PER_UNIT - 1)
+                                     & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
+
+      cfun_frame_layout.frame_size += crtl->outgoing_args_size;
+    }
+}
+
+/* Generate frame layout.  Fills in register and frame data for the current
+   function in cfun->machine.  This routine can be called multiple times;
+   it will re-do the complete frame layout every time.  */
+
+static void
+s390_init_frame_layout (void)
+{
+  HOST_WIDE_INT frame_size;
+  int base_used;
+  int clobbered_regs[16];
+
+  /* On S/390 machines, we may need to perform branch splitting, which
+     will require both base and return address register.  We have no
+     choice but to assume we're going to need them until right at the
+     end of the machine dependent reorg phase.  */
+  if (!TARGET_CPU_ZARCH)
+    cfun->machine->split_branches_pending_p = true;
+
+  do
+    {
+      frame_size = cfun_frame_layout.frame_size;
+
+      /* Try to predict whether we'll need the base register.  */
+      base_used = cfun->machine->split_branches_pending_p
+                 || crtl->uses_const_pool
+                 || (!DISP_IN_RANGE (frame_size)
+                     && !CONST_OK_FOR_K (frame_size));
+
+      /* Decide which register to use as literal pool base.  In small
+        leaf functions, try to use an unused call-clobbered register
+        as base register to avoid save/restore overhead.  */
+      if (!base_used)
+       cfun->machine->base_reg = NULL_RTX;
+      else if (current_function_is_leaf && !df_regs_ever_live_p (5))
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
+      else
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
+
+      s390_register_info (clobbered_regs);
+      s390_frame_info ();
+    }
+  while (frame_size != cfun_frame_layout.frame_size);
+}
+
+/* Update frame layout.  Recompute actual register save data based on
+   current info and update regs_ever_live for the special registers.
+   May be called multiple times, but may never cause *more* registers
+   to be saved than s390_init_frame_layout allocated room for.  */
+
+static void
+s390_update_frame_layout (void)
+{
+  int clobbered_regs[16];
+
+  s390_register_info (clobbered_regs);
+
+  df_set_regs_ever_live (BASE_REGNUM, 
+                        clobbered_regs[BASE_REGNUM] ? true : false);
+  df_set_regs_ever_live (RETURN_REGNUM, 
+                        clobbered_regs[RETURN_REGNUM] ? true : false);
+  df_set_regs_ever_live (STACK_POINTER_REGNUM, 
+                        clobbered_regs[STACK_POINTER_REGNUM] ? true : false);
+
+  if (cfun->machine->base_reg)
+    df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true);
+}
+
+/* Return true if it is legal to put a value with MODE into REGNO.  */
+
+bool
+s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
+{
+  switch (REGNO_REG_CLASS (regno))
+    {
+    case FP_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (mode == SImode || mode == DImode)
+           return true;
+
+         if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
+           return true;
+       }
+      break;
+    case ADDR_REGS:
+      if (FRAME_REGNO_P (regno) && mode == Pmode)
+       return true;
+
+      /* fallthrough */
+    case GENERAL_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (TARGET_64BIT 
+             || (mode != TFmode && mode != TCmode && mode != TDmode))
+           return true;
+       }         
+      break;
+    case CC_REGS:
+      if (GET_MODE_CLASS (mode) == MODE_CC)
+       return true;
+      break;
+    case ACCESS_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (mode == SImode || mode == Pmode)
+           return true;
+       }
+      break;
+    default:
+      return false;
+    }
+  
+  return false;
+}
+
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
+
+bool
+s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg)
+{
+   /* Once we've decided upon a register to use as base register, it must
+      no longer be used for any other purpose.  */
+  if (cfun->machine->base_reg)
+    if (REGNO (cfun->machine->base_reg) == old_reg
+       || REGNO (cfun->machine->base_reg) == new_reg)
+      return false;
+
+  return true;
+}
+
+/* Maximum number of registers to represent a value of mode MODE
+   in a register of class RCLASS.  */
+
+bool
+s390_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
+{
+  switch (rclass)
+    {
+    case FP_REGS:
+      if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+       return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8);
+      else
+       return (GET_MODE_SIZE (mode) + 8 - 1) / 8;
+    case ACCESS_REGS:
+      return (GET_MODE_SIZE (mode) + 4 - 1) / 4;
+    default:
+      break;
+    }
+  return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Return true if register FROM can be eliminated via register TO.  */
+
+bool
+s390_can_eliminate (int from, int to)
+{
+  /* On zSeries machines, we have not marked the base register as fixed.
+     Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
+     If a function requires the base register, we say here that this
+     elimination cannot be performed.  This will cause reload to free
+     up the base register (as if it were fixed).  On the other hand,
+     if the current function does *not* require the base register, we
+     say here the elimination succeeds, which in turn allows reload
+     to allocate the base register for any other purpose.  */
+  if (from == BASE_REGNUM && to == BASE_REGNUM)
+    {
+      if (TARGET_CPU_ZARCH)
+       {
+         s390_init_frame_layout ();
+         return cfun->machine->base_reg == NULL_RTX;
+       }
+
+      return false;
+    }
+
+  /* Everything else must point into the stack frame.  */
+  gcc_assert (to == STACK_POINTER_REGNUM
+             || to == HARD_FRAME_POINTER_REGNUM);
+
+  gcc_assert (from == FRAME_POINTER_REGNUM
+             || from == ARG_POINTER_REGNUM
+             || from == RETURN_ADDRESS_POINTER_REGNUM);
+
+  /* Make sure we actually saved the return address.  */
+  if (from == RETURN_ADDRESS_POINTER_REGNUM)
+    if (!crtl->calls_eh_return
+       && !cfun->stdarg
+       && !cfun_frame_layout.save_return_addr_p)
+      return false;
+
+  return true;
+}
+
+/* Return offset between register FROM and TO initially after prolog.  */
+
+HOST_WIDE_INT
+s390_initial_elimination_offset (int from, int to)
+{
+  HOST_WIDE_INT offset;
+  int index;
+
+  /* ??? Why are we called for non-eliminable pairs?  */
+  if (!s390_can_eliminate (from, to))
+    return 0;
+
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      offset = (get_frame_size() 
+               + STACK_POINTER_OFFSET
+               + crtl->outgoing_args_size);
+      break;
+
+    case ARG_POINTER_REGNUM:
+      s390_init_frame_layout ();
+      offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
+      break;
+
+    case RETURN_ADDRESS_POINTER_REGNUM:
+      s390_init_frame_layout ();
+      index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot;
+      gcc_assert (index >= 0);
+      offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset;
+      offset += index * UNITS_PER_WORD;
+      break;
+
+    case BASE_REGNUM:
+      offset = 0;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return offset;
+}
+
+/* Emit insn to save fpr REGNUM at offset OFFSET relative
+   to register BASE.  Return generated insn.  */
+
+static rtx
+save_fpr (rtx base, int offset, int regnum)
+{
+  rtx addr;
+  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
+
+  if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
+    set_mem_alias_set (addr, get_varargs_alias_set ());
+  else
+    set_mem_alias_set (addr, get_frame_alias_set ());
+
+  return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
+}
+
+/* Emit insn to restore fpr REGNUM from offset OFFSET relative
+   to register BASE.  Return generated insn.  */
+
+static rtx
+restore_fpr (rtx base, int offset, int regnum)
+{
+  rtx addr;
+  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
+  set_mem_alias_set (addr, get_frame_alias_set ());
+
+  return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
+}
+
+/* Generate insn to save registers FIRST to LAST into
+   the register save area located at offset OFFSET
+   relative to register BASE.  */
+
+static rtx
+save_gprs (rtx base, int offset, int first, int last)
+{
+  rtx addr, insn, note;
+  int i;
+
+  addr = plus_constant (base, offset);
+  addr = gen_rtx_MEM (Pmode, addr);
+
+  set_mem_alias_set (addr, get_frame_alias_set ());
+
+  /* Special-case single register.  */
+  if (first == last)
+    {
+      if (TARGET_64BIT)
+        insn = gen_movdi (addr, gen_rtx_REG (Pmode, first));
+      else
+        insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+      return insn;
+    }
+
+
+  insn = gen_store_multiple (addr,
+                            gen_rtx_REG (Pmode, first),
+                            GEN_INT (last - first + 1));
+
+  if (first <= 6 && cfun->stdarg)
+    for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+      {
+       rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0);
+       
+       if (first + i <= 6)
+         set_mem_alias_set (mem, get_varargs_alias_set ());
+      }
+
+  /* We need to set the FRAME_RELATED flag on all SETs
+     inside the store-multiple pattern.
+
+     However, we must not emit DWARF records for registers 2..5
+     if they are stored for use by variable arguments ...
+
+     ??? Unfortunately, it is not enough to simply not the
+     FRAME_RELATED flags for those SETs, because the first SET
+     of the PARALLEL is always treated as if it had the flag
+     set, even if it does not.  Therefore we emit a new pattern
+     without those registers as REG_FRAME_RELATED_EXPR note.  */
+
+  if (first >= 6)
+    {
+      rtx pat = PATTERN (insn);
+
+      for (i = 0; i < XVECLEN (pat, 0); i++)
+       if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
+         RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+  else if (last >= 6)
+    {
+      addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
+      note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
+                                gen_rtx_REG (Pmode, 6),
+                                GEN_INT (last - 6 + 1));
+      note = PATTERN (note);
+
+      REG_NOTES (insn) =
+       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                          note, REG_NOTES (insn));
+
+      for (i = 0; i < XVECLEN (note, 0); i++)
+       if (GET_CODE (XVECEXP (note, 0, i)) == SET)
+         RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+
+  return insn;
+}
+
+/* Generate insn to restore registers FIRST to LAST from
+   the register save area located at offset OFFSET
+   relative to register BASE.  */
+
+static rtx
+restore_gprs (rtx base, int offset, int first, int last)
+{
+  rtx addr, insn;
+
+  addr = plus_constant (base, offset);
+  addr = gen_rtx_MEM (Pmode, addr);
+  set_mem_alias_set (addr, get_frame_alias_set ());
+
+  /* Special-case single register.  */
+  if (first == last)
+    {
+      if (TARGET_64BIT)
+        insn = gen_movdi (gen_rtx_REG (Pmode, first), addr);
+      else
+        insn = gen_movsi (gen_rtx_REG (Pmode, first), addr);
+
+      return insn;
+    }
+
+  insn = gen_load_multiple (gen_rtx_REG (Pmode, first),
+                           addr,
+                           GEN_INT (last - first + 1));
+  return insn;
+}
+
+/* Return insn sequence to load the GOT register.  */
+
+static GTY(()) rtx got_symbol;
+rtx
+s390_load_got (void)
+{
+  rtx insns;
+
+  if (!got_symbol)
+    {
+      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
+    }
+
+  start_sequence ();
+
+  if (TARGET_CPU_ZARCH)
+    {
+      emit_move_insn (pic_offset_table_rtx, got_symbol);
+    }
+  else
+    {
+      rtx offset;
+
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol),
+                              UNSPEC_LTREL_OFFSET);
+      offset = gen_rtx_CONST (Pmode, offset);
+      offset = force_const_mem (Pmode, offset);
+
+      emit_move_insn (pic_offset_table_rtx, offset);
+
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
+                              UNSPEC_LTREL_BASE);
+      offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
+
+      emit_move_insn (pic_offset_table_rtx, offset);
+    }
+
+  insns = get_insns ();
+  end_sequence ();
+  return insns;
+}
+
+/* This ties together stack memory (MEM with an alias set of frame_alias_set)
+   and the change to the stack pointer.  */
+
+static void
+s390_emit_stack_tie (void)
+{
+  rtx mem = gen_frame_mem (BLKmode,
+                          gen_rtx_REG (Pmode, STACK_POINTER_REGNUM));
+
+  emit_insn (gen_stack_tie (mem));
+}
+
+/* Expand the prologue into a bunch of separate insns.  */
+
+void
+s390_emit_prologue (void)
+{
+  rtx insn, addr;
+  rtx temp_reg;
+  int i;
+  int offset;
+  int next_fpr = 0;
+
+  /* Complete frame layout.  */
+
+  s390_update_frame_layout ();
+
+  /* Annotate all constant pool references to let the scheduler know
+     they implicitly use the base register.  */
+
+  push_topmost_sequence ();
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (INSN_P (insn))
+      {
+       annotate_constant_pool_refs (&PATTERN (insn));
+       df_insn_rescan (insn);
+      }
+
+  pop_topmost_sequence ();
+
+  /* Choose best register to use for temp use within prologue.
+     See below for why TPF must use the register 1.  */
+
+  if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) 
+      && !current_function_is_leaf 
+      && !TARGET_TPF_PROFILING)
+    temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  else
+    temp_reg = gen_rtx_REG (Pmode, 1);
+
+  /* Save call saved gprs.  */
+  if (cfun_frame_layout.first_save_gpr != -1)
+    {
+      insn = save_gprs (stack_pointer_rtx, 
+                       cfun_frame_layout.gprs_offset + 
+                       UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr 
+                                         - cfun_frame_layout.first_save_gpr_slot),
+                       cfun_frame_layout.first_save_gpr, 
+                       cfun_frame_layout.last_save_gpr);
+      emit_insn (insn);
+    }
+
+  /* Dummy insn to mark literal pool slot.  */
+
+  if (cfun->machine->base_reg)
+    emit_insn (gen_main_pool (cfun->machine->base_reg));
+
+  offset = cfun_frame_layout.f0_offset;
+
+  /* Save f0 and f2.  */
+  for (i = 0; i < 2; i++)
+    {
+      if (cfun_fpr_bit_p (i))
+       {
+         save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+       }
+      else if (!TARGET_PACKED_STACK)
+         offset += 8;
+    }
+
+  /* Save f4 and f6.  */
+  offset = cfun_frame_layout.f4_offset;
+  for (i = 2; i < 4; i++)
+    {
+      if (cfun_fpr_bit_p (i))
+       {
+         insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+
+         /* If f4 and f6 are call clobbered they are saved due to stdargs and
+            therefore are not frame related.  */
+         if (!call_really_used_regs[i + 16])
+           RTX_FRAME_RELATED_P (insn) = 1;
+       }
+      else if (!TARGET_PACKED_STACK)
+       offset += 8;
+    }
+
+  if (TARGET_PACKED_STACK
+      && cfun_save_high_fprs_p
+      && cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
+    {
+      offset = (cfun_frame_layout.f8_offset
+               + (cfun_frame_layout.high_fprs - 1) * 8);
+
+      for (i = 15; i > 7 && offset >= 0; i--)
+       if (cfun_fpr_bit_p (i))
+         {
+           insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+                      
+           RTX_FRAME_RELATED_P (insn) = 1;
+           offset -= 8;
+         }
+      if (offset >= cfun_frame_layout.f8_offset)
+       next_fpr = i + 16;
+    }
+  
+  if (!TARGET_PACKED_STACK)
+    next_fpr = cfun_save_high_fprs_p ? 31 : 0;
+
+  /* Decrement stack pointer.  */
+
+  if (cfun_frame_layout.frame_size > 0)
+    {
+      rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
+
+      if (s390_stack_size)
+       {
+         HOST_WIDE_INT stack_guard;
+
+         if (s390_stack_guard)
+           stack_guard = s390_stack_guard;
+         else
+           {
+             /* If no value for stack guard is provided the smallest power of 2
+                larger than the current frame size is chosen.  */
+             stack_guard = 1;
+             while (stack_guard < cfun_frame_layout.frame_size)
+               stack_guard <<= 1;
+           }
+
+         if (cfun_frame_layout.frame_size >= s390_stack_size)
+           {
+             warning (0, "frame size of function %qs is "
+                      HOST_WIDE_INT_PRINT_DEC
+                      " bytes exceeding user provided stack limit of "
+                      HOST_WIDE_INT_PRINT_DEC " bytes.  "
+                      "An unconditional trap is added.",
+                      current_function_name(), cfun_frame_layout.frame_size,
+                      s390_stack_size);
+             emit_insn (gen_trap ());
+           }
+         else
+           {
+             HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
+                                               & ~(stack_guard - 1));
+             rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
+                                  GEN_INT (stack_check_mask));
+             if (TARGET_64BIT)
+               gen_cmpdi (t, const0_rtx);
+             else
+               gen_cmpsi (t, const0_rtx);
+
+             emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode,
+                                                          gen_rtx_REG (CCmode,
+                                                                    CC_REGNUM),
+                                                          const0_rtx),
+                                              const0_rtx));
+           }
+       }
+
+      if (s390_warn_framesize > 0 
+         && cfun_frame_layout.frame_size >= s390_warn_framesize)
+       warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes", 
+                current_function_name (), cfun_frame_layout.frame_size);
+
+      if (s390_warn_dynamicstack_p && cfun->calls_alloca)
+       warning (0, "%qs uses dynamic stack allocation", current_function_name ());
+
+      /* Save incoming stack pointer into temp reg.  */
+      if (TARGET_BACKCHAIN || next_fpr)
+       insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
+
+      /* Subtract frame size from stack pointer.  */
+
+      if (DISP_IN_RANGE (INTVAL (frame_off)))
+       {
+         insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                             gen_rtx_PLUS (Pmode, stack_pointer_rtx, 
+                                           frame_off));
+         insn = emit_insn (insn);
+       }
+      else
+       {
+         if (!CONST_OK_FOR_K (INTVAL (frame_off)))
+           frame_off = force_const_mem (Pmode, frame_off);
+
+          insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
+         annotate_constant_pool_refs (&PATTERN (insn));
+       }
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+      REG_NOTES (insn) =
+       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                              GEN_INT (-cfun_frame_layout.frame_size))),
+                          REG_NOTES (insn));
+
+      /* Set backchain.  */
+
+      if (TARGET_BACKCHAIN)
+       {
+         if (cfun_frame_layout.backchain_offset)
+           addr = gen_rtx_MEM (Pmode, 
+                               plus_constant (stack_pointer_rtx, 
+                                 cfun_frame_layout.backchain_offset));
+         else
+           addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);  
+         set_mem_alias_set (addr, get_frame_alias_set ());
+         insn = emit_insn (gen_move_insn (addr, temp_reg));
+       }
+
+      /* If we support asynchronous exceptions (e.g. for Java),
+        we need to make sure the backchain pointer is set up
+        before any possibly trapping memory access.  */
+
+      if (TARGET_BACKCHAIN && flag_non_call_exceptions)
+       {
+         addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+         emit_clobber (addr);
+       }
+    }
+
+  /* Save fprs 8 - 15 (64 bit ABI).  */
+
+  if (cfun_save_high_fprs_p && next_fpr)
+    {
+      /* If the stack might be accessed through a different register
+        we have to make sure that the stack pointer decrement is not
+        moved below the use of the stack slots.  */
+      s390_emit_stack_tie ();
+
+      insn = emit_insn (gen_add2_insn (temp_reg, 
+                                      GEN_INT (cfun_frame_layout.f8_offset)));
+
+      offset = 0;
+
+      for (i = 24; i <= next_fpr; i++)
+       if (cfun_fpr_bit_p (i - 16))
+         {
+           rtx addr = plus_constant (stack_pointer_rtx,
+                                     cfun_frame_layout.frame_size
+                                     + cfun_frame_layout.f8_offset
+                                     + offset);
+           
+           insn = save_fpr (temp_reg, offset, i);
+           offset += 8;
+           RTX_FRAME_RELATED_P (insn) = 1;
+           REG_NOTES (insn) =
+             gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                gen_rtx_SET (VOIDmode,
+                                             gen_rtx_MEM (DFmode, addr),
+                                             gen_rtx_REG (DFmode, i)),
+                                REG_NOTES (insn));
+         }
+    }
+
+  /* Set frame pointer, if needed.  */
+
+  if (frame_pointer_needed)
+    {
+      insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+
+  /* Set up got pointer, if needed.  */
+
+  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+    {
+      rtx insns = s390_load_got ();
+
+      for (insn = insns; insn; insn = NEXT_INSN (insn))
+       annotate_constant_pool_refs (&PATTERN (insn));
+
+      emit_insn (insns);
+    }
+
+  if (TARGET_TPF_PROFILING)
+    {
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.  */
+      emit_insn (gen_prologue_tpf ());
+
+      /* Emit a blockage here so that all code
+        lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
+    }
+}
+
+/* Expand the epilogue into a bunch of separate insns.  */
+
+void
+s390_emit_epilogue (bool sibcall)
+{
+  rtx frame_pointer, return_reg;
+  int area_bottom, area_top, offset = 0;
+  int next_offset;
+  rtvec p;
+  int i;
+
+  if (TARGET_TPF_PROFILING)
+    {
+
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.  */
+
+      /* Emit a blockage here so that all code
+         lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
+
+      emit_insn (gen_epilogue_tpf ());
+    }
+
+  /* Check whether to use frame or stack pointer for restore.  */
+
+  frame_pointer = (frame_pointer_needed 
+                  ? hard_frame_pointer_rtx : stack_pointer_rtx);
+
+  s390_frame_area (&area_bottom, &area_top);
+
+  /* Check whether we can access the register save area.
+     If not, increment the frame pointer as required.  */
+
+  if (area_top <= area_bottom)
+    {
+      /* Nothing to restore.  */
+    }
+  else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
+           && DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
+    {
+      /* Area is in range.  */
+      offset = cfun_frame_layout.frame_size;
+    }
+  else
+    {
+      rtx insn, frame_off;
+
+      offset = area_bottom < 0 ? -area_bottom : 0;
+      frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
+
+      if (DISP_IN_RANGE (INTVAL (frame_off)))
+       {
+         insn = gen_rtx_SET (VOIDmode, frame_pointer,
+                             gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
+         insn = emit_insn (insn);
+       }
+      else
+       {
+         if (!CONST_OK_FOR_K (INTVAL (frame_off)))
+           frame_off = force_const_mem (Pmode, frame_off);
+
+         insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+         annotate_constant_pool_refs (&PATTERN (insn));
+       }
+    }
+
+  /* Restore call saved fprs.  */
+
+  if (TARGET_64BIT)
+    {
+      if (cfun_save_high_fprs_p)
+       {
+         next_offset = cfun_frame_layout.f8_offset;
+         for (i = 24; i < 32; i++)
+           {
+             if (cfun_fpr_bit_p (i - 16))
+               {
+                 restore_fpr (frame_pointer,
+                              offset + next_offset, i);
+                 next_offset += 8;
+               }
+           }
+       }
+             
+    }
+  else
+    {
+      next_offset = cfun_frame_layout.f4_offset;
+      for (i = 18; i < 20; i++)
+       {
+         if (cfun_fpr_bit_p (i - 16))
+           {
+             restore_fpr (frame_pointer,
+                          offset + next_offset, i);
+             next_offset += 8;
+           }
+         else if (!TARGET_PACKED_STACK)
+           next_offset += 8;
+       }
+      
+    }
+
+  /* Return register.  */
+
+  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+
+  /* Restore call saved gprs.  */
+
+  if (cfun_frame_layout.first_restore_gpr != -1)
+    {
+      rtx insn, addr;
+      int i;
+
+      /* Check for global register and save them
+        to stack location from where they get restored.  */
+
+      for (i = cfun_frame_layout.first_restore_gpr;
+          i <= cfun_frame_layout.last_restore_gpr;
+          i++)
+       {
+         /* These registers are special and need to be
+            restored in any case.  */
+         if (i == STACK_POINTER_REGNUM
+              || i == RETURN_REGNUM
+              || i == BASE_REGNUM
+              || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
+           continue;
+
+         if (global_regs[i])
+           {
+             addr = plus_constant (frame_pointer,
+                                   offset + cfun_frame_layout.gprs_offset 
+                                   + (i - cfun_frame_layout.first_save_gpr_slot)
+                                   * UNITS_PER_WORD);
+             addr = gen_rtx_MEM (Pmode, addr);
+             set_mem_alias_set (addr, get_frame_alias_set ());
+             emit_move_insn (addr, gen_rtx_REG (Pmode, i));
+           }
+       }
+
+      if (! sibcall)
+       {
+         /* Fetch return address from stack before load multiple,
+            this will do good for scheduling.  */
+
+         if (cfun_frame_layout.save_return_addr_p
+             || (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
+                 && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
+           {
+             int return_regnum = find_unused_clobbered_reg();
+             if (!return_regnum)
+               return_regnum = 4;
+             return_reg = gen_rtx_REG (Pmode, return_regnum);
+
+             addr = plus_constant (frame_pointer,
+                                   offset + cfun_frame_layout.gprs_offset
+                                   + (RETURN_REGNUM 
+                                      - cfun_frame_layout.first_save_gpr_slot)
+                                   * UNITS_PER_WORD);
+             addr = gen_rtx_MEM (Pmode, addr);
+             set_mem_alias_set (addr, get_frame_alias_set ());
+             emit_move_insn (return_reg, addr);
+           }
+       }
+
+      insn = restore_gprs (frame_pointer,
+                          offset + cfun_frame_layout.gprs_offset
+                          + (cfun_frame_layout.first_restore_gpr 
+                             - cfun_frame_layout.first_save_gpr_slot)
+                          * UNITS_PER_WORD,
+                          cfun_frame_layout.first_restore_gpr,
+                          cfun_frame_layout.last_restore_gpr);
+      emit_insn (insn);
+    }
+
+  if (! sibcall)
+    {
+
+      /* Return to caller.  */
+
+      p = rtvec_alloc (2);
+
+      RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
+      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
+      emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
+    }
+}
+
+
+/* Return the size in bytes of a function argument of
+   type TYPE and/or mode MODE.  At least one of TYPE or
+   MODE must be specified.  */
+
+static int
+s390_function_arg_size (enum machine_mode mode, const_tree type)
+{
+  if (type)
+    return int_size_in_bytes (type);
+
+  /* No type info available for some library calls ...  */
+  if (mode != BLKmode)
+    return GET_MODE_SIZE (mode);
+
+  /* If we have neither type nor mode, abort */
+  gcc_unreachable ();
+}
+
+/* Return true if a function argument of type TYPE and mode MODE
+   is to be passed in a floating-point register, if available.  */
+
+static bool
+s390_function_arg_float (enum machine_mode mode, tree type)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
+
+  /* Soft-float changes the ABI: no floating-point registers are used.  */
+  if (TARGET_SOFT_FLOAT)
+    return false;
+
+  /* No type info available for some library calls ...  */
+  if (!type)
+    return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode;
+
+  /* The ABI says that record types with a single member are treated
+     just like that member would be.  */
+  while (TREE_CODE (type) == RECORD_TYPE)
+    {
+      tree field, single = NULL_TREE;
+
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       {
+         if (TREE_CODE (field) != FIELD_DECL)
+           continue;
+
+         if (single == NULL_TREE)
+           single = TREE_TYPE (field);
+         else
+           return false;
+       }
+
+      if (single == NULL_TREE)
+       return false;
+      else
+       type = single;
+    }
+
+  return TREE_CODE (type) == REAL_TYPE;
+}
+
+/* Return true if a function argument of type TYPE and mode MODE
+   is to be passed in an integer register, or a pair of integer
+   registers, if available.  */
+
+static bool
+s390_function_arg_integer (enum machine_mode mode, tree type)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
+
+  /* No type info available for some library calls ...  */
+  if (!type)
+    return GET_MODE_CLASS (mode) == MODE_INT
+          || (TARGET_SOFT_FLOAT &&  SCALAR_FLOAT_MODE_P (mode));
+
+  /* We accept small integral (and similar) types.  */
+  if (INTEGRAL_TYPE_P (type)
+      || POINTER_TYPE_P (type)
+      || TREE_CODE (type) == OFFSET_TYPE
+      || (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE))
+    return true;
+
+  /* We also accept structs of size 1, 2, 4, 8 that are not
+     passed in floating-point registers.  */
+  if (AGGREGATE_TYPE_P (type)
+      && exact_log2 (size) >= 0
+      && !s390_function_arg_float (mode, type))
+    return true;
+
+  return false;
+}
+
+/* Return 1 if a function argument of type TYPE and mode MODE
+   is to be passed by reference.  The ABI specifies that only
+   structures of size 1, 2, 4, or 8 bytes are passed by value,
+   all other structures (and complex numbers) are passed by
+   reference.  */
+
+static bool
+s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+                       enum machine_mode mode, const_tree type,
+                       bool named ATTRIBUTE_UNUSED)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return true;
+
+  if (type)
+    {
+      if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
+        return 1;
+
+      if (TREE_CODE (type) == COMPLEX_TYPE
+         || TREE_CODE (type) == VECTOR_TYPE)
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Update the data in CUM to advance over an argument of mode MODE and
+   data type TYPE.  (TYPE is null for libcalls where that information
+   may not be available.).  The boolean NAMED specifies whether the
+   argument is a named argument (as opposed to an unnamed argument
+   matching an ellipsis).  */
+
+void
+s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                          tree type, int named ATTRIBUTE_UNUSED)
+{
+  if (s390_function_arg_float (mode, type))
+    {
+      cum->fprs += 1;
+    }
+  else if (s390_function_arg_integer (mode, type))
+    {
+      int size = s390_function_arg_size (mode, type);
+      cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
+    }
+  else
+    gcc_unreachable ();
+}
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).
+
+   On S/390, we use general purpose registers 2 through 6 to
+   pass integer, pointer, and certain structure arguments, and
+   floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
+   to pass floating point arguments.  All remaining arguments
+   are pushed to the stack.  */
+
+rtx
+s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+                  int named ATTRIBUTE_UNUSED)
+{
+  if (s390_function_arg_float (mode, type))
+    {
+      if (cum->fprs + 1 > FP_ARG_NUM_REG)
+       return 0;
+      else
+       return gen_rtx_REG (mode, cum->fprs + 16);
+    }
+  else if (s390_function_arg_integer (mode, type))
+    {
+      int size = s390_function_arg_size (mode, type);
+      int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
+
+      if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
+       return 0;
+      else
+       return gen_rtx_REG (mode, cum->gprs + 2);
+    }
+
+  /* After the real arguments, expand_call calls us once again
+     with a void_type_node type.  Whatever we return here is
+     passed as operand 2 to the call expanders.
+
+     We don't need this feature ...  */
+  else if (type == void_type_node)
+    return const0_rtx;
+
+  gcc_unreachable ();
+}
+
+/* Return true if return values of type TYPE should be returned
+   in a memory buffer whose address is passed by the caller as
+   hidden first argument.  */
+
+static bool
+s390_return_in_memory (const_tree type, const_tree fundecl ATTRIBUTE_UNUSED)
+{
+  /* We accept small integral (and similar) types.  */
+  if (INTEGRAL_TYPE_P (type)
+      || POINTER_TYPE_P (type)
+      || TREE_CODE (type) == OFFSET_TYPE
+      || TREE_CODE (type) == REAL_TYPE)
+    return int_size_in_bytes (type) > 8;
+
+  /* Aggregates and similar constructs are always returned
+     in memory.  */
+  if (AGGREGATE_TYPE_P (type)
+      || TREE_CODE (type) == COMPLEX_TYPE
+      || TREE_CODE (type) == VECTOR_TYPE)
+    return true;
+
+  /* ??? We get called on all sorts of random stuff from
+     aggregate_value_p.  We can't abort, but it's not clear
+     what's safe to return.  Pretend it's a struct I guess.  */
+  return true;
+}
+
+/* Define where to return a (scalar) value of type TYPE.
+   If TYPE is null, define where to return a (scalar)
+   value of mode MODE from a libcall.  */
+
+rtx
+s390_function_value (const_tree type, enum machine_mode mode)
+{
+  if (type)
+    {
+      int unsignedp = TYPE_UNSIGNED (type);
+      mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
+    }
+
+  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode));
+  gcc_assert (GET_MODE_SIZE (mode) <= 8);
+
+  if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode))
+    return gen_rtx_REG (mode, 16);
+  else
+    return gen_rtx_REG (mode, 2);
+}
+
+
+/* Create and return the va_list datatype.
+
+   On S/390, va_list is an array type equivalent to
+
+      typedef struct __va_list_tag
+        {
+            long __gpr;
+            long __fpr;
+            void *__overflow_arg_area;
+            void *__reg_save_area;
+        } va_list[1];
+
+   where __gpr and __fpr hold the number of general purpose
+   or floating point arguments used up to now, respectively,
+   __overflow_arg_area points to the stack location of the
+   next argument passed on the stack, and __reg_save_area
+   always points to the start of the register area in the
+   call frame of the current function.  The function prologue
+   saves all registers used for argument passing into this
+   area if the function uses variable arguments.  */
+
+static tree
+s390_build_builtin_va_list (void)
+{
+  tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
+
+  record = lang_hooks.types.make_type (RECORD_TYPE);
+
+  type_decl =
+    build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
+
+  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
+                     long_integer_type_node);
+  f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
+                     long_integer_type_node);
+  f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
+                     ptr_type_node);
+  f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
+                     ptr_type_node);
+
+  va_list_gpr_counter_field = f_gpr;
+  va_list_fpr_counter_field = f_fpr;
+
+  DECL_FIELD_CONTEXT (f_gpr) = record;
+  DECL_FIELD_CONTEXT (f_fpr) = record;
+  DECL_FIELD_CONTEXT (f_ovf) = record;
+  DECL_FIELD_CONTEXT (f_sav) = record;
+
+  TREE_CHAIN (record) = type_decl;
+  TYPE_NAME (record) = type_decl;
+  TYPE_FIELDS (record) = f_gpr;
+  TREE_CHAIN (f_gpr) = f_fpr;
+  TREE_CHAIN (f_fpr) = f_ovf;
+  TREE_CHAIN (f_ovf) = f_sav;
+
+  layout_type (record);
+
+  /* The correct type is an array type of one element.  */
+  return build_array_type (record, build_index_type (size_zero_node));
+}
+
+/* Implement va_start by filling the va_list structure VALIST.
+   STDARG_P is always true, and ignored.
+   NEXTARG points to the first anonymous stack argument.
+
+   The following global variables are used to initialize
+   the va_list structure:
+
+     crtl->args.info:
+       holds number of gprs and fprs used for named arguments.
+     crtl->args.arg_offset_rtx:
+       holds the offset of the first anonymous stack argument
+       (relative to the virtual arg pointer).  */
+
+static void
+s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
+{
+  HOST_WIDE_INT n_gpr, n_fpr;
+  int off;
+  tree f_gpr, f_fpr, f_ovf, f_sav;
+  tree gpr, fpr, ovf, sav, t;
+
+  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+  f_fpr = TREE_CHAIN (f_gpr);
+  f_ovf = TREE_CHAIN (f_fpr);
+  f_sav = TREE_CHAIN (f_ovf);
+
+  valist = build_va_arg_indirect_ref (valist);
+  gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+
+  /* Count number of gp and fp argument registers used.  */
+
+  n_gpr = crtl->args.info.gprs;
+  n_fpr = crtl->args.info.fprs;
+
+  if (cfun->va_list_gpr_size)
+    {
+      t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
+                 build_int_cst (NULL_TREE, n_gpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+
+  if (cfun->va_list_fpr_size)
+    {
+      t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
+                 build_int_cst (NULL_TREE, n_fpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+
+  /* Find the overflow area.  */
+  if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG
+      || n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG)
+    {
+      t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
+
+      off = INTVAL (crtl->args.arg_offset_rtx);
+      off = off < 0 ? 0 : off;
+      if (TARGET_DEBUG_ARG)
+       fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
+                (int)n_gpr, (int)n_fpr, off);
+
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t, size_int (off));
+
+      t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+
+  /* Find the register save area.  */
+  if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG)
+      || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
+    {
+      t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t,
+                 size_int (-RETURN_REGNUM * UNITS_PER_WORD));
+  
+      t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+}
+
+/* Implement va_arg by updating the va_list structure
+   VALIST as required to retrieve an argument of type
+   TYPE, and returning that argument.
+
+   Generates code equivalent to:
+
+   if (integral value) {
+     if (size  <= 4 && args.gpr < 5 ||
+         size  > 4 && args.gpr < 4 )
+       ret = args.reg_save_area[args.gpr+8]
+     else
+       ret = *args.overflow_arg_area++;
+   } else if (float value) {
+     if (args.fgpr < 2)
+       ret = args.reg_save_area[args.fpr+64]
+     else
+       ret = *args.overflow_arg_area++;
+   } else if (aggregate value) {
+     if (args.gpr < 5)
+       ret = *args.reg_save_area[args.gpr]
+     else
+       ret = **args.overflow_arg_area++;
+   } */
+
+static tree
+s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, 
+                     gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+  tree f_gpr, f_fpr, f_ovf, f_sav;
+  tree gpr, fpr, ovf, sav, reg, t, u;
+  int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
+  tree lab_false, lab_over, addr;
+
+  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+  f_fpr = TREE_CHAIN (f_gpr);
+  f_ovf = TREE_CHAIN (f_fpr);
+  f_sav = TREE_CHAIN (f_ovf);
+
+  valist = build_va_arg_indirect_ref (valist);
+  gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+
+  /* The tree for args* cannot be shared between gpr/fpr and ovf since
+     both appear on a lhs.  */
+  valist = unshare_expr (valist);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+
+  size = int_size_in_bytes (type);
+
+  if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
+    {
+      if (TARGET_DEBUG_ARG)
+       {
+         fprintf (stderr, "va_arg: aggregate type");
+         debug_tree (type);
+       }
+
+      /* Aggregates are passed by reference.  */
+      indirect_p = 1;
+      reg = gpr;
+      n_reg = 1;
+
+      /* kernel stack layout on 31 bit: It is assumed here that no padding
+        will be added by s390_frame_info because for va_args always an even
+        number of gprs has to be saved r15-r2 = 14 regs.  */
+      sav_ofs = 2 * UNITS_PER_WORD;
+      sav_scale = UNITS_PER_WORD;
+      size = UNITS_PER_WORD;
+      max_reg = GP_ARG_NUM_REG - n_reg;
+    }
+  else if (s390_function_arg_float (TYPE_MODE (type), type))
+    {
+      if (TARGET_DEBUG_ARG)
+       {
+         fprintf (stderr, "va_arg: float type");
+         debug_tree (type);
+       }
+
+      /* FP args go in FP registers, if present.  */
+      indirect_p = 0;
+      reg = fpr;
+      n_reg = 1;
+      sav_ofs = 16 * UNITS_PER_WORD;
+      sav_scale = 8;
+      max_reg = FP_ARG_NUM_REG - n_reg;
+    }
+  else
+    {
+      if (TARGET_DEBUG_ARG)
+       {
+         fprintf (stderr, "va_arg: other type");
+         debug_tree (type);
+       }
+
+      /* Otherwise into GP registers.  */
+      indirect_p = 0;
+      reg = gpr;
+      n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+      /* kernel stack layout on 31 bit: It is assumed here that no padding
+        will be added by s390_frame_info because for va_args always an even
+        number of gprs has to be saved r15-r2 = 14 regs.  */
+      sav_ofs = 2 * UNITS_PER_WORD;
 
-/* Return true if floating point registers need to be saved.  */
+      if (size < UNITS_PER_WORD)
+       sav_ofs += UNITS_PER_WORD - size;
 
-static int 
-save_fprs_p ()
-{
-  int i;
-  if (!TARGET_64BIT)
-    return 0;
-  for (i=24; i<=31; i++) 
-    {
-      if (regs_ever_live[i] == 1)
-       return 1;
+      sav_scale = UNITS_PER_WORD;
+      max_reg = GP_ARG_NUM_REG - n_reg;
     }
-  return 0;
-}
-
-/* Find first call clobbered register unsused in a function.
-   This could be used as base register in a leaf function
-   or for holding the return address before epilogue.  */
 
-static int
-find_unused_clobbered_reg ()
-{
-  int i;
-  for (i = 0; i < 6; i++)
-    if (!regs_ever_live[i])
-      return i;
-  return 0;
-}
+  /* Pull the value out of the saved registers ...  */
 
-/* Fill FRAME with info about frame of current function.  */
+  lab_false = create_artificial_label ();
+  lab_over = create_artificial_label ();
+  addr = create_tmp_var (ptr_type_node, "addr");
+  DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set ();
 
-static void
-s390_frame_info (frame)
-     struct s390_frame *frame;
-{
-  int i, j;
-  HOST_WIDE_INT fsize = get_frame_size ();
+  t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
+  t = build2 (GT_EXPR, boolean_type_node, reg, t);
+  u = build1 (GOTO_EXPR, void_type_node, lab_false);
+  t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
+  gimplify_and_add (t, pre_p);
 
-  if (fsize > 0x7fff0000)
-    fatal_error ("Total size of local variables exceeds architecture limit.");
+  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, 
+             size_int (sav_ofs));
+  u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, 
+             fold_convert (TREE_TYPE (reg), size_int (sav_scale)));
+  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, fold_convert (sizetype, u));
 
-  /* fprs 8 - 15 are caller saved for 64 Bit ABI.  */
-  frame->save_fprs_p = save_fprs_p ();
+  gimplify_assign (addr, t, pre_p);
 
-  frame->frame_size = fsize + frame->save_fprs_p * 64;
+  gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over));
 
-  /* Does function need to setup frame and save area.  */
-  
-  if (! current_function_is_leaf
-      || frame->frame_size > 0
-      || current_function_calls_alloca 
-      || current_function_stdarg
-      || current_function_varargs)
-    frame->frame_size += STARTING_FRAME_OFFSET;
+  gimple_seq_add_stmt (pre_p, gimple_build_label (lab_false));
 
-  /* If we need to allocate a frame, the stack pointer is changed.  */ 
 
-  if (frame->frame_size > 0)
-    regs_ever_live[STACK_POINTER_REGNUM] = 1;
+  /* ... Otherwise out of the overflow area.  */
 
-  /* If the literal pool might overflow, the return register might
-     be used as temp literal pointer.  */
+  t = ovf;
+  if (size < UNITS_PER_WORD)
+    t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, 
+               size_int (UNITS_PER_WORD - size));
 
-  if (!TARGET_64BIT && get_pool_size () >= S390_POOL_CHUNK_MAX / 2)
-    regs_ever_live[RETURN_REGNUM] = 1;
+  gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  /* If there is (possibly) any pool entry, we need to 
-     load base register.  */
+  gimplify_assign (addr, t, pre_p);
 
-  if (get_pool_size () 
-      || !CONST_OK_FOR_LETTER_P (frame->frame_size, 'K')
-      || (!TARGET_64BIT && current_function_uses_pic_offset_table))
-    regs_ever_live[BASE_REGISTER] = 1; 
+  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, 
+             size_int (size));
+  gimplify_assign (ovf, t, pre_p);
 
-  /* If we need the GOT pointer, remember to save/restore it.  */
+  gimple_seq_add_stmt (pre_p, gimple_build_label (lab_over));
 
-  if (current_function_uses_pic_offset_table)
-    regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
 
-  /* Frame pointer needed.   */
-    
-  frame->frame_pointer_p = frame_pointer_needed;
+  /* Increment register save count.  */
 
-  /* Find first and last gpr to be saved.  */
-  
-  for (i = 6; i < 16; i++)
-    if (regs_ever_live[i])
-      break;
+  u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
+             fold_convert (TREE_TYPE (reg), size_int (n_reg)));
+  gimplify_and_add (u, pre_p);
 
-  for (j = 15; j > i; j--)
-    if (regs_ever_live[j])
-      break;
-  
-  if (i == 16)
+  if (indirect_p)
     {
-      /* Nothing to save / restore.  */ 
-      frame->first_save_gpr = -1;
-      frame->first_restore_gpr = -1;
-      frame->last_save_gpr = -1;
-      frame->return_reg_saved_p = 0;
+      t = build_pointer_type (build_pointer_type (type));
+      addr = fold_convert (t, addr);
+      addr = build_va_arg_indirect_ref (addr);
     }
   else
     {
-      /* Save / Restore from gpr i to j.  */
-      frame->first_save_gpr = i;
-      frame->first_restore_gpr = i;
-      frame->last_save_gpr  = j;
-      frame->return_reg_saved_p = (j >= RETURN_REGNUM && i <= RETURN_REGNUM);
+      t = build_pointer_type (type);
+      addr = fold_convert (t, addr);
     }
 
-  if (current_function_stdarg || current_function_varargs)
-    {
-      /* Varargs function need to save from gpr 2 to gpr 15.  */
-      frame->first_save_gpr = 2;
-    }
+  return build_va_arg_indirect_ref (addr);
 }
 
-/* Return offset between argument pointer and frame pointer 
-   initially after prologue.  */
 
-int 
-s390_arg_frame_offset ()
-{
-  struct s390_frame frame;
+/* Builtins.  */
 
-  /* Compute frame_info.  */
+enum s390_builtin
+{
+  S390_BUILTIN_THREAD_POINTER,
+  S390_BUILTIN_SET_THREAD_POINTER,
 
-  s390_frame_info (&frame);
+  S390_BUILTIN_max
+};
 
-  return frame.frame_size + STACK_POINTER_OFFSET;
-}
+static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_64,
+  CODE_FOR_set_tp_64
+};
 
-/* Emit insn to save fpr REGNUM at offset OFFSET relative
-   to register BASE.  Return generated insn.  */ 
+static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_31,
+  CODE_FOR_set_tp_31
+};
 
-static rtx
-save_fpr (base, offset, regnum)
-     rtx base;
-     int offset;
-     int regnum;     
+static void
+s390_init_builtins (void)
 {
-  rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
-  set_mem_alias_set (addr, s390_sr_alias_set);
+  tree ftype;
 
-  return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
+  ftype = build_function_type (ptr_type_node, void_list_node);
+  add_builtin_function ("__builtin_thread_pointer", ftype,
+                       S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+                       NULL, NULL_TREE);
+
+  ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
+  add_builtin_function ("__builtin_set_thread_pointer", ftype,
+                       S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+                       NULL, NULL_TREE);
 }
 
-/* Emit insn to restore fpr REGNUM from offset OFFSET relative
-   to register BASE.  Return generated insn.  */ 
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
 
 static rtx
-restore_fpr (base, offset, regnum)
-     rtx base;
-     int offset;
-     int regnum;
+s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
 {
-  rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
-  set_mem_alias_set (addr, s390_sr_alias_set);
+#define MAX_ARGS 2
+
+  unsigned int const *code_for_builtin =
+    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
+
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+  enum insn_code icode;
+  rtx op[MAX_ARGS], pat;
+  int arity;
+  bool nonvoid;
+  tree arg;
+  call_expr_arg_iterator iter;
+
+  if (fcode >= S390_BUILTIN_max)
+    internal_error ("bad builtin fcode");
+  icode = code_for_builtin[fcode];
+  if (icode == 0)
+    internal_error ("bad builtin fcode");
+
+  nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+
+  arity = 0;
+  FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+    {
+      const struct insn_operand_data *insn_op;
 
-  return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
+      if (arg == error_mark_node)
+       return NULL_RTX;
+      if (arity > MAX_ARGS)
+       return NULL_RTX;
+
+      insn_op = &insn_data[icode].operand[arity + nonvoid];
+
+      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
+
+      if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+       op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+      arity++;
+    }
+
+  if (nonvoid)
+    {
+      enum machine_mode tmode = insn_data[icode].operand[0].mode;
+      if (!target
+         || GET_MODE (target) != tmode
+         || !(*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+    }
+
+  switch (arity)
+    {
+    case 0:
+      pat = GEN_FCN (icode) (target);
+      break;
+    case 1:
+      if (nonvoid)
+        pat = GEN_FCN (icode) (target, op[0]);
+      else
+       pat = GEN_FCN (icode) (op[0]);
+      break;
+    case 2:
+      pat = GEN_FCN (icode) (target, op[0], op[1]);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
+
+  if (nonvoid)
+    return target;
+  else
+    return const0_rtx;
 }
 
-/* Output the function prologue assembly code to the 
-   stdio stream FILE.  The local frame size is passed
-   in LSIZE.  */
+
+/* Output assembly code for the trampoline template to
+   stdio stream FILE.
+
+   On S/390, we use gpr 1 internally in the trampoline code;
+   gpr 0 is used to hold the static chain.  */
 
 void
-s390_function_prologue (file, lsize)
-     FILE *file ATTRIBUTE_UNUSED;
-     HOST_WIDE_INT lsize ATTRIBUTE_UNUSED;
+s390_trampoline_template (FILE *file)
 {
-  s390_chunkify_pool ();
-  s390_split_branches ();
+  rtx op[2];
+  op[0] = gen_rtx_REG (Pmode, 0);
+  op[1] = gen_rtx_REG (Pmode, 1);
+
+  if (TARGET_64BIT)
+    {
+      output_asm_insn ("basr\t%1,0", op);
+      output_asm_insn ("lmg\t%0,%1,14(%1)", op);
+      output_asm_insn ("br\t%1", op);
+      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
+    }
+  else
+    {
+      output_asm_insn ("basr\t%1,0", op);
+      output_asm_insn ("lm\t%0,%1,6(%1)", op);
+      output_asm_insn ("br\t%1", op);
+      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
+    }
 }
 
-/* Output the function epilogue assembly code to the 
-   stdio stream FILE.  The local frame size is passed
-   in LSIZE.  */
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.  */
 
 void
-s390_function_epilogue (file, lsize)
-     FILE *file ATTRIBUTE_UNUSED;
-     HOST_WIDE_INT lsize ATTRIBUTE_UNUSED;
+s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
 {
-  current_function_uses_pic_offset_table = 0;
-  s390_function_count++;
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
+                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
+                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
 }
 
-/* Expand the prologue into a bunch of separate insns.  */
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
 
 void
-s390_emit_prologue ()
+s390_function_profiler (FILE *file, int labelno)
 {
-  struct s390_frame frame;
-  rtx insn, addr;
-  rtx temp_reg;
-  int i;
+  rtx op[7];
 
-  /* Compute frame_info.  */
+  char label[128];
+  ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
 
-  s390_frame_info (&frame);
+  fprintf (file, "# function profiler \n");
 
-  /* Choose best register to use for temp use within prologue.  */
-  
-  if (frame.return_reg_saved_p
-      && !has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
-      && get_pool_size () < S390_POOL_CHUNK_MAX / 2)
-    temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  else
-    temp_reg = gen_rtx_REG (Pmode, 1);
+  op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+  op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
 
-  /* Save call saved gprs.  */
+  op[2] = gen_rtx_REG (Pmode, 1);
+  op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
+  SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
 
-  if (frame.first_save_gpr != -1)
+  op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
+  if (flag_pic)
     {
-      addr = plus_constant (stack_pointer_rtx, 
-                           frame.first_save_gpr * UNITS_PER_WORD);
-      addr = gen_rtx_MEM (Pmode, addr);
-      set_mem_alias_set (addr, s390_sr_alias_set);
-
-      if (frame.first_save_gpr != frame.last_save_gpr )
-       {
-         insn = emit_insn (gen_store_multiple (addr,
-                             gen_rtx_REG (Pmode, frame.first_save_gpr),
-                             GEN_INT (frame.last_save_gpr 
-                                      - frame.first_save_gpr + 1)));
+      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
+      op[4] = gen_rtx_CONST (Pmode, op[4]);
+    }
 
-         /* We need to set the FRAME_RELATED flag on all SETs
-            inside the store-multiple pattern.
+  if (TARGET_64BIT)
+    {
+      output_asm_insn ("stg\t%0,%1", op);
+      output_asm_insn ("larl\t%2,%3", op);
+      output_asm_insn ("brasl\t%0,%4", op);
+      output_asm_insn ("lg\t%0,%1", op);
+    }
+  else if (!flag_pic)
+    {
+      op[6] = gen_label_rtx ();
 
-            However, we must not emit DWARF records for registers 2..5
-            if they are stored for use by variable arguments ...  
+      output_asm_insn ("st\t%0,%1", op);
+      output_asm_insn ("bras\t%2,%l6", op);
+      output_asm_insn (".long\t%4", op);
+      output_asm_insn (".long\t%3", op);
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
+      output_asm_insn ("l\t%0,0(%2)", op);
+      output_asm_insn ("l\t%2,4(%2)", op);
+      output_asm_insn ("basr\t%0,%0", op);
+      output_asm_insn ("l\t%0,%1", op);
+    }
+  else
+    {
+      op[5] = gen_label_rtx ();
+      op[6] = gen_label_rtx ();
 
-            ??? Unfortunately, it is not enough to simply not the the
-            FRAME_RELATED flags for those SETs, because the first SET
-            of the PARALLEL is always treated as if it had the flag
-            set, even if it does not.  Therefore we emit a new pattern
-            without those registers as REG_FRAME_RELATED_EXPR note.  */
+      output_asm_insn ("st\t%0,%1", op);
+      output_asm_insn ("bras\t%2,%l6", op);
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5]));
+      output_asm_insn (".long\t%4-%l5", op);
+      output_asm_insn (".long\t%3-%l5", op);
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
+      output_asm_insn ("lr\t%0,%2", op);
+      output_asm_insn ("a\t%0,0(%2)", op);
+      output_asm_insn ("a\t%2,4(%2)", op);
+      output_asm_insn ("basr\t%0,%0", op);
+      output_asm_insn ("l\t%0,%1", op);
+    }
+}
 
-         if (frame.first_save_gpr >= 6)
-           {
-             rtx pat = PATTERN (insn);
+/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
+   into its SYMBOL_REF_FLAGS.  */
 
-             for (i = 0; i < XVECLEN (pat, 0); i++)
-               if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
-                 RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
+static void
+s390_encode_section_info (tree decl, rtx rtl, int first)
+{
+  default_encode_section_info (decl, rtl, first);
 
-             RTX_FRAME_RELATED_P (insn) = 1;
-           }
-         else if (frame.last_save_gpr >= 6)
-           {
-             rtx note, naddr;
-             naddr = plus_constant (stack_pointer_rtx, 6 * UNITS_PER_WORD);
-             note = gen_store_multiple (gen_rtx_MEM (Pmode, naddr), 
-                                        gen_rtx_REG (Pmode, 6),
-                                        GEN_INT (frame.last_save_gpr - 6 + 1));
-             REG_NOTES (insn) =
-               gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, 
-                                  note, REG_NOTES (insn));
-
-             for (i = 0; i < XVECLEN (note, 0); i++)
-               if (GET_CODE (XVECEXP (note, 0, i)) == SET)
-                 RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
-
-             RTX_FRAME_RELATED_P (insn) = 1;
-           }
-       }
-      else
-       {
-         insn = emit_move_insn (addr, 
-                                gen_rtx_REG (Pmode, frame.first_save_gpr));
-          RTX_FRAME_RELATED_P (insn) = 1;
-       }
+  if (TREE_CODE (decl) == VAR_DECL)
+    {
+      /* If a variable has a forced alignment to < 2 bytes, mark it
+        with SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL
+        operand.  */
+      if (DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
+       SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
+      if (!DECL_SIZE (decl)
+         || !DECL_ALIGN (decl)
+         || !host_integerp (DECL_SIZE (decl), 0)
+         || (DECL_ALIGN (decl) <= 64
+             && DECL_ALIGN (decl) != tree_low_cst (DECL_SIZE (decl), 0)))
+       SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED;
     }
 
-  /* Dump constant pool and set constant pool register (13).  */
-  insn = emit_insn (gen_lit ());
-  
-  /* Save fprs for variable args.  */
+  /* Literal pool references don't have a decl so they are handled
+     differently here.  We rely on the information in the MEM_ALIGN
+     entry to decide upon natural alignment.  */
+  if (MEM_P (rtl)
+      && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
+      && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0))
+      && (MEM_ALIGN (rtl) == 0
+         || GET_MODE_BITSIZE (GET_MODE (rtl)) == 0
+         || MEM_ALIGN (rtl) < GET_MODE_BITSIZE (GET_MODE (rtl))))
+    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED;
+}
 
-  if (current_function_stdarg || current_function_varargs)
-    {
-      /* Save fpr 0 and 2.  */ 
+/* Output thunk to FILE that implements a C++ virtual function call (with
+   multiple inheritance) to FUNCTION.  The thunk adjusts the this pointer
+   by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment
+   stored at VCALL_OFFSET in the vtable whose address is located at offset 0
+   relative to the resulting this pointer.  */
 
-      save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 32, 16); 
-      save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 24, 17); 
-      
-      if (TARGET_64BIT)
-       {
-         /* Save fpr 4 and 6.  */
-         save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18); 
-         save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19); 
-       }
+static void
+s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
+{
+  rtx op[10];
+  int nonlocal = 0;
+
+  /* Operand 0 is the target function.  */
+  op[0] = XEXP (DECL_RTL (function), 0);
+  if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
+    {
+      nonlocal = 1;
+      op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]),
+                             TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT);
+      op[0] = gen_rtx_CONST (Pmode, op[0]);
     }
 
-  /* Save fprs 4 and 6 if used (31 bit ABI).  */
+  /* Operand 1 is the 'this' pointer.  */
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+    op[1] = gen_rtx_REG (Pmode, 3);
+  else
+    op[1] = gen_rtx_REG (Pmode, 2);
+
+  /* Operand 2 is the delta.  */
+  op[2] = GEN_INT (delta);
 
-  if (!TARGET_64BIT)
+  /* Operand 3 is the vcall_offset.  */
+  op[3] = GEN_INT (vcall_offset);
+
+  /* Operand 4 is the temporary register.  */
+  op[4] = gen_rtx_REG (Pmode, 1);
+
+  /* Operands 5 to 8 can be used as labels.  */
+  op[5] = NULL_RTX;
+  op[6] = NULL_RTX;
+  op[7] = NULL_RTX;
+  op[8] = NULL_RTX;
+
+  /* Operand 9 can be used for temporary register.  */
+  op[9] = NULL_RTX;
+
+  /* Generate code.  */
+  if (TARGET_64BIT)
     {
-      /* Save fpr 4 and 6.  */
-      if (regs_ever_live[18])
+      /* Setup literal pool pointer if required.  */
+      if ((!DISP_IN_RANGE (delta)
+          && !CONST_OK_FOR_K (delta)
+          && !CONST_OK_FOR_Os (delta))
+         || (!DISP_IN_RANGE (vcall_offset)
+             && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
-         insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18);
-         RTX_FRAME_RELATED_P (insn) = 1;
+         op[5] = gen_label_rtx ();
+         output_asm_insn ("larl\t%4,%5", op);
        }
-      if (regs_ever_live[19]) 
+
+      /* Add DELTA to this pointer.  */
+      if (delta)
        {
-         insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19); 
-         RTX_FRAME_RELATED_P (insn) = 1;
+         if (CONST_OK_FOR_J (delta))
+           output_asm_insn ("la\t%1,%2(%1)", op);
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_K (delta))
+           output_asm_insn ("aghi\t%1,%2", op);
+         else if (CONST_OK_FOR_Os (delta))
+           output_asm_insn ("agfi\t%1,%2", op);
+         else
+           {
+             op[6] = gen_label_rtx ();
+             output_asm_insn ("agf\t%1,%6-%5(%4)", op);
+           }
        }
-    }
 
-  /* Decrement stack pointer.  */
-
-  if (frame.frame_size > 0)
-    {
-      rtx frame_off = GEN_INT (-frame.frame_size);
-
-      /* Save incoming stack pointer into temp reg.  */
-      
-      if (TARGET_BACKCHAIN || frame.save_fprs_p)
+      /* Perform vcall adjustment.  */
+      if (vcall_offset)
        {
-         insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
+         if (DISP_IN_RANGE (vcall_offset))
+           {
+             output_asm_insn ("lg\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,%3(%4)", op);
+           }
+         else if (CONST_OK_FOR_K (vcall_offset))
+           {
+             output_asm_insn ("lghi\t%4,%3", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
+         else if (CONST_OK_FOR_Os (vcall_offset))
+           {
+             output_asm_insn ("lgfi\t%4,%3", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
+         else
+           {
+             op[7] = gen_label_rtx ();
+             output_asm_insn ("llgf\t%4,%7-%5(%4)", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
        }
-      
-      /* Substract frame size from stack pointer.  */
 
-      frame_off = GEN_INT (-frame.frame_size);
-      if (!CONST_OK_FOR_LETTER_P (-frame.frame_size, 'K'))
-       frame_off = force_const_mem (Pmode, frame_off);
-
-      insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
-      RTX_FRAME_RELATED_P (insn) = 1;
-      REG_NOTES (insn) = 
-       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                                  gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                                  GEN_INT (-frame.frame_size))),
-                          REG_NOTES (insn));
+      /* Jump to target.  */
+      output_asm_insn ("jg\t%0", op);
 
-      /* Set backchain.  */
-      
-      if (TARGET_BACKCHAIN)
+      /* Output literal pool if required.  */
+      if (op[5])
        {
-         addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
-         set_mem_alias_set (addr, s390_sr_alias_set);
-         insn = emit_insn (gen_move_insn (addr, temp_reg));
+         output_asm_insn (".align\t4", op);
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
+       }
+      if (op[6])
+       {
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[6]));
+         output_asm_insn (".long\t%2", op);
+       }
+      if (op[7])
+       {
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[7]));
+         output_asm_insn (".long\t%3", op);
        }
     }
-
-  /* Save fprs 8 - 15 (64 bit ABI).  */
-  
-  if (frame.save_fprs_p)
-    {
-      insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64)));
-
-      for (i = 24; i < 32; i++)
-       if (regs_ever_live[i])
-         {
-           rtx addr = plus_constant (stack_pointer_rtx, 
-                                     frame.frame_size - 64 + (i-24)*8);
-
-           insn = save_fpr (temp_reg, (i-24)*8, i);
-           RTX_FRAME_RELATED_P (insn) = 1;
-           REG_NOTES (insn) = 
-             gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode, 
-                            gen_rtx_MEM (DFmode, addr),
-                            gen_rtx_REG (DFmode, i)),
-               REG_NOTES (insn));
-         }
-    }
-           
-  /* Set frame pointer, if needed.  */
-  
-  if (frame.frame_pointer_p)
-    {
-      insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
-    }
-
-  /* Set up got pointer, if needed.  */
-  
-  if (current_function_uses_pic_offset_table)
+  else
     {
-      rtx got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-      SYMBOL_REF_FLAG (got_symbol) = 1;
-
-      if (TARGET_64BIT)
+      /* Setup base pointer if required.  */
+      if (!vcall_offset
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_K (delta)
+             && !CONST_OK_FOR_Os (delta))
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
-         insn = emit_insn (gen_movdi (pic_offset_table_rtx,
-                                      got_symbol));             
-
-          /* It can happen that the GOT pointer isn't really needed ...  */
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
+         op[5] = gen_label_rtx ();
+         output_asm_insn ("basr\t%4,0", op);
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
        }
-      else
-       {
-          got_symbol = gen_rtx_UNSPEC (VOIDmode, 
-                                      gen_rtvec (1, got_symbol), 100);
-          got_symbol = gen_rtx_CONST (VOIDmode, got_symbol);
-         got_symbol = force_const_mem (Pmode, got_symbol);
-         insn = emit_move_insn (pic_offset_table_rtx,
-                                got_symbol);            
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
 
-         insn = emit_insn (gen_add2_insn (pic_offset_table_rtx,
-                                          gen_rtx_REG (Pmode, BASE_REGISTER)));
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
+      /* Add DELTA to this pointer.  */
+      if (delta)
+       {
+         if (CONST_OK_FOR_J (delta))
+           output_asm_insn ("la\t%1,%2(%1)", op);
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_K (delta))
+           output_asm_insn ("ahi\t%1,%2", op);
+         else if (CONST_OK_FOR_Os (delta))
+           output_asm_insn ("afi\t%1,%2", op);
+         else
+           {
+             op[6] = gen_label_rtx ();
+             output_asm_insn ("a\t%1,%6-%5(%4)", op);
+           }
        }
-    }      
-}
 
-/* Expand the epilogue into a bunch of separate insns.  */
-
-void
-s390_emit_epilogue ()
-{
-  struct s390_frame frame;
-  rtx frame_pointer, return_reg;
-  int area_bottom, area_top, offset;
-  rtvec p;
+      /* Perform vcall adjustment.  */
+      if (vcall_offset)
+        {
+         if (CONST_OK_FOR_J (vcall_offset))
+           {
+             output_asm_insn ("l\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,%3(%4)", op);
+           }
+         else if (DISP_IN_RANGE (vcall_offset))
+           {
+             output_asm_insn ("l\t%4,0(%1)", op);
+             output_asm_insn ("ay\t%1,%3(%4)", op);
+           }
+         else if (CONST_OK_FOR_K (vcall_offset))
+           {
+             output_asm_insn ("lhi\t%4,%3", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
+         else if (CONST_OK_FOR_Os (vcall_offset))
+           {
+             output_asm_insn ("iilf\t%4,%3", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
+         else
+           {
+             op[7] = gen_label_rtx ();
+             output_asm_insn ("l\t%4,%7-%5(%4)", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
 
-  /* Compute frame_info.  */
-  s390_frame_info (&frame);
+         /* We had to clobber the base pointer register.
+            Re-setup the base pointer (with a different base).  */
+         op[5] = gen_label_rtx ();
+         output_asm_insn ("basr\t%4,0", op);
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
+       }
 
-  /* Check whether to use frame or stack pointer for restore.  */
+      /* Jump to target.  */
+      op[8] = gen_label_rtx ();
 
-  frame_pointer = frame.frame_pointer_p ? 
-    hard_frame_pointer_rtx : stack_pointer_rtx;
+      if (!flag_pic)
+       output_asm_insn ("l\t%4,%8-%5(%4)", op);
+      else if (!nonlocal)
+       output_asm_insn ("a\t%4,%8-%5(%4)", op);
+      /* We cannot call through .plt, since .plt requires %r12 loaded.  */
+      else if (flag_pic == 1)
+       {
+         output_asm_insn ("a\t%4,%8-%5(%4)", op);
+         output_asm_insn ("l\t%4,%0(%4)", op);
+       }
+      else if (flag_pic == 2)
+       {
+         op[9] = gen_rtx_REG (Pmode, 0);
+         output_asm_insn ("l\t%9,%8-4-%5(%4)", op);
+         output_asm_insn ("a\t%4,%8-%5(%4)", op);
+         output_asm_insn ("ar\t%4,%9", op);
+         output_asm_insn ("l\t%4,0(%4)", op);
+       }
 
-  /* Compute which parts of the save area we need to access.  */
+      output_asm_insn ("br\t%4", op);
 
-  if (frame.first_restore_gpr != -1)
-    {
-      area_bottom = frame.first_restore_gpr * UNITS_PER_WORD;
-      area_top = (frame.last_save_gpr + 1) * UNITS_PER_WORD;
-    }
-  else
-    {
-      area_bottom = INT_MAX;
-      area_top = INT_MIN;
-    }
+      /* Output literal pool.  */
+      output_asm_insn (".align\t4", op);
 
-  if (TARGET_64BIT)
-    {
-      if (frame.save_fprs_p)
+      if (nonlocal && flag_pic == 2)
+       output_asm_insn (".long\t%0", op);
+      if (nonlocal)
        {
-         if (area_bottom > -64)
-           area_bottom = -64;
-         if (area_top < 0)
-           area_top = 0;
+         op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+         SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL;
        }
-    }
-  else
-    {
-      if (regs_ever_live[18])
+
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
+      if (!flag_pic)
+       output_asm_insn (".long\t%0", op);
+      else
+       output_asm_insn (".long\t%0-%5", op);
+
+      if (op[6])
        {
-         if (area_bottom > STACK_POINTER_OFFSET - 16)
-           area_bottom = STACK_POINTER_OFFSET - 16;
-         if (area_top < STACK_POINTER_OFFSET - 8)
-           area_top = STACK_POINTER_OFFSET - 8;
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[6]));
+         output_asm_insn (".long\t%2", op);
        }
-      if (regs_ever_live[19])
+      if (op[7])
        {
-         if (area_bottom > STACK_POINTER_OFFSET - 8)
-           area_bottom = STACK_POINTER_OFFSET - 8;
-         if (area_top < STACK_POINTER_OFFSET)
-           area_top = STACK_POINTER_OFFSET;
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[7]));
+         output_asm_insn (".long\t%3", op);
        }
     }
+}
 
-  /* Check whether we can access the register save area.  
-     If not, increment the frame pointer as required.  */
-
-  if (area_top <= area_bottom)
-    {
-      /* Nothing to restore.  */
-    }
-  else if (frame.frame_size + area_bottom >= 0
-           && frame.frame_size + area_top <= 4096)
-    {
-      /* Area is in range.  */
-      offset = frame.frame_size;
-    }
-  else
-    {
-      rtx insn, frame_off;
-
-      offset = area_bottom < 0 ? -area_bottom : 0; 
-      frame_off = GEN_INT (frame.frame_size - offset);
+static bool
+s390_valid_pointer_mode (enum machine_mode mode)
+{
+  return (mode == SImode || (TARGET_64BIT && mode == DImode));
+}
 
-      if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
-       frame_off = force_const_mem (Pmode, frame_off);
+/* Checks whether the given CALL_EXPR would use a caller
+   saved register.  This is used to decide whether sibling call
+   optimization could be performed on the respective function
+   call.  */
 
-      insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
-    }
+static bool
+s390_call_saved_register_used (tree call_expr)
+{
+  CUMULATIVE_ARGS cum;
+  tree parameter;
+  enum machine_mode mode;
+  tree type;
+  rtx parm_rtx;
+  int reg, i;
 
-  /* Restore call saved fprs.  */
+  INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
 
-  if (TARGET_64BIT)
+  for (i = 0; i < call_expr_nargs (call_expr); i++)
     {
-      int i;
+      parameter = CALL_EXPR_ARG (call_expr, i);
+      gcc_assert (parameter);
 
-      if (frame.save_fprs_p)
-       for (i = 24; i < 32; i++)
-         if (regs_ever_live[i] && !global_regs[i])
-           restore_fpr (frame_pointer, 
-                        offset - 64 + (i-24) * 8, i);
-    }
-  else
-    {
-      if (regs_ever_live[18] && !global_regs[18])
-       restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 16, 18);
-      if (regs_ever_live[19] && !global_regs[19])
-       restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 8, 19);
-    }
+      /* For an undeclared variable passed as parameter we will get
+        an ERROR_MARK node here.  */
+      if (TREE_CODE (parameter) == ERROR_MARK)
+       return true;
 
-  /* Return register.  */
+      type = TREE_TYPE (parameter);
+      gcc_assert (type);
 
-  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); 
+      mode = TYPE_MODE (type);
+      gcc_assert (mode);
 
-  /* Restore call saved gprs.  */
+      if (pass_by_reference (&cum, mode, type, true))
+       {
+         mode = Pmode;
+         type = build_pointer_type (type);
+       }
 
-  if (frame.first_restore_gpr != -1)
-    {
-      rtx addr;
-      int i;
+       parm_rtx = s390_function_arg (&cum, mode, type, 0);
 
-      /* Check for global register and save them 
-        to stack location from where they get restored.  */
+       s390_function_arg_advance (&cum, mode, type, 0);
 
-      for (i = frame.first_restore_gpr; 
-          i <= frame.last_save_gpr;
-          i++)
-       {
-         /* These registers are special and need to be 
-            restored in any case.  */
-         if (i == STACK_POINTER_REGNUM 
-              || i == RETURN_REGNUM
-              || i == BASE_REGISTER 
-              || (flag_pic && i == PIC_OFFSET_TABLE_REGNUM))
-           continue;
+       if (parm_rtx && REG_P (parm_rtx))
+        {
+          for (reg = 0;
+               reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
+               reg++)
+            if (! call_used_regs[reg + REGNO (parm_rtx)])
+              return true;
+        }
+    }
+  return false;
+}
 
-         if (global_regs[i])
-           {
-             addr = plus_constant (frame_pointer, 
-                    offset + i * UNITS_PER_WORD);
-             addr = gen_rtx_MEM (Pmode, addr);
-             set_mem_alias_set (addr, s390_sr_alias_set);
-             emit_move_insn (addr, gen_rtx_REG (Pmode, i));
-           }  
-       }
+/* Return true if the given call expression can be
+   turned into a sibling call.
+   DECL holds the declaration of the function to be called whereas
+   EXP is the call expression itself.  */
+
+static bool
+s390_function_ok_for_sibcall (tree decl, tree exp)
+{
+  /* The TPF epilogue uses register 1.  */
+  if (TARGET_TPF_PROFILING)
+    return false;
+
+  /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved)
+     which would have to be restored before the sibcall.  */
+  if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
+    return false;
+
+  /* Register 6 on s390 is available as an argument register but unfortunately
+     "caller saved". This makes functions needing this register for arguments
+     not suitable for sibcalls.  */
+  return !s390_call_saved_register_used (exp);
+}
 
-      /* Fetch return address from stack before load multiple,
-        this will do good for scheduling.  */
+/* Return the fixed registers used for condition codes.  */
 
-      if (frame.last_save_gpr >= RETURN_REGNUM 
-         && frame.first_restore_gpr < RETURN_REGNUM)
-       {
-         int return_regnum = find_unused_clobbered_reg();
-         if (!return_regnum)
-           return_regnum = 4;
-         return_reg = gen_rtx_REG (Pmode, return_regnum);
-       
-         addr = plus_constant (frame_pointer, 
-                               offset + RETURN_REGNUM * UNITS_PER_WORD); 
-         addr = gen_rtx_MEM (Pmode, addr);
-         set_mem_alias_set (addr, s390_sr_alias_set);
-         emit_move_insn (return_reg, addr);
-       }
+static bool
+s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
+{
+  *p1 = CC_REGNUM;
+  *p2 = INVALID_REGNUM;
+  return true;
+}
 
-      /* ??? As references to the base register are not made
-        explicit in insn RTX code, we have to add a barrier here
-        to prevent incorrect scheduling.  */
+/* This function is used by the call expanders of the machine description.
+   It emits the call insn itself together with the necessary operations
+   to adjust the target address and returns the emitted insn.
+   ADDR_LOCATION is the target address rtx
+   TLS_CALL the location of the thread-local symbol
+   RESULT_REG the register where the result of the call should be stored
+   RETADDR_REG the register where the return address should be stored
+               If this parameter is NULL_RTX the call is considered
+               to be a sibling call.  */
 
-      emit_insn (gen_blockage());      
+rtx
+s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
+               rtx retaddr_reg)
+{
+  bool plt_call = false;
+  rtx insn;
+  rtx call;
+  rtx clobber;
+  rtvec vec;
 
-      addr = plus_constant (frame_pointer, 
-                           offset + frame.first_restore_gpr * UNITS_PER_WORD);
-      addr = gen_rtx_MEM (Pmode, addr);
-      set_mem_alias_set (addr, s390_sr_alias_set);
+  /* Direct function calls need special treatment.  */
+  if (GET_CODE (addr_location) == SYMBOL_REF)
+    {
+      /* When calling a global routine in PIC mode, we must
+         replace the symbol itself with the PLT stub.  */
+      if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
+        {
+         addr_location = gen_rtx_UNSPEC (Pmode,
+                                         gen_rtvec (1, addr_location),
+                                         UNSPEC_PLT);
+         addr_location = gen_rtx_CONST (Pmode, addr_location);
+         plt_call = true;
+        }
 
-      if (frame.first_restore_gpr != frame.last_save_gpr)
-       {
-         emit_insn (gen_load_multiple (
-                      gen_rtx_REG (Pmode, frame.first_restore_gpr),
-                       addr,
-                      GEN_INT (frame.last_save_gpr - frame.first_restore_gpr + 1)));
-       }
-      else
-       {
-         emit_move_insn (gen_rtx_REG (Pmode, frame.first_restore_gpr),
-                         addr); 
+      /* Unless we can use the bras(l) insn, force the
+         routine address into a register.  */
+      if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH)
+        {
+         if (flag_pic)
+           addr_location = legitimize_pic_address (addr_location, 0);
+         else
+           addr_location = force_reg (Pmode, addr_location);
        }
     }
 
-  /* Return to caller.  */
-
-  p = rtvec_alloc (2);
-  
-  RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
-  RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
-  emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
-}
-
+  /* If it is already an indirect call or the code above moved the
+     SYMBOL_REF to somewhere else make sure the address can be found in
+     register 1.  */
+  if (retaddr_reg == NULL_RTX
+      && GET_CODE (addr_location) != SYMBOL_REF
+      && !plt_call)
+    {
+      emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
+      addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
+    }
 
-/* Return the size in bytes of a function argument of 
-   type TYPE and/or mode MODE.  At least one of TYPE or
-   MODE must be specified.  */
+  addr_location = gen_rtx_MEM (QImode, addr_location);
+  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
 
-static int
-s390_function_arg_size (mode, type)
-     enum machine_mode mode;
-     tree type;
-{
-  if (type)
-    return int_size_in_bytes (type);
+  if (result_reg != NULL_RTX)
+    call = gen_rtx_SET (VOIDmode, result_reg, call);
 
-  /* No type info available for some library calls ...  */
-  if (mode != BLKmode)
-    return GET_MODE_SIZE (mode);
+  if (retaddr_reg != NULL_RTX)
+    {
+      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
 
-  /* If we have neither type nor mode, abort */
-  abort ();
-}
+      if (tls_call != NULL_RTX)
+       vec = gen_rtvec (3, call, clobber,
+                        gen_rtx_USE (VOIDmode, tls_call));
+      else
+       vec = gen_rtvec (2, call, clobber);
 
-/* Return 1 if a function argument of type TYPE and mode MODE
-   is to be passed by reference.  The ABI specifies that only
-   structures of size 1, 2, 4, or 8 bytes are passed by value,
-   all other structures (and complex numbers) are passed by
-   reference.  */
+      call = gen_rtx_PARALLEL (VOIDmode, vec);
+    }
 
-int
-s390_function_arg_pass_by_reference (mode, type)
-     enum machine_mode mode;
-     tree type;
-{
-  int size = s390_function_arg_size (mode, type);
+  insn = emit_call_insn (call);
 
-  if (type)
+  /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
+  if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
     {
-      if (AGGREGATE_TYPE_P (type) &&
-          size != 1 && size != 2 && size != 4 && size != 8)
-        return 1;
+      /* s390_function_ok_for_sibcall should
+        have denied sibcalls in this case.  */
+      gcc_assert (retaddr_reg != NULL_RTX);
 
-      if (TREE_CODE (type) == COMPLEX_TYPE)
-        return 1;
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
     }
-  return 0;
-
+  return insn;
 }
 
-/* Update the data in CUM to advance over an argument of mode MODE and
-   data type TYPE.  (TYPE is null for libcalls where that information
-   may not be available.).  The boolean NAMED specifies whether the
-   argument is a named argument (as opposed to an unnamed argument
-   matching an ellipsis).  */
+/* Implement CONDITIONAL_REGISTER_USAGE.  */
 
 void
-s390_function_arg_advance (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+s390_conditional_register_usage (void)
 {
-  if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
+  int i;
+
+  if (flag_pic)
+    {
+      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+    }
+  if (TARGET_CPU_ZARCH)
     {
-      cum->fprs++;
+      fixed_regs[BASE_REGNUM] = 0;
+      call_used_regs[BASE_REGNUM] = 0;
+      fixed_regs[RETURN_REGNUM] = 0;
+      call_used_regs[RETURN_REGNUM] = 0;
     }
-  else if (s390_function_arg_pass_by_reference (mode, type))
+  if (TARGET_64BIT)
     {
-      cum->gprs += 1;
+      for (i = 24; i < 32; i++)
+       call_used_regs[i] = call_really_used_regs[i] = 0;
     }
   else
     {
-      int size = s390_function_arg_size (mode, type);
-      cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
+      for (i = 18; i < 20; i++)
+       call_used_regs[i] = call_really_used_regs[i] = 0;
+    }
+
+  if (TARGET_SOFT_FLOAT)
+    {
+      for (i = 16; i < 32; i++)
+       call_used_regs[i] = fixed_regs[i] = 1;
     }
 }
 
-/* Define where to put the arguments to a function.
-   Value is zero to push the argument on the stack,
-   or a hard register in which to store the argument.
+/* Corresponding function to eh_return expander.  */
 
-   MODE is the argument's machine mode.
-   TYPE is the data type of the argument (as a tree).
-    This is null for libcalls where that information may
-    not be available.
-   CUM is a variable of type CUMULATIVE_ARGS which gives info about
-    the preceding args and about the function being called.
-   NAMED is nonzero if this argument is a named parameter
-    (otherwise it is an extra parameter matching an ellipsis).  
+static GTY(()) rtx s390_tpf_eh_return_symbol;
+void
+s390_emit_tpf_eh_return (rtx target)
+{
+  rtx insn, reg;
 
-   On S/390, we use general purpose registers 2 through 6 to
-   pass integer, pointer, and certain structure arguments, and
-   floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
-   to pass floating point arguments.  All remaining arguments
-   are pushed to the stack.  */
+  if (!s390_tpf_eh_return_symbol)
+    s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
 
-rtx
-s390_function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
-{
-  if (s390_function_arg_pass_by_reference (mode, type))
-      return 0;
+  reg = gen_rtx_REG (Pmode, 2);
 
-  if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
-    {
-      if (cum->fprs + 1 > (TARGET_64BIT? 4 : 2))
-       return 0;
-      else
-       return gen_rtx (REG, mode, cum->fprs + 16);
-    }
-  else
-    {
-      int size = s390_function_arg_size (mode, type);
-      int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
+  emit_move_insn (reg, target);
+  insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg,
+                                     gen_rtx_REG (Pmode, RETURN_REGNUM));
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
 
-      if (cum->gprs + n_gprs > 5)
-       return 0;
-      else
-       return gen_rtx (REG, mode, cum->gprs + 2);
-    }
+  emit_move_insn (EH_RETURN_HANDLER_RTX, reg);
 }
 
+/* Rework the prologue/epilogue to avoid saving/restoring
+   registers unnecessarily.  */
 
-/* Create and return the va_list datatype.
+static void
+s390_optimize_prologue (void)
+{
+  rtx insn, new_insn, next_insn;
 
-   On S/390, va_list is an array type equivalent to
+  /* Do a final recompute of the frame-related data.  */
 
-      typedef struct __va_list_tag
-        {
-            long __gpr;
-            long __fpr;
-            void *__overflow_arg_area;
-            void *__reg_save_area;
-            
-        } va_list[1];
+  s390_update_frame_layout ();
 
-   where __gpr and __fpr hold the number of general purpose
-   or floating point arguments used up to now, respectively,
-   __overflow_arg_area points to the stack location of the 
-   next argument passed on the stack, and __reg_save_area
-   always points to the start of the register area in the
-   call frame of the current function.  The function prologue
-   saves all registers used for argument passing into this
-   area if the function uses variable arguments.  */
+  /* If all special registers are in fact used, there's nothing we
+     can do, so no point in walking the insn list.  */
 
-tree
-s390_build_va_list ()
-{
-  tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
+  if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM 
+      && cfun_frame_layout.last_save_gpr >= BASE_REGNUM
+      && (TARGET_CPU_ZARCH 
+          || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM 
+              && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
+    return;
 
-  record = make_lang_type (RECORD_TYPE);
+  /* Search for prologue/epilogue insns and replace them.  */
 
-  type_decl =
-    build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
+  for (insn = get_insns (); insn; insn = next_insn)
+    {
+      int first, last, off;
+      rtx set, base, offset;
 
-  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"), 
-                     long_integer_type_node);
-  f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"), 
-                     long_integer_type_node);
-  f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
-                     ptr_type_node);
-  f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
-                     ptr_type_node);
+      next_insn = NEXT_INSN (insn);
 
-  DECL_FIELD_CONTEXT (f_gpr) = record;
-  DECL_FIELD_CONTEXT (f_fpr) = record;
-  DECL_FIELD_CONTEXT (f_ovf) = record;
-  DECL_FIELD_CONTEXT (f_sav) = record;
+      if (GET_CODE (insn) != INSN)
+       continue;
 
-  TREE_CHAIN (record) = type_decl;
-  TYPE_NAME (record) = type_decl;
-  TYPE_FIELDS (record) = f_gpr;
-  TREE_CHAIN (f_gpr) = f_fpr;
-  TREE_CHAIN (f_fpr) = f_ovf;
-  TREE_CHAIN (f_ovf) = f_sav;
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && store_multiple_operation (PATTERN (insn), VOIDmode))
+       {
+         set = XVECEXP (PATTERN (insn), 0, 0);
+         first = REGNO (SET_SRC (set));
+         last = first + XVECLEN (PATTERN (insn), 0) - 1;
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset);
+
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (cfun_frame_layout.first_save_gpr != -1
+             && (cfun_frame_layout.first_save_gpr < first
+                 || cfun_frame_layout.last_save_gpr > last))
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (first > BASE_REGNUM || last < BASE_REGNUM)
+           continue;
 
-  layout_type (record);
+         if (cfun_frame_layout.first_save_gpr != -1)
+           {
+             new_insn  = save_gprs (base, 
+                                    off + (cfun_frame_layout.first_save_gpr
+                                           - first) * UNITS_PER_WORD, 
+                                    cfun_frame_layout.first_save_gpr,
+                                    cfun_frame_layout.last_save_gpr);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
+           }
 
-  /* The correct type is an array type of one element.  */
-  return build_array_type (record, build_index_type (size_zero_node));
-}
+         remove_insn (insn);
+         continue;
+       }
 
-/* Implement va_start by filling the va_list structure VALIST.
-   STDARG_P is true if implementing __builtin_stdarg_va_start,
-   false if implementing __builtin_varargs_va_start.  NEXTARG
-   points to the first anonymous stack argument.
+      if (cfun_frame_layout.first_save_gpr == -1
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == REG
+         && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
+             || (!TARGET_CPU_ZARCH
+                 && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM))
+         && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
+       {
+         set = PATTERN (insn);
+         first = REGNO (SET_SRC (set));
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset);
 
-   The following global variables are used to initialize
-   the va_list structure:
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
 
-     current_function_args_info:
-       holds number of gprs and fprs used for named arguments.
-     current_function_arg_offset_rtx:
-       holds the offset of the first anonymous stack argument
-       (relative to the virtual arg pointer).  */
+         remove_insn (insn);
+         continue;
+       }
+
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && load_multiple_operation (PATTERN (insn), VOIDmode))
+       {
+         set = XVECEXP (PATTERN (insn), 0, 0);
+         first = REGNO (SET_DEST (set));
+         last = first + XVECLEN (PATTERN (insn), 0) - 1;
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+         off = INTVAL (offset);
+
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (cfun_frame_layout.first_restore_gpr != -1
+             && (cfun_frame_layout.first_restore_gpr < first
+                 || cfun_frame_layout.last_restore_gpr > last))
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (first > BASE_REGNUM || last < BASE_REGNUM)
+           continue;
+
+         if (cfun_frame_layout.first_restore_gpr != -1)
+           {
+             new_insn = restore_gprs (base, 
+                                      off + (cfun_frame_layout.first_restore_gpr
+                                             - first) * UNITS_PER_WORD, 
+                                      cfun_frame_layout.first_restore_gpr,
+                                      cfun_frame_layout.last_restore_gpr);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
+           }
+
+         remove_insn (insn);
+         continue;
+       }
+
+      if (cfun_frame_layout.first_restore_gpr == -1
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_DEST (PATTERN (insn))) == REG
+         && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
+             || (!TARGET_CPU_ZARCH
+                 && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM))
+         && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
+       {
+         set = PATTERN (insn);
+         first = REGNO (SET_DEST (set));
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+         off = INTVAL (offset);
+
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+
+         remove_insn (insn);
+         continue;
+       }
+    }
+}
 
-void
-s390_va_start (stdarg_p, valist, nextarg)
-     int stdarg_p;
-     tree valist;
-     rtx nextarg ATTRIBUTE_UNUSED;
+/* On z10 the dynamic branch prediction must see the backward jump in
+   a window of 384 bytes. If not it falls back to the static
+   prediction.  This function rearranges the loop backward branch in a
+   way which makes the static prediction always correct.  The function
+   returns true if it added an instruction.  */
+static bool
+s390_z10_fix_long_loop_prediction (rtx insn)
 {
-  HOST_WIDE_INT n_gpr, n_fpr;
-  int off;
-  tree f_gpr, f_fpr, f_ovf, f_sav;
-  tree gpr, fpr, ovf, sav, t;
-
-  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  rtx set = single_set (insn);
+  rtx code_label, label_ref, new_label;
+  rtx uncond_jump;
+  rtx cur_insn;
+  rtx tmp;
+  int distance;
+
+  /* This will exclude branch on count and branch on index patterns
+     since these are correctly statically predicted.  */
+  if (!set
+      || SET_DEST (set) != pc_rtx
+      || GET_CODE (SET_SRC(set)) != IF_THEN_ELSE)
+    return false;
+
+  label_ref = (GET_CODE (XEXP (SET_SRC (set), 1)) == LABEL_REF ?
+              XEXP (SET_SRC (set), 1) : XEXP (SET_SRC (set), 2));
+
+  gcc_assert (GET_CODE (label_ref) == LABEL_REF);
+
+  code_label = XEXP (label_ref, 0);
+
+  if (INSN_ADDRESSES (INSN_UID (code_label)) == -1
+      || INSN_ADDRESSES (INSN_UID (insn)) == -1
+      || (INSN_ADDRESSES (INSN_UID (insn))
+         - INSN_ADDRESSES (INSN_UID (code_label)) < Z10_PREDICT_DISTANCE))
+    return false;
+
+  for (distance = 0, cur_insn = PREV_INSN (insn);
+       distance < Z10_PREDICT_DISTANCE - 6;
+       distance += get_attr_length (cur_insn), cur_insn = PREV_INSN (cur_insn))
+    if (!cur_insn || JUMP_P (cur_insn) || LABEL_P (cur_insn))
+      return false;
+
+  new_label = gen_label_rtx ();
+  uncond_jump = emit_jump_insn_after (
+                 gen_rtx_SET (VOIDmode, pc_rtx,
+                              gen_rtx_LABEL_REF (VOIDmode, code_label)),
+                 insn);
+  emit_label_after (new_label, uncond_jump);
+
+  tmp = XEXP (SET_SRC (set), 1);
+  XEXP (SET_SRC (set), 1) = XEXP (SET_SRC (set), 2);
+  XEXP (SET_SRC (set), 2) = tmp;
+  INSN_CODE (insn) = -1;
+
+  XEXP (label_ref, 0) = new_label;
+  JUMP_LABEL (insn) = new_label;
+  JUMP_LABEL (uncond_jump) = code_label;
+
+  return true;
+}
 
-  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+/* Returns 1 if INSN reads the value of REG for purposes not related
+   to addressing of memory, and 0 otherwise.  */
+static int
+s390_non_addr_reg_read_p (rtx reg, rtx insn)
+{
+  return reg_referenced_p (reg, PATTERN (insn))
+    && !reg_used_in_mem_p (REGNO (reg), PATTERN (insn));
+}
 
-  /* Count number of gp and fp argument registers used.  */
+/* Starting from INSN find_cond_jump looks downwards in the insn
+   stream for a single jump insn which is the last user of the
+   condition code set in INSN.  */
+static rtx
+find_cond_jump (rtx insn)
+{
+  for (; insn; insn = NEXT_INSN (insn))
+    {
+      rtx ite, cc;
 
-  n_gpr = current_function_args_info.gprs;
-  n_fpr = current_function_args_info.fprs;
+      if (LABEL_P (insn))
+       break;
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      if (!JUMP_P (insn))
+       {
+         if (reg_mentioned_p (gen_rtx_REG (CCmode, CC_REGNUM), insn))
+           break;
+         continue;
+       }
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      /* This will be triggered by a return.  */
+      if (GET_CODE (PATTERN (insn)) != SET)
+       break;
 
-  /* Find the overflow area.  */
-  t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
+      gcc_assert (SET_DEST (PATTERN (insn)) == pc_rtx);
+      ite = SET_SRC (PATTERN (insn));
 
-  off = INTVAL (current_function_arg_offset_rtx);
-  off = off < 0 ? 0 : off;
-  if (! stdarg_p)
-    off = off > 0 ? off - UNITS_PER_WORD : off;
-  if (TARGET_DEBUG_ARG)
-    fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
-            (int)n_gpr, (int)n_fpr, off);
+      if (GET_CODE (ite) != IF_THEN_ELSE)
+       break;
 
-  t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_2 (off, 0));
+      cc = XEXP (XEXP (ite, 0), 0);
+      if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc)))
+       break;
 
-  t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      if (find_reg_note (insn, REG_DEAD, cc))
+       return insn;
+      break;
+    }
 
-  /* Find the register save area.  */
-  t = make_tree (TREE_TYPE (sav), virtual_incoming_args_rtx);
-  t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_2 (-STACK_POINTER_OFFSET, -1));
-  t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  return NULL_RTX;
 }
 
-/* Implement va_arg by updating the va_list structure 
-   VALIST as required to retrieve an argument of type
-   TYPE, and returning that argument. 
-   
-   Generates code equivalent to:
-   
-   if (integral value) {
-     if (size  <= 4 && args.gpr < 5 ||
-         size  > 4 && args.gpr < 4 ) 
-       ret = args.reg_save_area[args.gpr+8]
-     else
-       ret = *args.overflow_arg_area++;
-   } else if (float value) {
-     if (args.fgpr < 2)
-       ret = args.reg_save_area[args.fpr+64]
-     else
-       ret = *args.overflow_arg_area++;
-   } else if (aggregate value) {
-     if (args.gpr < 5)
-       ret = *args.reg_save_area[args.gpr]
-     else
-       ret = **args.overflow_arg_area++;
-   } */
-
-rtx
-s390_va_arg (valist, type)
-     tree valist;
-     tree type;
+/* Swap the condition in COND and the operands in OP0 and OP1 so that
+   the semantics does not change.  If NULL_RTX is passed as COND the
+   function tries to find the conditional jump starting with INSN.  */
+static void
+s390_swap_cmp (rtx cond, rtx *op0, rtx *op1, rtx insn)
 {
-  tree f_gpr, f_fpr, f_ovf, f_sav;
-  tree gpr, fpr, ovf, sav, reg, t, u;
-  int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
-  rtx lab_false, lab_over, addr_rtx, r;
+  rtx tmp = *op0;
 
-  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  if (cond == NULL_RTX)
+    {
+      rtx jump = find_cond_jump (NEXT_INSN (insn));
+      jump = jump ? single_set (jump) : NULL_RTX;
 
-  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+      if (jump == NULL_RTX)
+       return;
 
-  size = int_size_in_bytes (type);
+      cond = XEXP (XEXP (jump, 1), 0);
+    }
+
+  *op0 = *op1;
+  *op1 = tmp;
+  PUT_CODE (cond, swap_condition (GET_CODE (cond)));
+}
+
+/* On z10, instructions of the compare-and-branch family have the
+   property to access the register occurring as second operand with
+   its bits complemented.  If such a compare is grouped with a second
+   instruction that accesses the same register non-complemented, and
+   if that register's value is delivered via a bypass, then the
+   pipeline recycles, thereby causing significant performance decline.
+   This function locates such situations and exchanges the two
+   operands of the compare.  The function return true whenever it
+   added an insn.  */
+static bool
+s390_z10_optimize_cmp (rtx insn)
+{
+  rtx prev_insn, next_insn;
+  bool insn_added_p = false;
+  rtx cond, *op0, *op1;
 
-  if (s390_function_arg_pass_by_reference (TYPE_MODE (type), type))
+  if (GET_CODE (PATTERN (insn)) == PARALLEL)
     {
-      if (TARGET_DEBUG_ARG)
+      /* Handle compare and branch and branch on count
+        instructions.  */
+      rtx pattern = single_set (insn);
+
+      if (!pattern
+         || SET_DEST (pattern) != pc_rtx
+         || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
+       return false;
+
+      cond = XEXP (SET_SRC (pattern), 0);
+      op0 = &XEXP (cond, 0);
+      op1 = &XEXP (cond, 1);
+    }
+  else if (GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx src, dest;
+
+      /* Handle normal compare instructions.  */
+      src = SET_SRC (PATTERN (insn));
+      dest = SET_DEST (PATTERN (insn));
+
+      if (!REG_P (dest)
+         || !CC_REGNO_P (REGNO (dest))
+         || GET_CODE (src) != COMPARE)
+       return false;
+
+      /* s390_swap_cmp will try to find the conditional
+        jump when passing NULL_RTX as condition.  */
+      cond = NULL_RTX;
+      op0 = &XEXP (src, 0);
+      op1 = &XEXP (src, 1);
+    }
+  else
+    return false;
+
+  if (!REG_P (*op0) || !REG_P (*op1))
+    return false;
+
+  if (GET_MODE_CLASS (GET_MODE (*op0)) != MODE_INT)
+    return false;
+
+  /* Swap the COMPARE arguments and its mask if there is a
+     conflicting access in the previous insn.  */
+  prev_insn = prev_active_insn (insn);
+  if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+      && reg_referenced_p (*op1, PATTERN (prev_insn)))
+    s390_swap_cmp (cond, op0, op1, insn);
+
+  /* Check if there is a conflict with the next insn. If there
+     was no conflict with the previous insn, then swap the
+     COMPARE arguments and its mask.  If we already swapped
+     the operands, or if swapping them would cause a conflict
+     with the previous insn, issue a NOP after the COMPARE in
+     order to separate the two instuctions.  */
+  next_insn = next_active_insn (insn);
+  if (next_insn != NULL_RTX && INSN_P (next_insn)
+      && s390_non_addr_reg_read_p (*op1, next_insn))
+    {
+      if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+         && s390_non_addr_reg_read_p (*op0, prev_insn))
        {
-         fprintf (stderr, "va_arg: aggregate type");
-         debug_tree (type);
+         if (REGNO (*op1) == 0)
+           emit_insn_after (gen_nop1 (), insn);
+         else
+           emit_insn_after (gen_nop (), insn);
+         insn_added_p = true;
        }
-
-      /* Aggregates are passed by reference.  */
-      indirect_p = 1;
-      reg = gpr;
-      n_reg = 1;
-      sav_ofs = 2 * UNITS_PER_WORD;
-      sav_scale = UNITS_PER_WORD;
-      size = UNITS_PER_WORD;
-      max_reg = 4;
+      else
+       s390_swap_cmp (cond, op0, op1, insn);
     }
-  else if (FLOAT_TYPE_P (type) && ! TARGET_SOFT_FLOAT)
+  return insn_added_p;
+}
+
+/* Perform machine-dependent processing.  */
+
+static void
+s390_reorg (void)
+{
+  bool pool_overflow = false;
+
+  /* Make sure all splits have been performed; splits after
+     machine_dependent_reorg might confuse insn length counts.  */
+  split_all_insns_noflow ();
+
+  /* Install the main literal pool and the associated base
+     register load insns.
+
+     In addition, there are two problematic situations we need
+     to correct:
+
+     - the literal pool might be > 4096 bytes in size, so that
+       some of its elements cannot be directly accessed
+
+     - a branch target might be > 64K away from the branch, so that
+       it is not possible to use a PC-relative instruction.
+
+     To fix those, we split the single literal pool into multiple
+     pool chunks, reloading the pool base register at various
+     points throughout the function to ensure it always points to
+     the pool chunk the following code expects, and / or replace
+     PC-relative branches by absolute branches.
+
+     However, the two problems are interdependent: splitting the
+     literal pool can move a branch further away from its target,
+     causing the 64K limit to overflow, and on the other hand,
+     replacing a PC-relative branch by an absolute branch means
+     we need to put the branch target address into the literal
+     pool, possibly causing it to overflow.
+
+     So, we loop trying to fix up both problems until we manage
+     to satisfy both conditions at the same time.  Note that the
+     loop is guaranteed to terminate as every pass of the loop
+     strictly decreases the total number of PC-relative branches
+     in the function.  (This is not completely true as there
+     might be branch-over-pool insns introduced by chunkify_start.
+     Those never need to be split however.)  */
+
+  for (;;)
     {
-      if (TARGET_DEBUG_ARG)
+      struct constant_pool *pool = NULL;
+
+      /* Collect the literal pool.  */
+      if (!pool_overflow)
        {
-         fprintf (stderr, "va_arg: float type");
-         debug_tree (type);
+         pool = s390_mainpool_start ();
+         if (!pool)
+           pool_overflow = true;
        }
 
-      /* FP args go in FP registers, if present.  */
-      indirect_p = 0;
-      reg = fpr;
-      n_reg = 1;
-      sav_ofs = 16 * UNITS_PER_WORD;
-      sav_scale = 8;
-      /* TARGET_64BIT has up to 4 parameter in fprs */
-      max_reg = TARGET_64BIT ? 3 : 1;
+      /* If literal pool overflowed, start to chunkify it.  */
+      if (pool_overflow)
+        pool = s390_chunkify_start ();
+
+      /* Split out-of-range branches.  If this has created new
+        literal pool entries, cancel current chunk list and
+        recompute it.  zSeries machines have large branch
+        instructions, so we never need to split a branch.  */
+      if (!TARGET_CPU_ZARCH && s390_split_branches ())
+        {
+          if (pool_overflow)
+            s390_chunkify_cancel (pool);
+         else
+            s390_mainpool_cancel (pool);
+
+          continue;
+        }
+
+      /* If we made it up to here, both conditions are satisfied.
+        Finish up literal pool related changes.  */
+      if (pool_overflow)
+       s390_chunkify_finish (pool);
+      else
+       s390_mainpool_finish (pool);
+
+      /* We're done splitting branches.  */
+      cfun->machine->split_branches_pending_p = false;
+      break;
     }
-  else
+
+  /* Generate out-of-pool execute target insns.  */
+  if (TARGET_CPU_ZARCH)
     {
-      if (TARGET_DEBUG_ARG)
+      rtx insn, label, target;
+
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        {
-         fprintf (stderr, "va_arg: other type");
-         debug_tree (type);
-       }
+         label = s390_execute_label (insn);
+         if (!label)
+           continue;
 
-      /* Otherwise into GP registers.  */
-      indirect_p = 0;
-      reg = gpr;
-      n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-      sav_ofs = 2 * UNITS_PER_WORD;
-      if (TARGET_64BIT)
-       sav_ofs += TYPE_MODE (type) == SImode ? 4 : 
-                  TYPE_MODE (type) == HImode ? 6 : 
-                  TYPE_MODE (type) == QImode ? 7 : 0;
-      else
-       sav_ofs += TYPE_MODE (type) == HImode ? 2 : 
-                  TYPE_MODE (type) == QImode ? 3 : 0;
+         gcc_assert (label != const0_rtx);
 
-      sav_scale = UNITS_PER_WORD;
-      if (n_reg > 1)
-       max_reg = 3;
-      else
-       max_reg = 4;
+         target = emit_label (XEXP (label, 0));
+         INSN_ADDRESSES_NEW (target, -1);
+
+         target = emit_insn (s390_execute_target (insn));
+         INSN_ADDRESSES_NEW (target, -1);
+       }
     }
 
-  /* Pull the value out of the saved registers ...  */
+  /* Try to optimize prologue and epilogue further.  */
+  s390_optimize_prologue ();
 
-  lab_false = gen_label_rtx ();
-  lab_over = gen_label_rtx ();
-  addr_rtx = gen_reg_rtx (Pmode);
+  /* Walk over the insns and do some z10 specific changes.  */
+  if (s390_tune == PROCESSOR_2097_Z10)
+    {
+      rtx insn;
+      bool insn_added_p = false;
 
-  emit_cmp_and_jump_insns (expand_expr (reg, NULL_RTX, Pmode, EXPAND_NORMAL),
-                          GEN_INT (max_reg),
-                          GT, const1_rtx, Pmode, 0, lab_false);
+      /* The insn lengths and addresses have to be up to date for the
+        following manipulations.  */
+      shorten_branches (get_insns ());
 
-  if (sav_ofs)
-    t = build (PLUS_EXPR, ptr_type_node, sav, build_int_2 (sav_ofs, 0));
-  else
-    t = sav;
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+           continue;
 
-  u = build (MULT_EXPR, long_integer_type_node,
-            reg, build_int_2 (sav_scale, 0));
-  TREE_SIDE_EFFECTS (u) = 1;
+         if (JUMP_P (insn))
+           insn_added_p |= s390_z10_fix_long_loop_prediction (insn);
 
-  t = build (PLUS_EXPR, ptr_type_node, t, u);
-  TREE_SIDE_EFFECTS (t) = 1;
+         if (GET_CODE (PATTERN (insn)) == PARALLEL
+             || GET_CODE (PATTERN (insn)) == SET)
+           insn_added_p |= s390_z10_optimize_cmp (insn);
+       }
 
-  r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-  if (r != addr_rtx)
-    emit_move_insn (addr_rtx, r);
+      /* Adjust branches if we added new instructions.  */
+      if (insn_added_p)
+       shorten_branches (get_insns ());
+    }
+}
 
+/* Return true if INSN is a fp load insn writing register REGNO.  */
+static inline bool
+s390_fpload_toreg (rtx insn, unsigned int regno)
+{
+  rtx set;
+  enum attr_type flag = s390_safe_attr_type (insn);
 
-  emit_jump_insn (gen_jump (lab_over));
-  emit_barrier ();
-  emit_label (lab_false);
+  if (flag != TYPE_FLOADSF && flag != TYPE_FLOADDF)
+    return false;
 
-  /* ... Otherwise out of the overflow area.  */
+  set = single_set (insn);
 
-  t = save_expr (ovf);
+  if (set == NULL_RTX)
+    return false;
 
+  if (!REG_P (SET_DEST (set)) || !MEM_P (SET_SRC (set)))
+    return false;
 
-  /* In 64 BIT for each argument on stack, a full 64 bit slot is allocated.  */
-  if (size < UNITS_PER_WORD)
-    {
-      t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (UNITS_PER_WORD-size, 0));
-      t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-      TREE_SIDE_EFFECTS (t) = 1;
-      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (REGNO (SET_DEST (set)) != regno)
+    return false;
 
-      t = save_expr (ovf);
-    }
+  return true;
+}
 
-  r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-  if (r != addr_rtx)
-    emit_move_insn (addr_rtx, r);
+/* This value describes the distance to be avoided between an
+   aritmetic fp instruction and an fp load writing the same register.
+   Z10_EARLYLOAD_DISTANCE - 1 as well as Z10_EARLYLOAD_DISTANCE + 1 is
+   fine but the exact value has to be avoided. Otherwise the FP
+   pipeline will throw an exception causing a major penalty.  */
+#define Z10_EARLYLOAD_DISTANCE 7
 
-  t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (size, 0));
-  t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+/* Rearrange the ready list in order to avoid the situation described
+   for Z10_EARLYLOAD_DISTANCE.  A problematic load instruction is
+   moved to the very end of the ready list.  */
+static void
+s390_z10_prevent_earlyload_conflicts (rtx *ready, int *nready_p)
+{
+  unsigned int regno;
+  int nready = *nready_p;
+  rtx tmp;
+  int i;
+  rtx insn;
+  rtx set;
+  enum attr_type flag;
+  int distance;
+
+  /* Skip DISTANCE - 1 active insns.  */
+  for (insn = last_scheduled_insn, distance = Z10_EARLYLOAD_DISTANCE - 1;
+       distance > 0 && insn != NULL_RTX;
+       distance--, insn = prev_active_insn (insn))
+    if (CALL_P (insn) || JUMP_P (insn))
+      return;
 
-  emit_label (lab_over);
+  if (insn == NULL_RTX)
+    return;
 
-  /* If less than max_regs a registers are retrieved out 
-     of register save area, increment.  */
+  set = single_set (insn);
 
-  u = build (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, 
-            build_int_2 (n_reg, 0));
-  TREE_SIDE_EFFECTS (u) = 1;
-  expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (set == NULL_RTX || !REG_P (SET_DEST (set))
+      || GET_MODE_CLASS (GET_MODE (SET_DEST (set))) != MODE_FLOAT)
+    return;
 
-  if (indirect_p)
-    {
-      r = gen_rtx_MEM (Pmode, addr_rtx);
-      set_mem_alias_set (r, get_varargs_alias_set ());
-      emit_move_insn (addr_rtx, r);
-    }
+  flag = s390_safe_attr_type (insn);
 
+  if (flag == TYPE_FLOADSF || flag == TYPE_FLOADDF)
+    return;
 
-  return addr_rtx;
-}
+  regno = REGNO (SET_DEST (set));
+  i = nready - 1;
 
+  while (!s390_fpload_toreg (ready[i], regno) && i > 0)
+    i--;
 
-/* Output assembly code for the trampoline template to
-   stdio stream FILE.
+  if (!i)
+    return;
 
-   On S/390, we use gpr 1 internally in the trampoline code;
-   gpr 0 is used to hold the static chain.  */
+  tmp = ready[i];
+  memmove (&ready[1], &ready[0], sizeof (rtx) * i);
+  ready[0] = tmp;
+}
 
-void
-s390_trampoline_template (file)
-     FILE *file;
+/* This function is called via hook TARGET_SCHED_REORDER before
+   issueing one insn from list READY which contains *NREADYP entries.
+   For target z10 it reorders load instructions to avoid early load
+   conflicts in the floating point pipeline  */
+static int
+s390_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int clock ATTRIBUTE_UNUSED)
 {
-  if (TARGET_64BIT)
-    {
-      fprintf (file, "larl\t%s,0f\n", reg_names[1]);
-      fprintf (file, "lg\t%s,0(%s)\n", reg_names[0], reg_names[1]);
-      fprintf (file, "lg\t%s,8(%s)\n", reg_names[1], reg_names[1]);
-      fprintf (file, "br\t%s\n", reg_names[1]);
-      fprintf (file, "0:\t.quad\t0\n");
-      fprintf (file, ".quad\t0\n");
-    }
-  else
-    {
-      fprintf (file, "basr\t%s,0\n", reg_names[1]);
-      fprintf (file, "l\t%s,10(%s)\n", reg_names[0], reg_names[1]);
-      fprintf (file, "l\t%s,14(%s)\n", reg_names[1], reg_names[1]);
-      fprintf (file, "br\t%s\n", reg_names[1]);
-      fprintf (file, ".long\t0\n");
-      fprintf (file, ".long\t0\n");
-    }
-}
+  if (s390_tune == PROCESSOR_2097_Z10)
+    if (reload_completed && *nreadyp > 1)
+      s390_z10_prevent_earlyload_conflicts (ready, nreadyp);
 
-/* Emit RTL insns to initialize the variable parts of a trampoline.
-   FNADDR is an RTX for the address of the function's pure code.
-   CXT is an RTX for the static chain value for the function.  */
+  return s390_issue_rate ();
+}
 
-void
-s390_initialize_trampoline (addr, fnaddr, cxt)
-     rtx addr;
-     rtx fnaddr;
-     rtx cxt;
+/* This function is called via hook TARGET_SCHED_VARIABLE_ISSUE after
+   the scheduler has issued INSN.  It stores the last issued insn into
+   last_scheduled_insn in order to make it available for
+   s390_sched_reorder.  */
+static int
+s390_sched_variable_issue (FILE *file ATTRIBUTE_UNUSED,
+                           int verbose ATTRIBUTE_UNUSED,
+                         rtx insn, int more)
 {
-  emit_move_insn (gen_rtx 
-                 (MEM, Pmode,
-                  memory_address (Pmode, 
-                  plus_constant (addr, (TARGET_64BIT ? 20 : 12) ))), cxt);
-  emit_move_insn (gen_rtx
-                 (MEM, Pmode,
-                  memory_address (Pmode, 
-                  plus_constant (addr, (TARGET_64BIT ? 28 : 16) ))), fnaddr);
+  last_scheduled_insn = insn;
+
+  if (GET_CODE (PATTERN (insn)) != USE
+      && GET_CODE (PATTERN (insn)) != CLOBBER)
+    return more - 1;
+  else
+    return more;
 }
 
-/* Return rtx for 64-bit constant formed from the 32-bit subwords
-   LOW and HIGH, independent of the host word size.  */
+/* Initialize GCC target structure.  */
 
-rtx
-s390_gen_rtx_const_DI (high, low)
-     int high;
-     int low;
-{
-#if HOST_BITS_PER_WIDE_INT >= 64
-  HOST_WIDE_INT val;
-  val = (HOST_WIDE_INT)high;
-  val <<= 32;
-  val |= (HOST_WIDE_INT)low;
-  
-  return GEN_INT (val);
-#else
-#if HOST_BITS_PER_WIDE_INT >= 32
-  return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
-#else
-  abort ();
-#endif
+#undef  TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+#undef  TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+#undef  TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER s390_assemble_integer
+
+#undef  TARGET_ASM_OPEN_PAREN
+#define TARGET_ASM_OPEN_PAREN ""
+
+#undef  TARGET_ASM_CLOSE_PAREN
+#define TARGET_ASM_CLOSE_PAREN ""
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION s390_handle_option
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
 #endif
-} 
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem
 
-/* Output assembler code to FILE to increment profiler label # LABELNO
-   for profiling a function entry.  */
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
 
-void
-s390_function_profiler (file, labelno)
-     FILE *file;
-     int labelno;
-{
-  rtx op[7];
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
 
-  char label[128];
-  sprintf (label, "%sP%d", LPREFIX, labelno);
+#undef  TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS s390_init_builtins
+#undef  TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN s390_expand_builtin
 
-  fprintf (file, "# function profiler \n");
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
-  op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
-  op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
+#undef  TARGET_SCHED_ADJUST_PRIORITY
+#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE s390_issue_rate
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
+
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE s390_sched_variable_issue
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER s390_sched_reorder
+
+#undef TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS s390_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST s390_address_cost
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
+
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START s390_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
+
+#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_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
+
+#undef TARGET_FIXED_CONDITION_CODE_REGS
+#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
+
+#undef TARGET_CC_MODES_COMPATIBLE
+#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
+
+#undef TARGET_INVALID_WITHIN_DOLOOP
+#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_null
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel
+#endif
 
-  op[2] = gen_rtx_REG (Pmode, 1);
-  op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
-  SYMBOL_REF_FLAG (op[3]) = 1;
+#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
+#undef TARGET_MANGLE_TYPE
+#define TARGET_MANGLE_TYPE s390_mangle_type
+#endif
 
-  op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
-  if (flag_pic)
-    {
-      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), 113);
-      op[4] = gen_rtx_CONST (Pmode, op[4]);
-    }
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p
 
-  if (TARGET_64BIT)
-    {
-      output_asm_insn ("stg\t%0,%1", op);
-      output_asm_insn ("larl\t%2,%3", op);
-      output_asm_insn ("brasl\t%0,%4", op);
-      output_asm_insn ("lg\t%0,%1", op);
-    }
-  else if (!flag_pic)
-    {
-      op[6] = gen_label_rtx ();
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD s390_secondary_reload
 
-      output_asm_insn ("st\t%0,%1", op);
-      output_asm_insn ("bras\t%2,%l6", op);
-      output_asm_insn (".long\t%4", op);
-      output_asm_insn (".long\t%3", op);
-      ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6]));
-      output_asm_insn ("l\t%0,0(%2)", op);
-      output_asm_insn ("l\t%2,4(%2)", op);
-      output_asm_insn ("basr\t%0,%0", op);
-      output_asm_insn ("l\t%0,%1", op);
-    }
-  else
-    {
-      op[5] = gen_label_rtx ();
-      op[6] = gen_label_rtx ();
+#undef TARGET_LIBGCC_CMP_RETURN_MODE
+#define TARGET_LIBGCC_CMP_RETURN_MODE s390_libgcc_cmp_return_mode
 
-      output_asm_insn ("st\t%0,%1", op);
-      output_asm_insn ("bras\t%2,%l6", op);
-      ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[5]));
-      output_asm_insn (".long\t%4-%l5", op);
-      output_asm_insn (".long\t%3-%l5", op);
-      ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (op[6]));
-      output_asm_insn ("lr\t%0,%2", op);
-      output_asm_insn ("a\t%0,0(%2)", op);
-      output_asm_insn ("a\t%2,4(%2)", op);
-      output_asm_insn ("basr\t%0,%0", op);
-      output_asm_insn ("l\t%0,%1", op);
-    }
-}
+#undef TARGET_LIBGCC_SHIFT_COUNT_MODE
+#define TARGET_LIBGCC_SHIFT_COUNT_MODE s390_libgcc_shift_count_mode
+
+struct gcc_target targetm = TARGET_INITIALIZER;
 
+#include "gt-s390.h"