]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
s390: Change the handling of S390_ROUND_PER_FPC (which indicates that the
authorFlorian Krohm <florian@eich-krohm.de>
Wed, 12 Sep 2012 19:38:42 +0000 (19:38 +0000)
committerFlorian Krohm <florian@eich-krohm.de>
Wed, 12 Sep 2012 19:38:42 +0000 (19:38 +0000)
actual rounding mode is to be taken from the FPC register). Previously, this
was just mapped to S390_ROUND_NEAREST_EVEN, which obviously has correctness
issues.

First, we add a function get_bfp_rounding_mode_from_fpc to extract the
rounding mode from the guest FPC when building IR.
Second, have encode_bfp_rounding_mode invoke get_bfp_rounding_mode_from_fpc
whenever a S390_ROUND_PER_FPC is requested.
Third, in insn selection track whether (and if so to what value) the
rounding mode was set for the IRSB at hand. That way redundant assignments
can be avoided. This works well because the IR optimiser do a fine job
recognising end eliminating the expressions returned earlier from
get_bfp_rounding_more_from_fpc. So they get all mapped to the same IRTemp.
Note, VEX r2524 is essential to get this behaviour.
Fourth, remove the rounding more from the bfp_unop/binop/triop s390_insns.
Fifth, if the rounding mode can be set on the insn directly, prefer that
over setting it in the FPC and picking it up from there.

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

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 aa3e1331dac37d3118a1c55e5fc72f8a9cf7f4b3..d796171e77e31cac1124675c5b4f388e8fb2c7fe 100644 (file)
@@ -425,31 +425,6 @@ yield_if(IRExpr *condition)
                     S390X_GUEST_OFFSET(guest_IA)));
 }
 
-/* Encode the s390 rounding mode as it appears in the m3 field of certain
-   instructions to VEX's IRRoundingMode. Rounding modes that cannot be
-   represented in VEX are converted to Irrm_NEAREST. The rationale is, that
-   Irrm_NEAREST refers to IEEE 754's roundTiesToEven which the standard
-   considers the default rounding mode (4.3.3). */
-static IRTemp
-encode_bfp_rounding_mode(UChar mode)
-{
-   IRExpr *rm;
-
-   switch (mode) {
-   case S390_ROUND_PER_FPC:       /* not supported */
-   case S390_ROUND_NEAREST_AWAY:  /* not supported */
-   case S390_ROUND_PREPARE_SHORT: /* not supported */
-   case S390_ROUND_NEAREST_EVEN:  rm = mkU32(Irrm_NEAREST); break;
-   case S390_ROUND_ZERO:          rm = mkU32(Irrm_ZERO);    break;
-   case S390_ROUND_POSINF:        rm = mkU32(Irrm_PosINF);  break;
-   case S390_ROUND_NEGINF:        rm = mkU32(Irrm_NegINF);  break;
-   default:
-      vpanic("encode_bfp_rounding_mode");
-   }
-
-   return mktemp(Ity_I32, rm);
-}
-
 static __inline__ IRExpr *get_fpr_dw0(UInt);
 static __inline__ void    put_fpr_dw0(UInt, IRExpr *);
 
@@ -1418,6 +1393,76 @@ get_fpc_w0(void)
 }
 
 
+/*------------------------------------------------------------*/
+/*--- Rounding modes                                       ---*/
+/*------------------------------------------------------------*/
+
+/* Extract the rounding mode from the guest FPC reg and encode it as an
+   IRRoundingMode:
+
+   rounding mode | s390 | IR
+   -------------------------
+   to nearest    |  00  | 00
+   to zero       |  01  | 11
+   to +infinity  |  10  | 10
+   to -infinity  |  11  | 01
+
+   So:  IR = (4 - s390) & 3
+*/
+static IRExpr *
+get_rounding_mode_from_fpc(void)
+{
+   IRTemp fpc_bits = newTemp(Ity_I32);
+
+   /* For z196 and later the bfp rounding mode is stored in bits [29:31].
+      Prior to that bits [30:31] contained the bfp rounding mode with
+      bit 29 being unused and having a value of 0. So we can always
+      extract the least significant 3 bits. */
+   assign(fpc_bits, binop(Iop_And32, get_fpc_w0(), mkU32(7)));
+
+   /* fixs390:
+
+
+      if (! s390_host_has_fpext && rounding_mode > 3) {
+         emulation warning @ runtime and
+         set fpc to round nearest
+      }
+   */
+
+   /* For now silently adjust an unsupported rounding mode to "nearest" */
+   IRExpr *rm_s390 = mkite(binop(Iop_CmpLE32S, mkexpr(fpc_bits), mkU32(3)),
+                           mkexpr(fpc_bits),
+                           mkU32(S390_FPC_ROUND_NEAREST_EVEN));
+
+   // rm_IR = (4 - rm_s390) & 3;
+   return binop(Iop_And32, binop(Iop_Sub32, mkU32(4), rm_s390), mkU32(3));
+}
+
+/* Encode the s390 rounding mode as it appears in the m3 field of certain
+   instructions to VEX's IRRoundingMode. Rounding modes that cannot be
+   represented in VEX are converted to Irrm_NEAREST. The rationale is, that
+   Irrm_NEAREST refers to IEEE 754's roundTiesToEven which the standard
+   considers the default rounding mode (4.3.3). */
+static IRTemp
+encode_bfp_rounding_mode(UChar mode)
+{
+   IRExpr *rm;
+
+   switch (mode) {
+   case S390_ROUND_PER_FPC:       rm = get_rounding_mode_from_fpc(); break;
+   case S390_ROUND_NEAREST_AWAY:  /* not supported */
+   case S390_ROUND_PREPARE_SHORT: /* not supported */
+   case S390_ROUND_NEAREST_EVEN:  rm = mkU32(Irrm_NEAREST); break;
+   case S390_ROUND_ZERO:          rm = mkU32(Irrm_ZERO);    break;
+   case S390_ROUND_POSINF:        rm = mkU32(Irrm_PosINF);  break;
+   case S390_ROUND_NEGINF:        rm = mkU32(Irrm_NegINF);  break;
+   default:
+      vpanic("encode_bfp_rounding_mode");
+   }
+
+   return mktemp(Ity_I32, rm);
+}
+
 /*------------------------------------------------------------*/
 /*--- Build IR for formats                                 ---*/
 /*------------------------------------------------------------*/
