s390: More prep work bfp reorg. In the future unary/binary/ternary
operations on bfp data will no longer require a rounding mode in the
s390_insn. Only type conversion operations need a rounding mode.
So in this patch S390_BFP_CONVERT is introduced and 
S390_BFP128_CONVERT_TO/FROM are consolidated to S390_BFP128_CONVERT.
This also makes the representation of bfp and bfp128 symmetric.
s390_insn gets a new variant: s390_convert.
The type conversion ops get their own data type now: s390_conv_t


git-svn-id: svn://svn.valgrind.org/vex/trunk@2522 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_defs.c b/priv/host_s390_defs.c
index 04d8a39..fe5a633 100644
--- a/priv/host_s390_defs.c
+++ b/priv/host_s390_defs.c
@@ -680,6 +680,11 @@
       addHRegUse(u, HRmRead,  insn->variant.bfp_compare.op2);  /* right */
       break;
 
+   case S390_INSN_BFP_CONVERT:
+      addHRegUse(u, HRmWrite, insn->variant.bfp_convert.dst);
+      addHRegUse(u, HRmRead,  insn->variant.bfp_convert.op);  /* operand */
+      break;
+
    case S390_INSN_BFP128_BINOP:
       addHRegUse(u, HRmWrite, insn->variant.bfp128_binop.dst_hi);
       addHRegUse(u, HRmWrite, insn->variant.bfp128_binop.dst_lo);
@@ -704,16 +709,13 @@
       addHRegUse(u, HRmRead,  insn->variant.bfp128_unop.op_lo);
       break;
 
-   case S390_INSN_BFP128_CONVERT_TO:
-      addHRegUse(u, HRmWrite, insn->variant.bfp128_unop.dst_hi);
-      addHRegUse(u, HRmWrite, insn->variant.bfp128_unop.dst_lo);
-      addHRegUse(u, HRmRead,  insn->variant.bfp128_unop.op_hi);
-      break;
-
-   case S390_INSN_BFP128_CONVERT_FROM:
-      addHRegUse(u, HRmWrite, insn->variant.bfp128_unop.dst_hi);
-      addHRegUse(u, HRmRead,  insn->variant.bfp128_unop.op_hi);
-      addHRegUse(u, HRmRead,  insn->variant.bfp128_unop.op_lo);
+   case S390_INSN_BFP128_CONVERT:
+      addHRegUse(u, HRmWrite, insn->variant.bfp128_convert.dst_hi);
+      if (insn->variant.bfp128_convert.dst_lo != INVALID_HREG)
+         addHRegUse(u, HRmWrite, insn->variant.bfp128_convert.dst_lo);
+      addHRegUse(u, HRmRead,  insn->variant.bfp128_convert.op_hi);
+      if (insn->variant.bfp128_convert.op_lo != INVALID_HREG)
+         addHRegUse(u, HRmRead, insn->variant.bfp128_convert.op_lo);
       break;
 
    case S390_INSN_MFENCE:
@@ -899,6 +901,13 @@
       insn->variant.bfp_compare.op2 = lookupHRegRemap(m, insn->variant.bfp_compare.op2);
       break;
 
+   case S390_INSN_BFP_CONVERT:
+      insn->variant.bfp_convert.dst =
+         lookupHRegRemap(m, insn->variant.bfp_convert.dst);
+      insn->variant.bfp_convert.op  =
+         lookupHRegRemap(m, insn->variant.bfp_convert.op);
+      break;
+
    case S390_INSN_BFP128_BINOP:
       insn->variant.bfp128_binop.dst_hi =
          lookupHRegRemap(m, insn->variant.bfp128_binop.dst_hi);
@@ -934,22 +943,17 @@
          lookupHRegRemap(m, insn->variant.bfp128_unop.op_lo);
       break;
 
-   case S390_INSN_BFP128_CONVERT_TO:
-      insn->variant.bfp128_unop.dst_hi =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.dst_hi);
-      insn->variant.bfp128_unop.dst_lo =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.dst_lo);
-      insn->variant.bfp128_unop.op_hi =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.op_hi);
-      break;
-
-   case S390_INSN_BFP128_CONVERT_FROM:
-      insn->variant.bfp128_unop.dst_hi =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.dst_hi);
-      insn->variant.bfp128_unop.op_hi =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.op_hi);
-      insn->variant.bfp128_unop.op_lo =
-         lookupHRegRemap(m, insn->variant.bfp128_unop.op_lo);
+   case S390_INSN_BFP128_CONVERT:
+      insn->variant.bfp128_convert.dst_hi =
+         lookupHRegRemap(m, insn->variant.bfp128_convert.dst_hi);
+      if (insn->variant.bfp128_convert.dst_lo != INVALID_HREG)
+         insn->variant.bfp128_convert.dst_lo =
+            lookupHRegRemap(m, insn->variant.bfp128_convert.dst_lo);
+      insn->variant.bfp128_convert.op_hi =
+         lookupHRegRemap(m, insn->variant.bfp128_convert.op_hi);
+      if (insn->variant.bfp128_convert.op_lo != INVALID_HREG)
+         insn->variant.bfp128_convert.op_lo =
+            lookupHRegRemap(m, insn->variant.bfp128_convert.op_lo);
       break;
 
    case S390_INSN_MFENCE:
@@ -4746,6 +4750,23 @@
 
 
 s390_insn *
+s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst, HReg op,
+                      s390_round_t rounding_mode)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_BFP_CONVERT;
+   insn->size = size;
+   insn->variant.bfp_convert.tag = tag;
+   insn->variant.bfp_convert.dst = dst;
+   insn->variant.bfp_convert.op  = op;
+   insn->variant.bfp_convert.rounding_mode = rounding_mode;
+
+   return insn;
+}
+
+
+s390_insn *
 s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
                        HReg dst_lo, HReg op2_hi, HReg op2_lo,
                        s390_round_t rounding_mode)
@@ -4803,42 +4824,46 @@
 }
 
 
-s390_insn *
-s390_insn_bfp128_convert_to(UChar size, s390_bfp_unop_t tag, HReg dst_hi,
-                            HReg dst_lo, HReg op)
+static s390_insn *
+s390_insn_bfp128_convert(UChar size, s390_conv_t tag, HReg dst_hi,
+                         HReg dst_lo, HReg op_hi, HReg op_lo,
+                         s390_round_t rounding_mode)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
-   insn->tag  = S390_INSN_BFP128_CONVERT_TO;
+   insn->tag  = S390_INSN_BFP128_CONVERT;
    insn->size = size;
-   insn->variant.bfp128_unop.tag = tag;
-   insn->variant.bfp128_unop.dst_hi = dst_hi;
-   insn->variant.bfp128_unop.dst_lo = dst_lo;
-   insn->variant.bfp128_unop.op_hi = op;
-   insn->variant.bfp128_unop.op_lo = INVALID_HREG;  /* unused */
-   insn->variant.bfp128_unop.rounding_mode = S390_ROUND_NEAREST_EVEN; /* unused */
+   insn->variant.bfp128_convert.tag = tag;
+   insn->variant.bfp128_convert.dst_hi = dst_hi;
+   insn->variant.bfp128_convert.dst_lo = dst_lo;
+   insn->variant.bfp128_convert.op_hi = op_hi;
+   insn->variant.bfp128_convert.op_lo = op_lo;
+   insn->variant.bfp128_convert.rounding_mode = rounding_mode;
 
    return insn;
 }
 
 
 s390_insn *
-s390_insn_bfp128_convert_from(UChar size, s390_bfp_unop_t tag, HReg dst,
+s390_insn_bfp128_convert_to(UChar size, s390_conv_t tag, HReg dst_hi,
+                            HReg dst_lo, HReg op)
+{
+   /* Conversion to bfp128 never requires a rounding mode. Provide default
+      rounding mode. It will not be used when emitting insns. */
+   s390_round_t rounding_mode = S390_ROUND_NEAREST_EVEN;
+
+   return s390_insn_bfp128_convert(size, tag, dst_hi, dst_lo, op,
+                                   INVALID_HREG, rounding_mode);
+}
+
+
+s390_insn *
+s390_insn_bfp128_convert_from(UChar size, s390_conv_t tag, HReg dst,
                               HReg op_hi, HReg op_lo,
                               s390_round_t rounding_mode)
 {
-   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
-
-   insn->tag  = S390_INSN_BFP128_CONVERT_FROM;
-   insn->size = size;
-   insn->variant.bfp128_unop.tag = tag;
-   insn->variant.bfp128_unop.dst_hi = dst;
-   insn->variant.bfp128_unop.dst_lo = INVALID_HREG;  /* unused */
-   insn->variant.bfp128_unop.op_hi = op_hi;
-   insn->variant.bfp128_unop.op_lo = op_lo;
-   insn->variant.bfp128_unop.rounding_mode = rounding_mode;
-
-   return insn;
+   return s390_insn_bfp128_convert(size, tag, dst, INVALID_HREG, op_hi, op_lo,
+                                   rounding_mode);
 }
 
 
@@ -5327,6 +5352,14 @@
       case S390_BFP_NABS:        op = "v-fnabs"; break;
       case S390_BFP_NEG:         op = "v-fneg";  break;
       case S390_BFP_SQRT:        op = "v-fsqrt"; break;
+      default: goto fail;
+      }
+      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp_unop.dst,
+                   insn->variant.bfp_unop.op);
+      break;
+
+   case S390_INSN_BFP_CONVERT:
+      switch (insn->variant.bfp_convert.tag) {
       case S390_BFP_I32_TO_F32:
       case S390_BFP_I32_TO_F64:
       case S390_BFP_I32_TO_F128:
@@ -5359,8 +5392,8 @@
       case S390_BFP_F128_TO_F64: op = "v-f2f"; break;
       default: goto fail;
       }
-      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp_unop.dst,
-                   insn->variant.bfp_unop.op);
+      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp_convert.dst,
+                   insn->variant.bfp_convert.op);
       break;
 
    case S390_INSN_BFP128_BINOP:
@@ -5385,13 +5418,20 @@
       break;
 
    case S390_INSN_BFP128_UNOP:
-   case S390_INSN_BFP128_CONVERT_TO:
-   case S390_INSN_BFP128_CONVERT_FROM:
       switch (insn->variant.bfp128_unop.tag) {
       case S390_BFP_ABS:         op = "v-fabs";  break;
       case S390_BFP_NABS:        op = "v-fnabs"; break;
       case S390_BFP_NEG:         op = "v-fneg";  break;
       case S390_BFP_SQRT:        op = "v-fsqrt"; break;
+      default: goto fail;
+      }
+      /* Only write the register that identifies the register pair */
+      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp128_unop.dst_hi,
+                   insn->variant.bfp128_unop.op_hi);
+      break;
+
+   case S390_INSN_BFP128_CONVERT:
+      switch (insn->variant.bfp128_convert.tag) {
       case S390_BFP_I32_TO_F128:
       case S390_BFP_I64_TO_F128: op = "v-i2f";   break;
       case S390_BFP_U32_TO_F128:
@@ -5407,8 +5447,8 @@
       default: goto fail;
       }
       /* Only write the register that identifies the register pair */
-      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp128_unop.dst_hi,
-                   insn->variant.bfp128_unop.op_hi);
+      s390_sprintf(buf, "%M %R,%R", op, insn->variant.bfp128_convert.dst_hi,
+                   insn->variant.bfp128_convert.op_hi);
       break;
 
    case S390_INSN_MFENCE:
@@ -5531,9 +5571,10 @@
       }
 
    case S390_INSN_BFP128_UNOP:
-   case S390_INSN_BFP128_CONVERT_TO:
-   case S390_INSN_BFP128_CONVERT_FROM:
-      switch (insn->variant.bfp128_unop.tag) {
+      goto common;
+
+   case S390_INSN_BFP128_CONVERT:
+      switch (insn->variant.bfp128_convert.tag) {
       case S390_BFP_I32_TO_F128:
       case S390_BFP_U32_TO_F128:
       case S390_BFP_F32_TO_F128: p += vex_sprintf(p, "4 -> "); goto common;
@@ -7398,25 +7439,6 @@
    UInt  r1 = hregNumber(insn->variant.bfp_unop.dst);
    UInt  r2 = hregNumber(insn->variant.bfp_unop.op);
    s390_round_t rounding_mode = insn->variant.bfp_unop.rounding_mode;
-   s390_round_t m3 = rounding_mode;
-
-   /* The "convert to fixed" instructions have a field for the rounding
-      mode and no FPC modification is necessary. So we handle them
-      upfront. */
-   switch (insn->variant.bfp_unop.tag) {
-   case S390_BFP_F32_TO_I32:  return s390_emit_CFEBR(buf, m3, r1, r2);
-   case S390_BFP_F64_TO_I32:  return s390_emit_CFDBR(buf, m3, r1, r2);
-   case S390_BFP_F32_TO_I64:  return s390_emit_CGEBR(buf, m3, r1, r2);
-   case S390_BFP_F64_TO_I64:  return s390_emit_CGDBR(buf, m3, r1, r2);
-
-   /* We leave m4 as 0 - as gcc */
-   case S390_BFP_F32_TO_U32:  return s390_emit_CLFEBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F64_TO_U32:  return s390_emit_CLFDBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F32_TO_U64:  return s390_emit_CLGEBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F64_TO_U64:  return s390_emit_CLGDBR(buf, m3, 0, r1, r2);
-
-   default: break;
-   }
 
    /* For all other insns if a special rounding mode is requested,
       we need to set the FPC first and restore it later. */
@@ -7461,6 +7483,54 @@
       }
       break;
 
+   default: goto fail;
+   }
+
+   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+      /* Restore FPC register from guest state */
+      buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
+                           S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
+   }
+   return buf;
+
+ fail:
+   vpanic("s390_insn_bfp_unop_emit");
+}
+
+
+static UChar *
+s390_insn_bfp_convert_emit(UChar *buf, const s390_insn *insn)
+{
+   UInt  r1 = hregNumber(insn->variant.bfp_convert.dst);
+   UInt  r2 = hregNumber(insn->variant.bfp_convert.op);
+   s390_round_t rounding_mode = insn->variant.bfp_convert.rounding_mode;
+   s390_round_t m3 = rounding_mode;
+
+   /* The "convert to fixed" instructions have a field for the rounding
+      mode and no FPC modification is necessary. So we handle them
+      upfront. */
+   switch (insn->variant.bfp_convert.tag) {
+   case S390_BFP_F32_TO_I32:  return s390_emit_CFEBR(buf, m3, r1, r2);
+   case S390_BFP_F64_TO_I32:  return s390_emit_CFDBR(buf, m3, r1, r2);
+   case S390_BFP_F32_TO_I64:  return s390_emit_CGEBR(buf, m3, r1, r2);
+   case S390_BFP_F64_TO_I64:  return s390_emit_CGDBR(buf, m3, r1, r2);
+
+   /* We leave m4 as 0 - as gcc */
+   case S390_BFP_F32_TO_U32:  return s390_emit_CLFEBR(buf, m3, 0, r1, r2);
+   case S390_BFP_F64_TO_U32:  return s390_emit_CLFDBR(buf, m3, 0, r1, r2);
+   case S390_BFP_F32_TO_U64:  return s390_emit_CLGEBR(buf, m3, 0, r1, r2);
+   case S390_BFP_F64_TO_U64:  return s390_emit_CLGDBR(buf, m3, 0, r1, r2);
+
+   default: break;
+   }
+
+   /* For all other insns if a special rounding mode is requested,
+      we need to set the FPC first and restore it later. */
+   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+      buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
+   }
+
+   switch (insn->variant.bfp_convert.tag) {
    case S390_BFP_I32_TO_F32:  buf = s390_emit_CEFBRA(buf, 0, 0, r1, r2); break;
    case S390_BFP_I32_TO_F64:  buf = s390_emit_CDFBRA(buf, 0, 0, r1, r2); break;
    case S390_BFP_I32_TO_F128: buf = s390_emit_CXFBRA(buf, 0, 0, r1, r2); break;
@@ -7492,7 +7562,7 @@
    return buf;
 
  fail:
-   vpanic("s390_insn_bfp_unop_emit");
+   vpanic("s390_insn_bfp_convert_emit");
 }
 
 
@@ -7610,8 +7680,6 @@
    case S390_BFP_NABS:        buf = s390_emit_LNXBR(buf, r1_hi, r2_hi); break;
    case S390_BFP_NEG:         buf = s390_emit_LCXBR(buf, r1_hi, r2_hi); break;
    case S390_BFP_SQRT:        buf = s390_emit_SQXBR(buf, r1_hi, r2_hi); break;
-   case S390_BFP_F128_TO_F32: buf = s390_emit_LEXBRA(buf, 0, 0, r1_hi, r2_hi); break;
-   case S390_BFP_F128_TO_F64: buf = s390_emit_LDXBRA(buf, 0, 0, r1_hi, r2_hi); break;
    default:  goto fail;
    }
 
@@ -7627,72 +7695,72 @@
 }
 
 
-/* Conversion to 128-bit BFP does not require a rounding mode */
 static UChar *
-s390_insn_bfp128_convert_to_emit(UChar *buf, const s390_insn *insn)
+s390_insn_bfp128_convert_emit(UChar *buf, const s390_insn *insn)
 {
-   UInt r1_hi = hregNumber(insn->variant.bfp128_unop.dst_hi);
-   UInt r1_lo = hregNumber(insn->variant.bfp128_unop.dst_lo);
-   UInt r2    = hregNumber(insn->variant.bfp128_unop.op_hi);
+   UInt r1_hi = hregNumber(insn->variant.bfp128_convert.dst_hi);
+   UInt r1_lo = hregNumber(insn->variant.bfp128_convert.dst_lo);
+   UInt r2_hi = hregNumber(insn->variant.bfp128_convert.op_hi);
+   UInt r2_lo = hregNumber(insn->variant.bfp128_convert.op_lo);
+   s390_round_t rounding_mode = insn->variant.bfp128_convert.rounding_mode;
 
    /* Paranoia */
-   vassert(insn->size == 16);
-   vassert(r1_lo == r1_hi + 2);
-   vassert((r1_hi & 0x2) == 0);
+   vassert(r1_lo == hregNumber(INVALID_HREG) || r1_lo == r1_hi + 2);
+   vassert(r2_lo == hregNumber(INVALID_HREG) || r2_lo == r2_hi + 2);
+   vassert(r1_lo == hregNumber(INVALID_HREG) || (r1_hi & 0x2) == 0);
+   vassert(r2_lo == hregNumber(INVALID_HREG) || (r2_hi & 0x2) == 0);
 
-   switch (insn->variant.bfp128_unop.tag) {
-   case S390_BFP_I32_TO_F128: buf = s390_emit_CXFBRA(buf, 0, 0, r1_hi, r2); break;
-   case S390_BFP_I64_TO_F128: buf = s390_emit_CXGBRA(buf, 0, 0, r1_hi, r2); break;
-   /* Rounding makes no sense -> m3 == 0. m4 is also 0 */
-   case S390_BFP_U32_TO_F128: buf = s390_emit_CXLFBR(buf, 0, 0, r1_hi, r2);
-                              break;
-   case S390_BFP_U64_TO_F128: buf = s390_emit_CXLGBR(buf, 0, 0, r1_hi, r2);
-                              break;
-   case S390_BFP_F32_TO_F128: buf = s390_emit_LXEBR(buf, r1_hi, r2); break;
-   case S390_BFP_F64_TO_F128: buf = s390_emit_LXDBR(buf, r1_hi, r2); break;
+   switch (insn->variant.bfp128_convert.tag) {
+      /* Conversion to 128-bit never requires a rounding mode */
+   case S390_BFP_I32_TO_F128: return s390_emit_CXFBRA(buf, 0, 0, r1_hi, r2_hi);
+   case S390_BFP_I64_TO_F128: return s390_emit_CXGBRA(buf, 0, 0, r1_hi, r2_hi);
+   case S390_BFP_U32_TO_F128: return s390_emit_CXLFBR(buf, 0, 0, r1_hi, r2_hi);
+   case S390_BFP_U64_TO_F128: return s390_emit_CXLGBR(buf, 0, 0, r1_hi, r2_hi);
+   case S390_BFP_F32_TO_F128: return s390_emit_LXEBR(buf, r1_hi, r2_hi);
+   case S390_BFP_F64_TO_F128: return s390_emit_LXDBR(buf, r1_hi, r2_hi);
+
+      /* Conversion from 128-bit requires a rounding mode */
+   case S390_BFP_F128_TO_I32:
+      return s390_emit_CFXBR(buf, rounding_mode, r1_hi, r2_hi);
+
+   case S390_BFP_F128_TO_I64:
+      return s390_emit_CGXBR(buf, rounding_mode, r1_hi, r2_hi);
+
+   case S390_BFP_F128_TO_U32:
+      return s390_emit_CLFXBR(buf, rounding_mode, 0, r1_hi, r2_hi);
+
+   case S390_BFP_F128_TO_U64:
+      return s390_emit_CLGXBR(buf, rounding_mode, 0, r1_hi, r2_hi);
+
+   case S390_BFP_F128_TO_F32:
+      if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+         buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
+      }
+      buf = s390_emit_LEXBRA(buf, 0, 0, r1_hi, r2_hi);
+      if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+         /* Restore FPC register from guest state */
+         buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
+                              S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
+      }
+      return buf;
+
+   case S390_BFP_F128_TO_F64:
+      if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+         buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
+      }
+      buf = s390_emit_LDXBRA(buf, 0, 0, r1_hi, r2_hi);
+      if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
+         /* Restore FPC register from guest state */
+         buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
+                              S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
+      }
+      return buf;
+
    default:  goto fail;
    }
 
