]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add support for these DFP insns:
authorFlorian Krohm <florian@eich-krohm.de>
Fri, 21 Dec 2012 17:32:12 +0000 (17:32 +0000)
committerFlorian Krohm <florian@eich-krohm.de>
Fri, 21 Dec 2012 17:32:12 +0000 (17:32 +0000)
AXTRA, CDTR, CXTR, DXTRA, LDETR, LXDTR, LDXTR, LEDTR, LTXTR, MXTRA, SXTRA
This is part of fixing BZ #307113.
Patch by Maran Pakkirisamy (maranp@linux.vnet.ibm.com) with some minor
mods.

git-svn-id: svn://svn.valgrind.org/vex/trunk@2605

VEX/priv/guest_s390_defs.h
VEX/priv/guest_s390_helpers.c
VEX/priv/guest_s390_toIR.c
VEX/priv/host_s390_defs.c
VEX/priv/host_s390_defs.h
VEX/priv/host_s390_isel.c

index e73190772ee3f86ffa701f9da837943b517d9c2f..51eaaeea617fc360ddca538a2062e99b7da92ee9 100644 (file)
@@ -137,7 +137,8 @@ enum {
    S390_CC_OP_BFP_32_TO_UINT_64 = 39,
    S390_CC_OP_BFP_64_TO_UINT_64 = 40,
    S390_CC_OP_BFP_128_TO_UINT_64 = 41,
-   S390_CC_OP_DFP_RESULT_64 = 42
+   S390_CC_OP_DFP_RESULT_64 = 42,
+   S390_CC_OP_DFP_RESULT_128 = 43
 };
 
 /*------------------------------------------------------------*/
@@ -196,6 +197,7 @@ enum {
    | S390_CC_OP_BFP_64_TO_UINT_64   | F source              | Z rounding mode      |                 |
    | S390_CC_OP_BFP_128_TO_UINT_64  | F source hi 64 bits   | F source low 64 bits | Z rounding mode |
    | S390_CC_OP_DFP_RESULT_64       | D result              |                      |                 |
+   | S390_CC_OP_DFP_RESULT_128      | D result hi 64 bits   | D result low 64 bits |                 |
    +--------------------------------+-----------------------+----------------------+-----------------+
 */
 
index 891ee88c1ef8b0491730a9f05a1f892559d32b6a..930636c773358aad96c463a3402044fd5fd66a0d 100644 (file)
@@ -1165,6 +1165,18 @@ decode_bfp_rounding_mode(UInt irrm)
    psw >> 28;   /* cc */ \
 })
 
+#define S390_CC_FOR_DFP128_RESULT(hi,lo) \
+({ \
+   __asm__ volatile ( \
+                     "ldr   4,%[high]\n\t"                 \
+                     "ldr   6,%[low]\n\t"                  \
+                     ".insn rre, 0xb3de0000,0,4\n\t"    /* LTXTR */     \
+                     "ipm %[psw]\n\t"           : [psw] "=d"(psw)       \
+                     : [high] "f"(hi), [low] "f"(lo)                    \
+                     : "cc", "f0", "f2", "f4", "f6");                   \
+   psw >> 28;   /* cc */                                                \
+})
+
 
 /* Return the value of the condition code from the supplied thunk parameters.
    This is not the value of the PSW. It is the value of the 2 CC bits within
@@ -1384,6 +1396,9 @@ s390_calculate_cc(ULong cc_op, ULong cc_dep1, ULong cc_dep2, ULong cc_ndep)
    case S390_CC_OP_DFP_RESULT_64:
       return S390_CC_FOR_DFP_RESULT(cc_dep1);
 
+   case S390_CC_OP_DFP_RESULT_128:
+      return S390_CC_FOR_DFP128_RESULT(cc_dep1, cc_dep2);
+
    default:
       break;
    }
index 7663920a35a43965aa4fc4180e57087ace3e4001..e77efc2aea334a3bd3e7a47c2852dc9cb65f9d7b 100644 (file)
@@ -50,6 +50,7 @@
 static UInt s390_decode_and_irgen(UChar *, UInt, DisResult *);
 static void s390_irgen_xonc(IROp, IRTemp, IRTemp, IRTemp);
 static void s390_irgen_CLC_EX(IRTemp, IRTemp, IRTemp);
+static IRExpr *convert_vex_fpcc_to_s390(IRTemp);
 
 
 /*------------------------------------------------------------*/
@@ -455,6 +456,29 @@ put_fpr_pair(UInt archreg, IRExpr *expr)
    put_fpr_dw0(archreg + 2, low);
 }
 
+/* Read a floating point register pair cointaining DFP value
+   and combine their contents into a 128-bit value */
+
+static IRExpr *
+get_dpr_pair(UInt archreg)
+{
+   IRExpr *high = get_dpr_dw0(archreg);
+   IRExpr *low  = get_dpr_dw0(archreg + 2);
+
+   return binop(Iop_D64HLtoD128, high, low);
+}
+
+/* Write a 128-bit decimal floating point value into a register pair. */
+static void
+put_dpr_pair(UInt archreg, IRExpr *expr)
+{
+   IRExpr *high = unop(Iop_D128HItoD64, expr);
+   IRExpr *low  = unop(Iop_D128LOtoD64, expr);
+
+   put_dpr_dw0(archreg,     high);
+   put_dpr_dw0(archreg + 2, low);
+}
+
 /* Terminate the current IRSB with an emulation failure. */
 static void
 emulation_failure(VexEmNote fail_kind)
@@ -667,6 +691,22 @@ s390_cc_thunk_put1f128Z(UInt opc, IRTemp d1, IRTemp nd)
 }
 
 
+/* Write a 128-bit decimal floating point value into the flags thunk.
+   This is done by splitting the value into two 64-bits values. */
+static void
+s390_cc_thunk_put1d128(UInt opc, IRTemp d1)
+{
+   IRExpr *op, *hi, *lo, *ndep;
+
+   op   = mkU64(opc);
+   hi   = unop(Iop_D128HItoD64, mkexpr(d1));
+   lo   = unop(Iop_D128LOtoD64, mkexpr(d1));
+   ndep = mkU64(0);
+
+   s390_cc_thunk_fill(op, hi, lo, ndep);
+}
+
+
 static void
 s390_cc_set(UInt val)
 {
@@ -912,6 +952,22 @@ get_fpr_dw0(UInt archreg)
    return IRExpr_Get(fpr_dw0_offset(archreg), Ity_F64);
 }
 
+/* Write word #0 of a dpr to the guest state. */
+static __inline__ void
+put_dpr_w0(UInt archreg, IRExpr *expr)
+{
+   vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_D32);
+
+   stmt(IRStmt_Put(fpr_w0_offset(archreg), expr));
+}
+
+/* Read word #0 of a dpr register. */
+static __inline__ IRExpr *
+get_dpr_w0(UInt archreg)
+{
+   return IRExpr_Get(fpr_w0_offset(archreg), Ity_D32);
+}
+
 /* Write double word #0 of a fpr containg DFP value to the guest state. */
 static __inline__ void
 put_dpr_dw0(UInt archreg, IRExpr *expr)
@@ -1888,6 +1944,16 @@ s390_format_RRF_UUFF(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1,
       s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), mnm, r1, m3, r2, m4);
 }
 