index b79fc9d740b88b7f7ce9409788a11518040010ce..e7c40ad28d3cb0968e0d63f5d95514a1130df6c8 100644 (file)
@@ -9,6 +9,7 @@
    framework.
 
    Copyright IBM Corp. 2010-2012
+   Copyright (C) 2012-2012  Florian Krohm   (britzel@acm.org)
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -707,6 +708,10 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn)
    case S390_INSN_GADD:
       break;
 
+   case S390_INSN_SET_FPCRM:
+      addHRegUse(u, HRmRead,  insn->variant.set_fpcrm.mode);
+      break;
+
    case S390_INSN_EVCHECK:
       s390_amode_get_reg_usage(u, insn->variant.evcheck.counter);
       s390_amode_get_reg_usage(u, insn->variant.evcheck.fail_addr);
@@ -931,6 +936,11 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn)
    case S390_INSN_GADD:
       break;
 
+   case S390_INSN_SET_FPCRM:
+      insn->variant.set_fpcrm.mode =
+         lookupHRegRemap(m, insn->variant.set_fpcrm.mode);
+      break;
+
    case S390_INSN_EVCHECK:
       s390_amode_map_regs(m, insn->variant.evcheck.counter);
       s390_amode_map_regs(m, insn->variant.evcheck.fail_addr);
@@ -4652,7 +4662,7 @@ s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
 
 s390_insn *
 s390_insn_bfp_triop(UChar size, s390_bfp_triop_t tag, HReg dst, HReg op2,
-                    HReg op3, s390_round_t rounding_mode)
+                    HReg op3)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -4664,15 +4674,13 @@ s390_insn_bfp_triop(UChar size, s390_bfp_triop_t tag, HReg dst, HReg op2,
    insn->variant.bfp_triop.dst = dst;
    insn->variant.bfp_triop.op2 = op2;
    insn->variant.bfp_triop.op3 = op3;
-   insn->variant.bfp_triop.rounding_mode = rounding_mode;
 
    return insn;
 }
 
 
 s390_insn *
-s390_insn_bfp_binop(UChar size, s390_bfp_binop_t tag, HReg dst, HReg op2,
-                    s390_round_t rounding_mode)
+s390_insn_bfp_binop(UChar size, s390_bfp_binop_t tag, HReg dst, HReg op2)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -4685,15 +4693,13 @@ s390_insn_bfp_binop(UChar size, s390_bfp_binop_t tag, HReg dst, HReg op2,
    insn->variant.bfp_binop.op2_hi = op2;
    insn->variant.bfp_binop.dst_lo = INVALID_HREG;
    insn->variant.bfp_binop.op2_lo = INVALID_HREG;
-   insn->variant.bfp_binop.rounding_mode = rounding_mode;
 
    return insn;
 }
 
 
 s390_insn *
-s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst, HReg op,
-                   s390_round_t rounding_mode)
+s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst, HReg op)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -4706,7 +4712,6 @@ s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst, HReg op,
    insn->variant.bfp_unop.op_hi  = op;
    insn->variant.bfp_unop.dst_lo = INVALID_HREG;
    insn->variant.bfp_unop.op_lo  = INVALID_HREG;
-   insn->variant.bfp_unop.rounding_mode = rounding_mode;
 
    return insn;
 }
@@ -4769,8 +4774,7 @@ is_valid_bfp128_regpair(HReg hi, HReg lo)
 
 s390_insn *
 s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
-                       HReg dst_lo, HReg op2_hi, HReg op2_lo,
-                       s390_round_t rounding_mode)
+                       HReg dst_lo, HReg op2_hi, HReg op2_lo)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -4785,7 +4789,6 @@ s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
    insn->variant.bfp_binop.dst_lo = dst_lo;
    insn->variant.bfp_binop.op2_hi = op2_hi;
    insn->variant.bfp_binop.op2_lo = op2_lo;
-   insn->variant.bfp_binop.rounding_mode = rounding_mode;
 
    return insn;
 }
@@ -4793,8 +4796,7 @@ s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
 
 s390_insn *
 s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t tag, HReg dst_hi,
-                      HReg dst_lo, HReg op_hi, HReg op_lo,
-                      s390_round_t rounding_mode)
+                      HReg dst_lo, HReg op_hi, HReg op_lo)
 {
    s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
 
@@ -4809,7 +4811,6 @@ s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t tag, HReg dst_hi,
    insn->variant.bfp_unop.dst_lo = dst_lo;
    insn->variant.bfp_unop.op_hi = op_hi;
    insn->variant.bfp_unop.op_lo = op_lo;
-   insn->variant.bfp_unop.rounding_mode = rounding_mode;
 
    return insn;
 }
@@ -4930,6 +4931,21 @@ s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value)
 }
 
 
+s390_insn *
+s390_insn_set_fpcrm(UChar size, HReg mode)
+{
+   vassert(size == 4);
+
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_SET_FPCRM;
+   insn->size = size;
+   insn->variant.set_fpcrm.mode = mode;
+
+   return insn;
+}
+
+
 s390_insn *
 s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
                   Bool to_fast_entry)
@@ -5435,6 +5451,10 @@ s390_insn_as_string(const s390_insn *insn)
                    insn->variant.gadd.value);
       break;
 
+   case S390_INSN_SET_FPCRM:
+      s390_sprintf(buf, "%M %R", "v-set-fpcrm", insn->variant.set_fpcrm.mode);
+      break;
+
    case S390_INSN_EVCHECK:
       s390_sprintf(buf, "%M counter = %A, fail-addr = %A", "v-evcheck",
                    insn->variant.evcheck.counter,
@@ -7262,59 +7282,26 @@ s390_insn_cond_move_emit(UChar *buf, const s390_insn *insn)
 }
 
 
