s390x: Support "compare biased exponent" insns CEDTR, CEXTR.
To do that properly, two new IROps are needed: Iop_CmpExpD64 and
Iop_CmpExpD128. It might seem that extracting the exponents using
Iop_ExtractExpD64/D128 and comparing the values could be used here.
But that only works for finite DFP values. Hence, the new IROps.

Patch by Maran Pakkirisamy (maranp@linux.vnet.ibm.com).
This is part of fixing BZ 307113.


git-svn-id: svn://svn.valgrind.org/vex/trunk@2617 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index ec47531..f90f05d 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -9283,6 +9283,44 @@
 }
 
 static const HChar *
+s390_irgen_CEDTR(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);
+
+   vassert(s390_host_has_dfp);
+   assign(op1, get_dpr_dw0(r1));
+   assign(op2, get_dpr_dw0(r2));
+   assign(cc_vex, binop(Iop_CmpExpD64, mkexpr(op1), mkexpr(op2)));
+
+   assign(cc_s390, convert_vex_dfpcc_to_s390(cc_vex));
+   s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
+
+   return "cedtr";
+}
+
+static const HChar *
+s390_irgen_CEXTR(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);
+
+   vassert(s390_host_has_dfp);
+   assign(op1, get_dpr_pair(r1));
+   assign(op2, get_dpr_pair(r2));
+   assign(cc_vex, binop(Iop_CmpExpD128, mkexpr(op1), mkexpr(op2)));
+
+   assign(cc_s390, convert_vex_dfpcc_to_s390(cc_vex));
+   s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False);
+
+   return "cextr";
+}
+
+static const HChar *
 s390_irgen_DDTRA(UChar r3, UChar m4, UChar r1, UChar r2)
 {
    IRTemp op1 = newTemp(Ity_D64);
@@ -13534,14 +13572,16 @@
    case 0xb3f1: /* CDGTR */ goto unimplemented;
    case 0xb3f2: /* CDUTR */ goto unimplemented;
    case 0xb3f3: /* CDSTR */ goto unimplemented;
-   case 0xb3f4: /* CEDTR */ goto unimplemented;
+   case 0xb3f4: s390_format_RRE_FF(s390_irgen_CEDTR, ovl.fmt.RRE.r1,
+                                   ovl.fmt.RRE.r2);  goto ok;
    case 0xb3f5: /* QADTR */ goto unimplemented;
    case 0xb3f6: /* IEDTR */ goto unimplemented;
    case 0xb3f7: /* RRDTR */ goto unimplemented;
    case 0xb3f9: /* CXGTR */ goto unimplemented;
    case 0xb3fa: /* CXUTR */ goto unimplemented;
    case 0xb3fb: /* CXSTR */ goto unimplemented;
-   case 0xb3fc: /* CEXTR */ goto unimplemented;
+   case 0xb3fc: s390_format_RRE_FF(s390_irgen_CEXTR, ovl.fmt.RRE.r1,
+                                   ovl.fmt.RRE.r2);  goto ok;
    case 0xb3fd: /* QAXTR */ goto unimplemented;
    case 0xb3fe: /* IEXTR */ goto unimplemented;
    case 0xb3ff: /* RRXTR */ goto unimplemented;
diff --git a/priv/host_s390_defs.c b/priv/host_s390_defs.c
index 48695ba..3a82222 100644
--- a/priv/host_s390_defs.c
+++ b/priv/host_s390_defs.c
@@ -4009,6 +4009,26 @@
 
 
 static UChar *
+s390_emit_CEDTR(UChar *p, UChar r1, UChar r2)
+{
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC3(MNM, FPR, FPR), "cedtr", r1, r2);
+
+   return emit_RRE(p, 0xb3f40000, r1, r2);
+}
+
+
+static UChar *
+s390_emit_CEXTR(UChar *p, UChar r1, UChar r2)
+{
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC3(MNM, FPR, FPR), "cextr", r1, r2);
+
+   return emit_RRE(p, 0xb3fc0000, r1, r2);
+}
+
+
+static UChar *
 s390_emit_DDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2)
 {
    vassert(s390_host_has_dfp);
@@ -5292,7 +5312,8 @@
 
 
 s390_insn *
-s390_insn_dfp_compare(UChar size, HReg dst, HReg op1, HReg op2)
+s390_insn_dfp_compare(UChar size, s390_dfp_cmp_t tag, HReg dst,
+                      HReg op1, HReg op2)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -5300,6 +5321,7 @@
 
    insn->tag  = S390_INSN_DFP_COMPARE;
    insn->size = size;
+   insn->variant.dfp_compare.tag = tag;
    insn->variant.dfp_compare.dst = dst;
    insn->variant.dfp_compare.op1_hi = op1;
    insn->variant.dfp_compare.op2_hi = op2;
@@ -5362,8 +5384,8 @@
 
 
 s390_insn *
-s390_insn_dfp128_compare(UChar size, HReg dst, HReg op1_hi, HReg op1_lo,
-                         HReg op2_hi, HReg op2_lo)
+s390_insn_dfp128_compare(UChar size, s390_dfp_cmp_t tag, HReg dst, HReg op1_hi,
+                         HReg op1_lo, HReg op2_hi, HReg op2_lo)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -5373,6 +5395,7 @@
 
    insn->tag  = S390_INSN_DFP_COMPARE;
    insn->size = size;
+   insn->variant.dfp_compare.tag = tag;
    insn->variant.dfp_compare.dst = dst;
    insn->variant.dfp_compare.op1_hi = op1_hi;
    insn->variant.dfp_compare.op1_lo = op1_lo;
@@ -6025,7 +6048,12 @@
    }
 
    case S390_INSN_DFP_COMPARE:
-      s390_sprintf(buf, "%M %R,%R,%R", "v-dcmp", insn->variant.dfp_compare.dst,
+      switch (insn->variant.dfp_compare.tag) {
+      case S390_DFP_COMPARE:     op = "v-dcmp"; break;
+      case S390_DFP_COMPARE_EXP: op = "v-dcmpexp"; break;
+      default: goto fail;
+      }
+      s390_sprintf(buf, "%M %R,%R,%R", op, insn->variant.dfp_compare.dst,
                    insn->variant.dfp_compare.op1_hi,
                    insn->variant.dfp_compare.op2_hi);
       break;
@@ -8215,8 +8243,22 @@
    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;
+   case 8:
+      switch(insn->variant.dfp_compare.tag) {
+      case S390_DFP_COMPARE:     buf = s390_emit_CDTR(buf, r1, r2); break;
+      case S390_DFP_COMPARE_EXP: buf = s390_emit_CEDTR(buf, r1, r2); break;
+      default: goto fail;
+      }
+      break;
+
+   case 16:
+      switch(insn->variant.dfp_compare.tag) {
+      case S390_DFP_COMPARE:     buf = s390_emit_CXTR(buf, r1, r2); break;
+      case S390_DFP_COMPARE_EXP: buf = s390_emit_CEXTR(buf, r1, r2); break;
+      default: goto fail;
+      }
+      break;
+
    default:  goto fail;
    }
 
diff --git a/priv/host_s390_defs.h b/priv/host_s390_defs.h
index 03b6b8d..4b795c4 100644
--- a/priv/host_s390_defs.h
+++ b/priv/host_s390_defs.h
@@ -254,6 +254,12 @@
    S390_DFP_DIV
 } s390_dfp_binop_t;
 
+/* The kind of DFP compare operations */
+typedef enum {
+   S390_DFP_COMPARE,
+   S390_DFP_COMPARE_EXP,
+} s390_dfp_cmp_t;
+
 
 /* The details of a CDAS insn. Carved out to keep the size of
    s390_insn low */
@@ -446,6 +452,7 @@
          HReg         op_lo;  /* 128-bit operand low part */
       } dfp_convert;
       struct {
+         s390_dfp_cmp_t tag;
          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 */
@@ -560,15 +567,17 @@
 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_compare(UChar size, s390_dfp_cmp_t, 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_compare(UChar size, s390_dfp_cmp_t, 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,
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index 568e4c3..3027045 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -1218,21 +1218,29 @@
          return convert_s390_to_vex_bfpcc(env, cc_s390);
       }
 
-      case Iop_CmpD64: {
+      case Iop_CmpD64:
+      case Iop_CmpExpD64: {
          HReg cc_s390, h2;
+         s390_dfp_cmp_t cmp;
 
          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));
+         switch(expr->Iex.Binop.op) {
+         case Iop_CmpD64:    cmp = S390_DFP_COMPARE; break;
+         case Iop_CmpExpD64: cmp = S390_DFP_COMPARE_EXP; break;
+         default: goto irreducible;
+         }
+         addInstr(env, s390_insn_dfp_compare(8, cmp, cc_s390, h1, h2));
 
          return convert_s390_to_vex_dfpcc(env, cc_s390);
       }
 
-      case Iop_CmpD128: {
+      case Iop_CmpD128:
+      case Iop_CmpExpD128: {
          HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15, cc_s390;
+         s390_dfp_cmp_t cmp;
 
          s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, arg1); /* 1st operand */
          s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, arg2); /* 2nd operand */
@@ -1252,8 +1260,13 @@
          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));
+         switch(expr->Iex.Binop.op) {
+         case Iop_CmpD128:    cmp = S390_DFP_COMPARE; break;
+         case Iop_CmpExpD128: cmp = S390_DFP_COMPARE_EXP; break;
+         default: goto irreducible;
+         }
+         addInstr(env, s390_insn_dfp128_compare(16, cmp, cc_s390, f12, f14,
+                                                f13, f15));
 
          return convert_s390_to_vex_dfpcc(env, cc_s390);
       }
diff --git a/priv/ir_defs.c b/priv/ir_defs.c
index 39c5f26..35a9ee9 100644
--- a/priv/ir_defs.c
+++ b/priv/ir_defs.c
@@ -985,11 +985,13 @@
       case Iop_ExtractExpD128: vex_printf("Iop_ExtractExpD128"); return;
       case Iop_InsertExpD64:   vex_printf("Iop_InsertExpD64");   return;
       case Iop_InsertExpD128:  vex_printf("Iop_InsertExpD128");  return;
-      case Iop_CmpD64:         vex_printf("CmpD64");    return;
-      case Iop_CmpD128:        vex_printf("CmpD128");   return;
-      case Iop_D64HLtoD128: vex_printf("D64HLtoD128");  return;
-      case Iop_D128HItoD64: vex_printf("D128HItoD64");  return;
-      case Iop_D128LOtoD64: vex_printf("D128LOtoD64");  return;
+      case Iop_CmpD64:         vex_printf("CmpD64");     return;
+      case Iop_CmpD128:        vex_printf("CmpD128");    return;
+      case Iop_CmpExpD64:      vex_printf("CmpExpD64");  return;
+      case Iop_CmpExpD128:     vex_printf("CmpExpD128"); return;
+      case Iop_D64HLtoD128: vex_printf("D64HLtoD128");   return;
+      case Iop_D128HItoD64: vex_printf("D128HItoD64");   return;
+      case Iop_D128LOtoD64: vex_printf("D128LOtoD64");   return;
       case Iop_SignificanceRoundD64: vex_printf("Iop_SignificanceRoundD64");
          return;
       case Iop_SignificanceRoundD128: vex_printf("Iop_SignificanceRoundD128");
@@ -2800,9 +2802,11 @@
          BINARY(ity_RMode, Ity_D64, Ity_D64);
 
       case Iop_CmpD64:
+      case Iop_CmpExpD64:
          BINARY(Ity_D64,Ity_D64, Ity_I32);
 
       case Iop_CmpD128:
+      case Iop_CmpExpD128:
          BINARY(Ity_D128,Ity_D128, Ity_I32);
 
       case Iop_QuantizeD64:
diff --git a/pub/libvex_ir.h b/pub/libvex_ir.h
index ad21816..8280e53 100644
--- a/pub/libvex_ir.h
+++ b/pub/libvex_ir.h
@@ -1073,6 +1073,13 @@
       /* D128 x D128 -> IRCmpD128Result(I32) */
       Iop_CmpD128,
 
+      /* COMPARE BIASED EXPONENET INSTRUCTIONS
+       * D64 x D64 -> IRCmpD64Result(I32) */
+      Iop_CmpExpD64,
+
+      /* D128 x D128 -> IRCmpD128Result(I32) */
+      Iop_CmpExpD128,
+
       /* QUANTIZE AND ROUND INSTRUCTIONS
        * The source operand is converted and rounded to the form with the 
        * immediate exponent specified by the rounding and exponent parameter.