-   return buf;
-
  fail:
-   vpanic("s390_insn_bfp128_convert_to_emit");
-}
-
-
-static UChar *
-s390_insn_bfp128_convert_from_emit(UChar *buf, const s390_insn *insn)
-{
-   UInt r1    = hregNumber(insn->variant.bfp128_unop.dst_hi);
-   UInt r2_hi = hregNumber(insn->variant.bfp128_unop.op_hi);
-   UInt r2_lo = hregNumber(insn->variant.bfp128_unop.op_lo);
-   s390_round_t rounding_mode = insn->variant.bfp128_unop.rounding_mode;
-
-   /* Paranoia */
-   vassert(insn->size != 16);
-   vassert(r2_lo == r2_hi + 2);
-   vassert((r2_hi & 0x2) == 0);
-
-   /* The "convert to fixed" instructions have a field for the rounding
-      mode and no FPC modification is necessary. So we handle them
-      upfront. */
-   switch (insn->variant.bfp_unop.tag) {
-   case S390_BFP_F128_TO_I32:
-      return s390_emit_CFXBR(buf, rounding_mode, r1, r2_hi);
-
-   case S390_BFP_F128_TO_I64:
-      return s390_emit_CGXBR(buf, rounding_mode, r1, r2_hi);
-
-   case S390_BFP_F128_TO_U32:
-      return s390_emit_CLFXBR(buf, rounding_mode, 0, r1, r2_hi);
-
-   case S390_BFP_F128_TO_U64:
-      return s390_emit_CLGXBR(buf, rounding_mode, 0, r1, r2_hi);
-
-   default: break;
-   }
-
-   vpanic("s390_insn_bfp128_convert_from_emit");
+   vpanic("s390_insn_bfp128_convert_emit");
 }
 
 
@@ -8245,6 +8313,10 @@
       end = s390_insn_bfp_compare_emit(buf, insn);
       break;
 
+   case S390_INSN_BFP_CONVERT:
+      end = s390_insn_bfp_convert_emit(buf, insn);
+      break;
+
    case S390_INSN_BFP128_BINOP:
       end = s390_insn_bfp128_binop_emit(buf, insn);
       break;
@@ -8257,12 +8329,8 @@
       end = s390_insn_bfp128_unop_emit(buf, insn);
       break;
 
-   case S390_INSN_BFP128_CONVERT_TO:
-      end = s390_insn_bfp128_convert_to_emit(buf, insn);
-      break;
-
-   case S390_INSN_BFP128_CONVERT_FROM:
-      end = s390_insn_bfp128_convert_from_emit(buf, insn);
+   case S390_INSN_BFP128_CONVERT:
+      end = s390_insn_bfp128_convert_emit(buf, insn);
       break;
 
    case S390_INSN_MFENCE:
diff --git a/priv/host_s390_defs.h b/priv/host_s390_defs.h
index a3c5759..7a49f13 100644
--- a/priv/host_s390_defs.h
+++ b/priv/host_s390_defs.h
@@ -137,11 +137,11 @@
    S390_INSN_BFP_UNOP,
    S390_INSN_BFP_TRIOP,
    S390_INSN_BFP_COMPARE,
+   S390_INSN_BFP_CONVERT,
    S390_INSN_BFP128_BINOP, /* Binary floating point 128-bit */
    S390_INSN_BFP128_UNOP,
    S390_INSN_BFP128_COMPARE,
-   S390_INSN_BFP128_CONVERT_TO,
-   S390_INSN_BFP128_CONVERT_FROM,
+   S390_INSN_BFP128_CONVERT,
    S390_INSN_MFENCE,
    S390_INSN_GZERO,   /* Assign zero to a guest register */
    S390_INSN_GADD,    /* Add a value to a guest register */
@@ -193,13 +193,16 @@
    S390_BFP_DIV
 } s390_bfp_binop_t;
 
-
 /* The kind of unary BFP operations */
 typedef enum {
    S390_BFP_ABS,
    S390_BFP_NABS,
    S390_BFP_NEG,
-   S390_BFP_SQRT,
+   S390_BFP_SQRT
+} s390_bfp_unop_t;
+
+/* Type conversion operations: to and/or from floating point */
+typedef enum {
    S390_BFP_I32_TO_F32,
    S390_BFP_I32_TO_F64,
    S390_BFP_I32_TO_F128,
@@ -230,7 +233,7 @@
    S390_BFP_F128_TO_U64,
    S390_BFP_F128_TO_F32,
    S390_BFP_F128_TO_F64
-} s390_bfp_unop_t;
+} s390_conv_t;
 
 
 /* Condition code. The encoding of the enumerators matches the value of
@@ -341,11 +344,6 @@
          s390_opnd_RMI src2;
       } compare;
       struct {
-         HReg          dst;  /* condition code in s390 encoding */
-         HReg          op1;
-         HReg          op2;
-      } bfp_compare;
-      struct {
          s390_opnd_RMI src;
       } test;
       /* Convert the condition code to a boolean value. */
@@ -402,6 +400,17 @@
          HReg            op;   /* operand */
       } bfp_unop;
       struct {
+         s390_conv_t     tag;
+         s390_round_t    rounding_mode;
+         HReg            dst;  /* result */
+         HReg            op;   /* operand */
+      } bfp_convert;
+      struct {
+         HReg            dst;  /* condition code in s390 encoding */
+         HReg            op1;
+         HReg            op2;
+      } bfp_compare;
+      struct {
          s390_bfp_binop_t tag;
          s390_round_t     rounding_mode;
          HReg             dst_hi; /* left operand; high part */
@@ -409,8 +418,6 @@
          HReg             op2_hi; /* right operand; high part */
          HReg             op2_lo; /* right operand; low part */
       } bfp128_binop;
-      /* This variant is also used by the BFP128_CONVERT_TO and
-         BFP128_CONVERT_FROM insns. */
       struct {
          s390_bfp_unop_t  tag;
          s390_round_t     rounding_mode;
@@ -420,6 +427,14 @@
          HReg             op_lo;  /* operand; low part */
       } bfp128_unop;
       struct {
+         s390_conv_t  tag;
+         s390_round_t rounding_mode;
+         HReg         dst_hi; /* 128-bit result high part; 32/64-bit result */
+         HReg         dst_lo; /* 128-bit result low part */
+         HReg         op_hi;  /* 128-bit operand high part; 32/64-bit opnd */
+         HReg         op_lo;  /* 128-bit operand low part */
+      } bfp128_convert;
+      struct {
          HReg             dst;    /* condition code in s390 encoding */
          HReg             op1_hi; /* left operand; high part */
          HReg             op1_lo; /* left operand; low part */
@@ -510,6 +525,8 @@
 s390_insn *s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst,
                               HReg op, s390_round_t);
 s390_insn *s390_insn_bfp_compare(UChar size, HReg dst, HReg op1, HReg op2);
+s390_insn *s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst,
+                                 HReg op, s390_round_t);
 s390_insn *s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t, HReg dst_hi,
                                   HReg dst_lo, HReg op2_hi, HReg op2_lo,
                                   s390_round_t);
@@ -518,9 +535,9 @@
                                  s390_round_t);
 s390_insn *s390_insn_bfp128_compare(UChar size, HReg dst, HReg op1_hi,
                                     HReg op1_lo, HReg op2_hi, HReg op2_lo);
-s390_insn *s390_insn_bfp128_convert_to(UChar size, s390_bfp_unop_t,
+s390_insn *s390_insn_bfp128_convert_to(UChar size, s390_conv_t,
                                        HReg dst_hi, HReg dst_lo, HReg op);
-s390_insn *s390_insn_bfp128_convert_from(UChar size, s390_bfp_unop_t,
+s390_insn *s390_insn_bfp128_convert_from(UChar size, s390_conv_t,
                                          HReg dst, HReg op_hi, HReg op_lo,
                                          s390_round_t);
 s390_insn *s390_insn_mfence(void);
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index 6616aed..42b92c8 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -785,7 +785,7 @@
 {
    IRType ty = typeOfIRExpr(env->type_env, expr);
    UChar size;
-   s390_bfp_unop_t bfpop;
+   s390_conv_t conv;
 
    vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 || ty == Ity_I64);
 
@@ -911,18 +911,18 @@
             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_F32toI32U:  bfpop = S390_BFP_F32_TO_U32;  goto do_convert;
-      case Iop_F32toI64U:  bfpop = S390_BFP_F32_TO_U64;  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_F64toI32U:  bfpop = S390_BFP_F64_TO_U32;  goto do_convert;
-      case Iop_F64toI64U:  bfpop = S390_BFP_F64_TO_U64;  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;
-      case Iop_F128toI32U: bfpop = S390_BFP_F128_TO_U32; goto do_convert_128;
-      case Iop_F128toI64U: bfpop = S390_BFP_F128_TO_U64; goto do_convert_128;
+      case Iop_F32toI32S:  conv = S390_BFP_F32_TO_I32;  goto do_convert;
+      case Iop_F32toI64S:  conv = S390_BFP_F32_TO_I64;  goto do_convert;
+      case Iop_F32toI32U:  conv = S390_BFP_F32_TO_U32;  goto do_convert;
+      case Iop_F32toI64U:  conv = S390_BFP_F32_TO_U64;  goto do_convert;
+      case Iop_F64toI32S:  conv = S390_BFP_F64_TO_I32;  goto do_convert;
+      case Iop_F64toI64S:  conv = S390_BFP_F64_TO_I64;  goto do_convert;
+      case Iop_F64toI32U:  conv = S390_BFP_F64_TO_U32;  goto do_convert;
+      case Iop_F64toI64U:  conv = S390_BFP_F64_TO_U64;  goto do_convert;
+      case Iop_F128toI32S: conv = S390_BFP_F128_TO_I32; goto do_convert_128;
+      case Iop_F128toI64S: conv = S390_BFP_F128_TO_I64; goto do_convert_128;
+      case Iop_F128toI32U: conv = S390_BFP_F128_TO_U32; goto do_convert_128;
+      case Iop_F128toI64U: conv = S390_BFP_F128_TO_U64; goto do_convert_128;
 
       do_convert: {
          s390_round_t rounding_mode;
@@ -931,7 +931,7 @@
          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));
+         addInstr(env, s390_insn_bfp_convert(size, conv, res, h1, rounding_mode));
          return res;
       }
 
@@ -951,7 +951,7 @@
          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,
+         addInstr(env, s390_insn_bfp128_convert_from(size, conv, res, f13, f15,
                                                      rounding_mode));
          return res;
       }
@@ -1665,6 +1665,7 @@
       IRExpr *left = expr->Iex.Unop.arg;
       s390_bfp_unop_t bfpop;
       s390_round_t rounding_mode;
+      s390_conv_t conv;
       HReg op_hi, op_lo, op, f12, f13, f14, f15;
 
       /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
@@ -1674,14 +1675,14 @@
       f15 = make_fpr(15);
 
       switch (expr->Iex.Unop.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_I32UtoF128:    bfpop = S390_BFP_U32_TO_F128;  goto convert_int;
-      case Iop_I64UtoF128:    bfpop = S390_BFP_U64_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;
+      case Iop_NegF128:     bfpop = S390_BFP_NEG;         goto float128_opnd;
+      case Iop_AbsF128:     bfpop = S390_BFP_ABS;         goto float128_opnd;
+      case Iop_I32StoF128:  conv = S390_BFP_I32_TO_F128;  goto convert_int;
+      case Iop_I64StoF128:  conv = S390_BFP_I64_TO_F128;  goto convert_int;
+      case Iop_I32UtoF128:  conv = S390_BFP_U32_TO_F128;  goto convert_int;
+      case Iop_I64UtoF128:  conv = S390_BFP_U64_TO_F128;  goto convert_int;
+      case Iop_F32toF128:   conv = S390_BFP_F32_TO_F128;  goto convert_float;
+      case Iop_F64toF128:   conv = S390_BFP_F64_TO_F128;  goto convert_float;
       default:
          goto irreducible;
       }
@@ -1700,14 +1701,12 @@
 
    convert_float:
       op  = s390_isel_float_expr(env, left);
-      addInstr(env, s390_insn_bfp128_convert_to(16, bfpop, f12, f14,
-                                                op));
+      addInstr(env, s390_insn_bfp128_convert_to(16, conv, 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));
+      addInstr(env, s390_insn_bfp128_convert_to(16, conv, f12, f14, op));
       goto move_dst;
 
    move_dst:
@@ -1874,51 +1873,61 @@
       /* --------- BINARY OP --------- */
    case Iex_Binop: {
       IROp    op   = expr->Iex.Binop.op;
+      IRExpr *irrm = expr->Iex.Binop.arg1;
       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;
+      s390_conv_t  conv;
 
       switch (op) {
       case Iop_SqrtF32:
       case Iop_SqrtF64:
-         bfpop = S390_BFP_SQRT;
-         integer_operand = 0;
-         break;
+         h1  = s390_isel_float_expr(env, left);
+         dst = newVRegF(env);
+         rounding_mode = decode_rounding_mode(irrm);
+         addInstr(env, s390_insn_bfp_unop(size, S390_BFP_SQRT, dst, h1,
+                                          rounding_mode));
+         return dst;
 
-      case Iop_F64toF32:
-         bfpop = S390_BFP_F64_TO_F32;
-         integer_operand = 0;
-         break;
+      case Iop_F64toF32:  conv = S390_BFP_F64_TO_F32; goto convert_float;
+      case Iop_I32StoF32: conv = S390_BFP_I32_TO_F32; goto convert_int;
+      case Iop_I32UtoF32: conv = S390_BFP_U32_TO_F32; goto convert_int;
+      case Iop_I64StoF32: conv = S390_BFP_I64_TO_F32; goto convert_int;
+      case Iop_I64StoF64: conv = S390_BFP_I64_TO_F64; goto convert_int;
+      case Iop_I64UtoF32: conv = S390_BFP_U64_TO_F32; goto convert_int;
+      case Iop_I64UtoF64: conv = S390_BFP_U64_TO_F64; goto convert_int;
 
-      case Iop_I32StoF32: bfpop = S390_BFP_I32_TO_F32; break;
-      case Iop_I32UtoF32: bfpop = S390_BFP_U32_TO_F32; break;
-      case Iop_I64StoF32: bfpop = S390_BFP_I64_TO_F32; break;
-      case Iop_I64StoF64: bfpop = S390_BFP_I64_TO_F64; break;
-      case Iop_I64UtoF32: bfpop = S390_BFP_U64_TO_F32; break;
-      case Iop_I64UtoF64: bfpop = S390_BFP_U64_TO_F64; break;
+      convert_float:
+         h1 = s390_isel_float_expr(env, left);
+         goto convert;
 
+      convert_int:
+         h1 = s390_isel_int_expr(env, left);
+         goto convert;
+
+      convert:
+         dst = newVRegF(env);
+         rounding_mode = decode_rounding_mode(irrm);
+         addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1,
+                                             rounding_mode));
+         return dst;
+         
       default:
          goto irreducible;
 
       case Iop_F128toF64:
       case Iop_F128toF32: {
-         HReg op_hi, op_lo, f12, f13, f14, f15;
+         HReg op_hi, op_lo, f13, f15;
 
-         bfpop = op == Iop_F128toF32 ? S390_BFP_F128_TO_F32
-            : S390_BFP_F128_TO_F64;
+         conv = op == Iop_F128toF32 ? S390_BFP_F128_TO_F32
+                                    : S390_BFP_F128_TO_F64;
 
-         rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
+         rounding_mode = decode_rounding_mode(irrm);
 
-         s390_isel_float128_expr(&op_hi, &op_lo, env, expr->Iex.Binop.arg2);
+         s390_isel_float128_expr(&op_hi, &op_lo, env, left);
 
-         /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
-         f12 = make_fpr(12);
+         /* We use non-virtual registers as pairs (f13, f15) */
          f13 = make_fpr(13);
-         f14 = make_fpr(14);
          f15 = make_fpr(15);
 
          /* operand --> (f13, f15) */
@@ -1926,26 +1935,11 @@
          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));
+         addInstr(env, s390_insn_bfp128_convert_from(16, conv, dst, f13, f15,
+                                                     rounding_mode));
          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 --------- */
@@ -1953,7 +1947,10 @@
       IROp    op   = expr->Iex.Unop.op;
       IRExpr *left = expr->Iex.Unop.arg;
       s390_bfp_unop_t bfpop;
-      s390_round_t rounding_mode;
+      /* No rounding mode is needed for these conversions. Provide the
+         default rounding mode. It will not be used. */
+      s390_round_t rounding_mode = S390_ROUND_NEAREST_EVEN;
+      s390_conv_t conv;
       HReg h1, dst;
 
       if (op == Iop_F128HItoF64 || op == Iop_F128LOtoF64) {
@@ -1982,24 +1979,34 @@
          break;
 
       case Iop_AbsF32:
-      case Iop_AbsF64:        bfpop = S390_BFP_ABS;  break;
-      case Iop_I32StoF64:     bfpop = S390_BFP_I32_TO_F64;  break;
-      case Iop_I32UtoF64:     bfpop = S390_BFP_U32_TO_F64;  break;
-      case Iop_F32toF64:      bfpop = S390_BFP_F32_TO_F64;  break;
+      case Iop_AbsF64:
+         bfpop = S390_BFP_ABS;
+         break;
+
+      case Iop_I32StoF64:  conv = S390_BFP_I32_TO_F64;  goto convert_int1;
+      case Iop_I32UtoF64:  conv = S390_BFP_U32_TO_F64;  goto convert_int1;
+      case Iop_F32toF64:   conv = S390_BFP_F32_TO_F64;  goto convert_float1;
+
+      convert_float1:
+         h1 = s390_isel_float_expr(env, left);
+         goto convert1;
+
+      convert_int1:
+         h1 = s390_isel_int_expr(env, left);
+         goto convert1;
+
+      convert1:
+         dst = newVRegF(env);
+         addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1, rounding_mode));
+         return dst;
+
       default:
          goto irreducible;
       }
 
       /* Process operand */
-      if (op == Iop_I32StoF64 || op == Iop_I32UtoF64)
-         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);
-
+      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;
    }