-/* Little helper function to the rounding mode in the real FPC
-   register */
-static UChar *
-s390_set_fpc_rounding_mode(UChar *buf, s390_round_t rounding_mode)
-{
-   UChar bits;
-
-   /* Determine BFP rounding bits */
-   switch (rounding_mode) {
-   case S390_ROUND_NEAREST_EVEN: bits = 0; break;
-   case S390_ROUND_ZERO:         bits = 1; break;
-   case S390_ROUND_POSINF:       bits = 2; break;
-   case S390_ROUND_NEGINF:       bits = 3; break;
-   default: vpanic("invalid rounding mode\n");
-   }
-
-   /* Copy FPC from guest state to R0 and OR in the new rounding mode */
-   buf = s390_emit_L(buf, R0, 0, S390_REGNO_GUEST_STATE_POINTER,
-                     S390X_GUEST_OFFSET(guest_fpc));   // r0 = guest_fpc
-
-   buf = s390_emit_NILL(buf, R0, 0xFFFC); /* Clear out right-most 2 bits */
-   buf = s390_emit_OILL(buf, R0, bits);   /* OR in the new rounding mode */
-   buf = s390_emit_SFPC(buf, R0, 0);      /* Load FPC register from R0 */
-
-   return buf;
-}
-
-
 static UChar *
 s390_insn_bfp_triop_emit(UChar *buf, const s390_insn *insn)
 {
    UInt r1 = hregNumber(insn->variant.bfp_triop.dst);
    UInt r2 = hregNumber(insn->variant.bfp_triop.op2);
    UInt r3 = hregNumber(insn->variant.bfp_triop.op3);
-   s390_round_t rounding_mode = insn->variant.bfp_triop.rounding_mode;
-
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
-   }
 
    switch (insn->size) {
    case 4:
       switch (insn->variant.bfp_triop.tag) {
-      case S390_BFP_MADD:  buf = s390_emit_MAEBR(buf, r1, r3, r2); break;
-      case S390_BFP_MSUB:  buf = s390_emit_MSEBR(buf, r1, r3, r2); break;
+      case S390_BFP_MADD:  return s390_emit_MAEBR(buf, r1, r3, r2);
+      case S390_BFP_MSUB:  return s390_emit_MSEBR(buf, r1, r3, r2);
       default:  goto fail;
       }
       break;
 
    case 8:
       switch (insn->variant.bfp_triop.tag) {
-      case S390_BFP_MADD:  buf = s390_emit_MADBR(buf, r1, r3, r2); break;
-      case S390_BFP_MSUB:  buf = s390_emit_MSDBR(buf, r1, r3, r2); break;
+      case S390_BFP_MADD:  return s390_emit_MADBR(buf, r1, r3, r2);
+      case S390_BFP_MSUB:  return s390_emit_MSDBR(buf, r1, r3, r2);
       default:  goto fail;
       }
       break;
@@ -7322,13 +7309,6 @@ s390_insn_bfp_triop_emit(UChar *buf, const s390_insn *insn)
    default:  goto fail;
    }
 
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      /* Restore FPC register from guest state */
-      buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
-                           S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
-   }
-   return buf;
-
  fail:
    vpanic("s390_insn_bfp_triop_emit");
 }
@@ -7339,39 +7319,34 @@ s390_insn_bfp_binop_emit(UChar *buf, const s390_insn *insn)
 {
    UInt r1 = hregNumber(insn->variant.bfp_binop.dst_hi);
    UInt r2 = hregNumber(insn->variant.bfp_binop.op2_hi);
-   s390_round_t rounding_mode = insn->variant.bfp_binop.rounding_mode;
-
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
-   }
 
    switch (insn->size) {
    case 4:
       switch (insn->variant.bfp_binop.tag) {
-      case S390_BFP_ADD:     buf = s390_emit_AEBR(buf, r1, r2);  break;
-      case S390_BFP_SUB:     buf = s390_emit_SEBR(buf, r1, r2);  break;
-      case S390_BFP_MUL:     buf = s390_emit_MEEBR(buf, r1, r2); break;
-      case S390_BFP_DIV:     buf = s390_emit_DEBR(buf, r1, r2);  break;
+      case S390_BFP_ADD:     return s390_emit_AEBR(buf, r1, r2);
+      case S390_BFP_SUB:     return s390_emit_SEBR(buf, r1, r2);
+      case S390_BFP_MUL:     return s390_emit_MEEBR(buf, r1, r2);
+      case S390_BFP_DIV:     return s390_emit_DEBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
 
    case 8:
       switch (insn->variant.bfp_binop.tag) {
-      case S390_BFP_ADD:     buf = s390_emit_ADBR(buf, r1, r2); break;
-      case S390_BFP_SUB:     buf = s390_emit_SDBR(buf, r1, r2); break;
-      case S390_BFP_MUL:     buf = s390_emit_MDBR(buf, r1, r2); break;
-      case S390_BFP_DIV:     buf = s390_emit_DDBR(buf, r1, r2); break;
+      case S390_BFP_ADD:     return s390_emit_ADBR(buf, r1, r2);
+      case S390_BFP_SUB:     return s390_emit_SDBR(buf, r1, r2);
+      case S390_BFP_MUL:     return s390_emit_MDBR(buf, r1, r2);
+      case S390_BFP_DIV:     return s390_emit_DDBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
 
    case 16:
       switch (insn->variant.bfp_binop.tag) {
-      case S390_BFP_ADD:     buf = s390_emit_AXBR(buf, r1, r2); break;
-      case S390_BFP_SUB:     buf = s390_emit_SXBR(buf, r1, r2); break;
-      case S390_BFP_MUL:     buf = s390_emit_MXBR(buf, r1, r2); break;
-      case S390_BFP_DIV:     buf = s390_emit_DXBR(buf, r1, r2); break;
+      case S390_BFP_ADD:     return s390_emit_AXBR(buf, r1, r2);
+      case S390_BFP_SUB:     return s390_emit_SXBR(buf, r1, r2);
+      case S390_BFP_MUL:     return s390_emit_MXBR(buf, r1, r2);
+      case S390_BFP_DIV:     return s390_emit_DXBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
@@ -7379,13 +7354,6 @@ s390_insn_bfp_binop_emit(UChar *buf, const s390_insn *insn)
    default:  goto fail;
    }
 
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      /* Restore FPC register from guest state */
-      buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
-                           S390X_GUEST_OFFSET(guest_fpc));
-   }
-   return buf;
-
  fail:
    vpanic("s390_insn_bfp_binop_emit");
 }
@@ -7396,47 +7364,40 @@ s390_insn_bfp_unop_emit(UChar *buf, const s390_insn *insn)
 {
    UInt  r1 = hregNumber(insn->variant.bfp_unop.dst_hi);
    UInt  r2 = hregNumber(insn->variant.bfp_unop.op_hi);
-   s390_round_t rounding_mode = insn->variant.bfp_unop.rounding_mode;
-
-   /* For all other insns if a special rounding mode is requested,
-      we need to set the FPC first and restore it later. */
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
-   }
 
    switch (insn->variant.bfp_unop.tag) {
    case S390_BFP_ABS:
       switch (insn->size) {
-      case 4:   buf = s390_emit_LPEBR(buf, r1, r2); break;
-      case 8:   buf = s390_emit_LPDBR(buf, r1, r2); break;
-      case 16:  buf = s390_emit_LPXBR(buf, r1, r2); break;
+      case 4:   return s390_emit_LPEBR(buf, r1, r2);
+      case 8:   return s390_emit_LPDBR(buf, r1, r2);
+      case 16:  return s390_emit_LPXBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
 
    case S390_BFP_NABS:
       switch (insn->size) {
-      case 4:   buf = s390_emit_LNEBR(buf, r1, r2); break;
-      case 8:   buf = s390_emit_LNDBR(buf, r1, r2); break;
-      case 16:  buf = s390_emit_LNXBR(buf, r1, r2); break;
+      case 4:   return s390_emit_LNEBR(buf, r1, r2);
+      case 8:   return s390_emit_LNDBR(buf, r1, r2);
+      case 16:  return s390_emit_LNXBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
 
    case S390_BFP_NEG:
       switch (insn->size) {
-      case 4:   buf = s390_emit_LCEBR(buf, r1, r2); break;
-      case 8:   buf = s390_emit_LCDBR(buf, r1, r2); break;
-      case 16:  buf = s390_emit_LCXBR(buf, r1, r2); break;
+      case 4:   return s390_emit_LCEBR(buf, r1, r2);
+      case 8:   return s390_emit_LCDBR(buf, r1, r2);
+      case 16:  return s390_emit_LCXBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
 
    case S390_BFP_SQRT:
       switch (insn->size) {
-      case 4:   buf = s390_emit_SQEBR(buf, r1, r2); break;
-      case 8:   buf = s390_emit_SQDBR(buf, r1, r2); break;
-      case 16:  buf = s390_emit_SQXBR(buf, r1, r2); break;
+      case 4:   return s390_emit_SQEBR(buf, r1, r2);
+      case 8:   return s390_emit_SQDBR(buf, r1, r2);
+      case 16:  return s390_emit_SQXBR(buf, r1, r2);
       default:  goto fail;
       }
       break;
@@ -7444,13 +7405,6 @@ s390_insn_bfp_unop_emit(UChar *buf, const s390_insn *insn)
    default: goto fail;
    }
 
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      /* Restore FPC register from guest state */
-      buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
-                           S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
-   }
-   return buf;
-
  fail:
    vpanic("s390_insn_bfp_unop_emit");
 }
@@ -7461,8 +7415,10 @@ s390_insn_bfp_convert_emit(UChar *buf, const s390_insn *insn)
 {
    UInt  r1 = hregNumber(insn->variant.bfp_convert.dst_hi);
    UInt  r2 = hregNumber(insn->variant.bfp_convert.op_hi);
-   s390_round_t rounding_mode = insn->variant.bfp_convert.rounding_mode;
-   s390_round_t m3 = rounding_mode;
+   s390_round_t m3 = insn->variant.bfp_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.bfp_convert.tag) {
       /* Convert to fixed */
@@ -7474,64 +7430,42 @@ s390_insn_bfp_convert_emit(UChar *buf, const s390_insn *insn)
    case S390_BFP_F128_TO_I64: return s390_emit_CGXBR(buf, m3, r1, r2);
 
       /* Convert to logical */
-      /* We leave m4 as 0 - as gcc */
-   case S390_BFP_F32_TO_U32:  return s390_emit_CLFEBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F64_TO_U32:  return s390_emit_CLFDBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F128_TO_U32: return s390_emit_CLFXBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F32_TO_U64:  return s390_emit_CLGEBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F64_TO_U64:  return s390_emit_CLGDBR(buf, m3, 0, r1, r2);
-   case S390_BFP_F128_TO_U64: return s390_emit_CLGXBR(buf, m3, 0, r1, r2);
-
-      /* Conversion to 128-bit never requires a rounding mode */
-   case S390_BFP_I32_TO_F128: return s390_emit_CXFBRA(buf, 0, 0, r1, r2);
-   case S390_BFP_I64_TO_F128: return s390_emit_CXGBRA(buf, 0, 0, r1, r2);
-   case S390_BFP_U32_TO_F128: return s390_emit_CXLFBR(buf, 0, 0, r1, r2);
-   case S390_BFP_U64_TO_F128: return s390_emit_CXLGBR(buf, 0, 0, r1, r2);
+   case S390_BFP_F32_TO_U32:  return s390_emit_CLFEBR(buf, m3, m4, r1, r2);
+   case S390_BFP_F64_TO_U32:  return s390_emit_CLFDBR(buf, m3, m4, r1, r2);
+   case S390_BFP_F128_TO_U32: return s390_emit_CLFXBR(buf, m3, m4, r1, r2);
+   case S390_BFP_F32_TO_U64:  return s390_emit_CLGEBR(buf, m3, m4, r1, r2);
+   case S390_BFP_F64_TO_U64:  return s390_emit_CLGDBR(buf, m3, m4, r1, r2);
+   case S390_BFP_F128_TO_U64: return s390_emit_CLGXBR(buf, m3, m4, r1, r2);
+
+      /* Convert from fixed */
+   case S390_BFP_I32_TO_F32:  return s390_emit_CEFBRA(buf, m3, m4, r1, r2);
+   case S390_BFP_I32_TO_F64:  return s390_emit_CDFBRA(buf,  0, m4, r1, r2);
+   case S390_BFP_I32_TO_F128: return s390_emit_CXFBRA(buf,  0, m4, r1, r2);
+   case S390_BFP_I64_TO_F32:  return s390_emit_CEGBRA(buf, m3, m4, r1, r2);
+   case S390_BFP_I64_TO_F64:  return s390_emit_CDGBRA(buf, m3, m4, r1, r2);
+   case S390_BFP_I64_TO_F128: return s390_emit_CXGBRA(buf,  0, m4, r1, r2);
+
+      /* Convert from logical */
+   case S390_BFP_U32_TO_F32:  return s390_emit_CELFBR(buf, m3, m4, r1, r2);
+   case S390_BFP_U32_TO_F64:  return s390_emit_CDLFBR(buf, m3, m4, r1, r2);
+   case S390_BFP_U32_TO_F128: return s390_emit_CXLFBR(buf, m3, m4, r1, r2);
+   case S390_BFP_U64_TO_F32:  return s390_emit_CELGBR(buf, m3, m4, r1, r2);
+   case S390_BFP_U64_TO_F64:  return s390_emit_CDLGBR(buf, m3, m4, r1, r2);
+   case S390_BFP_U64_TO_F128: return s390_emit_CXLGBR(buf, m3, m4, r1, r2);
+
+      /* Load lengthened */
+   case S390_BFP_F32_TO_F64:  return s390_emit_LDEBR(buf, r1, r2);
    case S390_BFP_F32_TO_F128: return s390_emit_LXEBR(buf, r1, r2);
    case S390_BFP_F64_TO_F128: return s390_emit_LXDBR(buf, r1, r2);
-   default: break;
-   }
 
-   /* For all other insns if a special rounding mode is requested,
-      we need to set the FPC first and restore it later. */
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
-   }
-
-   switch (insn->variant.bfp_convert.tag) {
-   case S390_BFP_I32_TO_F32:  buf = s390_emit_CEFBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_I32_TO_F64:  buf = s390_emit_CDFBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_I32_TO_F128: buf = s390_emit_CXFBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_I64_TO_F32:  buf = s390_emit_CEGBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_I64_TO_F64:  buf = s390_emit_CDGBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_I64_TO_F128: buf = s390_emit_CXGBRA(buf, 0, 0, r1, r2); break;
-
-   /* We leave m4 as 0 - as gcc */
-   case S390_BFP_U32_TO_F32:  buf = s390_emit_CELFBR(buf, m3, 0, r1, r2); break;
-   case S390_BFP_U32_TO_F64:  buf = s390_emit_CDLFBR(buf, m3, 0, r1, r2); break;
-   case S390_BFP_U32_TO_F128: buf = s390_emit_CXLFBR(buf, m3, 0, r1, r2); break;
-   case S390_BFP_U64_TO_F32:  buf = s390_emit_CELGBR(buf, m3, 0, r1, r2); break;
-   case S390_BFP_U64_TO_F64:  buf = s390_emit_CDLGBR(buf, m3, 0, r1, r2); break;
-   case S390_BFP_U64_TO_F128: buf = s390_emit_CXLGBR(buf, m3, 0, r1, r2); break;
-
-   case S390_BFP_F32_TO_F64:  buf = s390_emit_LDEBR(buf, r1, r2); break;
-   case S390_BFP_F32_TO_F128: buf = s390_emit_LXEBR(buf, r1, r2); break;
-   case S390_BFP_F64_TO_F32:  buf = s390_emit_LEDBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_F64_TO_F128: buf = s390_emit_LXDBR(buf, r1, r2); break;
-
-   case S390_BFP_F128_TO_F32: buf = s390_emit_LEXBRA(buf, 0, 0, r1, r2); break;
-   case S390_BFP_F128_TO_F64: buf = s390_emit_LDXBRA(buf, 0, 0, r1, r2); break;
+      /* Load rounded */
+   case S390_BFP_F64_TO_F32:  return s390_emit_LEDBRA(buf, m3, m4, r1, r2);
+   case S390_BFP_F128_TO_F32: return s390_emit_LEXBRA(buf, m3, m4, r1, r2);
+   case S390_BFP_F128_TO_F64: return s390_emit_LDXBRA(buf, m3, m4, r1, r2);
 
    default: goto fail;
    }
 
-   if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
-      /* Restore FPC register from guest state */
-      buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
-                           S390X_GUEST_OFFSET(guest_fpc));   // fpc = guest_fpc
-   }
-   return buf;
-
  fail:
    vpanic("s390_insn_bfp_convert_emit");
 }
@@ -7583,6 +7517,23 @@ s390_insn_gadd_emit(UChar *buf, const s390_insn *insn)
 }
 
 
+static UChar *
+s390_insn_set_fpcrm_emit(UChar *buf, const s390_insn *insn)
+{
+   UInt mode = hregNumber(insn->variant.set_fpcrm.mode);
+
+   /* Copy FPC from guest state to R0 and OR in the new rounding mode */
+   buf = s390_emit_L(buf, R0, 0, S390_REGNO_GUEST_STATE_POINTER,
+                     S390X_GUEST_OFFSET(guest_fpc));   // r0 = guest_fpc
+
+   buf = s390_emit_NILL(buf, R0, 0xFFF8); /* Clear out right-most 3 bits */
+   buf = s390_emit_OR(buf, R0, mode);     /* OR in the new rounding mode */
+   buf = s390_emit_SFPC(buf, R0, 0);      /* Load FPC register from R0 */
+
+   return buf;
+}
+
+
 /* Define convenience functions needed for translation chaining.
    Any changes need to be applied to the functions in concert. */
 
@@ -8123,6 +8074,10 @@ emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn,
       end = s390_insn_gadd_emit(buf, insn);
       break;
 
+   case S390_INSN_SET_FPCRM:
+      end = s390_insn_set_fpcrm_emit(buf, insn);
+      break;
+
    case S390_INSN_PROFINC:
       end = s390_insn_profinc_emit(buf, insn);
       /* Tell the caller .. */