+static void
+s390_format_RRF_0UFF(const HChar *(*irgen)(UChar m4, UChar r1, UChar r2),
+                     UChar m4, UChar r1, UChar r2)
+{
+   const HChar *mnm = irgen(m4, r1, r2);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
+      s390_disasm(ENC4(MNM, FPR, FPR, UINT), mnm, r1, r2, m4);
+}
+
 static void
 s390_format_RRF_UUFR(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1,
                                            UChar r2),
@@ -8928,18 +8994,6 @@ s390_irgen_LEDBR(UChar m3, UChar m4 __attribute__((unused)),
    return "ledbr";
 }
 
-static const HChar *
-s390_irgen_LTDTR(UChar r1, UChar r2)
-{
-   IRTemp result = newTemp(Ity_D64);
-
-   assign(result, get_dpr_dw0(r2));
-   put_dpr_dw0(r1, mkexpr(result));
-   s390_cc_thunk_putF(S390_CC_OP_DFP_RESULT_64, result);
-
-   return "ltdtr";
-}
-
 static const HChar *
 s390_irgen_MEEBR(UChar r1, UChar r2)
 {
@@ -9104,6 +9158,66 @@ s390_irgen_ADTRA(UChar r3, UChar m4, UChar r1, UChar r2)
    return (m4 == 0) ? "adtr" : "adtra";
 }
 
+static const HChar *
+s390_irgen_AXTRA(UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D128);
+   IRTemp op2 = newTemp(Ity_D128);
+   IRTemp result = newTemp(Ity_D128);
+   IRTemp rounding_mode;
+
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set.
+      since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */
+   rounding_mode = encode_dfp_rounding_mode(m4);
+   assign(op1, get_dpr_pair(r2));
+   assign(op2, get_dpr_pair(r3));
+   assign(result, triop(Iop_AddD128, mkexpr(rounding_mode), mkexpr(op1),
+                        mkexpr(op2)));
+   put_dpr_pair(r1, mkexpr(result));
+
+   s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result);
+
+   return (m4 == 0) ? "axtr" : "axtra";
+}
+
+static const HChar *
+s390_irgen_CDTR(UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D64);
+   IRTemp op2 = newTemp(Ity_D64);
+   IRTemp cc_vex  = newTemp(Ity_I32);
+   IRTemp cc_s390 = newTemp(Ity_I32);
+
+   assign(op1, get_dpr_dw0(r1));
+   assign(op2, get_dpr_dw0(r2));
+   assign(cc_vex, binop(Iop_CmpD64, mkexpr(op1), mkexpr(op2)));
+
+   assign(cc_s390, convert_vex_fpcc_to_s390(cc_vex));
+   s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
+
+   return "cdtr";
+}
+
+static const HChar *
+s390_irgen_CXTR(UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D128);
+   IRTemp op2 = newTemp(Ity_D128);
+   IRTemp cc_vex  = newTemp(Ity_I32);
+   IRTemp cc_s390 = newTemp(Ity_I32);
+
+   assign(op1, get_dpr_pair(r1));
+   assign(op2, get_dpr_pair(r2));
+   assign(cc_vex, binop(Iop_CmpD128, mkexpr(op1), mkexpr(op2)));
+
+   assign(cc_s390, convert_vex_fpcc_to_s390(cc_vex));
+   s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
+
+   return "cxtr";
+}
+
 static const HChar *
 s390_irgen_DDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -9126,6 +9240,112 @@ s390_irgen_DDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
    return (m4 == 0) ? "ddtr" : "ddtra";
 }
 
+static const HChar *
+s390_irgen_DXTRA(UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D128);
+   IRTemp op2 = newTemp(Ity_D128);
+   IRTemp result = newTemp(Ity_D128);
+   IRTemp rounding_mode;
+
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set.
+      since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */
+   rounding_mode = encode_dfp_rounding_mode(m4);
+   assign(op1, get_dpr_pair(r2));
+   assign(op2, get_dpr_pair(r3));
+   assign(result, triop(Iop_DivD128, mkexpr(rounding_mode), mkexpr(op1),
+                        mkexpr(op2)));
+   put_dpr_pair(r1, mkexpr(result));
+
+   return (m4 == 0) ? "dxtr" : "dxtra";
+}
+
+static const HChar *
+s390_irgen_LDETR(UChar m4 __attribute__((unused)), UChar r1, UChar r2)
+{
+   IRTemp op = newTemp(Ity_D32);
+
+   vassert(s390_host_has_dfp);
+
+   assign(op, get_dpr_w0(r2));
+   put_dpr_dw0(r1, unop(Iop_D32toD64, mkexpr(op)));
+
+   return "ldetr";
+}
+
+static const HChar *
+s390_irgen_LXDTR(UChar m4 __attribute__((unused)), UChar r1, UChar r2)
+{
+   IRTemp op = newTemp(Ity_D64);
+
+   assign(op, get_dpr_dw0(r2));
+   put_dpr_pair(r1, unop(Iop_D64toD128, mkexpr(op)));
+
+   return "lxdtr";
+}
+
+static const HChar *
+s390_irgen_LDXTR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (! s390_host_has_fpext && m3 != S390_DFP_ROUND_PER_FPC_0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = S390_DFP_ROUND_PER_FPC_0;
+   }
+   IRTemp result = newTemp(Ity_D64);
+
+   assign(result, binop(Iop_D128toD64, mkexpr(encode_dfp_rounding_mode(m3)),
+                        get_dpr_pair(r2)));
+   put_dpr_dw0(r1, mkexpr(result));
+
+   return "ldxtr";
+}
+
+static const HChar *
+s390_irgen_LEDTR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (! s390_host_has_fpext && m3 != S390_DFP_ROUND_PER_FPC_0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = S390_DFP_ROUND_PER_FPC_0;
+   }
+   IRTemp op = newTemp(Ity_D64);
+
+   assign(op, get_dpr_dw0(r2));
+   put_dpr_w0(r1, binop(Iop_D64toD32, mkexpr(encode_dfp_rounding_mode(m3)),
+                        mkexpr(op)));
+
+   return "ledtr";
+}
+
+static const HChar *
+s390_irgen_LTDTR(UChar r1, UChar r2)
+{
+   IRTemp result = newTemp(Ity_D64);
+
+   assign(result, get_dpr_dw0(r2));
+   put_dpr_dw0(r1, mkexpr(result));
+   s390_cc_thunk_putF(S390_CC_OP_DFP_RESULT_64, result);
+
+   return "ltdtr";
+}
+
+static const HChar *
+s390_irgen_LTXTR(UChar r1, UChar r2)
+{
+   IRTemp result = newTemp(Ity_D128);
+
+   assign(result, get_dpr_pair(r2));
+   put_dpr_pair(r1, mkexpr(result));
+   s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result);
+
+   return "ltxtr";
+}
+
 static const HChar *
 s390_irgen_MDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -9148,6 +9368,28 @@ s390_irgen_MDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
    return (m4 == 0) ? "mdtr" : "mdtra";
 }
 
+static const HChar *
+s390_irgen_MXTRA(UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D128);
+   IRTemp op2 = newTemp(Ity_D128);
+   IRTemp result = newTemp(Ity_D128);
+   IRTemp rounding_mode;
+
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set.
+      since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */
+   rounding_mode = encode_dfp_rounding_mode(m4);
+   assign(op1, get_dpr_pair(r2));
+   assign(op2, get_dpr_pair(r3));
+   assign(result, triop(Iop_MulD128, mkexpr(rounding_mode), mkexpr(op1),
+                        mkexpr(op2)));
+   put_dpr_pair(r1, mkexpr(result));
+
+   return (m4 == 0) ? "mxtr" : "mxtra";
+}
+
 static const HChar *
 s390_irgen_SDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -9171,6 +9413,29 @@ s390_irgen_SDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
    return (m4 == 0) ? "sdtr" : "sdtra";
 }
 
+static const HChar *
+s390_irgen_SXTRA(UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   IRTemp op1 = newTemp(Ity_D128);
+   IRTemp op2 = newTemp(Ity_D128);
+   IRTemp result = newTemp(Ity_D128);
+   IRTemp rounding_mode;
+
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set.
+      since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */
+   rounding_mode = encode_dfp_rounding_mode(m4);
+   assign(op1, get_dpr_pair(r2));
+   assign(op2, get_dpr_pair(r3));
+   assign(result, triop(Iop_SubD128, mkexpr(rounding_mode), mkexpr(op1),
+                        mkexpr(op2)));
+   put_dpr_pair(r1, mkexpr(result));
+
+   s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result);
+
+   return (m4 == 0) ? "sxtr" : "sxtra";
+}
 
 static const HChar *
 s390_irgen_CLC(UChar length, IRTemp start1, IRTemp start2)
@@ -12720,6 +12985,13 @@ s390_decode_4byte_and_irgen(UChar *bytes)
          unsigned int r1 :  4;
          unsigned int r2 :  4;
       } RRF4;
+      struct {
+         unsigned int op : 16;
+         unsigned int    :  4;
+         unsigned int m4 :  4;
+         unsigned int r1 :  4;
+         unsigned int r2 :  4;
+      } RRF5;
       struct {
          unsigned int op :  8;
          unsigned int r1 :  4;
@@ -13196,31 +13468,48 @@ s390_decode_4byte_and_irgen(UChar *bytes)
    case 0xb3d3: s390_format_RRF_FUFF2(s390_irgen_SDTRA, ovl.fmt.RRF4.r3,
                                       ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1,
                                       ovl.fmt.RRF4.r2); goto ok;
-   case 0xb3d4: /* LDETR */ goto unimplemented;
-   case 0xb3d5: /* LEDTR */ goto unimplemented;
+   case 0xb3d4: s390_format_RRF_0UFF(s390_irgen_LDETR, ovl.fmt.RRF5.m4,
+                                     ovl.fmt.RRF5.r1, ovl.fmt.RRF5.r2); goto ok;
+   case 0xb3d5: s390_format_RRF_UUFF(s390_irgen_LEDTR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
    case 0xb3d6: s390_format_RRE_FF(s390_irgen_LTDTR, ovl.fmt.RRE.r1,
                                    ovl.fmt.RRE.r2);  goto ok;
    case 0xb3d7: /* FIDTR */ goto unimplemented;
-   case 0xb3d8: /* MXTR */ goto unimplemented;
-   case 0xb3d9: /* DXTR */ goto unimplemented;
-   case 0xb3da: /* AXTR */ goto unimplemented;
-   case 0xb3db: /* SXTR */ goto unimplemented;
-   case 0xb3dc: /* LXDTR */ goto unimplemented;
-   case 0xb3dd: /* LDXTR */ goto unimplemented;
-   case 0xb3de: /* LTXTR */ goto unimplemented;
+   case 0xb3d8: s390_format_RRF_FUFF2(s390_irgen_MXTRA, ovl.fmt.RRF4.r3,
+                                     ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1,
+                                     ovl.fmt.RRF4.r2); goto ok;
+   case 0xb3d9: s390_format_RRF_FUFF2(s390_irgen_DXTRA, ovl.fmt.RRF4.r3,
+                                     ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1,
+                                     ovl.fmt.RRF4.r2); goto ok;
+   case 0xb3da: s390_format_RRF_FUFF2(s390_irgen_AXTRA, ovl.fmt.RRF4.r3,
+                                     ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1,
+                                     ovl.fmt.RRF4.r2); goto ok;
+   case 0xb3db: s390_format_RRF_FUFF2(s390_irgen_SXTRA, ovl.fmt.RRF4.r3,
+                                     ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1,
+                                     ovl.fmt.RRF4.r2); goto ok;
+   case 0xb3dc: s390_format_RRF_0UFF(s390_irgen_LXDTR, ovl.fmt.RRF5.m4,
+                                     ovl.fmt.RRF5.r1, ovl.fmt.RRF5.r2); goto ok;
+   case 0xb3dd: s390_format_RRF_UUFF(s390_irgen_LDXTR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3de: s390_format_RRE_FF(s390_irgen_LTXTR, ovl.fmt.RRE.r1,
+                                   ovl.fmt.RRE.r2);  goto ok;
    case 0xb3df: /* FIXTR */ goto unimplemented;
    case 0xb3e0: /* KDTR */ goto unimplemented;
    case 0xb3e1: /* CGDTR */ goto unimplemented;
    case 0xb3e2: /* CUDTR */ goto unimplemented;
    case 0xb3e3: /* CSDTR */ goto unimplemented;
-   case 0xb3e4: /* CDTR */ goto unimplemented;
+   case 0xb3e4: s390_format_RRE_FF(s390_irgen_CDTR, ovl.fmt.RRE.r1,
+                                   ovl.fmt.RRE.r2);  goto ok;
    case 0xb3e5: /* EEDTR */ goto unimplemented;
    case 0xb3e7: /* ESDTR */ goto unimplemented;
    case 0xb3e8: /* KXTR */ goto unimplemented;
    case 0xb3e9: /* CGXTR */ goto unimplemented;
    case 0xb3ea: /* CUXTR */ goto unimplemented;
    case 0xb3eb: /* CSXTR */ goto unimplemented;
-   case 0xb3ec: /* CXTR */ goto unimplemented;
+   case 0xb3ec: s390_format_RRE_FF(s390_irgen_CXTR, ovl.fmt.RRE.r1,
+                                   ovl.fmt.RRE.r2);  goto ok;
    case 0xb3ed: /* EEXTR */ goto unimplemented;
    case 0xb3ef: /* ESXTR */ goto unimplemented;
    case 0xb3f1: /* CDGTR */ goto unimplemented;
index e5f6a842a2a86e171da5e5845f0551c582dfd856..8057ba4ffce6863d02e756e766aa9b1131ee18ea 100644 (file)
@@ -714,6 +714,25 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn)
       }
       break;
 
+   case S390_INSN_DFP_COMPARE:
+      addHRegUse(u, HRmWrite, insn->variant.dfp_compare.dst);
+      addHRegUse(u, HRmRead,  insn->variant.dfp_compare.op1_hi);  /* left */
+      addHRegUse(u, HRmRead,  insn->variant.dfp_compare.op2_hi);  /* right */
+      if (insn->size == 16) {
+         addHRegUse(u, HRmRead,  insn->variant.dfp_compare.op1_lo);  /* left */
+         addHRegUse(u, HRmRead,  insn->variant.dfp_compare.op2_lo);  /* right */
+      }
+      break;
+
+   case S390_INSN_DFP_CONVERT:
+      addHRegUse(u, HRmWrite, insn->variant.dfp_convert.dst_hi);
+      if (insn->variant.dfp_convert.dst_lo != INVALID_HREG)
+         addHRegUse(u, HRmWrite, insn->variant.dfp_convert.dst_lo);
+      addHRegUse(u, HRmRead,  insn->variant.dfp_convert.op_hi);  /* operand */
+      if (insn->variant.dfp_convert.op_lo != INVALID_HREG)
+         addHRegUse(u, HRmRead, insn->variant.dfp_convert.op_lo); /* operand */
+      break;
+
    case S390_INSN_MZERO:
       s390_amode_get_reg_usage(u, insn->variant.mzero.dst);
       break;
@@ -966,6 +985,34 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn)
       }
       break;
 
+   case S390_INSN_DFP_COMPARE:
+      insn->variant.dfp_compare.dst =
+         lookupHRegRemap(m, insn->variant.dfp_compare.dst);
+      insn->variant.dfp_compare.op1_hi =
+         lookupHRegRemap(m, insn->variant.dfp_compare.op1_hi);
+      insn->variant.dfp_compare.op2_hi =
+         lookupHRegRemap(m, insn->variant.dfp_compare.op2_hi);
+      if (insn->size == 16) {
+         insn->variant.dfp_compare.op1_lo =
+            lookupHRegRemap(m, insn->variant.dfp_compare.op1_lo);
+         insn->variant.dfp_compare.op2_lo =
+            lookupHRegRemap(m, insn->variant.dfp_compare.op2_lo);
+      }
+      break;
+
+   case S390_INSN_DFP_CONVERT:
+      insn->variant.dfp_convert.dst_hi =
+         lookupHRegRemap(m, insn->variant.dfp_convert.dst_hi);
+      if (insn->variant.dfp_convert.dst_lo != INVALID_HREG)
+         insn->variant.dfp_convert.dst_lo =
+            lookupHRegRemap(m, insn->variant.dfp_convert.dst_lo);
+      insn->variant.dfp_convert.op_hi =
+         lookupHRegRemap(m, insn->variant.dfp_convert.op_hi);
+      if (insn->variant.dfp_convert.op_lo != INVALID_HREG)
+         insn->variant.dfp_convert.op_lo =
+            lookupHRegRemap(m, insn->variant.dfp_convert.op_lo);
+      break;
+
    case S390_INSN_MZERO:
       s390_amode_map_regs(m, insn->variant.mzero.dst);
       break;
@@ -1163,6 +1210,19 @@ emit_RRF4(UChar *p, UInt op, UChar r3, UChar m4, UChar r1, UChar r2)
 }
 
 
+static UChar *
+emit_RRF5(UChar *p, UInt op, UChar m4, UChar r1, UChar r2)
+{
+   ULong the_insn = op;
+
+   the_insn |= ((ULong)m4) << 8;
+   the_insn |= ((ULong)r1) << 4;
+   the_insn |= ((ULong)r2) << 0;
+
+   return emit_4bytes(p, the_insn);
+}
+
+
 static UChar *
 emit_RS(UChar *p, UInt op, UChar r1, UChar r3, UChar b2, UShort d2)
 {
@@ -3894,6 +3954,44 @@ s390_emit_ADTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 }
 
 
+static UChar *
+s390_emit_AXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      if (m4 == 0)
+         s390_disasm(ENC4(MNM, FPR, FPR, FPR), "axtr", r1, r2, r3);
+      else
+         s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "axtra", r1, r2, r3, m4);
+   }
+
+   return emit_RRF4(p, 0xb3da0000, r3, m4, r1, r2);
+}
+
+
+static UChar *
+s390_emit_CDTR(UChar *p, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC3(MNM, FPR, FPR), "cdtr", r1, r2);
+
+   return emit_RRE(p, 0xb3e40000, r1, r2);
+}
+
+
+static UChar *
+s390_emit_CXTR(UChar *p, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC3(MNM, FPR, FPR), "cxtr", r1, r2);
+
+   return emit_RRE(p, 0xb3ec0000, r1, r2);
+}
+
+
 static UChar *
 s390_emit_DDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -3910,6 +4008,76 @@ s390_emit_DDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 }
 
 
+static UChar *
+s390_emit_DXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      if (m4 == 0)
+         s390_disasm(ENC4(MNM, FPR, FPR, FPR), "dxtr", r1, r2, r3);
+      else
+         s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "dxtra", r1, r2, r3, m4);
+   }
+
+   return emit_RRF4(p, 0xb3d90000, r3, m4, r1, r2);
+}
+
+
+static UChar *
+s390_emit_LDETR(UChar *p, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC4(MNM, FPR, FPR, UINT), "ldetr", r1, r2, m4);
+
+   return emit_RRF5(p, 0xb3d40000, m4, r1, r2);
+}
+
+
+static UChar *
+s390_emit_LXDTR(UChar *p, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC4(MNM, FPR, FPR, UINT), "lxdtr", r1, r2, m4);
+
+   return emit_RRF5(p, 0xb3dc0000, m4, r1, r2);
+}
+
+
+static UChar *
+s390_emit_LEDTR(UChar *p, UChar m3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0);
+   vassert(m3 == 0 || s390_host_has_fpext);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT),
+                  "ledtr", r1, m3, r2, m4);
+   }
+
+   return emit_RRF2(p, 0xb3d50000, m3, m4, r1, r2);
+}
+
+
+static UChar *
+s390_emit_LDXTR(UChar *p, UChar m3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0);
+   vassert(m3 == 0 || s390_host_has_fpext);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT),
+                  "ldxtr", r1, m3, r2, m4);
+   }
+
+   return emit_RRF2(p, 0xb3dd0000, m3, m4, r1, r2);
+}
+
+
 static UChar *
 s390_emit_MDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -3926,6 +4094,22 @@ s390_emit_MDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 }
 
 
+static UChar *
+s390_emit_MXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      if (m4 == 0)
+         s390_disasm(ENC4(MNM, FPR, FPR, FPR), "mxtr", r1, r2, r3);
+      else
+         s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "mxtra", r1, r2, r3, m4);
+   }
+
+   return emit_RRF4(p, 0xb3d80000, r3, m4, r1, r2);
+}
+
+
 static UChar *
 s390_emit_SDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 {
@@ -3942,6 +4126,22 @@ s390_emit_SDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 }
 
 
+static UChar *
+s390_emit_SXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
+{
+   vassert(s390_host_has_dfp);
+   vassert(m4 == 0 || s390_host_has_fpext);
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) {
+      if (m4 == 0)
+         s390_disasm(ENC4(MNM, FPR, FPR, FPR), "sxtr", r1, r2, r3);
+      else
+         s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "sxtra", r1, r2, r3, m4);
+   }
+
+   return emit_RRF4(p, 0xb3db0000, r3, m4, r1, r2);
+}
+
+
 static UChar *
 s390_emit_LOCGR(UChar *p, UChar m3, UChar r1, UChar r2)
 {
@@ -4914,11 +5114,11 @@ s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst, HReg op,
 }
 
 
-/* Check validity of a register pair for 128-bit BFP. Valid register
+/* Check validity of a register pair for 128-bit FP. Valid register
    pairs are (0,2), (1,3), (4, 6), (5, 7), (8, 10), (9, 11), (12, 14),
    and (13, 15). */
 static Bool
-is_valid_bfp128_regpair(HReg hi, HReg lo)
+is_valid_fp128_regpair(HReg hi, HReg lo)
 {
    UInt hi_regno = hregNumber(hi);
    UInt lo_regno = hregNumber(lo);
@@ -4936,8 +5136,8 @@ s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
    vassert(size == 16);
-   vassert(is_valid_bfp128_regpair(dst_hi, dst_lo));
-   vassert(is_valid_bfp128_regpair(op2_hi, op2_lo));
+   vassert(is_valid_fp128_regpair(dst_hi, dst_lo));
+   vassert(is_valid_fp128_regpair(op2_hi, op2_lo));
 
    insn->tag  = S390_INSN_BFP_BINOP;
    insn->size = size;
@@ -4958,8 +5158,8 @@ s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t tag, HReg dst_hi,
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
    vassert(size == 16);
-   vassert(is_valid_bfp128_regpair(dst_hi, dst_lo));
-   vassert(is_valid_bfp128_regpair(op_hi, op_lo));
+   vassert(is_valid_fp128_regpair(dst_hi, dst_lo));
+   vassert(is_valid_fp128_regpair(op_hi, op_lo));
 
    insn->tag  = S390_INSN_BFP_UNOP;
    insn->size = size;
@@ -4980,8 +5180,8 @@ s390_insn_bfp128_compare(UChar size, HReg dst, HReg op1_hi, HReg op1_lo,
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
    vassert(size == 16);
-   vassert(is_valid_bfp128_regpair(op1_hi, op1_lo));
-   vassert(is_valid_bfp128_regpair(op2_hi, op2_lo));
+   vassert(is_valid_fp128_regpair(op1_hi, op1_lo));
+   vassert(is_valid_fp128_regpair(op2_hi, op2_lo));
 
    insn->tag  = S390_INSN_BFP_COMPARE;
    insn->size = size;
@@ -5004,11 +5204,11 @@ s390_insn_bfp128_convert(UChar size, s390_conv_t tag, HReg dst_hi,
 
    if (size == 16) {
       /* From smaller size to 16 bytes */
-      vassert(is_valid_bfp128_regpair(dst_hi, dst_lo));
+      vassert(is_valid_fp128_regpair(dst_hi, dst_lo));
       vassert(op_lo == INVALID_HREG);
    } else {
       /* From 16 bytes to smaller size */
-      vassert(is_valid_bfp128_regpair(op_hi, op_lo));
+      vassert(is_valid_fp128_regpair(op_hi, op_lo));
       vassert(dst_lo == INVALID_HREG);
    }
 
@@ -5071,6 +5271,149 @@ s390_insn_dfp_binop(UChar size, s390_dfp_binop_t tag, HReg dst, HReg op2,
 }
 
 
+s390_insn *
+s390_insn_dfp_compare(UChar size, HReg dst, HReg op1, HReg op2)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(size == 8);
+
+   insn->tag  = S390_INSN_DFP_COMPARE;
+   insn->size = size;
+   insn->variant.dfp_compare.dst = dst;
+   insn->variant.dfp_compare.op1_hi = op1;
+   insn->variant.dfp_compare.op2_hi = op2;
+   insn->variant.dfp_compare.op1_lo = INVALID_HREG;
+   insn->variant.dfp_compare.op2_lo = INVALID_HREG;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_dfp_convert(UChar size, s390_dfp_conv_t tag, HReg dst, HReg op,
+                      s390_dfp_round_t rounding_mode)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(size == 8);
+
+   insn->tag  = S390_INSN_DFP_CONVERT;
+   insn->size = size;
+   insn->variant.dfp_convert.tag = tag;
+   insn->variant.dfp_convert.dst_hi = dst;
+   insn->variant.dfp_convert.op_hi  = op;
+   insn->variant.dfp_convert.dst_lo = INVALID_HREG;
+   insn->variant.dfp_convert.op_lo  = INVALID_HREG;
+   insn->variant.dfp_convert.rounding_mode = rounding_mode;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_dfp128_binop(UChar size, s390_dfp_binop_t tag, HReg dst_hi,
+                       HReg dst_lo, HReg op2_hi, HReg op2_lo, HReg op3_hi,
+                       HReg op3_lo, s390_dfp_round_t rounding_mode)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(size == 16);
+   vassert(is_valid_fp128_regpair(dst_hi, dst_lo));
+   vassert(is_valid_fp128_regpair(op2_hi, op2_lo));
+   vassert(is_valid_fp128_regpair(op3_hi, op3_lo));
+
+
+   insn->tag  = S390_INSN_DFP_BINOP;
+   insn->size = size;
+   insn->variant.dfp_binop.tag = tag;
+   insn->variant.dfp_binop.dst_hi = dst_hi;
+   insn->variant.dfp_binop.dst_lo = dst_lo;
+   insn->variant.dfp_binop.op2_hi = op2_hi;
+   insn->variant.dfp_binop.op2_lo = op2_lo;
+   insn->variant.dfp_binop.op3_hi = op3_hi;
+   insn->variant.dfp_binop.op3_lo = op3_lo;
+   insn->variant.dfp_binop.rounding_mode = rounding_mode;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_dfp128_compare(UChar size, HReg dst, HReg op1_hi, HReg op1_lo,
+                         HReg op2_hi, HReg op2_lo)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(size == 16);
+   vassert(is_valid_fp128_regpair(op1_hi, op1_lo));
+   vassert(is_valid_fp128_regpair(op2_hi, op2_lo));
+
+   insn->tag  = S390_INSN_DFP_COMPARE;
+   insn->size = size;
+   insn->variant.dfp_compare.dst = dst;
+   insn->variant.dfp_compare.op1_hi = op1_hi;
+   insn->variant.dfp_compare.op1_lo = op1_lo;
+   insn->variant.dfp_compare.op2_hi = op2_hi;
+   insn->variant.dfp_compare.op2_lo = op2_lo;
+
+   return insn;
+}
+
+
+static s390_insn *
+s390_insn_dfp128_convert(UChar size, s390_dfp_conv_t tag, HReg dst_hi,
+                         HReg dst_lo, HReg op_hi, HReg op_lo,
+                         s390_dfp_round_t rounding_mode)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   if (size == 16) {
+      /* From smaller size to 16 bytes */
+      vassert(is_valid_fp128_regpair(dst_hi, dst_lo));
+      vassert(op_lo == INVALID_HREG);
+   } else {
+      /* From 16 bytes to smaller size */
+      vassert(is_valid_fp128_regpair(op_hi, op_lo));
+      vassert(dst_lo == INVALID_HREG);
+   }
+
+   insn->tag  = S390_INSN_DFP_CONVERT;
+   insn->size = size;
+   insn->variant.dfp_convert.tag = tag;
+   insn->variant.dfp_convert.dst_hi = dst_hi;
+   insn->variant.dfp_convert.dst_lo = dst_lo;
+   insn->variant.dfp_convert.op_hi = op_hi;
+   insn->variant.dfp_convert.op_lo = op_lo;
+   insn->variant.dfp_convert.rounding_mode = rounding_mode;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_dfp128_convert_to(UChar size, s390_dfp_conv_t tag, HReg dst_hi,
+                            HReg dst_lo, HReg op)
+{
+   /* Conversion to dfp128 never requires a rounding mode. Provide default
+      rounding mode. It will not be used when emitting insns. */
+   s390_dfp_round_t rounding_mode = S390_DFP_ROUND_NEAREST_EVEN_4;
+
+   return s390_insn_dfp128_convert(size, tag, dst_hi, dst_lo, op,
+                                   INVALID_HREG, rounding_mode);
+}
+
+
+s390_insn *
+s390_insn_dfp128_convert_from(UChar size, s390_dfp_conv_t tag, HReg dst,
+                              HReg op_hi, HReg op_lo,
+                              s390_dfp_round_t rounding_mode)
+{
+   return s390_insn_dfp128_convert(size, tag, dst, INVALID_HREG, op_hi, op_lo,
+                                   rounding_mode);
+}
+
+
 s390_insn *
 s390_insn_mfence(void)
 {
@@ -5649,6 +5992,24 @@ s390_insn_as_string(const s390_insn *insn)
                    insn->variant.dfp_binop.op3_hi);
       break;
 
+   case S390_INSN_DFP_COMPARE:
+      s390_sprintf(buf, "%M %R,%R,%R", "v-dcmp", insn->variant.dfp_compare.dst,
+                   insn->variant.dfp_compare.op1_hi,
+                   insn->variant.dfp_compare.op2_hi);
+      break;
+
+   case S390_INSN_DFP_CONVERT:
+      switch (insn->variant.dfp_convert.tag) {
+      case S390_DFP_D32_TO_D64:
+      case S390_DFP_D64_TO_D32:
+      case S390_DFP_D64_TO_D128:
+      case S390_DFP_D128_TO_D64: op = "v-d2d"; break;
+      default: goto fail;
+      }
+      s390_sprintf(buf, "%M %R,%R", op, insn->variant.dfp_convert.dst_hi,
+                   insn->variant.dfp_convert.op_hi);
+      break;
+
    case S390_INSN_MFENCE:
       s390_sprintf(buf, "%M", "v-mfence");
       return buf;   /* avoid printing "size = ..." which is meaningless */
@@ -5778,6 +6139,16 @@ s390_insn_as_string(const s390_insn *insn)
          goto common;
       }
 
+   case S390_INSN_DFP_CONVERT:
+      switch (insn->variant.dfp_convert.tag) {
+      case S390_DFP_D32_TO_D64: p += vex_sprintf(p, "4 -> "); goto common;
+      case S390_DFP_D64_TO_D32:
+      case S390_DFP_D64_TO_D128:p += vex_sprintf(p, "8 -> "); goto common;
+      case S390_DFP_D128_TO_D64:p += vex_sprintf(p, "16 -> "); goto common;
+      default:
+         goto common;
+      }
+
    default:
       goto common;
    }
@@ -7782,6 +8153,17 @@ s390_insn_dfp_binop_emit(UChar *buf, const s390_insn *insn)
       default:  goto fail;
       }
       break;
+
+   case 16:
+      switch (insn->variant.dfp_binop.tag) {
+      case S390_DFP_ADD:     return s390_emit_AXTRA(buf, r3, m4, r1, r2);
+      case S390_DFP_SUB:     return s390_emit_SXTRA(buf, r3, m4, r1, r2);
+      case S390_DFP_MUL:     return s390_emit_MXTRA(buf, r3, m4, r1, r2);
+      case S390_DFP_DIV:     return s390_emit_DXTRA(buf, r3, m4, r1, r2);
+      default:  goto fail;
+      }
+      break;
+
    default:  goto fail;
    }
 
@@ -7790,6 +8172,54 @@ s390_insn_dfp_binop_emit(UChar *buf, const s390_insn *insn)
 }
 
 
+static UChar *
+s390_insn_dfp_compare_emit(UChar *buf, const s390_insn *insn)
+{
+   UInt dst = hregNumber(insn->variant.dfp_compare.dst);
+   UInt r1  = hregNumber(insn->variant.dfp_compare.op1_hi);
+   UInt r2  = hregNumber(insn->variant.dfp_compare.op2_hi);
+
+   switch (insn->size) {
+   case 8:  buf = s390_emit_CDTR(buf, r1, r2); break;
+   case 16: buf = s390_emit_CXTR(buf, r1, r2); break;
+   default:  goto fail;
+   }
+
+   return s390_emit_load_cc(buf, dst);  /* Load condition code into DST */
+
+ fail:
+   vpanic("s390_insn_dfp_compare_emit");
+}
+
+
+static UChar *
+s390_insn_dfp_convert_emit(UChar *buf, const s390_insn *insn)
+{
+   UInt  r1 = hregNumber(insn->variant.dfp_convert.dst_hi);
+   UInt  r2 = hregNumber(insn->variant.dfp_convert.op_hi);
+   s390_dfp_round_t m3 = insn->variant.dfp_convert.rounding_mode;
+   /* The IEEE-inexact-exception control is not modelled. So the
+      m4 field is 0 (which is what GCC does, too) */
+   const UInt m4 = 0;
+
+   switch (insn->variant.dfp_convert.tag) {
+
+      /* Load lengthened */
+   case S390_DFP_D32_TO_D64:   return s390_emit_LDETR(buf, m4, r1, r2);
+   case S390_DFP_D64_TO_D128:  return s390_emit_LXDTR(buf, m4, r1, r2);
+
+      /* Load rounded */
+   case S390_DFP_D64_TO_D32:   return s390_emit_LEDTR(buf, m3, m4, r1, r2);
+   case S390_DFP_D128_TO_D64:  return s390_emit_LDXTR(buf, m3, m4, r1, r2);
+
+   default: goto fail;
+   }
+
+ fail:
+   vpanic("s390_insn_dfp_convert_emit");
+}
+
+
 static UChar *
 s390_insn_mfence_emit(UChar *buf, const s390_insn *insn)
 {
@@ -8383,6 +8813,14 @@ emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn,
       end = s390_insn_dfp_binop_emit(buf, insn);
       break;
 
+   case S390_INSN_DFP_COMPARE:
+      end = s390_insn_dfp_compare_emit(buf, insn);
+      break;
+
+   case S390_INSN_DFP_CONVERT:
+      end = s390_insn_dfp_convert_emit(buf, insn);
+      break;
+
    case S390_INSN_MFENCE:
       end = s390_insn_mfence_emit(buf, insn);
       break;
index 2e93b2fd6a8ee3bc0414d985aebdd41445152706..75e91c77dd35315117fd19481ec40769bd377486 100644 (file)
@@ -138,6 +138,8 @@ typedef enum {
    S390_INSN_BFP_COMPARE,
    S390_INSN_BFP_CONVERT,
    S390_INSN_DFP_BINOP, /* Decimal floating point */
+   S390_INSN_DFP_COMPARE,
+   S390_INSN_DFP_CONVERT,
    S390_INSN_MFENCE,
    S390_INSN_MZERO,   /* Assign zero to a memory location */
    S390_INSN_GADD,    /* Add a value to a guest register */
@@ -199,7 +201,7 @@ typedef enum {
    S390_BFP_SQRT
 } s390_bfp_unop_t;
 
-/* Type conversion operations: to and/or from floating point */
+/* Type conversion operations: to and/or from binary floating point */
 typedef enum {
    S390_BFP_I32_TO_F32,
    S390_BFP_I32_TO_F64,
@@ -233,6 +235,13 @@ typedef enum {
    S390_BFP_F128_TO_F64
 } s390_conv_t;
 
+/* Type conversion operations: to and/or from decimal floating point */
+typedef enum {
+   S390_DFP_D32_TO_D64,
+   S390_DFP_D64_TO_D32,
+   S390_DFP_D64_TO_D128,
+   S390_DFP_D128_TO_D64
+} s390_dfp_conv_t;
 
 /* The kind of binary DFP operations */
 typedef enum {
@@ -507,6 +516,21 @@ typedef struct {
          HReg         op3_lo; /* 128-bit operand low part */
          s390_dfp_round_t rounding_mode;
       } dfp_binop;
+      struct {
+         s390_dfp_conv_t  tag;
+         s390_dfp_round_t rounding_mode;
+         HReg         dst_hi; /* 128-bit result high part; 64-bit result */
+         HReg         dst_lo; /* 128-bit result low part */
+         HReg         op_hi;  /* 128-bit operand high part; 64-bit opnd */
+         HReg         op_lo;  /* 128-bit operand low part */
+      } dfp_convert;
+      struct {
+         HReg         dst;     /* condition code in s390 encoding */
+         HReg         op1_hi;  /* 128-bit operand high part; 64-bit opnd 1 */
+         HReg         op1_lo;  /* 128-bit operand low part */
+         HReg         op2_hi;  /* 128-bit operand high part; 64-bit opnd 2 */
+         HReg         op2_lo;  /* 128-bit operand low part */
+      } dfp_compare;
 
       /* Miscellaneous */
       struct {
@@ -615,6 +639,20 @@ s390_insn *s390_insn_bfp128_convert_from(UChar size, s390_conv_t,
 s390_insn *s390_insn_dfp_binop(UChar size, s390_dfp_binop_t, HReg dst,
                                HReg op2, HReg op3,
                                s390_dfp_round_t rounding_mode);
+s390_insn *s390_insn_dfp_compare(UChar size, HReg dst, HReg op1, HReg op2);
+s390_insn *s390_insn_dfp_convert(UChar size, s390_dfp_conv_t tag, HReg dst,
+                                 HReg op, s390_dfp_round_t);
+s390_insn *s390_insn_dfp128_binop(UChar size, s390_dfp_binop_t, HReg dst_hi,
+                                  HReg dst_lo, HReg op2_hi, HReg op2_lo,
+                                  HReg op3_hi, HReg op3_lo,
+                                  s390_dfp_round_t rounding_mode);
+s390_insn *s390_insn_dfp128_compare(UChar size, HReg dst, HReg op1_hi,
+                                    HReg op1_lo, HReg op2_hi, HReg op2_lo);
+s390_insn *s390_insn_dfp128_convert_to(UChar size, s390_dfp_conv_t,
+                                       HReg dst_hi, HReg dst_lo, HReg op);
+s390_insn *s390_insn_dfp128_convert_from(UChar size, s390_dfp_conv_t,
+                                         HReg dst, HReg op_hi, HReg op_lo,
+                                         s390_dfp_round_t);
 s390_insn *s390_insn_mfence(void);
 s390_insn *s390_insn_mzero(UChar size, s390_amode *dst);
 s390_insn *s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value);
index 0b1da75f2851ec03f6e30fb48e83ee903b39d499..6916731bc3a48e1f3a273571c0f3b2109c5b85ff 100644 (file)
@@ -136,6 +136,7 @@ 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 *);
 static HReg          s390_isel_dfp_expr(ISelEnv *, IRExpr *);
+static void          s390_isel_dfp128_expr(HReg *, HReg *, ISelEnv *, IRExpr *);
 
 
 static Int
@@ -1206,6 +1207,46 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr)
          return convert_s390_fpcc_to_vex(env, cc_s390);
       }
 
+      case Iop_CmpD64: {
+         HReg cc_s390, h2;
+
+         h1 = s390_isel_dfp_expr(env, arg1);
+         h2 = s390_isel_dfp_expr(env, arg2);
+         cc_s390 = newVRegI(env);
+         size = 8;
+
+         addInstr(env, s390_insn_dfp_compare(size, cc_s390, h1, h2));
+
+         return convert_s390_fpcc_to_vex(env, cc_s390);
+      }
+
+      case Iop_CmpD128: {
+         HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15, cc_s390;
+
+         s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, arg1); /* 1st operand */
+         s390_isel_dfp128_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_dfp128_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:
@@ -1357,6 +1398,14 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr)
          return dst;
       }
 
+      if (unop == Iop_ReinterpD64asI64) {
+         dst = newVRegI(env);
+         h1  = s390_isel_dfp_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);
@@ -2215,6 +2264,189 @@ s390_isel_float_expr(ISelEnv *env, IRExpr *expr)
 }
 
 
+/*---------------------------------------------------------*/
+/*--- ISEL: Decimal point expressions (128 bit)         ---*/
+/*---------------------------------------------------------*/
+static void
+s390_isel_dfp128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
+                          IRExpr *expr)
+{
+   IRType ty = typeOfIRExpr(env->type_env, expr);
+
+   vassert(ty == Ity_D128);
+
+   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_dpr_pair instead. */
+      vpanic("Iex_Get with D128 data");
+
+      /* --------- 4-ary OP --------- */
+   case Iex_Qop:
+      vpanic("Iex_Qop with D128 data");
+
+      /* --------- TERNARY OP --------- */
+   case Iex_Triop: {
+      IRTriop *triop = expr->Iex.Triop.details;
+      IROp    op     = triop->op;
+      IRExpr *irrm   = triop->arg1;
+      IRExpr *left   = triop->arg2;
+      IRExpr *right  = triop->arg3;
+      s390_dfp_round_t rounding_mode;
+      s390_dfp_binop_t dfpop;
+      HReg op1_hi, op1_lo, op2_hi, op2_lo, f9, f11, f12, f13, f14, f15;
+
+      s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, left);  /* 1st operand */
+      s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, right); /* 2nd operand */
+
+      /* We use non-virtual registers as pairs with (f9, f11) as op1,
+         (f12, f14) as op2 and (f13, f15)  as destination) */
+      f9  = make_fpr(9);
+      f11 = make_fpr(11);
+      f12 = make_fpr(12);
+      f13 = make_fpr(13);
+      f14 = make_fpr(14);
+      f15 = make_fpr(15);
+
+      /* 1st operand --> (f9, f11) */
+      addInstr(env, s390_insn_move(8, f9,  op1_hi));
+      addInstr(env, s390_insn_move(8, f11, op1_lo));
+
+      /* 2nd operand --> (f12, f14) */
+      addInstr(env, s390_insn_move(8, f12, op2_hi));
+      addInstr(env, s390_insn_move(8, f14, op2_lo));
+
+      switch (op) {
+      case Iop_AddD128: dfpop = S390_DFP_ADD; break;
+      case Iop_SubD128: dfpop = S390_DFP_SUB; break;
+      case Iop_MulD128: dfpop = S390_DFP_MUL; break;
+      case Iop_DivD128: dfpop = S390_DFP_DIV; break;
+      default:
+         goto irreducible;
+      }
+
+      /* DFP binary ops have insns with rounding mode field
+         when the floating point extension facility is installed. */
+      if (s390_host_has_fpext) {
+         rounding_mode = get_dfp_rounding_mode(env, irrm);
+      } else {
+         set_dfp_rounding_mode_in_fpc(env, irrm);
+         rounding_mode = S390_DFP_ROUND_PER_FPC_0;
+      }
+
+      addInstr(env, s390_insn_dfp128_binop(16, dfpop, f13, f15, f9, f11,
+                                           f12, f14, rounding_mode));
+
+      /* Move result to virtual destination register */
+      *dst_hi = newVRegF(env);
+      *dst_lo = newVRegF(env);
+      addInstr(env, s390_insn_move(8, *dst_hi, f13));
+      addInstr(env, s390_insn_move(8, *dst_lo, f15));
+
+      return;
+   }
+
+      /* --------- BINARY OP --------- */
+   case Iex_Binop: {
+      switch (expr->Iex.Binop.op) {
+      case Iop_D64HLtoD128:
+         *dst_hi = s390_isel_dfp_expr(env, expr->Iex.Binop.arg1);
+         *dst_lo = s390_isel_dfp_expr(env, expr->Iex.Binop.arg2);
+         return;
+
+      default:
+         goto irreducible;
+      }
+   }
+
+      /* --------- UNARY OP --------- */
+   case Iex_Unop: {
+      IRExpr *left = expr->Iex.Unop.arg;
+      s390_dfp_conv_t conv;
+      //      HReg op, f12, f13, f14, f15;
+      HReg op, f12, f14;
+
+      /* 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.Unop.op) {
+      case Iop_D64toD128:   conv = S390_DFP_D64_TO_D128;  goto convert_dfp;
+      default:
+         goto irreducible;
+      }
+
+   convert_dfp:
+      op  = s390_isel_dfp_expr(env, left);
+      addInstr(env, s390_insn_dfp128_convert_to(16, conv, 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_dfp128_expr_wrk: 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_dfp128_expr(HReg *dst_hi, HReg *dst_lo, ISelEnv *env, IRExpr *expr)
+{
+   s390_isel_dfp128_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: Decimal point expressions (64 bit)          ---*/
 /*---------------------------------------------------------*/
@@ -2225,7 +2457,7 @@ s390_isel_dfp_expr_wrk(ISelEnv *env, IRExpr *expr)
    IRType ty = typeOfIRExpr(env->type_env, expr);
    UChar size;
 
-   vassert(ty == Ity_D64);
+   vassert(ty == Ity_D64 || ty == Ity_D32);
 
    size = sizeofIRType(ty);
 
@@ -2257,6 +2489,114 @@ s390_isel_dfp_expr_wrk(ISelEnv *env, IRExpr *expr)
       return dst;
    }
 
+      /* --------- 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_dfp_conv_t  conv;
+
+      switch (op) {
+      case Iop_D64toD32:  conv = S390_DFP_D64_TO_D32; goto convert_dfp;
+
+      convert_dfp:
+         h1 = s390_isel_dfp_expr(env, left);
+         goto convert;
+
+      convert: {
+            s390_dfp_round_t rounding_mode;
+            /* convert-from-fixed and load-rounded have a rounding mode field
+               when the floating point extension facility is installed. */
+            dst = newVRegF(env);
+            if (s390_host_has_fpext) {
+               rounding_mode = get_dfp_rounding_mode(env, irrm);
+            } else {
+               set_dfp_rounding_mode_in_fpc(env, irrm);
+               rounding_mode = S390_DFP_ROUND_PER_FPC_0;
+            }
+            addInstr(env, s390_insn_dfp_convert(size, conv, dst, h1,
+                                                rounding_mode));
+            return dst;
+         }
+      default:
+         goto irreducible;
+
+      case Iop_D128toD64: {
+         HReg op_hi, op_lo, f13, f15;
+         s390_dfp_round_t rounding_mode;
+
+         conv = S390_DFP_D128_TO_D64;
+
+         s390_isel_dfp128_expr(&op_hi, &op_lo, env, left);
+
+         /* We use non-virtual registers as pairs (f13, f15) */
+         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));
+
+         dst = newVRegF(env);
+         /* load-rounded has a rounding mode field when the floating point
+            extension facility is installed. */
+         if (s390_host_has_fpext) {
+            rounding_mode = get_dfp_rounding_mode(env, irrm);
+         } else {
+            set_dfp_rounding_mode_in_fpc(env, irrm);
+            rounding_mode = S390_DFP_ROUND_PER_FPC_0;
+         }
+         addInstr(env, s390_insn_dfp128_convert_from(size, conv, dst, f13, f15,
+                                                     rounding_mode));
+         return dst;
+      }
+
+      }
+   }
+
+      /* --------- UNARY OP --------- */
+   case Iex_Unop: {
+      IROp    op   = expr->Iex.Unop.op;
+      IRExpr *left = expr->Iex.Unop.arg;
+      s390_dfp_conv_t conv;
+      HReg h1, dst;
+
+      if (op == Iop_D128HItoD64 || op == Iop_D128LOtoD64) {
+         HReg dst_hi, dst_lo;
+
+         s390_isel_dfp128_expr(&dst_hi, &dst_lo, env, left);
+         return op == Iop_D128LOtoD64 ? dst_lo : dst_hi;
+      }
+
+      if (op == Iop_ReinterpI64asD64) {
+         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_D32toD64:  conv = S390_DFP_D32_TO_D64;  goto convert_dfp1;
+
+      convert_dfp1:
+         h1 = s390_isel_dfp_expr(env, left);
+         goto convert1;
+
+      convert1:
+         dst = newVRegF(env);
+         /* No rounding mode is needed for these conversions. Just stick
+            one in. It won't be used later on. */
+         addInstr(env, s390_insn_dfp_convert(size, conv, dst, h1,
+                                             S390_DFP_ROUND_NEAREST_EVEN_4));
+         return dst;
+
+      default:
+         goto irreducible;
+      }
+   }
+
       /* --------- TERNARY OP --------- */
    case Iex_Triop: {
       IRTriop *triop = expr->Iex.Triop.details;
@@ -2572,8 +2912,9 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
          break;
 
       case Ity_F128:
+      case Ity_D128:
          /* Cannot occur. No such instruction */
-         vpanic("Ist_Store with F128 data");
+         vpanic("Ist_Store with 128-bit floating point data");
 
       default:
          goto stmt_fail;
@@ -2703,9 +3044,11 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
          break;
 
       case Ity_F128:
-         /* Does not occur. See function put_fpr_pair. */
-         vpanic("Ist_Put with F128 data");
+      case Ity_D128:
+         /* Does not occur. See function put_(f|d)pr_pair. */
+         vpanic("Ist_Put with 128-bit floating point data");
 
+      case Ity_D32:
       case Ity_D64:
          src = s390_isel_dfp_expr(env, stmt->Ist.Put.data);
          break;
@@ -2768,11 +3111,23 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
          return;
       }
 
+      case Ity_D32:
       case Ity_D64:
          src = s390_isel_dfp_expr(env, stmt->Ist.WrTmp.data);
          dst = lookupIRTemp(env, tmp);
          break;
 
+      case Ity_D128: {
+         HReg dst_hi, dst_lo, res_hi, res_lo;
+
+         s390_isel_dfp128_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;
       }
@@ -3138,11 +3493,13 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
 
       case Ity_F32:
       case Ity_F64:
+      case Ity_D32:
       case Ity_D64:
          hreg = mkHReg(j++, HRcFlt64, True);
          break;
 
       case Ity_F128:
+      case Ity_D128:
          hreg   = mkHReg(j++, HRcFlt64, True);
          hregHI = mkHReg(j++, HRcFlt64, True);
          break;