]> 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
 /* 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
    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
 
 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 "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
 #include "tm_p.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 "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;
 
 
 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 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
 /* 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;
   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 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;
 };
 
   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
 /* 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.  */
    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;
 
 {
   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;
 
   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:
   switch (set_mode)
     {
     case CCSmode:
-      if (req_mode != CCSmode)
-        return 0;
-      break;
+    case CCSRmode:
     case CCUmode:
     case CCUmode:
-      if (req_mode != CCUmode)
-        return 0;
-      break;
+    case CCURmode:
     case CCLmode:
     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;
         return 0;
       break;
+
     case CCZmode:
     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;
         return 0;
       break;
+
+    case CCAPmode:
+    case CCANmode:
+      if (req_mode != CCAmode)
+        return 0;
+      break;
+
     default:
     default:
-      abort ();
+      gcc_unreachable ();
     }
     }
+
   return (GET_MODE (SET_SRC (set)) == set_mode);
 }
 
   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;
 
 {
   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);
 
   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))
           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
    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:
 {
   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;
 
          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:
        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:
       case UNORDERED:
       case ORDERED:
       case UNEQ:
@@ -244,45 +626,306 @@ s390_select_ccmode (code, op0, op1)
       case UNGE:
       case UNGT:
       case LTGT:
       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;
 
        return CCSmode;
 
-      case LEU:
       case LTU:
       case GEU:
       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:
       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:
        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;
 
   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:
 
   switch (GET_MODE (XEXP (code, 0)))
     {
     case CCZmode:
+    case CCZ1mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
        case NE:        return CC1 | CC2 | CC3;
       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;
 
         }
       break;
 
@@ -291,13 +934,40 @@ s390_branch_condition_mask (code)
         {
         case EQ:       return CC0 | CC2;
        case NE:        return CC1 | CC3;
         {
         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;
 
         }
       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))
         {
     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;
         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;
 
         }
       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;
         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:
 
     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",
     {
       NULL, "o", "h", "nle",
       "l", "nhe", "lh", "ne",
@@ -355,909 +1119,605 @@ s390_branch_condition_mnemonic (code, inv)
       "le", "nh", "no", NULL
     };
 
       "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 (inv)
     mask ^= 15;
 
-  if (mask < 1 || mask > 14)
-    abort ();
+  gcc_assert (mask >= 1 && mask <= 14);
 
   return mnemonic[mask];
 }
 
 
   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
 
 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
       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
 
 /* 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.
 
    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
    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;
   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.  */
 
 
   /* Decompose address into base + index + displacement.  */
 
@@ -1312,3035 +1781,8434 @@ s390_decompose_address (addr, out, strict)
 
       else
        {
 
       else
        {
-         return FALSE;
+         return false;
        }
     }
 
   else
     disp = addr;               /* displacement */
 
        }
     }
 
   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)
 
   /* 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) == 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))
           || (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)
     }
 
   /* 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) == 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))
           || (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.  */
     }
 
   /* 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
       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)
     }
 
   if (!base && !indx)
-    pointer = TRUE;
-   
+    pointer = true;
+
   if (out)
     {
       out->base = base;
       out->indx = indx;
   if (out)
     {
       out->base = base;
       out->indx = indx;
-      out->disp = disp;
+      out->disp = orig_disp;
       out->pointer = pointer;
       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;
 {
   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_INT:
+    case CONST_DOUBLE:
+      /* Accept all non-symbolic constants.  */
+      return false;
+
     case LABEL_REF:
     case LABEL_REF:
-    case CODE_LABEL:
+      /* Labels are OK iff we are non-PIC.  */
+      return flag_pic != 0;
+
     case SYMBOL_REF:
     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:
 
     case UNSPEC:
-      if (XVECLEN (x, 0) != 1)
-        output_operand_lossage ("invalid UNSPEC as operand (1)");
       switch (XINT (x, 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:
        default:
-         output_operand_lossage ("invalid UNSPEC as operand (2)");
-         break;
-        }
+         return true;
+       }
       break;
 
     default:
       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
       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
 
 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
        }
       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
       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
     {
     }
   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
 
 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
 
 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
 
 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
 
 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
 
 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
     {
     }
   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
 #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"