Add support for these DFP insns:
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 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index 0b1da75..6916731 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -136,6 +136,7 @@
 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 @@
          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 @@
          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);
@@ -2216,6 +2265,189 @@
 
 
 /*---------------------------------------------------------*/
+/*--- 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 @@
    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 @@
       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 @@
          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 @@
          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 @@
          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 @@
 
       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;