Add a port to IBM z/Architecture (s390x) running Linux -- VEX
side components. (Florian Krohm <britzel@acm.org> and Christian
Borntraeger <borntraeger@de.ibm.com>).  Fixes #243404.



git-svn-id: svn://svn.valgrind.org/vex/trunk@2105 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
new file mode 100644
index 0000000..095221f
--- /dev/null
+++ b/priv/host_s390_isel.c
@@ -0,0 +1,2441 @@
+/* -*- mode: C; c-basic-offset: 3; -*- */
+
+/*---------------------------------------------------------------*/
+/*--- begin                                  host_s390_isel.c ---*/
+/*---------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright IBM Corp. 2010-2011
+
+   This program 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 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Florian Krohm */
+
+#include "libvex_basictypes.h"
+#include "libvex_ir.h"
+#include "libvex.h"
+#include "libvex_s390x_common.h"
+
+#include "ir_match.h"
+#include "main_util.h"
+#include "main_globals.h"
+#include "host_generic_regs.h"
+#include "host_s390_defs.h"
+
+/*---------------------------------------------------------*/
+/*--- ISelEnv                                           ---*/
+/*---------------------------------------------------------*/
+
+/* This carries around:
+
+   - A mapping from IRTemp to IRType, giving the type of any IRTemp we
+     might encounter.  This is computed before insn selection starts,
+     and does not change.
+
+   - A mapping from IRTemp to HReg.  This tells the insn selector
+     which virtual register(s) are associated with each IRTemp
+      temporary.  This is computed before insn selection starts, and
+      does not change.  We expect this mapping to map precisely the
+      same set of IRTemps as the type mapping does.
+
+         - vregmap   holds the primary register for the IRTemp.
+         - vregmapHI holds the secondary register for the IRTemp,
+              if any is needed.  That's only for Ity_I64 temps
+              in 32 bit mode or Ity_I128 temps in 64-bit mode.
+
+    - The code array, that is, the insns selected so far.
+
+    - A counter, for generating new virtual registers.
+
+    - The host subarchitecture we are selecting insns for.
+      This is set at the start and does not change.
+*/
+
+typedef struct {
+   IRTypeEnv   *type_env;
+
+   HReg        *vregmap;
+   HReg        *vregmapHI;
+   UInt         n_vregmap;
+
+   HInstrArray *code;
+
+   UInt         vreg_ctr;
+
+   UInt         hwcaps;
+
+} ISelEnv;
+
+
+/* Forward declarations */
+static HReg          s390_isel_int_expr(ISelEnv *, IRExpr *);
+static s390_amode   *s390_isel_amode(ISelEnv *, IRExpr *);
+static s390_cc_t     s390_isel_cc(ISelEnv *, IRExpr *);
+static s390_opnd_RMI s390_isel_int_expr_RMI(ISelEnv *, IRExpr *);
+static void          s390_isel_int128_expr(HReg *, HReg *, ISelEnv *, IRExpr *);
+static HReg          s390_isel_float_expr(ISelEnv *, IRExpr *);
+static void          s390_isel_float128_expr(HReg *, HReg *, ISelEnv *, IRExpr *);
+
+
+/* Add an instruction */
+static void
+addInstr(ISelEnv *env, s390_insn *insn)
+{
+   addHInstr(env->code, insn);
+
+   if (vex_traceflags & VEX_TRACE_VCODE) {
+      vex_printf("%s\n", s390_insn_as_string(insn));
+   }
+}
+
+
+static __inline__ IRExpr *
+mkU64(ULong value)
+{
+   return IRExpr_Const(IRConst_U64(value));
+}
+
+
+/*---------------------------------------------------------*/
+/*--- Registers                                         ---*/
+/*---------------------------------------------------------*/
+
+/* Return the virtual register to which a given IRTemp is mapped. */
+static HReg
+lookupIRTemp(ISelEnv *env, IRTemp tmp)
+{
+   vassert(tmp < env->n_vregmap);
+   vassert(env->vregmap[tmp] != INVALID_HREG);
+
+   return env->vregmap[tmp];
+}
+
+
+/* Return the two virtual registers to which the IRTemp is mapped. */
+static void
+lookupIRTemp128(HReg *hi, HReg *lo, ISelEnv *env, IRTemp tmp)
+{
+   vassert(tmp < env->n_vregmap);
+   vassert(env->vregmapHI[tmp] != INVALID_HREG);
+
+   *lo = env->vregmap[tmp];
+   *hi = env->vregmapHI[tmp];
+}
+
+
+/* Allocate a new integer register */
+static HReg
+newVRegI(ISelEnv *env)
+{
+   HReg reg = mkHReg(env->vreg_ctr, HRcInt64, True /* virtual */ );
+   env->vreg_ctr++;
+
+   return reg;
+}
+
+
+/* Allocate a new floating point register */
+static HReg
+newVRegF(ISelEnv *env)
+{
+   HReg reg = mkHReg(env->vreg_ctr, HRcFlt64, True /* virtual */ );
+
+   env->vreg_ctr++;
+
+   return reg;
+}
+
+
+/* Construct a non-virtual general purpose register */
+static __inline__ HReg
+make_gpr(ISelEnv *env, UInt regno)
+{
+   return mkHReg(regno, HRcInt64, False /* virtual */ );
+}
+
+
+/* Construct a non-virtual floating point register */
+static __inline__ HReg
+make_fpr(UInt regno)
+{
+   return mkHReg(regno, HRcFlt64, False /* virtual */ );
+}
+
+
+/*---------------------------------------------------------*/
+/*--- Amode                                             ---*/
+/*---------------------------------------------------------*/
+
+static __inline__ Bool
+ulong_fits_unsigned_12bit(ULong val)
+{
+   return (val & 0xFFFu) == val;
+}
+
+
+static __inline__ Bool
+ulong_fits_signed_20bit(ULong val)
+{
+   Long v = val & 0xFFFFFu;
+
+   v = (v << 44) >> 44;  /* sign extend */
+
+   return val == (ULong)v;
+}
+
+
+/* EXPR is an expression that is used as an address. Return an s390_amode
+   for it. */
+static s390_amode *
+s390_isel_amode_wrk(ISelEnv *env, IRExpr *expr)
+{
+   if (expr->tag == Iex_Binop && expr->Iex.Binop.op == Iop_Add64) {
+      IRExpr *arg1 = expr->Iex.Binop.arg1;
+      IRExpr *arg2 = expr->Iex.Binop.arg2;
+
+      /* Move constant into right subtree */
+      if (arg1->tag == Iex_Const) {
+         IRExpr *tmp;
+         tmp  = arg1;
+         arg1 = arg2;
+         arg2 = tmp;
+      }
+
+      /* r + constant: Check for b12 first, then b20 */
+      if (arg2->tag == Iex_Const && arg2->Iex.Const.con->tag == Ico_U64) {
+         ULong value = arg2->Iex.Const.con->Ico.U64;
+
+         if (ulong_fits_unsigned_12bit(value)) {
+            return s390_amode_b12((Int)value, s390_isel_int_expr(env, arg1));
+         }
+         if (ulong_fits_signed_20bit(value)) {
+            return s390_amode_b20((Int)value, s390_isel_int_expr(env, arg1));
+         }
+      }
+   }
+
+   /* Doesn't match anything in particular.  Generate it into
+      a register and use that. */
+   return s390_amode_b12(0, s390_isel_int_expr(env, expr));
+}
+
+
+static s390_amode *
+s390_isel_amode(ISelEnv *env, IRExpr *expr)
+{
+   s390_amode *am = s390_isel_amode_wrk(env, expr);
+
+   /* Address computation should yield a 64-bit value */
+   vassert(typeOfIRExpr(env->type_env, expr) == Ity_I64);
+
+   am = s390_isel_amode_wrk(env, expr);
+
+   /* Check post-condition */
+   vassert(s390_amode_is_sane(am));
+
+   return am;
+}
+
+
+/*---------------------------------------------------------*/
+/*--- Helper functions                                  ---*/
+/*---------------------------------------------------------*/
+
+/* Constants and memory accesses should be right operands */
+#define order_commutative_operands(left, right)                   \
+        do {                                                      \
+          if (left->tag == Iex_Const || left->tag == Iex_Load ||  \
+              left->tag == Iex_Get) {                             \
+            IRExpr *tmp;                                          \
+            tmp   = left;                                         \
+            left  = right;                                        \
+            right = tmp;                                          \
+          }                                                       \
+        } while (0)
+
+
+/* Copy an RMI operand to the DST register */
+static s390_insn *
+s390_opnd_copy(UChar size, HReg dst, s390_opnd_RMI opnd)
+{
+   switch (opnd.tag) {
+   case S390_OPND_AMODE:
+      return s390_insn_load(size, dst, opnd.variant.am);
+
+   case S390_OPND_REG:
+      return s390_insn_move(size, dst, opnd.variant.reg);
+
+   case S390_OPND_IMMEDIATE:
+      return s390_insn_load_immediate(size, dst, opnd.variant.imm);
+
+   default:
+      vpanic("s390_opnd_copy");
+   }
+}
+
+
+/* Construct a RMI operand for a register */
+static __inline__ s390_opnd_RMI
+s390_opnd_reg(HReg reg)
+{
+   s390_opnd_RMI opnd;
+
+   opnd.tag  = S390_OPND_REG;
+   opnd.variant.reg = reg;
+
+   return opnd;
+}
+
+
+/* Construct a RMI operand for an immediate constant */
+static __inline__ s390_opnd_RMI
+s390_opnd_imm(ULong value)
+{
+   s390_opnd_RMI opnd;
+
+   opnd.tag  = S390_OPND_IMMEDIATE;
+   opnd.variant.imm = value;
+
+   return opnd;
+}
+
+
+/* Return 1, if EXPR represents the cosntant 0 */
+static int
+s390_expr_is_const_zero(IRExpr *expr)
+{
+   ULong value;
+
+   if (expr->tag == Iex_Const) {
+      switch (expr->Iex.Const.con->tag) {
+      case Ico_U1:  value = expr->Iex.Const.con->Ico.U1;  break;
+      case Ico_U8:  value = expr->Iex.Const.con->Ico.U8;  break;
+      case Ico_U16: value = expr->Iex.Const.con->Ico.U16; break;
+      case Ico_U32: value = expr->Iex.Const.con->Ico.U32; break;
+      case Ico_U64: value = expr->Iex.Const.con->Ico.U64; break;
+      default:
+         vpanic("s390_expr_is_const_zero");
+      }
+      return value == 0;
+   }
+
+   return 0;
+}
+
+
+/* Call a helper (clean or dirty)
+   Arguments must satisfy the following conditions:
+   (a) they are expressions yielding an integer result
+   (b) there can be no more than S390_NUM_GPRPARMS arguments
+       guard is a Ity_Bit expression indicating whether or not the
+       call happens.  If guard==NULL, the call is unconditional.
+*/
+static void
+doHelperCall(ISelEnv *env, Bool passBBP, IRExpr *guard,
+             IRCallee *callee, IRExpr **args)
+{
+   UInt n_args, i, argreg, size;
+   ULong target;
+   HReg tmpregs[S390_NUM_GPRPARMS];
+   s390_cc_t cc;
+
+   n_args = 0;
+   for (i = 0; args[i]; i++)
+      ++n_args;
+
+   if (n_args > (S390_NUM_GPRPARMS - (passBBP ? 1 : 0))) {
+      vpanic("doHelperCall: too many arguments");
+   }
+
+   /* This is the "slow scheme". fixs390: implement the fast one */
+   argreg = 0;
+
+   /* If we need the guest state pointer put it in a temporary arg reg */
+   if (passBBP) {
+      tmpregs[argreg] = newVRegI(env);
+      addInstr(env, s390_insn_move(sizeof(ULong), tmpregs[argreg],
+                                   s390_hreg_guest_state_pointer()));
+      argreg++;
+   }
+
+   /* Compute the function arguments into a temporary register each */
+   for (i = 0; i < n_args; i++) {
+      tmpregs[argreg] = s390_isel_int_expr(env, args[i]);
+      argreg++;
+   }
+
+   /* Compute the condition */
+   cc = S390_CC_ALWAYS;
+   if (guard) {
+      if (guard->tag == Iex_Const
+          && guard->Iex.Const.con->tag == Ico_U1
+          && guard->Iex.Const.con->Ico.U1 == True) {
+         /* unconditional -- do nothing */
+      } else {
+         cc = s390_isel_cc(env, guard);
+      }
+   }
+
+   /* Move the args to the final register */
+   for (i = 0; i < argreg; i++) {
+      HReg finalreg;
+
+      finalreg = mkHReg(s390_gprno_from_arg_index(i), HRcInt64, False);
+      size = sizeofIRType(Ity_I64);
+      addInstr(env, s390_insn_move(size, finalreg, tmpregs[i]));
+   }
+
+   target = Ptr_to_ULong(callee->addr);
+
+   /* Finally, the call itself. */
+   addInstr(env, s390_insn_helper_call(cc, (Addr64)target, n_args,
+                                       callee->name));
+}
+
+
+/* Given an expression representing a rounding mode using IRRoundingMode
+   encoding convert it to an s390_round_t value.  */
+static s390_round_t
+decode_rounding_mode(IRExpr *rounding_expr)
+{
+   if (rounding_expr->tag == Iex_Const &&
+       rounding_expr->Iex.Const.con->tag == Ico_U32) {
+      IRRoundingMode mode = rounding_expr->Iex.Const.con->Ico.U32;
+
+      switch (mode) {
+      case Irrm_NEAREST:       return S390_ROUND_NEAREST_EVEN;
+      case Irrm_ZERO:          return S390_ROUND_ZERO;
+      case Irrm_PosINF:        return S390_ROUND_POSINF;
+      case Irrm_NegINF:        return S390_ROUND_NEGINF;
+      }
+   }
+
+   vpanic("decode_rounding_mode");
+}
+
+
+/* CC_S390 holds the condition code in s390 encoding. Convert it to
+   VEX encoding
+
+   s390     VEX              b6 b2 b0   cc.1  cc.0
+   0      0x40 EQ             1  0  0     0     0
+   1      0x01 LT             0  0  1     0     1
+   2      0x00 GT             0  0  0     1     0
+   3      0x45 Unordered      1  1  1     1     1
+
+   b0 = cc.0
+   b2 = cc.0 & cc.1
+   b6 = ~(cc.0 ^ cc.1)   // ((cc.0 - cc.1) + 0x1 ) & 0x1
+
+   VEX = b0 | (b2 << 2) | (b6 << 6);
+*/
+static HReg
+convert_s390_fpcc_to_vex(ISelEnv *env, HReg cc_s390)
+{
+   HReg cc0, cc1, b2, b6, cc_vex;
+
+   cc0 = newVRegI(env);
+   addInstr(env, s390_insn_move(4, cc0, cc_s390));
+   addInstr(env, s390_insn_alu(4, S390_ALU_AND, cc0, s390_opnd_imm(1)));
+
+   cc1 = newVRegI(env);
+   addInstr(env, s390_insn_move(4, cc1, cc_s390));
+   addInstr(env, s390_insn_alu(4, S390_ALU_RSH, cc1, s390_opnd_imm(1)));
+
+   b2 = newVRegI(env);
+   addInstr(env, s390_insn_move(4, b2, cc0));
+   addInstr(env, s390_insn_alu(4, S390_ALU_AND, b2, s390_opnd_reg(cc1)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_LSH, b2, s390_opnd_imm(2)));
+
+   b6 = newVRegI(env);
+   addInstr(env, s390_insn_move(4, b6, cc0));
+   addInstr(env, s390_insn_alu(4, S390_ALU_SUB, b6, s390_opnd_reg(cc1)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_ADD, b6, s390_opnd_imm(1)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_AND, b6, s390_opnd_imm(1)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_LSH, b6, s390_opnd_imm(6)));
+
+   cc_vex = newVRegI(env);
+   addInstr(env, s390_insn_move(4, cc_vex, cc0));
+   addInstr(env, s390_insn_alu(4, S390_ALU_OR, cc_vex, s390_opnd_reg(b2)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_OR, cc_vex, s390_opnd_reg(b6)));
+
+   return cc_vex;
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Integer expressions (128 bit)               ---*/
+/*---------------------------------------------------------*/
+static void
+s390_isel_int128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
+                          IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+
+   vassert(ty == Ity_I128);
+
+   /* No need to consider the following
+      - 128-bit constants (they do not exist in VEX)
+      - 128-bit loads from memory (will not be generated)
+   */
+
+   /* Read 128-bit IRTemp */
+   if (expr->tag == Iex_RdTmp) {
+      lookupIRTemp128(dst_hi, dst_lo, env, expr->Iex.RdTmp.tmp);
+      return;
+   }
+
+   if (expr->tag == Iex_Binop) {
+      IRExpr *arg1 = expr->Iex.Binop.arg1;
+      IRExpr *arg2 = expr->Iex.Binop.arg2;
+      Bool is_signed_multiply, is_signed_divide;
+
+      switch (expr->Iex.Binop.op) {
+      case Iop_MullU64:
+         is_signed_multiply = False;
+         goto do_multiply64;
+
+      case Iop_MullS64:
+         is_signed_multiply = True;
+         goto do_multiply64;
+
+      case Iop_DivModU128to64:
+         is_signed_divide = False;
+         goto do_divide64;
+
+      case Iop_DivModS128to64:
+         is_signed_divide = True;
+         goto do_divide64;
+
+      case Iop_64HLto128:
+         *dst_hi = s390_isel_int_expr(env, arg1);
+         *dst_lo = s390_isel_int_expr(env, arg2);
+         return;
+
+      case Iop_DivModS64to64: {
+         HReg r10, r11, h1;
+         s390_opnd_RMI op2;
+
+         h1  = s390_isel_int_expr(env, arg1);       /* Process 1st operand */
+         op2 = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+
+         /* We use non-virtual registers r10 and r11 as pair */
+         r10  = make_gpr(env, 10);
+         r11  = make_gpr(env, 11);
+
+         /* Move 1st operand into r11 and */
+         addInstr(env, s390_insn_move(8, r11, h1));
+
+         /* Divide */
+         addInstr(env, s390_insn_divs(8, r10, r11, op2));
+
+         /* The result is in registers r10 (remainder) and r11 (quotient).
+            Move the result into the reg pair that is being returned such
+            such that the low 64 bits are the quotient and the upper 64 bits
+            are the remainder. (see libvex_ir.h). */
+         *dst_hi = newVRegI(env);
+         *dst_lo = newVRegI(env);
+         addInstr(env, s390_insn_move(8, *dst_hi, r10));
+         addInstr(env, s390_insn_move(8, *dst_lo, r11));
+         return;
+      }
+
+      default:
+         break;
+
+      do_multiply64: {
+            HReg r10, r11, h1;
+            s390_opnd_RMI op2;
+
+            order_commutative_operands(arg1, arg2);
+
+            h1   = s390_isel_int_expr(env, arg1);       /* Process 1st operand */
+            op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+
+            /* We use non-virtual registers r10 and r11 as pair */
+            r10  = make_gpr(env, 10);
+            r11  = make_gpr(env, 11);
+
+            /* Move the first operand to r11 */
+            addInstr(env, s390_insn_move(8, r11, h1));
+
+            /* Multiply */
+            addInstr(env, s390_insn_mul(8, r10, r11, op2, is_signed_multiply));
+
+            /* The result is in registers r10 and r11. Assign to two virtual regs
+               and return. */
+            *dst_hi = newVRegI(env);
+            *dst_lo = newVRegI(env);
+            addInstr(env, s390_insn_move(8, *dst_hi, r10));
+            addInstr(env, s390_insn_move(8, *dst_lo, r11));
+            return;
+         }
+
+      do_divide64: {
+         HReg r10, r11, hi, lo;
+         s390_opnd_RMI op2;
+
+         s390_isel_int128_expr(&hi, &lo, env, arg1);
+         op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+
+         /* We use non-virtual registers r10 and r11 as pair */
+         r10  = make_gpr(env, 10);
+         r11  = make_gpr(env, 11);
+
+         /* Move high 64 bits of the 1st operand into r10 and
+            the low 64 bits into r11. */
+         addInstr(env, s390_insn_move(8, r10, hi));
+         addInstr(env, s390_insn_move(8, r11, lo));
+
+         /* Divide */
+         addInstr(env, s390_insn_div(8, r10, r11, op2, is_signed_divide));
+
+         /* The result is in registers r10 (remainder) and r11 (quotient).
+            Move the result into the reg pair that is being returned such
+            such that the low 64 bits are the quotient and the upper 64 bits
+            are the remainder. (see libvex_ir.h). */
+         *dst_hi = newVRegI(env);
+         *dst_lo = newVRegI(env);
+         addInstr(env, s390_insn_move(8, *dst_hi, r10));
+         addInstr(env, s390_insn_move(8, *dst_lo, r11));
+         return;
+      }
+      }
+   }
+
+   vpanic("s390_isel_int128_expr");
+}
+
+
+/* Compute a 128-bit value into two 64-bit registers. These may be either
+   real or virtual regs; in any case they must not be changed by subsequent
+   code emitted by the caller. */
+static void
+s390_isel_int128_expr(HReg *dst_hi, HReg *dst_lo, ISelEnv *env, IRExpr *expr)
+{
+   s390_isel_int128_expr_wrk(dst_hi, dst_lo, env, expr);
+
+   /* Sanity checks ... */
+   vassert(hregIsVirtual(*dst_hi));
+   vassert(hregIsVirtual(*dst_lo));
+   vassert(hregClass(*dst_hi) == HRcInt64);
+   vassert(hregClass(*dst_lo) == HRcInt64);
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Integer expressions (64/32/16/8 bit)        ---*/
+/*---------------------------------------------------------*/
+
+/* Select insns for an integer-typed expression, and add them to the
+   code list.  Return a reg holding the result.  This reg will be a
+   virtual register.  THE RETURNED REG MUST NOT BE MODIFIED.  If you
+   want to modify it, ask for a new vreg, copy it in there, and modify
+   the copy.  The register allocator will do its best to map both
+   vregs to the same real register, so the copies will often disappear
+   later in the game.
+
+   This should handle expressions of 64, 32, 16 and 8-bit type.
+   All results are returned in a 64bit register.
+   For 16- and 8-bit expressions, the upper (32/48/56 : 16/24) bits
+   are arbitrary, so you should mask or sign extend partial values
+   if necessary.
+*/
+
+/* DO NOT CALL THIS DIRECTLY ! */
+static HReg
+s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+   UChar size;
+   s390_bfp_unop_t bfpop;
+
+   vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 || ty == Ity_I64);
+
+   size = sizeofIRType(ty);   /* size of the result after evaluating EXPR */
+
+   switch (expr->tag) {
+
+      /* --------- TEMP --------- */
+   case Iex_RdTmp:
+      /* Return the virtual register that holds the temporary. */
+      return lookupIRTemp(env, expr->Iex.RdTmp.tmp);
+
+      /* --------- LOAD --------- */
+   case Iex_Load: {
+      HReg        dst = newVRegI(env);
+      s390_amode *am  = s390_isel_amode(env, expr->Iex.Load.addr);
+
+      if (expr->Iex.Load.end != Iend_BE)
+         goto irreducible;
+
+      addInstr(env, s390_insn_load(size, dst, am));
+
+      return dst;
+   }
+
+      /* --------- BINARY OP --------- */
+   case Iex_Binop: {
+      IRExpr *arg1 = expr->Iex.Binop.arg1;
+      IRExpr *arg2 = expr->Iex.Binop.arg2;
+      HReg h1, res;
+      s390_alu_t opkind;
+      s390_opnd_RMI op2, value, opnd;
+      s390_insn *insn;
+      Bool is_commutative, is_signed_multiply, is_signed_divide;
+
+      is_commutative = True;
+
+      switch (expr->Iex.Binop.op) {
+      case Iop_MullU8:
+      case Iop_MullU16:
+      case Iop_MullU32:
+         is_signed_multiply = False;
+         goto do_multiply;
+
+      case Iop_MullS8:
+      case Iop_MullS16:
+      case Iop_MullS32:
+         is_signed_multiply = True;
+         goto do_multiply;
+
+      do_multiply: {
+            HReg r10, r11;
+            UInt arg_size = size / 2;
+
+            order_commutative_operands(arg1, arg2);
+
+            h1   = s390_isel_int_expr(env, arg1);       /* Process 1st operand */
+            op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+
+            /* We use non-virtual registers r10 and r11 as pair */
+            r10  = make_gpr(env, 10);
+            r11  = make_gpr(env, 11);
+
+            /* Move the first operand to r11 */
+            addInstr(env, s390_insn_move(arg_size, r11, h1));
+
+            /* Multiply */
+            addInstr(env, s390_insn_mul(arg_size, r10, r11, op2, is_signed_multiply));
+
+            /* The result is in registers r10 and r11. Combine them into a SIZE-bit
+               value into the destination register. */
+            res  = newVRegI(env);
+            addInstr(env, s390_insn_move(arg_size, res, r10));
+            value = s390_opnd_imm(arg_size * 8);
+            addInstr(env, s390_insn_alu(size, S390_ALU_LSH, res, value));
+            value = s390_opnd_imm((((ULong)1) << arg_size * 8) - 1);
+            addInstr(env, s390_insn_alu(size, S390_ALU_AND, r11, value));
+            opnd = s390_opnd_reg(r11);
+            addInstr(env, s390_insn_alu(size, S390_ALU_OR,  res, opnd));
+            return res;
+         }
+
+      case Iop_DivModS64to32:
+         is_signed_divide = True;
+         goto do_divide;
+
+      case Iop_DivModU64to32:
+         is_signed_divide = False;
+         goto do_divide;
+
+      do_divide: {
+            HReg r10, r11;
+
+            h1   = s390_isel_int_expr(env, arg1);       /* Process 1st operand */
+            op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+
+            /* We use non-virtual registers r10 and r11 as pair */
+            r10  = make_gpr(env, 10);
+            r11  = make_gpr(env, 11);
+
+            /* Split the first operand and put the high 32 bits into r10 and
+               the low 32 bits into r11. */
+            addInstr(env, s390_insn_move(8, r10, h1));
+            addInstr(env, s390_insn_move(8, r11, h1));
+            value = s390_opnd_imm(32);
+            addInstr(env, s390_insn_alu(8, S390_ALU_RSH, r10, value));
+
+            /* Divide */
+            addInstr(env, s390_insn_div(4, r10, r11, op2, is_signed_divide));
+
+            /* The result is in registers r10 (remainder) and r11 (quotient).
+               Combine them into a 64-bit value such that the low 32 bits are
+               the quotient and the upper 32 bits are the remainder. (see
+               libvex_ir.h). */
+            res  = newVRegI(env);
+            addInstr(env, s390_insn_move(8, res, r10));
+            value = s390_opnd_imm(32);
+            addInstr(env, s390_insn_alu(8, S390_ALU_LSH, res, value));
+            value = s390_opnd_imm((((ULong)1) << 32) - 1);
+            addInstr(env, s390_insn_alu(8, S390_ALU_AND, r11, value));
+            opnd = s390_opnd_reg(r11);
+            addInstr(env, s390_insn_alu(8, S390_ALU_OR,  res, opnd));
+            return res;
+         }
+
+      case Iop_F32toI32S:  bfpop = S390_BFP_F32_TO_I32;  goto do_convert;
+      case Iop_F32toI64S:  bfpop = S390_BFP_F32_TO_I64;  goto do_convert;
+      case Iop_F64toI32S:  bfpop = S390_BFP_F64_TO_I32;  goto do_convert;
+      case Iop_F64toI64S:  bfpop = S390_BFP_F64_TO_I64;  goto do_convert;
+      case Iop_F128toI32S: bfpop = S390_BFP_F128_TO_I32; goto do_convert_128;
+      case Iop_F128toI64S: bfpop = S390_BFP_F128_TO_I64; goto do_convert_128;
+
+      do_convert: {
+         s390_round_t rounding_mode;
+
+         res  = newVRegI(env);
+         h1   = s390_isel_float_expr(env, arg2);   /* Process operand */
+
+         rounding_mode = decode_rounding_mode(arg1);
+         addInstr(env, s390_insn_bfp_unop(size, bfpop, res, h1, rounding_mode));
+         return res;
+      }
+
+      do_convert_128: {
+         s390_round_t rounding_mode;
+         HReg op_hi, op_lo, f13, f15;
+
+         res = newVRegI(env);
+         s390_isel_float128_expr(&op_hi, &op_lo, env, arg2); /* operand */
+
+         /* We use non-virtual registers r13 and r15 as pair */
+         f13 = make_fpr(13);
+         f15 = make_fpr(15);
+
+         /* operand --> (f13, f15) */
+         addInstr(env, s390_insn_move(8, f13, op_hi));
+         addInstr(env, s390_insn_move(8, f15, op_lo));
+
+         rounding_mode = decode_rounding_mode(arg1);
+         addInstr(env, s390_insn_bfp128_convert_from(size, bfpop, res, f13, f15,
+                                                     rounding_mode));
+         return res;
+      }
+
+      case Iop_8HLto16:
+      case Iop_16HLto32:
+      case Iop_32HLto64: {
+         HReg h2;
+         UInt arg_size = size / 2;
+
+         res  = newVRegI(env);
+         h1   = s390_isel_int_expr(env, arg1);   /* Process 1st operand */
+         h2   = s390_isel_int_expr(env, arg2);   /* Process 2nd operand */
+
+         addInstr(env, s390_insn_move(arg_size, res, h1));
+         value = s390_opnd_imm(arg_size * 8);
+         addInstr(env, s390_insn_alu(size, S390_ALU_LSH, res, value));
+         value = s390_opnd_imm((((ULong)1) << arg_size * 8) - 1);
+         addInstr(env, s390_insn_alu(size, S390_ALU_AND, h2, value));
+         opnd = s390_opnd_reg(h2);
+         addInstr(env, s390_insn_alu(size, S390_ALU_OR,  res, opnd));
+         return res;
+      }
+
+      case Iop_Max32U: {
+         /* arg1 > arg2 ? arg1 : arg2   using uint32_t arguments */
+         res = newVRegI(env);
+         h1  = s390_isel_int_expr(env, arg1);
+         op2 = s390_isel_int_expr_RMI(env, arg2);
+
+         addInstr(env, s390_insn_move(size, res, h1));
+         addInstr(env, s390_insn_compare(size, res, op2, False /* signed */));
+         addInstr(env, s390_insn_cond_move(size, S390_CC_L, res, op2));
+         return res;
+      }
+
+      case Iop_CmpF32:
+      case Iop_CmpF64: {
+         HReg cc_s390, h2;
+
+         h1 = s390_isel_float_expr(env, arg1);
+         h2 = s390_isel_float_expr(env, arg2);
+         cc_s390 = newVRegI(env);
+
+         size = (expr->Iex.Binop.op == Iop_CmpF32) ? 4 : 8;
+
+         addInstr(env, s390_insn_bfp_compare(size, cc_s390, h1, h2));
+
+         return convert_s390_fpcc_to_vex(env, cc_s390);
+      }
+
+      case Iop_CmpF128: {
+         HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15, cc_s390;
+
+         s390_isel_float128_expr(&op1_hi, &op1_lo, env, arg1); /* 1st operand */
+         s390_isel_float128_expr(&op2_hi, &op2_lo, env, arg2); /* 2nd operand */
+         cc_s390 = newVRegI(env);
+
+         /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
+         f12 = make_fpr(12);
+         f13 = make_fpr(13);
+         f14 = make_fpr(14);
+         f15 = make_fpr(15);
+
+         /* 1st operand --> (f12, f14) */
+         addInstr(env, s390_insn_move(8, f12, op1_hi));
+         addInstr(env, s390_insn_move(8, f14, op1_lo));
+
+         /* 2nd operand --> (f13, f15) */
+         addInstr(env, s390_insn_move(8, f13, op2_hi));
+         addInstr(env, s390_insn_move(8, f15, op2_lo));
+
+         res = newVRegI(env);
+         addInstr(env, s390_insn_bfp128_compare(16, cc_s390, f12, f14, f13, f15));
+
+         return convert_s390_fpcc_to_vex(env, cc_s390);
+      }
+
+      case Iop_Add8:
+      case Iop_Add16:
+      case Iop_Add32:
+      case Iop_Add64:
+         opkind = S390_ALU_ADD;
+         break;
+
+      case Iop_Sub8:
+      case Iop_Sub16:
+      case Iop_Sub32:
+      case Iop_Sub64:
+         opkind = S390_ALU_SUB;
+         is_commutative = False;
+         break;
+
+      case Iop_And8:
+      case Iop_And16:
+      case Iop_And32:
+      case Iop_And64:
+         opkind = S390_ALU_AND;
+         break;
+
+      case Iop_Or8:
+      case Iop_Or16:
+      case Iop_Or32:
+      case Iop_Or64:
+         opkind = S390_ALU_OR;
+         break;
+
+      case Iop_Xor8:
+      case Iop_Xor16:
+      case Iop_Xor32:
+      case Iop_Xor64:
+         opkind = S390_ALU_XOR;
+         break;
+
+      case Iop_Shl8:
+      case Iop_Shl16:
+      case Iop_Shl32:
+      case Iop_Shl64:
+         opkind = S390_ALU_LSH;
+         is_commutative = False;
+         break;
+
+      case Iop_Shr8:
+      case Iop_Shr16:
+      case Iop_Shr32:
+      case Iop_Shr64:
+         opkind = S390_ALU_RSH;
+         is_commutative = False;
+         break;
+
+      case Iop_Sar8:
+      case Iop_Sar16:
+      case Iop_Sar32:
+      case Iop_Sar64:
+         opkind = S390_ALU_RSHA;
+         is_commutative = False;
+         break;
+
+      default:
+         goto irreducible;
+      }
+
+      /* Pattern match: 0 - arg1  -->  -arg1   */
+      if (opkind == S390_ALU_SUB && s390_expr_is_const_zero(arg1)) {
+         res  = newVRegI(env);
+         op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+         insn = s390_insn_unop(size, S390_NEGATE, res, op2);
+         addInstr(env, insn);
+
+         return res;
+      }
+
+      if (is_commutative) {
+         order_commutative_operands(arg1, arg2);
+      }
+
+      h1   = s390_isel_int_expr(env, arg1);       /* Process 1st operand */
+      op2  = s390_isel_int_expr_RMI(env, arg2);   /* Process 2nd operand */
+      res  = newVRegI(env);
+      addInstr(env, s390_insn_move(size, res, h1));
+      insn = s390_insn_alu(size, opkind, res, op2);
+
+      addInstr(env, insn);
+
+      return res;
+   }
+
+      /* --------- UNARY OP --------- */
+   case Iex_Unop: {
+      static s390_opnd_RMI mask  = { S390_OPND_IMMEDIATE };
+      static s390_opnd_RMI shift = { S390_OPND_IMMEDIATE };
+      s390_opnd_RMI opnd;
+      s390_insn    *insn;
+      IRExpr *arg;
+      HReg    dst, h1;
+      IROp    unop, binop;
+
+      arg = expr->Iex.Unop.arg;
+
+      /* Special cases are handled here */
+
+      /* 32-bit multiply with 32-bit result or
+         64-bit multiply with 64-bit result */
+      unop  = expr->Iex.Unop.op;
+      binop = arg->Iex.Binop.op;
+
+      if ((arg->tag == Iex_Binop &&
+           ((unop == Iop_64to32 &&
+             (binop == Iop_MullS32 || binop == Iop_MullU32)) ||
+            (unop == Iop_128to64 &&
+             (binop == Iop_MullS64 || binop == Iop_MullU64))))) {
+         h1   = s390_isel_int_expr(env, arg->Iex.Binop.arg1);     /* 1st opnd */
+         opnd = s390_isel_int_expr_RMI(env, arg->Iex.Binop.arg2); /* 2nd opnd */
+         dst  = newVRegI(env);     /* Result goes into a new register */
+         addInstr(env, s390_insn_move(size, dst, h1));
+         addInstr(env, s390_insn_alu(size, S390_ALU_MUL, dst, opnd));
+
+         return dst;
+      }
+
+      if (unop == Iop_ReinterpF64asI64) {
+         dst = newVRegI(env);
+         h1  = s390_isel_float_expr(env, arg);     /* Process the operand */
+         addInstr(env, s390_insn_move(size, dst, h1));
+
+         return dst;
+      }
+
+      /* Expressions whose argument is 1-bit wide */
+      if (typeOfIRExpr(env->type_env, arg) == Ity_I1) {
+         s390_cc_t cond = s390_isel_cc(env, arg);
+         dst = newVRegI(env);     /* Result goes into a new register */
+         addInstr(env, s390_insn_cc2bool(dst, cond));
+
+         switch (unop) {
+         case Iop_1Uto8:
+         case Iop_1Uto32:
+         case Iop_1Uto64:
+            /* Nothing to do */
+            break;
+
+         case Iop_1Sto8:
+         case Iop_1Sto16:
+         case Iop_1Sto32:
+            shift.variant.imm = 31;
+            addInstr(env, s390_insn_alu(4, S390_ALU_LSH,  dst, shift));
+            addInstr(env, s390_insn_alu(4, S390_ALU_RSHA, dst, shift));
+            break;
+
+         case Iop_1Sto64:
+            shift.variant.imm = 63;
+            addInstr(env, s390_insn_alu(8, S390_ALU_LSH,  dst, shift));
+            addInstr(env, s390_insn_alu(8, S390_ALU_RSHA, dst, shift));
+            break;
+
+         default:
+            goto irreducible;
+         }
+
+         return dst;
+      }
+
+      /* Regular processing */
+
+      if (unop == Iop_128to64) {
+         HReg dst_hi, dst_lo;
+
+         s390_isel_int128_expr(&dst_hi, &dst_lo, env, arg);
+         return dst_lo;
+      }
+
+      if (unop == Iop_128HIto64) {
+         HReg dst_hi, dst_lo;
+
+         s390_isel_int128_expr(&dst_hi, &dst_lo, env, arg);
+         return dst_hi;
+      }
+
+      dst  = newVRegI(env);     /* Result goes into a new register */
+      opnd = s390_isel_int_expr_RMI(env, arg);     /* Process the operand */
+
+      switch (unop) {
+      case Iop_8Uto16:
+      case Iop_8Uto32:
+      case Iop_8Uto64:
+         insn = s390_insn_unop(size, S390_ZERO_EXTEND_8, dst, opnd);
+         break;
+
+      case Iop_16Uto32:
+      case Iop_16Uto64:
+         insn = s390_insn_unop(size, S390_ZERO_EXTEND_16, dst, opnd);
+         break;
+
+      case Iop_32Uto64:
+         insn = s390_insn_unop(size, S390_ZERO_EXTEND_32, dst, opnd);
+         break;
+
+      case Iop_8Sto16:
+      case Iop_8Sto32:
+      case Iop_8Sto64:
+         insn = s390_insn_unop(size, S390_SIGN_EXTEND_8, dst, opnd);
+         break;
+
+      case Iop_16Sto32:
+      case Iop_16Sto64:
+         insn = s390_insn_unop(size, S390_SIGN_EXTEND_16, dst, opnd);
+         break;
+
+      case Iop_32Sto64:
+         insn = s390_insn_unop(size, S390_SIGN_EXTEND_32, dst, opnd);
+         break;
+
+      case Iop_64to8:
+      case Iop_64to16:
+      case Iop_64to32:
+      case Iop_32to8:
+      case Iop_32to16:
+      case Iop_16to8:
+         /* Down-casts are no-ops. Upstream operations will only look at
+            the bytes that make up the result of the down-cast. So there
+            is no point setting the other bytes to 0. */
+         insn = s390_opnd_copy(8, dst, opnd);
+         break;
+
+      case Iop_64HIto32:
+         addInstr(env, s390_opnd_copy(8, dst, opnd));
+         shift.variant.imm = 32;
+         insn = s390_insn_alu(8, S390_ALU_RSH, dst, shift);
+         break;
+
+      case Iop_32HIto16:
+         addInstr(env, s390_opnd_copy(4, dst, opnd));
+         shift.variant.imm = 16;
+         insn = s390_insn_alu(4, S390_ALU_RSH, dst, shift);
+         break;
+
+      case Iop_16HIto8:
+         addInstr(env, s390_opnd_copy(2, dst, opnd));
+         shift.variant.imm = 8;
+         insn = s390_insn_alu(2, S390_ALU_RSH, dst, shift);
+         break;
+
+      case Iop_Not8:
+      case Iop_Not16:
+      case Iop_Not32:
+      case Iop_Not64:
+         /* XOR with ffff... */
+         mask.variant.imm = ~(ULong)0;
+         addInstr(env, s390_opnd_copy(size, dst, opnd));
+         insn = s390_insn_alu(size, S390_ALU_XOR, dst, mask);
+         break;
+
+      case Iop_Left8:
+      case Iop_Left16:
+      case Iop_Left32:
+      case Iop_Left64:
+         addInstr(env, s390_insn_unop(size, S390_NEGATE, dst, opnd));
+         insn = s390_insn_alu(size, S390_ALU_OR, dst, opnd);
+         break;
+
+      case Iop_CmpwNEZ32:
+      case Iop_CmpwNEZ64: {
+         /* Use the fact that x | -x == 0 iff x == 0. Otherwise, either X
+            or -X will have a 1 in the MSB. */
+         addInstr(env, s390_insn_unop(size, S390_NEGATE, dst, opnd));
+         addInstr(env, s390_insn_alu(size, S390_ALU_OR,  dst, opnd));
+         shift.variant.imm = (unop == Iop_CmpwNEZ32) ? 31 : 63;
+         addInstr(env, s390_insn_alu(size, S390_ALU_RSHA,  dst, shift));
+         return dst;
+      }
+
+      case Iop_Clz64: {
+         HReg r10, r11;
+
+         /* We use non-virtual registers r10 and r11 as pair for the two
+            output values */
+         r10  = make_gpr(env, 10);
+         r11  = make_gpr(env, 11);
+
+         /* flogr */
+         addInstr(env, s390_insn_flogr(8, r10, r11, opnd));
+
+         /* The result is in registers r10 (bit position) and r11 (modified
+            input value). The value in r11 is not needed and will be
+            discarded. */
+         addInstr(env, s390_insn_move(8, dst, r10));
+         return dst;
+      }
+
+      default:
+         goto irreducible;
+      }
+
+      addInstr(env, insn);
+
+      return dst;
+   }
+
+      /* --------- GET --------- */
+   case Iex_Get: {
+      HReg dst = newVRegI(env);
+      s390_amode *am = s390_amode_for_guest_state(expr->Iex.Get.offset);
+
+      /* We never load more than 8 bytes from the guest state, because the
+         floating point register pair is not contiguous. */
+      vassert(size <= 8);
+
+      addInstr(env, s390_insn_load(size, dst, am));
+
+      return dst;
+   }
+
+   case Iex_GetI:
+      /* not needed */
+      break;
+
+      /* --------- CCALL --------- */
+   case Iex_CCall: {
+      HReg dst = newVRegI(env);
+
+      doHelperCall(env, False, NULL, expr->Iex.CCall.cee,
+                   expr->Iex.CCall.args);
+
+      /* Move the returned value into the return register */
+      addInstr(env, s390_insn_move(sizeofIRType(expr->Iex.CCall.retty), dst,
+                                   mkHReg(S390_REGNO_RETURN_VALUE,
+                                          HRcInt64, False)));
+      return dst;
+   }
+
+      /* --------- LITERAL --------- */
+
+      /* Load a literal into a register. Create a "load immediate"
+         v-insn and return the register. */
+   case Iex_Const: {
+      ULong value;
+      HReg  dst = newVRegI(env);
+      const IRConst *con = expr->Iex.Const.con;
+
+      /* Bitwise copy of the value. No sign/zero-extension */
+      switch (con->tag) {
+      case Ico_U64: value = con->Ico.U64; break;
+      case Ico_U32: value = con->Ico.U32; break;
+      case Ico_U16: value = con->Ico.U16; break;
+      case Ico_U8:  value = con->Ico.U8;  break;
+      default:      vpanic("s390_isel_int_expr: invalid constant");
+      }
+
+      addInstr(env, s390_insn_load_immediate(size, dst, value));
+
+      return dst;
+   }
+
+      /* --------- MULTIPLEX --------- */
+   case Iex_Mux0X: {
+      IRExpr *cond_expr;
+      HReg dst, tmp, rX;
+      s390_opnd_RMI cond, r0, zero;
+
+      cond_expr = expr->Iex.Mux0X.cond;
+
+      dst  = newVRegI(env);
+      r0   = s390_isel_int_expr_RMI(env, expr->Iex.Mux0X.expr0);
+      rX   = s390_isel_int_expr(env, expr->Iex.Mux0X.exprX);
+      size = sizeofIRType(typeOfIRExpr(env->type_env, expr->Iex.Mux0X.exprX));
+
+      if (cond_expr->tag == Iex_Unop && cond_expr->Iex.Unop.op == Iop_1Uto8) {
+         s390_cc_t cc = s390_isel_cc(env, cond_expr->Iex.Unop.arg);
+
+         addInstr(env, s390_insn_move(size, dst, rX));
+         addInstr(env, s390_insn_cond_move(size, s390_cc_invert(cc), dst, r0));
+         return dst;
+      }
+
+      /* Assume the condition is true and move rX to the destination reg. */
+      addInstr(env, s390_insn_move(size, dst, rX));
+
+      /* Compute the condition ... */
+      cond = s390_isel_int_expr_RMI(env, cond_expr);
+
+      /* tmp = cond & 0xFF */
+      tmp  = newVRegI(env);
+      addInstr(env, s390_insn_load_immediate(4, tmp, 0xFF));
+      addInstr(env, s390_insn_alu(4, S390_ALU_AND, tmp, cond));
+
+      /* ... and compare it with zero */
+      zero = s390_opnd_imm(0);
+      addInstr(env, s390_insn_compare(4, tmp, zero, 0 /* signed */));
+
+      /* ... and if it compared equal move r0 to the destination reg. */
+      size = sizeofIRType(typeOfIRExpr(env->type_env, expr->Iex.Mux0X.expr0));
+      addInstr(env, s390_insn_cond_move(size, S390_CC_E, dst, r0));
+
+      return dst;
+   }
+
+   default:
+      break;
+   }
+
+   /* We get here if no pattern matched. */
+ irreducible:
+   ppIRExpr(expr);
+   vpanic("s390_isel_int_expr: cannot reduce tree");
+}
+
+
+static HReg
+s390_isel_int_expr(ISelEnv *env, IRExpr *expr)
+{
+   HReg dst = s390_isel_int_expr_wrk(env, expr);
+
+   /* Sanity checks ... */
+   vassert(hregClass(dst) == HRcInt64);
+   vassert(hregIsVirtual(dst));
+
+   return dst;
+}
+
+
+static s390_opnd_RMI
+s390_isel_int_expr_RMI(ISelEnv *env, IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+   s390_opnd_RMI dst;
+
+   vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 ||
+           ty == Ity_I64);
+
+   if (expr->tag == Iex_Load) {
+      dst.tag = S390_OPND_AMODE;
+      dst.variant.am = s390_isel_amode(env, expr->Iex.Load.addr);
+   } else if (expr->tag == Iex_Get) {
+      dst.tag = S390_OPND_AMODE;
+      dst.variant.am = s390_amode_for_guest_state(expr->Iex.Get.offset);
+   } else if (expr->tag == Iex_Const) {
+      ULong value;
+
+      /* The bit pattern for the value will be stored as is in the least
+         significant bits of VALUE. */
+      switch (expr->Iex.Const.con->tag) {
+      case Ico_U1:  value = expr->Iex.Const.con->Ico.U1;  break;
+      case Ico_U8:  value = expr->Iex.Const.con->Ico.U8;  break;
+      case Ico_U16: value = expr->Iex.Const.con->Ico.U16; break;
+      case Ico_U32: value = expr->Iex.Const.con->Ico.U32; break;
+      case Ico_U64: value = expr->Iex.Const.con->Ico.U64; break;
+      default:
+         vpanic("s390_isel_int_expr_RMI");
+      }
+
+      dst.tag = S390_OPND_IMMEDIATE;
+      dst.variant.imm = value;
+   } else {
+      dst.tag = S390_OPND_REG;
+      dst.variant.reg = s390_isel_int_expr(env, expr);
+   }
+
+   return dst;
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Floating point expressions (128 bit)        ---*/
+/*---------------------------------------------------------*/
+static void
+s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
+                            IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+
+   vassert(ty == Ity_F128);
+
+   /* Read 128-bit IRTemp */
+   if (expr->tag == Iex_RdTmp) {
+      lookupIRTemp128(dst_hi, dst_lo, env, expr->Iex.RdTmp.tmp);
+      return;
+   }
+
+   switch (expr->tag) {
+   case Iex_RdTmp:
+      /* Return the virtual registers that hold the temporary. */
+      lookupIRTemp128(dst_hi, dst_lo, env, expr->Iex.RdTmp.tmp);
+      return;
+
+      /* --------- LOAD --------- */
+   case Iex_Load: {
+      IRExpr *addr_hi, *addr_lo;
+      s390_amode *am_hi, *am_lo;
+
+      if (expr->Iex.Load.end != Iend_BE)
+         goto irreducible;
+
+      addr_hi = expr->Iex.Load.addr;
+      addr_lo = IRExpr_Binop(Iop_Add64, addr_hi, mkU64(8));
+
+      am_hi  = s390_isel_amode(env, addr_hi);
+      am_lo  = s390_isel_amode(env, addr_lo);
+
+      *dst_hi = newVRegF(env);
+      *dst_lo = newVRegF(env);
+      addInstr(env, s390_insn_load(8, *dst_hi, am_hi));
+      addInstr(env, s390_insn_load(8, *dst_hi, am_lo));
+      return;
+   }
+
+
+      /* --------- GET --------- */
+   case Iex_Get:
+      /* This is not supported because loading 128-bit from the guest
+         state is almost certainly wrong. Use get_fpr_pair instead. */
+      vpanic("Iex_Get with F128 data");
+
+      /* --------- 4-ary OP --------- */
+   case Iex_Qop:
+      vpanic("Iex_Qop with F128 data");
+
+      /* --------- TERNARY OP --------- */
+   case Iex_Triop: {
+      IROp    op    = expr->Iex.Triop.op;
+      IRExpr *left  = expr->Iex.Triop.arg2;
+      IRExpr *right = expr->Iex.Triop.arg3;
+      s390_bfp_binop_t bfpop;
+      s390_round_t rounding_mode;
+      HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15;
+
+      s390_isel_float128_expr(&op1_hi, &op1_lo, env, left);  /* 1st operand */
+      s390_isel_float128_expr(&op2_hi, &op2_lo, env, right); /* 2nd operand */
+
+      /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
+      f12 = make_fpr(12);
+      f13 = make_fpr(13);
+      f14 = make_fpr(14);
+      f15 = make_fpr(15);
+
+      /* 1st operand --> (f12, f14) */
+      addInstr(env, s390_insn_move(8, f12, op1_hi));
+      addInstr(env, s390_insn_move(8, f14, op1_lo));
+
+      /* 2nd operand --> (f13, f15) */
+      addInstr(env, s390_insn_move(8, f13, op2_hi));
+      addInstr(env, s390_insn_move(8, f15, op2_lo));
+
+      switch (op) {
+      case Iop_AddF128: bfpop = S390_BFP_ADD; break;
+      case Iop_SubF128: bfpop = S390_BFP_SUB; break;
+      case Iop_MulF128: bfpop = S390_BFP_MUL; break;
+      case Iop_DivF128: bfpop = S390_BFP_DIV; break;
+      default:
+         goto irreducible;
+      }
+
+      rounding_mode = decode_rounding_mode(expr->Iex.Triop.arg1);
+      addInstr(env, s390_insn_bfp128_binop(16, bfpop, f12, f14, f13,
+                                           f15, rounding_mode));
+
+      /* Move result to virtual destination register */
+      *dst_hi = newVRegF(env);
+      *dst_lo = newVRegF(env);
+      addInstr(env, s390_insn_move(8, *dst_hi, f12));
+      addInstr(env, s390_insn_move(8, *dst_lo, f14));
+
+      return;
+   }
+
+      /* --------- BINARY OP --------- */
+   case Iex_Binop: {
+      HReg op_hi, op_lo, f12, f13, f14, f15;
+      s390_bfp_binop_t bfpop;
+      s390_round_t rounding_mode;
+
+      /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
+      f12 = make_fpr(12);
+      f13 = make_fpr(13);
+      f14 = make_fpr(14);
+      f15 = make_fpr(15);
+
+      switch (expr->Iex.Binop.op) {
+      case Iop_SqrtF128:
+         s390_isel_float128_expr(&op_hi, &op_lo, env, expr->Iex.Binop.arg2);
+
+         /* operand --> (f13, f15) */
+         addInstr(env, s390_insn_move(8, f13, op_hi));
+         addInstr(env, s390_insn_move(8, f15, op_lo));
+
+         bfpop = S390_BFP_SQRT;
+         rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
+
+         addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
+                                             rounding_mode));
+
+         /* Move result to virtual destination registers */
+         *dst_hi = newVRegF(env);
+         *dst_lo = newVRegF(env);
+         addInstr(env, s390_insn_move(8, *dst_hi, f12));
+         addInstr(env, s390_insn_move(8, *dst_lo, f14));
+         return;
+
+      case Iop_F64HLtoF128:
+         *dst_hi = s390_isel_float_expr(env, expr->Iex.Binop.arg1);
+         *dst_lo = s390_isel_float_expr(env, expr->Iex.Binop.arg2);
+         return;
+
+      default:
+         goto irreducible;
+      }
+   }
+
+      /* --------- UNARY OP --------- */
+   case Iex_Unop: {
+      IRExpr *left = expr->Iex.Binop.arg1;
+      s390_bfp_unop_t bfpop;
+      s390_round_t rounding_mode;
+      HReg op_hi, op_lo, op, f12, f13, f14, f15;
+
+      /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
+      f12 = make_fpr(12);
+      f13 = make_fpr(13);
+      f14 = make_fpr(14);
+      f15 = make_fpr(15);
+
+      switch (expr->Iex.Binop.op) {
+      case Iop_NegF128:       bfpop = S390_BFP_NEG;          goto float128_opnd;
+      case Iop_AbsF128:       bfpop = S390_BFP_ABS;          goto float128_opnd;
+      case Iop_I32StoF128:    bfpop = S390_BFP_I32_TO_F128;  goto convert_int;
+      case Iop_I64StoF128:    bfpop = S390_BFP_I64_TO_F128;  goto convert_int;
+      case Iop_F32toF128:     bfpop = S390_BFP_F32_TO_F128;  goto convert_float;
+      case Iop_F64toF128:     bfpop = S390_BFP_F64_TO_F128;  goto convert_float;
+      default:
+         goto irreducible;
+      }
+
+   float128_opnd:
+      s390_isel_float128_expr(&op_hi, &op_lo, env, left);
+
+      /* operand --> (f13, f15) */
+      addInstr(env, s390_insn_move(8, f13, op_hi));
+      addInstr(env, s390_insn_move(8, f15, op_lo));
+
+      rounding_mode = S390_ROUND_NEAREST_EVEN;  /* will not be used later on */
+      addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
+                                          rounding_mode));
+      goto move_dst;
+
+   convert_float:
+      op  = s390_isel_float_expr(env, left);
+      addInstr(env, s390_insn_bfp128_convert_to(16, bfpop, f12, f14,
+                                                op));
+      goto move_dst;
+
+   convert_int:
+      op  = s390_isel_int_expr(env, left);
+      addInstr(env, s390_insn_bfp128_convert_to(16, bfpop, f12, f14,
+                                                op));
+      goto move_dst;
+
+   move_dst:
+      /* Move result to virtual destination registers */
+      *dst_hi = newVRegF(env);
+      *dst_lo = newVRegF(env);
+      addInstr(env, s390_insn_move(8, *dst_hi, f12));
+      addInstr(env, s390_insn_move(8, *dst_lo, f14));
+      return;
+   }
+
+   default:
+      goto irreducible;
+   }
+
+   /* We get here if no pattern matched. */
+ irreducible:
+   ppIRExpr(expr);
+   vpanic("s390_isel_int_expr: cannot reduce tree");
+}
+
+/* Compute a 128-bit value into two 64-bit registers. These may be either
+   real or virtual regs; in any case they must not be changed by subsequent
+   code emitted by the caller. */
+static void
+s390_isel_float128_expr(HReg *dst_hi, HReg *dst_lo, ISelEnv *env, IRExpr *expr)
+{
+   s390_isel_float128_expr_wrk(dst_hi, dst_lo, env, expr);
+
+   /* Sanity checks ... */
+   vassert(hregIsVirtual(*dst_hi));
+   vassert(hregIsVirtual(*dst_lo));
+   vassert(hregClass(*dst_hi) == HRcFlt64);
+   vassert(hregClass(*dst_lo) == HRcFlt64);
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Floating point expressions (64 bit)         ---*/
+/*---------------------------------------------------------*/
+
+static HReg
+s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+   UChar size;
+
+   vassert(ty == Ity_F32 || ty == Ity_F64);
+
+   size = sizeofIRType(ty);
+
+   switch (expr->tag) {
+   case Iex_RdTmp:
+      /* Return the virtual register that holds the temporary. */
+      return lookupIRTemp(env, expr->Iex.RdTmp.tmp);
+
+      /* --------- LOAD --------- */
+   case Iex_Load: {
+      HReg        dst = newVRegF(env);
+      s390_amode *am  = s390_isel_amode(env, expr->Iex.Load.addr);
+
+      if (expr->Iex.Load.end != Iend_BE)
+         goto irreducible;
+
+      addInstr(env, s390_insn_load(size, dst, am));
+
+      return dst;
+   }
+
+      /* --------- GET --------- */
+   case Iex_Get: {
+      HReg dst = newVRegF(env);
+      s390_amode *am = s390_amode_for_guest_state(expr->Iex.Get.offset);
+
+      addInstr(env, s390_insn_load(size, dst, am));
+
+      return dst;
+   }
+
+      /* --------- LITERAL --------- */
+
+      /* Load a literal into a register. Create a "load immediate"
+         v-insn and return the register. */
+   case Iex_Const: {
+      ULong value;
+      HReg  dst = newVRegF(env);
+      const IRConst *con = expr->Iex.Const.con;
+
+      /* Bitwise copy of the value. No sign/zero-extension */
+      switch (con->tag) {
+      case Ico_F32i: value = con->Ico.F32i; break;
+      case Ico_F64i: value = con->Ico.F64i; break;
+      default:       vpanic("s390_isel_float_expr: invalid constant");
+      }
+
+      if (value != 0) vpanic("cannot load immediate floating point constant");
+
+      addInstr(env, s390_insn_load_immediate(size, dst, value));
+
+      return dst;
+   }
+
+      /* --------- 4-ary OP --------- */
+   case Iex_Qop: {
+      HReg op1, op2, op3, dst;
+      s390_bfp_triop_t bfpop;
+      s390_round_t rounding_mode;
+
+      op1 = s390_isel_float_expr(env, expr->Iex.Qop.arg2);
+      op2 = s390_isel_float_expr(env, expr->Iex.Qop.arg3);
+      op3 = s390_isel_float_expr(env, expr->Iex.Qop.arg4);
+      dst = newVRegF(env);
+      addInstr(env, s390_insn_move(size, dst, op1));
+
+      switch (expr->Iex.Qop.op) {
+      case Iop_MAddF32:
+      case Iop_MAddF64:  bfpop = S390_BFP_MADD; break;
+      case Iop_MSubF32:
+      case Iop_MSubF64:  bfpop = S390_BFP_MSUB; break;
+
+      default:
+         goto irreducible;
+      }
+
+      rounding_mode = decode_rounding_mode(expr->Iex.Qop.arg1);
+      addInstr(env, s390_insn_bfp_triop(size, bfpop, dst, op2, op3,
+                                        rounding_mode));
+      return dst;
+   }
+
+      /* --------- TERNARY OP --------- */
+   case Iex_Triop: {
+      IROp    op    = expr->Iex.Triop.op;
+      IRExpr *left  = expr->Iex.Triop.arg2;
+      IRExpr *right = expr->Iex.Triop.arg3;
+      s390_bfp_binop_t bfpop;
+      s390_round_t rounding_mode;
+      HReg h1, op2, dst;
+
+      h1   = s390_isel_float_expr(env, left);  /* Process 1st operand */
+      op2  = s390_isel_float_expr(env, right); /* Process 2nd operand */
+      dst  = newVRegF(env);
+      addInstr(env, s390_insn_move(size, dst, h1));
+      switch (op) {
+      case Iop_AddF32:
+      case Iop_AddF64:  bfpop = S390_BFP_ADD; break;
+      case Iop_SubF32:
+      case Iop_SubF64:  bfpop = S390_BFP_SUB; break;
+      case Iop_MulF32:
+      case Iop_MulF64:  bfpop = S390_BFP_MUL; break;
+      case Iop_DivF32:
+      case Iop_DivF64:  bfpop = S390_BFP_DIV; break;
+
+      default:
+         goto irreducible;
+      }
+
+      rounding_mode = decode_rounding_mode(expr->Iex.Triop.arg1);
+      addInstr(env, s390_insn_bfp_binop(size, bfpop, dst, op2, rounding_mode));
+      return dst;
+   }
+
+      /* --------- BINARY OP --------- */
+   case Iex_Binop: {
+      IROp    op   = expr->Iex.Binop.op;
+      IRExpr *left = expr->Iex.Binop.arg2;
+      HReg h1, dst;
+      s390_bfp_unop_t bfpop;
+      s390_round_t rounding_mode;
+      Int integer_operand;
+
+      integer_operand = 1;
+
+      switch (op) {
+      case Iop_SqrtF32:
+      case Iop_SqrtF64:
+         bfpop = S390_BFP_SQRT;
+         integer_operand = 0;
+         break;
+
+      case Iop_F64toF32:
+         bfpop = S390_BFP_F64_TO_F32;
+         integer_operand = 0;
+         break;
+
+      case Iop_I32StoF32: bfpop = S390_BFP_I32_TO_F32; break;
+      case Iop_I64StoF32: bfpop = S390_BFP_I64_TO_F32; break;
+      case Iop_I64StoF64: bfpop = S390_BFP_I64_TO_F64; break;
+      default:
+         goto irreducible;
+
+      case Iop_F128toF64:
+      case Iop_F128toF32: {
+         HReg op_hi, op_lo, f12, f13, f14, f15;
+
+         bfpop = op == Iop_F128toF32 ? S390_BFP_F128_TO_F32
+            : S390_BFP_F128_TO_F64;
+
+         rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
+
+         s390_isel_float128_expr(&op_hi, &op_lo, env, expr->Iex.Binop.arg2);
+
+         /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
+         f12 = make_fpr(12);
+         f13 = make_fpr(13);
+         f14 = make_fpr(14);
+         f15 = make_fpr(15);
+
+         /* operand --> (f13, f15) */
+         addInstr(env, s390_insn_move(8, f13, op_hi));
+         addInstr(env, s390_insn_move(8, f15, op_lo));
+
+         dst = newVRegF(env);
+         addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
+                                             rounding_mode));
+
+         /* Move result to virtual destination registers */
+         addInstr(env, s390_insn_move(8, dst, f12));
+         return dst;
+      }
+      }
+
+      /* Process operand */
+      if (integer_operand) {
+         h1  = s390_isel_int_expr(env, left);
+      } else {
+         h1  = s390_isel_float_expr(env, left);
+      }
+
+      dst = newVRegF(env);
+      rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
+      addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1, rounding_mode));
+      return dst;
+   }
+
+      /* --------- UNARY OP --------- */
+   case Iex_Unop: {
+      IROp    op   = expr->Iex.Unop.op;
+      IRExpr *left = expr->Iex.Unop.arg;
+      s390_bfp_unop_t bfpop;
+      s390_round_t rounding_mode;
+      HReg h1, dst;
+
+      if (op == Iop_F128HItoF64 || op == Iop_F128LOtoF64) {
+         HReg dst_hi, dst_lo;
+
+         s390_isel_float128_expr(&dst_hi, &dst_lo, env, left);
+         return op == Iop_F128LOtoF64 ? dst_lo : dst_hi;
+      }
+
+      if (op == Iop_ReinterpI64asF64) {
+         dst = newVRegF(env);
+         h1  = s390_isel_int_expr(env, left);     /* Process the operand */
+         addInstr(env, s390_insn_move(size, dst, h1));
+
+         return dst;
+      }
+
+      switch (op) {
+      case Iop_NegF32:
+      case Iop_NegF64:
+         if (left->tag == Iex_Unop &&
+             (left->Iex.Unop.op == Iop_AbsF32 || left->Iex.Unop.op == Iop_AbsF64))
+            bfpop = S390_BFP_NABS;
+         else
+            bfpop = S390_BFP_NEG;
+         break;
+
+      case Iop_AbsF32:
+      case Iop_AbsF64:        bfpop = S390_BFP_ABS;  break;
+      case Iop_I32StoF64:     bfpop = S390_BFP_I32_TO_F64;  break;
+      case Iop_F32toF64:      bfpop = S390_BFP_F32_TO_F64;  break;
+      default:
+         goto irreducible;
+      }
+
+      /* Process operand */
+      if (op == Iop_I32StoF64)
+         h1 = s390_isel_int_expr(env, left);
+      else if (bfpop == S390_BFP_NABS)
+         h1 = s390_isel_float_expr(env, left->Iex.Unop.arg);
+      else
+         h1 = s390_isel_float_expr(env, left);
+
+      dst = newVRegF(env);
+      rounding_mode = S390_ROUND_NEAREST_EVEN;  /* will not be used later on */
+      addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1, rounding_mode));
+      return dst;
+   }
+
+   default:
+      goto irreducible;
+   }
+
+   /* We get here if no pattern matched. */
+ irreducible:
+   ppIRExpr(expr);
+   vpanic("s390_isel_float_expr: cannot reduce tree");
+}
+
+
+static HReg
+s390_isel_float_expr(ISelEnv *env, IRExpr *expr)
+{
+   HReg dst = s390_isel_float_expr_wrk(env, expr);
+
+   /* Sanity checks ... */
+   vassert(hregClass(dst) == HRcFlt64);
+   vassert(hregIsVirtual(dst));
+
+   return dst;
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Condition Code                              ---*/
+/*---------------------------------------------------------*/
+
+/* This function handles all operators that produce a 1-bit result */
+static s390_cc_t
+s390_isel_cc(ISelEnv *env, IRExpr *cond)
+{
+   UChar size;
+
+   vassert(typeOfIRExpr(env->type_env, cond) == Ity_I1);
+
+   /* Constant: either 1 or 0 */
+   if (cond->tag == Iex_Const) {
+      vassert(cond->Iex.Const.con->tag == Ico_U1);
+      vassert(cond->Iex.Const.con->Ico.U1 == True
+              || cond->Iex.Const.con->Ico.U1 == False);
+
+      return cond->Iex.Const.con->Ico.U1 == True ? S390_CC_ALWAYS : S390_CC_NEVER;
+   }
+
+   /* Variable: values are 1 or 0 */
+   if (cond->tag == Iex_RdTmp) {
+      IRTemp tmp = cond->Iex.RdTmp.tmp;
+      HReg   reg = lookupIRTemp(env, tmp);
+
+      /* Load-and-test does not modify REG; so this is OK. */
+      if (typeOfIRTemp(env->type_env, tmp) == Ity_I1)
+         size = 4;
+      else
+         size = sizeofIRType(typeOfIRTemp(env->type_env, tmp));
+      addInstr(env, s390_insn_test(size, s390_opnd_reg(reg)));
+      return S390_CC_NE;
+   }
+
+   /* Unary operators */
+   if (cond->tag == Iex_Unop) {
+      IRExpr *arg = cond->Iex.Unop.arg;
+
+      switch (cond->Iex.Unop.op) {
+      case Iop_Not1:  /* Not1(cond) */
+         /* Generate code for EXPR, and negate the test condition */
+         return s390_cc_invert(s390_isel_cc(env, arg));
+
+         /* Iop_32/64to1  select the LSB from their operand */
+      case Iop_32to1:
+      case Iop_64to1: {
+         HReg dst = s390_isel_int_expr(env, arg);
+
+         size = sizeofIRType(typeOfIRExpr(env->type_env, arg));
+
+         addInstr(env, s390_insn_alu(size, S390_ALU_AND, dst, s390_opnd_imm(1)));
+         addInstr(env, s390_insn_test(size, s390_opnd_reg(dst)));
+         return S390_CC_NE;
+      }
+
+      case Iop_CmpNEZ8:
+      case Iop_CmpNEZ16: {
+         s390_opnd_RMI src;
+         s390_unop_t   op;
+         HReg dst;
+
+         op  = (cond->Iex.Unop.op == Iop_CmpNEZ8) ? S390_ZERO_EXTEND_8
+            : S390_ZERO_EXTEND_16;
+         dst = newVRegI(env);
+         src = s390_isel_int_expr_RMI(env, arg);
+         addInstr(env, s390_insn_unop(4, op, dst, src));
+         addInstr(env, s390_insn_test(4, s390_opnd_reg(dst)));
+         return S390_CC_NE;
+      }
+
+      case Iop_CmpNEZ32:
+      case Iop_CmpNEZ64: {
+         s390_opnd_RMI src;
+
+         src = s390_isel_int_expr_RMI(env, arg);
+         size = sizeofIRType(typeOfIRExpr(env->type_env, arg));
+         addInstr(env, s390_insn_test(size, src));
+         return S390_CC_NE;
+      }
+
+      default:
+         goto fail;
+      }
+   }
+
+   /* Binary operators */
+   if (cond->tag == Iex_Binop) {
+      IRExpr *arg1 = cond->Iex.Binop.arg1;
+      IRExpr *arg2 = cond->Iex.Binop.arg2;
+      HReg reg1, reg2;
+
+      size = sizeofIRType(typeOfIRExpr(env->type_env, arg1));
+
+      switch (cond->Iex.Binop.op) {
+         s390_unop_t op;
+         s390_cc_t   result;
+
+      case Iop_CmpEQ8:
+      case Iop_CasCmpEQ8:
+         op     = S390_ZERO_EXTEND_8;
+         result = S390_CC_E;
+         goto do_compare_ze;
+
+      case Iop_CmpNE8:
+      case Iop_CasCmpNE8:
+         op     = S390_ZERO_EXTEND_8;
+         result = S390_CC_NE;
+         goto do_compare_ze;
+
+      case Iop_CmpEQ16:
+      case Iop_CasCmpEQ16:
+         op     = S390_ZERO_EXTEND_16;
+         result = S390_CC_E;
+         goto do_compare_ze;
+
+      case Iop_CmpNE16:
+      case Iop_CasCmpNE16:
+         op     = S390_ZERO_EXTEND_16;
+         result = S390_CC_NE;
+         goto do_compare_ze;
+
+      do_compare_ze: {
+            s390_opnd_RMI op1, op2;
+
+            op1  = s390_isel_int_expr_RMI(env, arg1);
+            reg1 = newVRegI(env);
+            addInstr(env, s390_insn_unop(4, op, reg1, op1));
+
+            op2  = s390_isel_int_expr_RMI(env, arg2);
+            reg2 = newVRegI(env);
+            addInstr(env, s390_insn_unop(4, op, reg2, op2));  /* zero extend */
+
+            op2 = s390_opnd_reg(reg2);
+            addInstr(env, s390_insn_compare(4, reg1, op2, False));
+
+            return result;
+         }
+
+      case Iop_CmpEQ32:
+      case Iop_CmpEQ64:
+      case Iop_CasCmpEQ32:
+      case Iop_CasCmpEQ64:
+         result = S390_CC_E;
+         goto do_compare;
+
+      case Iop_CmpNE32:
+      case Iop_CmpNE64:
+      case Iop_CasCmpNE32:
+      case Iop_CasCmpNE64:
+         result = S390_CC_NE;
+         goto do_compare;
+
+      do_compare: {
+            HReg op1;
+            s390_opnd_RMI op2;
+
+            order_commutative_operands(arg1, arg2);
+
+            op1 = s390_isel_int_expr(env, arg1);
+            op2 = s390_isel_int_expr_RMI(env, arg2);
+
+            addInstr(env, s390_insn_compare(size, op1, op2, False));
+
+            return result;
+         }
+
+      case Iop_CmpLT32S:
+      case Iop_CmpLE32S:
+      case Iop_CmpLT64S:
+      case Iop_CmpLE64S: {
+         HReg op1;
+         s390_opnd_RMI op2;
+
+         op1 = s390_isel_int_expr(env, arg1);
+         op2 = s390_isel_int_expr_RMI(env, arg2);
+
+         addInstr(env, s390_insn_compare(size, op1, op2, True));
+
+         return (cond->Iex.Binop.op == Iop_CmpLT32S ||
+                 cond->Iex.Binop.op == Iop_CmpLT64S) ? S390_CC_L : S390_CC_LE;
+      }
+
+      case Iop_CmpLT32U:
+      case Iop_CmpLE32U:
+      case Iop_CmpLT64U:
+      case Iop_CmpLE64U: {
+         HReg op1;
+         s390_opnd_RMI op2;
+
+         op1 = s390_isel_int_expr(env, arg1);
+         op2 = s390_isel_int_expr_RMI(env, arg2);
+
+         addInstr(env, s390_insn_compare(size, op1, op2, False));
+
+         return (cond->Iex.Binop.op == Iop_CmpLT32U ||
+                 cond->Iex.Binop.op == Iop_CmpLT64U) ? S390_CC_L : S390_CC_LE;
+      }
+
+      default:
+         goto fail;
+      }
+   }
+
+ fail:
+   ppIRExpr(cond);
+   vpanic("s390_isel_cc: unexpected operator");
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Statements                                  ---*/
+/*---------------------------------------------------------*/
+
+static void
+s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
+{
+   if (vex_traceflags & VEX_TRACE_VCODE) {
+      vex_printf("\n -- ");
+      ppIRStmt(stmt);
+      vex_printf("\n");
+   }
+
+   switch (stmt->tag) {
+
+      /* --------- STORE --------- */
+   case Ist_Store: {
+      IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Store.data);
+      s390_amode *am;
+      HReg src;
+
+      if (stmt->Ist.Store.end != Iend_BE) goto stmt_fail;
+
+      am = s390_isel_amode(env, stmt->Ist.Store.addr);
+
+      switch (tyd) {
+      case Ity_I8:
+      case Ity_I16:
+      case Ity_I32:
+      case Ity_I64:
+         src = s390_isel_int_expr(env, stmt->Ist.Store.data);
+         break;
+
+      case Ity_F32:
+      case Ity_F64:
+         src = s390_isel_float_expr(env, stmt->Ist.Store.data);
+         break;
+
+      case Ity_F128:
+         /* Cannot occur. No such instruction */
+         vpanic("Ist_Store with F128 data");
+
+      default:
+         goto stmt_fail;
+      }
+
+      addInstr(env, s390_insn_store(sizeofIRType(tyd), am, src));
+      return;
+   }
+
+      /* --------- PUT --------- */
+   case Ist_Put: {
+      IRType tyd = typeOfIRExpr(env->type_env, stmt->Ist.Put.data);
+      HReg src;
+      s390_amode *am;
+
+      am = s390_amode_for_guest_state(stmt->Ist.Put.offset);
+
+      switch (tyd) {
+      case Ity_I8:
+      case Ity_I16:
+      case Ity_I32:
+      case Ity_I64:
+         src = s390_isel_int_expr(env, stmt->Ist.Put.data);
+         break;
+
+      case Ity_F32:
+      case Ity_F64:
+         src = s390_isel_float_expr(env, stmt->Ist.Put.data);
+         break;
+
+      case Ity_F128:
+         /* Does not occur. See function put_fpr_pair. */
+         vpanic("Ist_Put with F128 data");
+
+      default:
+         goto stmt_fail;
+      }
+
+      addInstr(env, s390_insn_store(sizeofIRType(tyd), am, src));
+      return;
+   }
+
+      /* --------- TMP --------- */
+   case Ist_WrTmp: {
+      IRTemp tmp = stmt->Ist.WrTmp.tmp;
+      IRType tyd = typeOfIRTemp(env->type_env, tmp);
+      HReg src, dst;
+
+      switch (tyd) {
+      case Ity_I128: {
+         HReg dst_hi, dst_lo, res_hi, res_lo;
+
+         s390_isel_int128_expr(&res_hi, &res_lo, env, stmt->Ist.WrTmp.data);
+         lookupIRTemp128(&dst_hi, &dst_lo, env, tmp);
+
+         addInstr(env, s390_insn_move(8, dst_hi, res_hi));
+         addInstr(env, s390_insn_move(8, dst_lo, res_lo));
+         return;
+      }
+
+      case Ity_I8:
+      case Ity_I16:
+      case Ity_I32:
+      case Ity_I64:
+         src = s390_isel_int_expr(env, stmt->Ist.WrTmp.data);
+         dst = lookupIRTemp(env, tmp);
+         break;
+
+      case Ity_I1: {
+         s390_cc_t cond = s390_isel_cc(env, stmt->Ist.WrTmp.data);
+         dst = lookupIRTemp(env, tmp);
+         addInstr(env, s390_insn_cc2bool(dst, cond));
+         return;
+      }
+
+      case Ity_F32:
+      case Ity_F64:
+         src = s390_isel_float_expr(env, stmt->Ist.WrTmp.data);
+         dst = lookupIRTemp(env, tmp);
+         break;
+
+      case Ity_F128: {
+         HReg dst_hi, dst_lo, res_hi, res_lo;
+
+         s390_isel_float128_expr(&res_hi, &res_lo, env, stmt->Ist.WrTmp.data);
+         lookupIRTemp128(&dst_hi, &dst_lo, env, tmp);
+
+         addInstr(env, s390_insn_move(8, dst_hi, res_hi));
+         addInstr(env, s390_insn_move(8, dst_lo, res_lo));
+         return;
+      }
+
+      default:
+         goto stmt_fail;
+      }
+
+      addInstr(env, s390_insn_move(sizeofIRType(tyd), dst, src));
+      return;
+   }
+
+      /* --------- Call to DIRTY helper --------- */
+   case Ist_Dirty: {
+      IRType   retty;
+      IRDirty* d = stmt->Ist.Dirty.details;
+      Bool     passBBP;
+
+      if (d->nFxState == 0)
+         vassert(!d->needsBBP);
+
+      passBBP = toBool(d->nFxState > 0 && d->needsBBP);
+
+      doHelperCall(env, passBBP, d->guard, d->cee, d->args);
+
+      /* Now figure out what to do with the returned value, if any. */
+      if (d->tmp == IRTemp_INVALID)
+         /* No return value.  Nothing to do. */
+         return;
+
+      retty = typeOfIRTemp(env->type_env, d->tmp);
+      if (retty == Ity_I64 || retty == Ity_I32
+          || retty == Ity_I16 || retty == Ity_I8) {
+         /* Move the returned value into the return register */
+         HReg dst = lookupIRTemp(env, d->tmp);
+         addInstr(env, s390_insn_move(sizeofIRType(retty), dst,
+                                      mkHReg(S390_REGNO_RETURN_VALUE,
+                                             HRcInt64, False)));
+         return;
+      }
+      break;
+   }
+
+   case Ist_CAS:
+      if (stmt->Ist.CAS.details->oldHi == IRTemp_INVALID) {
+         IRCAS *cas = stmt->Ist.CAS.details;
+         s390_amode *op2 = s390_isel_amode(env, cas->addr);
+         HReg op3 = s390_isel_int_expr(env, cas->dataLo);  /* new value */
+         HReg op1 = s390_isel_int_expr(env, cas->expdLo);  /* expected value */
+         HReg old = lookupIRTemp(env, cas->oldLo);
+
+         if (typeOfIRTemp(env->type_env, cas->oldLo) == Ity_I32) {
+            addInstr(env, s390_insn_cas(4, op1, op2, op3, old));
+         } else {
+            addInstr(env, s390_insn_cas(8, op1, op2, op3, old));
+         }
+         return;
+      } else {
+         vpanic("compare double and swap not implemented\n");
+      }
+      break;
+
+      /* --------- EXIT --------- */
+   case Ist_Exit: {
+      s390_opnd_RMI dst;
+      s390_cc_t cond;
+      IRConstTag tag = stmt->Ist.Exit.dst->tag;
+
+      if (tag != Ico_U64)
+         vpanic("s390_isel_stmt: Ist_Exit: dst is not a 64-bit value");
+
+      dst  = s390_isel_int_expr_RMI(env, IRExpr_Const(stmt->Ist.Exit.dst));
+      cond = s390_isel_cc(env, stmt->Ist.Exit.guard);
+      addInstr(env, s390_insn_branch(stmt->Ist.Exit.jk, cond, dst));
+      return;
+   }
+
+      /* --------- MEM FENCE --------- */
+   case Ist_MBE:  /* fixs390 later */
+      break;
+
+      /* --------- Miscellaneous --------- */
+
+   case Ist_PutI:    /* Not needed */
+   case Ist_IMark:   /* Doesn't generate any executable code */
+   case Ist_NoOp:    /* Doesn't generate any executable code */
+   case Ist_AbiHint: /* Meaningless in IR */
+      return;
+
+   default:
+      break;
+   }
+
+ stmt_fail:
+   ppIRStmt(stmt);
+   vpanic("s390_isel_stmt");
+}
+
+
+/*---------------------------------------------------------*/
+/*--- ISEL: Basic block terminators (Nexts)             ---*/
+/*---------------------------------------------------------*/
+
+static void
+iselNext(ISelEnv *env, IRExpr *next, IRJumpKind jk)
+{
+   s390_opnd_RMI dst;
+
+   if (vex_traceflags & VEX_TRACE_VCODE) {
+      vex_printf("\n-- goto {");
+      ppIRJumpKind(jk);
+      vex_printf("} ");
+      ppIRExpr(next);
+      vex_printf("\n");
+   }
+
+   dst = s390_isel_int_expr_RMI(env, next);
+   addInstr(env, s390_insn_branch(jk, S390_CC_ALWAYS, dst));
+}
+
+
+/*---------------------------------------------------------*/
+/*--- Insn selector top-level                           ---*/
+/*---------------------------------------------------------*/
+
+/* Translate an entire SB to s390 code. */
+
+HInstrArray *
+iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
+             VexAbiInfo *vbi)
+{
+   UInt     i, j;
+   HReg     hreg, hregHI;
+   ISelEnv *env;
+   UInt     hwcaps_host = archinfo_host->hwcaps;
+
+   /* KLUDGE: export archinfo_host. */
+   s390_archinfo_host = archinfo_host;
+
+
+   /* Do some sanity checks */
+   vassert((hwcaps_host & ~(VEX_HWCAPS_S390X_ALL)) == 0);
+
+   /* Make up an initial environment to use. */
+   env = LibVEX_Alloc(sizeof(ISelEnv));
+   env->vreg_ctr = 0;
+
+   /* Set up output code array. */
+   env->code = newHInstrArray();
+
+   /* Copy BB's type env. */
+   env->type_env = bb->tyenv;
+
+   /* Make up an IRTemp -> virtual HReg mapping.  This doesn't
+      change as we go along. For some reason types_used has Int type -- but
+      it should be unsigned. Internally we use an unsigned type; so we
+      assert it here. */
+   vassert(bb->tyenv->types_used >= 0);
+
+   env->n_vregmap = bb->tyenv->types_used;
+   env->vregmap   = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+   env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+
+   /* and finally ... */
+   env->hwcaps    = hwcaps_host;
+
+   /* For each IR temporary, allocate a suitably-kinded virtual
+      register. */
+   j = 0;
+   for (i = 0; i < env->n_vregmap; i++) {
+      hregHI = hreg = INVALID_HREG;
+      switch (bb->tyenv->types[i]) {
+      case Ity_I1:
+      case Ity_I8:
+      case Ity_I16:
+      case Ity_I32:
+         hreg = mkHReg(j++, HRcInt64, True);
+         break;
+
+      case Ity_I64:
+         hreg   = mkHReg(j++, HRcInt64, True);
+         break;
+
+      case Ity_I128:
+         hreg   = mkHReg(j++, HRcInt64, True);
+         hregHI = mkHReg(j++, HRcInt64, True);
+         break;
+
+      case Ity_F32:
+      case Ity_F64:
+         hreg = mkHReg(j++, HRcFlt64, True);
+         break;
+
+      case Ity_F128:
+         hreg   = mkHReg(j++, HRcFlt64, True);
+         hregHI = mkHReg(j++, HRcFlt64, True);
+         break;
+
+      case Ity_V128: /* fall through */
+      default:
+         ppIRType(bb->tyenv->types[i]);
+         vpanic("s390_isel_sb: IRTemp type");
+      }
+
+      env->vregmap[i]   = hreg;
+      env->vregmapHI[i] = hregHI;
+   }
+   env->vreg_ctr = j;
+
+   /* Ok, finally we can iterate over the statements. */
+   for (i = 0; i < bb->stmts_used; i++)
+      if (bb->stmts[i])
+         s390_isel_stmt(env, bb->stmts[i]);
+
+   iselNext(env, bb->next, bb->jumpkind);
+
+   /* Record the number of vregs we used. */
+   env->code->n_vregs = env->vreg_ctr;
+
+   return env->code;
+}
+
+/*---------------------------------------------------------------*/
+/*--- end                                    host_s390_isel.c ---*/
+/*---------------------------------------------------------------*/