index 9a4be9170066162d9dc6672dc41646280b115a98..72275dafd27406810bf5a1bc4832ae674a66d195 100644 (file)
@@ -141,6 +141,7 @@ typedef enum {
    S390_INSN_MFENCE,
    S390_INSN_GZERO,   /* Assign zero to a guest register */
    S390_INSN_GADD,    /* Add a value to a guest register */
+   S390_INSN_SET_FPCRM, /* Set the rounding mode in the FPC */
    /* The following 5 insns are mandated by translation chaining */
    S390_INSN_XDIRECT,     /* direct transfer to guest address */
    S390_INSN_XINDIR,      /* indirect transfer to guest address */
@@ -268,6 +269,19 @@ typedef enum {
 } s390_round_t;
 
 
+/* Rounding mode as it is encoded in bits [29:31] of the FPC register.
+   Only rounding modes 0..3 are universally supported. Others require
+   additional hardware facilities. */
+typedef enum {
+   S390_FPC_ROUND_NEAREST_EVEN  = 0,
+   S390_FPC_ROUND_ZERO          = 1,
+   S390_FPC_ROUND_POSINF        = 2,
+   S390_FPC_ROUND_NEGINF        = 3,
+   /* 4,5,6 are not allowed */
+   S390_FPC_ROUND_PREPARE_SHORT = 7
+} s390_fpc_round_t;
+
+
 /* Invert the condition code */
 static __inline__ s390_cc_t
 s390_cc_invert(s390_cc_t cond)
@@ -389,7 +403,7 @@ typedef struct {
          to store only one register of the pair in order to represent it.
          We chose not to do that as being explicit about all registers
          helps with debugging and does not require special handling in 
-         e.g. s390_insn_get_reg_usage, It'd be all to easy to forget about
+         e.g. s390_insn_get_reg_usage, It'd be all too easy to forget about
          the "other" register in a pair if it is implicit.
 
          The convention for all fp s390_insn is that the _hi register will
@@ -405,14 +419,12 @@ typedef struct {
       /* There are currently no ternary 128-bit BFP operations. */
       struct {
          s390_bfp_triop_t tag;
-         s390_round_t     rounding_mode;
          HReg         dst;
          HReg         op2;
          HReg         op3;
       } bfp_triop;
       struct {
          s390_bfp_binop_t tag;
-         s390_round_t     rounding_mode;
          HReg         dst_hi; /* 128-bit result high part; 32/64-bit result */
          HReg         dst_lo; /* 128-bit result low part */
          HReg         op2_hi; /* 128-bit operand high part; 32/64-bit opnd */
@@ -420,7 +432,6 @@ typedef struct {
       } bfp_binop;
       struct {
          s390_bfp_unop_t  tag;
-         s390_round_t     rounding_mode;
          HReg         dst_hi; /* 128-bit result high part; 32/64-bit result */
          HReg         dst_lo; /* 128-bit result low part */
          HReg         op_hi;  /* 128-bit operand high part; 32/64-bit opnd */
@@ -451,6 +462,9 @@ typedef struct {
          UChar            delta;
          ULong            value;  /* for debugging only */
       } gadd;
+      struct {
+         HReg             mode;
+      } set_fpcrm;
 
       /* The next 5 entries are generic to support translation chaining */
 
@@ -520,21 +534,19 @@ s390_insn *s390_insn_compare(UChar size, HReg dst, s390_opnd_RMI opnd,
                              Bool signed_comparison);
 s390_insn *s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
                                  HChar *name, HReg dst);
-s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst, HReg op2,
-                               HReg op3, s390_round_t);
-s390_insn *s390_insn_bfp_binop(UChar size, s390_bfp_binop_t, HReg dst, HReg op2,
-                               s390_round_t);
+s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst,
+                               HReg op2, HReg op3);
+s390_insn *s390_insn_bfp_binop(UChar size, s390_bfp_binop_t, HReg dst,
+                               HReg op2);
 s390_insn *s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst,
-                              HReg op, s390_round_t);
+                              HReg op);
 s390_insn *s390_insn_bfp_compare(UChar size, HReg dst, HReg op1, HReg op2);
 s390_insn *s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst,
                                  HReg op, s390_round_t);
 s390_insn *s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t, HReg dst_hi,
-                                  HReg dst_lo, HReg op2_hi, HReg op2_lo,
-                                  s390_round_t);
+                                  HReg dst_lo, HReg op2_hi, HReg op2_lo);
 s390_insn *s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t, HReg dst_hi,
-                                 HReg dst_lo, HReg op_hi, HReg op_lo,
-                                 s390_round_t);
+                                 HReg dst_lo, HReg op_hi, HReg op_lo);
 s390_insn *s390_insn_bfp128_compare(UChar size, HReg dst, HReg op1_hi,
                                     HReg op1_lo, HReg op2_hi, HReg op2_lo);
 s390_insn *s390_insn_bfp128_convert_to(UChar size, s390_conv_t,
@@ -545,6 +557,7 @@ s390_insn *s390_insn_bfp128_convert_from(UChar size, s390_conv_t,
 s390_insn *s390_insn_mfence(void);
 s390_insn *s390_insn_gzero(UChar size, UInt offset);
 s390_insn *s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value);
+s390_insn *s390_insn_set_fpcrm(UChar size, HReg mode);
 
 /* Five for translation chaining */
 s390_insn *s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
index 0a47105596b00515a6bf2c82d761f0fadb48f724..0acf262c815a5472d7dc7d5659a0b8d18f16a9a6 100644 (file)
@@ -9,6 +9,7 @@
    framework.
 
    Copyright IBM Corp. 2010-2012
+   Copyright (C) 2012-2012  Florian Krohm   (britzel@acm.org)
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -112,6 +113,8 @@ typedef struct {
    UInt         vreg_ctr;
    UInt         hwcaps;
 
+   IRExpr      *previous_bfp_rounding_mode;
+
    ULong        old_value[NUM_TRACKED_REGS];
 
    /* The next two are for translation chaining */
@@ -530,24 +533,77 @@ doHelperCall(ISelEnv *env, Bool passBBP, IRExpr *guard,
 }
 
 
-/* Given an expression representing a rounding mode using IRRoundingMode
-   encoding convert it to an s390_round_t value.  */
+/*---------------------------------------------------------*/
+/*--- BFP helper functions                              ---*/
+/*---------------------------------------------------------*/
+
+/* Set the BFP rounding mode in the FPC. This function is called for
+   all non-conversion BFP instructions as those will always get the
+   rounding mode from the FPC. */
+static void 
+set_bfp_rounding_mode_in_fpc(ISelEnv *env, IRExpr *irrm)
+{
+   vassert(typeOfIRExpr(env->type_env, irrm) == Ity_I32);
+
+   /* Do we need to do anything? */
+   if (env->previous_bfp_rounding_mode &&
+       env->previous_bfp_rounding_mode->tag == Iex_RdTmp &&
+       irrm->tag == Iex_RdTmp &&
+       env->previous_bfp_rounding_mode->Iex.RdTmp.tmp == irrm->Iex.RdTmp.tmp) {
+      /* No - new mode is identical to previous mode.  */
+      return;
+   }
+
+   /* No luck - we better set it, and remember what we set it to. */
+   env->previous_bfp_rounding_mode = irrm;
+
+   /* The incoming rounding mode is in VEX IR encoding. Need to change
+      to s390.
+
+      rounding mode | s390 | IR
+      -------------------------
+      to nearest    |  00  | 00
+      to zero       |  01  | 11
+      to +infinity  |  10  | 10
+      to -infinity  |  11  | 01
+
+      So: s390 = (4 - IR) & 3
+   */
+   HReg ir = s390_isel_int_expr(env, irrm);
+
+   HReg mode = newVRegI(env);
+
+   addInstr(env, s390_insn_load_immediate(4, mode, 4));
+   addInstr(env, s390_insn_alu(4, S390_ALU_SUB, mode, s390_opnd_reg(ir)));
+   addInstr(env, s390_insn_alu(4, S390_ALU_AND, mode, s390_opnd_imm(3)));
+
+   addInstr(env, s390_insn_set_fpcrm(4, mode));
+}
+
+
+/* This function is invoked for insns that support a specification of
+   a rounding mode in the insn itself. In that case there is no need to
+   stick the rounding mode into the FPC -- a good thing. However, the
+   rounding mode must be known. */
 static s390_round_t
-decode_rounding_mode(IRExpr *rounding_expr)
+get_bfp_rounding_mode(ISelEnv *env, IRExpr *irrm)
 {
-   if (rounding_expr->tag == Iex_Const &&
-       rounding_expr->Iex.Const.con->tag == Ico_U32) {
-      IRRoundingMode mode = rounding_expr->Iex.Const.con->Ico.U32;
+   if (irrm->tag == Iex_Const) {          /* rounding mode is known */
+      vassert(irrm->Iex.Const.con->tag == Ico_U32);
+      IRRoundingMode mode = irrm->Iex.Const.con->Ico.U32;
 
       switch (mode) {
-      case Irrm_NEAREST:       return S390_ROUND_NEAREST_EVEN;
-      case Irrm_ZERO:          return S390_ROUND_ZERO;
-      case Irrm_PosINF:        return S390_ROUND_POSINF;
-      case Irrm_NegINF:        return S390_ROUND_NEGINF;
+      case Irrm_NEAREST:  return S390_ROUND_NEAREST_EVEN;
+      case Irrm_ZERO:     return S390_ROUND_ZERO;
+      case Irrm_PosINF:   return S390_ROUND_POSINF;
+      case Irrm_NegINF:   return S390_ROUND_NEGINF;
+      default:
+         vpanic("get_bfp_rounding_mode");
       }
    }
 
-   vpanic("decode_rounding_mode");
+   set_bfp_rounding_mode_in_fpc(env, irrm);
+   return S390_ROUND_PER_FPC;
 }
 
 
@@ -930,8 +986,9 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr)
          res  = newVRegI(env);
          h1   = s390_isel_float_expr(env, arg2);   /* Process operand */
 
-         rounding_mode = decode_rounding_mode(arg1);
-         addInstr(env, s390_insn_bfp_convert(size, conv, res, h1, rounding_mode));
+         rounding_mode = get_bfp_rounding_mode(env, arg1);
+         addInstr(env, s390_insn_bfp_convert(size, conv, res, h1,
+                                             rounding_mode));
          return res;
       }
 
@@ -950,7 +1007,7 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr)
          addInstr(env, s390_insn_move(8, f13, op_hi));
          addInstr(env, s390_insn_move(8, f15, op_lo));
 
-         rounding_mode = decode_rounding_mode(arg1);
+         rounding_mode = get_bfp_rounding_mode(env, arg1);
          addInstr(env, s390_insn_bfp128_convert_from(size, conv, res, f13, f15,
                                                      rounding_mode));
          return res;
@@ -1575,7 +1632,6 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
       IRExpr *left   = triop->arg2;
       IRExpr *right  = triop->arg3;
       s390_bfp_binop_t bfpop;
-      s390_round_t rounding_mode;
       HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15;
 
       s390_isel_float128_expr(&op1_hi, &op1_lo, env, left);  /* 1st operand */
@@ -1604,9 +1660,8 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
          goto irreducible;
       }
 
-      rounding_mode = decode_rounding_mode(triop->arg1);
-      addInstr(env, s390_insn_bfp128_binop(16, bfpop, f12, f14, f13,
-                                           f15, rounding_mode));
+      set_bfp_rounding_mode_in_fpc(env, triop->arg1);
+      addInstr(env, s390_insn_bfp128_binop(16, bfpop, f12, f14, f13, f15));
 
       /* Move result to virtual destination register */
       *dst_hi = newVRegF(env);
@@ -1620,8 +1675,6 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
       /* --------- BINARY OP --------- */
    case Iex_Binop: {
       HReg op_hi, op_lo, f12, f13, f14, f15;
-      s390_bfp_unop_t bfpop;
-      s390_round_t rounding_mode;
 
       /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
       f12 = make_fpr(12);
@@ -1637,11 +1690,9 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
          addInstr(env, s390_insn_move(8, f13, op_hi));
          addInstr(env, s390_insn_move(8, f15, op_lo));
 
-         bfpop = S390_BFP_SQRT;
-         rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
-
-         addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
-                                             rounding_mode));
+         set_bfp_rounding_mode_in_fpc(env, expr->Iex.Binop.arg1);
+         addInstr(env, s390_insn_bfp128_unop(16, S390_BFP_SQRT, f12, f14,
+                                             f13, f15));
 
          /* Move result to virtual destination registers */
          *dst_hi = newVRegF(env);
@@ -1664,7 +1715,6 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
    case Iex_Unop: {
       IRExpr *left = expr->Iex.Unop.arg;
       s390_bfp_unop_t bfpop;
-      s390_round_t rounding_mode;
       s390_conv_t conv;
       HReg op_hi, op_lo, op, f12, f13, f14, f15;
 
@@ -1694,9 +1744,7 @@ s390_isel_float128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env,
       addInstr(env, s390_insn_move(8, f13, op_hi));
       addInstr(env, s390_insn_move(8, f15, op_lo));
 
-      rounding_mode = S390_ROUND_NEAREST_EVEN;  /* will not be used later on */
-      addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
-                                          rounding_mode));
+      addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15));
       goto move_dst;
 
    convert_float:
@@ -1813,7 +1861,6 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
    case Iex_Qop: {
       HReg op1, op2, op3, dst;
       s390_bfp_triop_t bfpop;
-      s390_round_t rounding_mode;
 
       op1 = s390_isel_float_expr(env, expr->Iex.Qop.details->arg2);
       op2 = s390_isel_float_expr(env, expr->Iex.Qop.details->arg3);
@@ -1831,9 +1878,8 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
          goto irreducible;
       }
 
-      rounding_mode = decode_rounding_mode(expr->Iex.Qop.details->arg1);
-      addInstr(env, s390_insn_bfp_triop(size, bfpop, dst, op2, op3,
-                                        rounding_mode));
+      set_bfp_rounding_mode_in_fpc(env, expr->Iex.Qop.details->arg1);
+      addInstr(env, s390_insn_bfp_triop(size, bfpop, dst, op2, op3));
       return dst;
    }
 
@@ -1844,7 +1890,6 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       IRExpr *left   = triop->arg2;
       IRExpr *right  = triop->arg3;
       s390_bfp_binop_t bfpop;
-      s390_round_t rounding_mode;
       HReg h1, op2, dst;
 
       h1   = s390_isel_float_expr(env, left);  /* Process 1st operand */
@@ -1865,8 +1910,8 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
          goto irreducible;
       }
 
-      rounding_mode = decode_rounding_mode(triop->arg1);
-      addInstr(env, s390_insn_bfp_binop(size, bfpop, dst, op2, rounding_mode));
+      set_bfp_rounding_mode_in_fpc(env, triop->arg1);
+      addInstr(env, s390_insn_bfp_binop(size, bfpop, dst, op2));
       return dst;
    }
 
@@ -1876,7 +1921,6 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       IRExpr *irrm = expr->Iex.Binop.arg1;
       IRExpr *left = expr->Iex.Binop.arg2;
       HReg h1, dst;
-      s390_round_t rounding_mode;
       s390_conv_t  conv;
 
       switch (op) {
@@ -1884,9 +1928,8 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       case Iop_SqrtF64:
          h1  = s390_isel_float_expr(env, left);
          dst = newVRegF(env);
-         rounding_mode = decode_rounding_mode(irrm);
-         addInstr(env, s390_insn_bfp_unop(size, S390_BFP_SQRT, dst, h1,
-                                          rounding_mode));
+         set_bfp_rounding_mode_in_fpc(env, irrm);
+         addInstr(env, s390_insn_bfp_unop(size, S390_BFP_SQRT, dst, h1));
          return dst;
 
       case Iop_F64toF32:  conv = S390_BFP_F64_TO_F32; goto convert_float;
@@ -1905,12 +1948,21 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
          h1 = s390_isel_int_expr(env, left);
          goto convert;
 
-      convert:
+      convert: {
+         s390_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);
-         rounding_mode = decode_rounding_mode(irrm);
+         if (s390_host_has_fpext) {
+            rounding_mode = get_bfp_rounding_mode(env, irrm);
+         } else {
+            set_bfp_rounding_mode_in_fpc(env, irrm);
+            rounding_mode = S390_ROUND_PER_FPC;
+         }
          addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1,
                                              rounding_mode));
          return dst;
+      }
          
       default:
          goto irreducible;
@@ -1918,12 +1970,11 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       case Iop_F128toF64:
       case Iop_F128toF32: {
          HReg op_hi, op_lo, f13, f15;
+         s390_round_t rounding_mode;
 
          conv = op == Iop_F128toF32 ? S390_BFP_F128_TO_F32
                                     : S390_BFP_F128_TO_F64;
 
-         rounding_mode = decode_rounding_mode(irrm);
-
          s390_isel_float128_expr(&op_hi, &op_lo, env, left);
 
          /* We use non-virtual registers as pairs (f13, f15) */
@@ -1935,6 +1986,14 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
          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_bfp_rounding_mode(env, irrm);
+         } else {
+            set_bfp_rounding_mode_in_fpc(env, irrm);
+            rounding_mode = S390_ROUND_PER_FPC;
+         }
          addInstr(env, s390_insn_bfp128_convert_from(size, conv, dst, f13, f15,
                                                      rounding_mode));
          return dst;
@@ -1947,9 +2006,6 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       IROp    op   = expr->Iex.Unop.op;
       IRExpr *left = expr->Iex.Unop.arg;
       s390_bfp_unop_t bfpop;
-      /* No rounding mode is needed for these conversions. Provide the
-         default rounding mode. It will not be used. */
-      s390_round_t rounding_mode = S390_ROUND_NEAREST_EVEN;
       s390_conv_t conv;
       HReg h1, dst;
 
@@ -1997,7 +2053,10 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
 
       convert1:
          dst = newVRegF(env);
-         addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1, rounding_mode));
+         /* No rounding mode is needed for these conversions. Just stick
+            one in. It won't be used later on. */
+         addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1,
+                                             S390_ROUND_NEAREST_EVEN));
          return dst;
 
       default:
@@ -2007,7 +2066,7 @@ s390_isel_float_expr_wrk(ISelEnv *env, IRExpr *expr)
       /* Process operand */
       h1  = s390_isel_float_expr(env, left);
       dst = newVRegF(env);
-      addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1, rounding_mode));
+      addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1));
       return dst;
    }
 
@@ -2804,6 +2863,8 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
    env->vregmap   = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
    env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
 
+   env->previous_bfp_rounding_mode = NULL;
+
    /* and finally ... */
    env->hwcaps    = hwcaps_host;