s390: Support the following DFP insns:
- extract basied exponent
- insert biased exponent
- quantize
- reround to significance
Patch by Maran Pakkirisamy (maranp@linux.vnet.ibm.com).
Part of fixing BZ #307113.


git-svn-id: svn://svn.valgrind.org/vex/trunk@2684 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index 1203459..2f42afd 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -1495,16 +1495,28 @@
          return dst;
       }
 
-      if (unop == Iop_ExtractSigD64) {
+      if (unop == Iop_ExtractExpD64 || unop == Iop_ExtractSigD64) {
+         s390_dfp_unop_t dfpop;
+         switch(unop) {
+         case Iop_ExtractExpD64: dfpop = S390_DFP_EXTRACT_EXP_D64; break;
+         case Iop_ExtractSigD64: dfpop = S390_DFP_EXTRACT_SIG_D64; break;
+         default: goto irreducible;
+         }
          dst = newVRegI(env);
          h1  = s390_isel_dfp_expr(env, arg);     /* Process the operand */
-         addInstr(env,
-                  s390_insn_dfp_unop(size, S390_DFP_EXTRACT_SIG_D64, dst, h1));
+         addInstr(env, s390_insn_dfp_unop(size, dfpop, dst, h1));
          return dst;
       }
 
-      if (unop == Iop_ExtractSigD128) {
+      if (unop == Iop_ExtractExpD128 || unop == Iop_ExtractSigD128) {
+         s390_dfp_unop_t dfpop;
          HReg op_hi, op_lo, f13, f15;
+
+         switch(unop) {
+         case Iop_ExtractExpD128: dfpop = S390_DFP_EXTRACT_EXP_D128; break;
+         case Iop_ExtractSigD128: dfpop = S390_DFP_EXTRACT_SIG_D128; break;
+         default: goto irreducible;
+         }
          dst = newVRegI(env);
          s390_isel_dfp128_expr(&op_hi, &op_lo, env, arg); /* Process operand */
 
@@ -1516,8 +1528,7 @@
          addInstr(env, s390_insn_move(8, f13, op_hi));
          addInstr(env, s390_insn_move(8, f15, op_lo));
 
-         addInstr(env, s390_insn_dfp128_unop(size, S390_DFP_EXTRACT_SIG_D128,
-                                             dst, f13, f15));
+         addInstr(env, s390_insn_dfp128_unop(size, dfpop, dst, f13, f15));
          return dst;
       }
 
@@ -2417,9 +2428,6 @@
       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);
@@ -2429,42 +2437,68 @@
       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;
+      case Iop_AddD128:       dfpop = S390_DFP_ADD;      goto evaluate_dfp128;
+      case Iop_SubD128:       dfpop = S390_DFP_SUB;      goto evaluate_dfp128;
+      case Iop_MulD128:       dfpop = S390_DFP_MUL;      goto evaluate_dfp128;
+      case Iop_DivD128:       dfpop = S390_DFP_DIV;      goto evaluate_dfp128;
+      case Iop_QuantizeD128:  dfpop = S390_DFP_QUANTIZE; goto evaluate_dfp128;
+
+      evaluate_dfp128: {
+         /* Process 1st operand */
+         s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, left);
+         /* 1st operand --> (f9, f11) */
+         addInstr(env, s390_insn_move(8, f9,  op1_hi));
+         addInstr(env, s390_insn_move(8, f11, op1_lo));
+
+         /* Process 2nd operand */
+         s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, right);
+         /* 2nd operand --> (f12, f14) */
+         addInstr(env, s390_insn_move(8, f12, op2_hi));
+         addInstr(env, s390_insn_move(8, f14, op2_lo));
+
+         /* DFP arithmetic ops take rounding mode only when fpext is
+            installed. But, DFP quantize operation takes rm irrespective
+            of fpext facility . */
+         if (s390_host_has_fpext || dfpop == Iop_QuantizeD128) {
+            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;
+      }
+
+      case Iop_SignificanceRoundD128: {
+         /* Process 1st operand */
+         HReg op1 = s390_isel_int_expr(env, left);
+         /* Process 2nd operand */
+         s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, right);
+         /* 2nd operand --> (f12, f14) */
+         addInstr(env, s390_insn_move(8, f12, op2_hi));
+         addInstr(env, s390_insn_move(8, f14, op2_lo));
+
+         rounding_mode = get_dfp_rounding_mode(env, irrm);
+         addInstr(env, s390_insn_dfp128_reround(16, f13, f15, op1, 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;
+      }
+
       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 --------- */
@@ -2477,15 +2511,29 @@
          return;
 
       case Iop_ShlD128:
-      case Iop_ShrD128: {
+      case Iop_ShrD128:
+      case Iop_InsertExpD128: {
          HReg op1_hi, op1_lo, op2, f9, f11, f13, f15;
          s390_dfp_intop_t intop;
-         IRExpr *left   = expr->Iex.Binop.arg1;
-         IRExpr *right  = expr->Iex.Binop.arg2;
+         IRExpr *dfp_op;
+         IRExpr *int_op;
 
          switch (expr->Iex.Binop.op) {
-         case Iop_ShlD128: intop = S390_DFP_SHIFT_LEFT;  break;
-         case Iop_ShrD128: intop = S390_DFP_SHIFT_RIGHT; break;
+         case Iop_ShlD128:       /* (D128, I64) -> D128 */
+            intop = S390_DFP_SHIFT_LEFT;
+            dfp_op = expr->Iex.Binop.arg1;
+            int_op = expr->Iex.Binop.arg2;
+            break;
+         case Iop_ShrD128:       /* (D128, I64) -> D128 */
+            intop = S390_DFP_SHIFT_RIGHT;
+            dfp_op = expr->Iex.Binop.arg1;
+            int_op = expr->Iex.Binop.arg2;
+            break;
+         case Iop_InsertExpD128: /* (I64, D128) -> D128 */
+            intop = S390_DFP_INSERT_EXP;
+            int_op = expr->Iex.Binop.arg1;
+            dfp_op = expr->Iex.Binop.arg2;
+            break;
          default: goto irreducible;
          }
 
@@ -2496,11 +2544,13 @@
          f13 = make_fpr(13); /* 128 bit dfp destination */
          f15 = make_fpr(15);
 
-         s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, left);  /* dfp operand */
+         /* Process dfp operand */
+         s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, dfp_op);
+         /* op1 -> (f9,f11) */
          addInstr(env, s390_insn_move(8, f9,  op1_hi));
          addInstr(env, s390_insn_move(8, f11, op1_lo));
 
-         op2 = s390_isel_int_expr(env, right);  /* int operand */
+         op2 = s390_isel_int_expr(env, int_op);  /* int operand */
 
          addInstr(env,
                   s390_insn_dfp128_intop(16, intop, f13, f15, op2, f9, f11));
@@ -2695,21 +2745,35 @@
       }
 
       case Iop_ShlD64:
-      case Iop_ShrD64: {
+      case Iop_ShrD64:
+      case Iop_InsertExpD64: {
          HReg op2;
          HReg op3;
+         IRExpr *dfp_op;
+         IRExpr *int_op;
          s390_dfp_intop_t intop;
-         IRExpr *op1   = expr->Iex.Binop.arg1;
-         IRExpr *shift = expr->Iex.Binop.arg2;
 
          switch (expr->Iex.Binop.op) {
-         case Iop_ShlD64: intop = S390_DFP_SHIFT_LEFT;  break;
-         case Iop_ShrD64: intop = S390_DFP_SHIFT_RIGHT; break;
+         case Iop_ShlD64:       /* (D64, I64) -> D64 */
+            intop = S390_DFP_SHIFT_LEFT;
+            dfp_op = expr->Iex.Binop.arg1;
+            int_op = expr->Iex.Binop.arg2;
+            break;
+         case Iop_ShrD64:       /* (D64, I64) -> D64 */
+            intop = S390_DFP_SHIFT_RIGHT;
+            dfp_op = expr->Iex.Binop.arg1;
+            int_op = expr->Iex.Binop.arg2;
+            break;
+         case Iop_InsertExpD64: /* (I64, D64) -> D64 */
+            intop = S390_DFP_INSERT_EXP;
+            int_op = expr->Iex.Binop.arg1;
+            dfp_op = expr->Iex.Binop.arg2;
+            break;
          default: goto irreducible;
          }
 
-         op2 = s390_isel_int_expr(env, shift);
-         op3 = s390_isel_dfp_expr(env, op1);
+         op2 = s390_isel_int_expr(env, int_op);
+         op3 = s390_isel_dfp_expr(env, dfp_op);
          dst = newVRegF(env);
 
          addInstr(env, s390_insn_dfp_intop(size, intop, dst, op2, op3));
@@ -2780,29 +2844,43 @@
       s390_dfp_binop_t dfpop;
       HReg op2, op3, dst;
 
-      op2  = s390_isel_dfp_expr(env, left);  /* Process 1st operand */
-      op3  = s390_isel_dfp_expr(env, right); /* Process 2nd operand */
-      dst  = newVRegF(env);
       switch (op) {
-      case Iop_AddD64:  dfpop = S390_DFP_ADD; break;
-      case Iop_SubD64:  dfpop = S390_DFP_SUB; break;
-      case Iop_MulD64:  dfpop = S390_DFP_MUL; break;
-      case Iop_DivD64:  dfpop = S390_DFP_DIV; break;
+      case Iop_AddD64:      dfpop = S390_DFP_ADD;      goto evaluate_dfp;
+      case Iop_SubD64:      dfpop = S390_DFP_SUB;      goto evaluate_dfp;
+      case Iop_MulD64:      dfpop = S390_DFP_MUL;      goto evaluate_dfp;
+      case Iop_DivD64:      dfpop = S390_DFP_DIV;      goto evaluate_dfp;
+      case Iop_QuantizeD64: dfpop = S390_DFP_QUANTIZE; goto evaluate_dfp;
+
+      evaluate_dfp: {
+         op2  = s390_isel_dfp_expr(env, left);  /* Process 1st operand */
+         op3  = s390_isel_dfp_expr(env, right); /* Process 2nd operand */
+         dst  = newVRegF(env);
+         /* DFP arithmetic ops take rounding mode only when fpext is
+            installed. But, DFP quantize operation takes rm irrespective
+            of fpext facility . */
+         if (s390_host_has_fpext || dfpop == S390_DFP_QUANTIZE) {
+            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_binop(size, dfpop, dst, op2, op3,
+                                           rounding_mode));
+         return dst;
+      }
+
+      case Iop_SignificanceRoundD64:
+         op2  = s390_isel_int_expr(env, left);  /* Process 1st operand */
+         op3  = s390_isel_dfp_expr(env, right); /* Process 2nd operand */
+         dst  = newVRegF(env);
+         rounding_mode = get_dfp_rounding_mode(env, irrm);
+         addInstr(env, s390_insn_dfp_reround(size, dst, op2, op3,
+                                             rounding_mode));
+         return dst;
+
       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_dfp_binop(size, dfpop, dst, op2, op3, rounding_mode));
-      return dst;
    }
 
    default: