Support the variety of "convert to/from fixed" and "load rounded" opcodes
that have an additional m3 and/or m4 field. 
Add emulation warning EmWarn_S390X_fpext_rounding and issue it in case
the current opcode cannot be emulated correctly (i.e. with the specified
rounding mode).
New function: emulation_warning. 
Part of fixing bugzilla #306098.


git-svn-id: svn://svn.valgrind.org/vex/trunk@2501 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index 3001152..8a3ec93 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -431,6 +431,7 @@
 encode_rounding_mode(UChar mode)
 {
    switch (mode) {
+   default: return Irrm_NEAREST;  // fixs390: for now
    case S390_ROUND_NEAREST_EVEN:  return Irrm_NEAREST;
    case S390_ROUND_ZERO:          return Irrm_ZERO;
    case S390_ROUND_POSINF:        return Irrm_PosINF;
@@ -469,11 +470,19 @@
 emulation_failure(VexEmNote fail_kind)
 {
    stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_EMNOTE), mkU32(fail_kind)));
-   put_IA(mkaddr_expr(guest_IA_next_instr));
    dis_res->whatNext = Dis_StopHere;
    dis_res->jk_StopHere = Ijk_EmFail;
 }
 
+/* Terminate the current IRSB with an emulation warning. */
+static void
+emulation_warning(VexEmNote warn_kind)
+{
+   stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_EMNOTE), mkU32(warn_kind)));
+   dis_res->whatNext = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_EmWarn;
+}
+
 /*------------------------------------------------------------*/
 /*--- IR Debugging aids.                                   ---*/
 /*------------------------------------------------------------*/
@@ -1712,6 +1721,16 @@
 }
 
 static void
+s390_format_RRF_UUFF(HChar *(*irgen)(UChar m3, UChar m4, UChar r1, UChar r2),
+                     UChar m3, UChar m4, UChar r1, UChar r2)
+{
+   HChar *mnm = irgen(m3, m4, r1, r2);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
+      s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), mnm, r1, m3, r2, m4);
+}
+
+static void
 s390_format_RRF_UUFR(HChar *(*irgen)(UChar m3, UChar m4, UChar r1, UChar r2),
                      UChar m3, UChar m4, UChar r1, UChar r2)
 {
@@ -1743,16 +1762,6 @@
 }
 
 static void
-s390_format_RRF_U0RF(HChar *(*irgen)(UChar r3, UChar r1, UChar r2),
-                     UChar r3, UChar r1, UChar r2)
-{
-   HChar *mnm = irgen(r3, r1, r2);
-
-   if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
-      s390_disasm(ENC4(MNM, GPR, UINT, FPR), mnm, r1, r3, r2);
-}
-
-static void
 s390_format_RRF_F0FF2(HChar *(*irgen)(UChar, UChar, UChar),
                       UChar r3, UChar r1, UChar r2)
 {
@@ -8237,18 +8246,25 @@
 }
 
 static HChar *
-s390_irgen_CEFBR(UChar r1, UChar r2)
+s390_irgen_CEFBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op2 = newTemp(Ity_I32);
 
    assign(op2, get_gpr_w1(r2));
-   put_fpr_w0(r1, binop(Iop_I32StoF32, mkU32(Irrm_NEAREST), mkexpr(op2)));
+   put_fpr_w0(r1, binop(Iop_I32StoF32, mkU32(encode_rounding_mode(m3)),
+                        mkexpr(op2)));
 
    return "cefbr";
 }
 
 static HChar *
-s390_irgen_CDFBR(UChar r1, UChar r2)
+s390_irgen_CDFBR(UChar m3 __attribute__((unused)),
+                 UChar m4 __attribute__((unused)), UChar r1, UChar r2)
 {
    IRTemp op2 = newTemp(Ity_I32);
 
@@ -8259,23 +8275,35 @@
 }
 
 static HChar *
-s390_irgen_CEGBR(UChar r1, UChar r2)
+s390_irgen_CEGBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op2 = newTemp(Ity_I64);
 
    assign(op2, get_gpr_dw0(r2));
-   put_fpr_w0(r1, binop(Iop_I64StoF32, mkU32(Irrm_NEAREST), mkexpr(op2)));
+   put_fpr_w0(r1, binop(Iop_I64StoF32, mkU32(encode_rounding_mode(m3)),
+                        mkexpr(op2)));
 
    return "cegbr";
 }
 
 static HChar *
-s390_irgen_CDGBR(UChar r1, UChar r2)
+s390_irgen_CDGBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op2 = newTemp(Ity_I64);
 
    assign(op2, get_gpr_dw0(r2));
-   put_fpr_dw0(r1, binop(Iop_I64StoF64, mkU32(Irrm_NEAREST), mkexpr(op2)));
+   put_fpr_dw0(r1, binop(Iop_I64StoF64, mkU32(encode_rounding_mode(m3)),
+                         mkexpr(op2)));
 
    return "cdgbr";
 }
@@ -8420,13 +8448,18 @@
 }
 
 static HChar *
-s390_irgen_CFEBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CFEBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F32);
    IRTemp result = newTemp(Ity_I32);
 
    assign(op, get_fpr_w0(r2));
-   assign(result, binop(Iop_F32toI32S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F32toI32S, mkU32(encode_rounding_mode(m3)),
           mkexpr(op)));
    put_gpr_w1(r1, mkexpr(result));
    s390_cc_thunk_putF(S390_CC_OP_BFP_32_TO_INT_32, op);
@@ -8435,13 +8468,18 @@
 }
 
 static HChar *
-s390_irgen_CFDBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CFDBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F64);
    IRTemp result = newTemp(Ity_I32);
 
    assign(op, get_fpr_dw0(r2));
-   assign(result, binop(Iop_F64toI32S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F64toI32S, mkU32(encode_rounding_mode(m3)),
           mkexpr(op)));
    put_gpr_w1(r1, mkexpr(result));
    s390_cc_thunk_putF(S390_CC_OP_BFP_64_TO_INT_32, op);
@@ -8450,13 +8488,18 @@
 }
 
 static HChar *
-s390_irgen_CGEBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CGEBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F32);
    IRTemp result = newTemp(Ity_I64);
 
    assign(op, get_fpr_w0(r2));
-   assign(result, binop(Iop_F32toI64S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F32toI64S, mkU32(encode_rounding_mode(m3)),
           mkexpr(op)));
    put_gpr_dw0(r1, mkexpr(result));
    s390_cc_thunk_putF(S390_CC_OP_BFP_32_TO_INT_64, op);
@@ -8465,13 +8508,18 @@
 }
 
 static HChar *
-s390_irgen_CGDBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CGDBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F64);
    IRTemp result = newTemp(Ity_I64);
 
    assign(op, get_fpr_dw0(r2));
-   assign(result, binop(Iop_F64toI64S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F64toI64S, mkU32(encode_rounding_mode(m3)),
           mkexpr(op)));
    put_gpr_dw0(r1, mkexpr(result));
    s390_cc_thunk_putF(S390_CC_OP_BFP_64_TO_INT_64, op);
@@ -8614,12 +8662,14 @@
 }
 
 static HChar *
-s390_irgen_LEDBR(UChar r1, UChar r2)
+s390_irgen_LEDBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
    IRTemp op = newTemp(Ity_F64);
 
    assign(op, get_fpr_dw0(r2));
-   put_fpr_w0(r1, binop(Iop_F64toF32, mkU32(Irrm_NEAREST), mkexpr(op)));
+   put_fpr_w0(r1, binop(Iop_F64toF32, mkU32(encode_rounding_mode(m3)),
+                        mkexpr(op)));
 
    return "ledbr";
 }
@@ -10304,7 +10354,8 @@
 }
 
 static HChar *
-s390_irgen_CXFBR(UChar r1, UChar r2)
+s390_irgen_CXFBR(UChar m3 __attribute__((unused)),
+                 UChar m4 __attribute__((unused)), UChar r1, UChar r2)
 {
    IRTemp op2 = newTemp(Ity_I32);
 
@@ -10331,7 +10382,8 @@
 
 
 static HChar *
-s390_irgen_CXGBR(UChar r1, UChar r2)
+s390_irgen_CXGBR(UChar m3 __attribute__((unused)),
+                 UChar m4 __attribute__((unused)), UChar r1, UChar r2)
 {
    IRTemp op2 = newTemp(Ity_I64);
 
@@ -10357,13 +10409,18 @@
 }
 
 static HChar *
-s390_irgen_CFXBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CFXBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F128);
    IRTemp result = newTemp(Ity_I32);
 
    assign(op, get_fpr_pair(r2));
-   assign(result, binop(Iop_F128toI32S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F128toI32S, mkU32(encode_rounding_mode(m3)),
                         mkexpr(op)));
    put_gpr_w1(r1, mkexpr(result));
    s390_cc_thunk_put1f128(S390_CC_OP_BFP_128_TO_INT_32, op);
@@ -10392,13 +10449,18 @@
 
 
 static HChar *
-s390_irgen_CGXBR(UChar r3, UChar r1, UChar r2)
+s390_irgen_CGXBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
+   if (! s390_host_has_fpext && m3 != 0) {
+      emulation_warning(EmWarn_S390X_fpext_rounding);
+      m3 = 0;
+   }
    IRTemp op = newTemp(Ity_F128);
    IRTemp result = newTemp(Ity_I64);
 
    assign(op, get_fpr_pair(r2));
-   assign(result, binop(Iop_F128toI64S, mkU32(encode_rounding_mode(r3)),
+   assign(result, binop(Iop_F128toI64S, mkU32(encode_rounding_mode(m3)),
                         mkexpr(op)));
    put_gpr_dw0(r1, mkexpr(result));
    s390_cc_thunk_put1f128(S390_CC_OP_BFP_128_TO_INT_64, op);
@@ -10582,22 +10644,26 @@
 }
 
 static HChar *
-s390_irgen_LDXBR(UChar r1, UChar r2)
+s390_irgen_LDXBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
    IRTemp result = newTemp(Ity_F64);
 
-   assign(result, binop(Iop_F128toF64, mkU32(Irrm_NEAREST), get_fpr_pair(r2)));
+   assign(result, binop(Iop_F128toF64, mkU32(encode_rounding_mode(m3)),
+                        get_fpr_pair(r2)));
    put_fpr_dw0(r1, mkexpr(result));
 
    return "ldxbr";
 }
 
 static HChar *
-s390_irgen_LEXBR(UChar r1, UChar r2)
+s390_irgen_LEXBR(UChar m3, UChar m4 __attribute__((unused)),
+                 UChar r1, UChar r2)
 {
    IRTemp result = newTemp(Ity_F32);
 
-   assign(result, binop(Iop_F128toF32, mkU32(Irrm_NEAREST), get_fpr_pair(r2)));
+   assign(result, binop(Iop_F128toF32, mkU32(encode_rounding_mode(m3)),
+                        get_fpr_pair(r2)));
    put_fpr_w0(r1, mkexpr(result));
 
    return "lexbr";
@@ -12561,12 +12627,15 @@
                                    ovl.fmt.RRE.r2);  goto ok;
    case 0xb343: s390_format_RRE_FF(s390_irgen_LCXBR, ovl.fmt.RRE.r1,
                                    ovl.fmt.RRE.r2);  goto ok;
-   case 0xb344: s390_format_RRE_FF(s390_irgen_LEDBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb345: s390_format_RRE_FF(s390_irgen_LDXBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb346: s390_format_RRE_FF(s390_irgen_LEXBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
+   case 0xb344: s390_format_RRF_UUFF(s390_irgen_LEDBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb345: s390_format_RRF_UUFF(s390_irgen_LDXBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb346: s390_format_RRF_UUFF(s390_irgen_LEXBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
    case 0xb347: /* FIXBR */ goto unimplemented;
    case 0xb348: /* KXBR */ goto unimplemented;
    case 0xb349: s390_format_RRE_FF(s390_irgen_CXBR, ovl.fmt.RRE.r1,
@@ -12622,21 +12691,24 @@
    case 0xb392: s390_format_RRF_UUFR(s390_irgen_CXLFBR, ovl.fmt.RRF2.m3,
                                      ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
                                      ovl.fmt.RRF2.r2);  goto ok;
-   case 0xb394: s390_format_RRE_FR(s390_irgen_CEFBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb395: s390_format_RRE_FR(s390_irgen_CDFBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb396: s390_format_RRE_FR(s390_irgen_CXFBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb398: s390_format_RRF_U0RF(s390_irgen_CFEBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
-   case 0xb399: s390_format_RRF_U0RF(s390_irgen_CFDBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
-   case 0xb39a: s390_format_RRF_U0RF(s390_irgen_CFXBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
+   case 0xb394: s390_format_RRF_UUFR(s390_irgen_CEFBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb395: s390_format_RRF_UUFR(s390_irgen_CDFBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb396: s390_format_RRF_UUFR(s390_irgen_CXFBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb398: s390_format_RRF_UURF(s390_irgen_CFEBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb399: s390_format_RRF_UURF(s390_irgen_CFDBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb39a: s390_format_RRF_UURF(s390_irgen_CFXBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
    case 0xb39c: s390_format_RRF_UURF(s390_irgen_CLFEBR, ovl.fmt.RRF2.m3,
                                      ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
                                      ovl.fmt.RRF2.r2);  goto ok;
@@ -12655,21 +12727,24 @@
    case 0xb3a2: s390_format_RRF_UUFR(s390_irgen_CXLGBR, ovl.fmt.RRF2.m3,
                                      ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
                                      ovl.fmt.RRF2.r2);  goto ok;
-   case 0xb3a4: s390_format_RRE_FR(s390_irgen_CEGBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb3a5: s390_format_RRE_FR(s390_irgen_CDGBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb3a6: s390_format_RRE_FR(s390_irgen_CXGBR, ovl.fmt.RRE.r1,
-                                   ovl.fmt.RRE.r2);  goto ok;
-   case 0xb3a8: s390_format_RRF_U0RF(s390_irgen_CGEBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
-   case 0xb3a9: s390_format_RRF_U0RF(s390_irgen_CGDBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
-   case 0xb3aa: s390_format_RRF_U0RF(s390_irgen_CGXBR, ovl.fmt.RRF3.r3,
-                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
-                                     goto ok;
+   case 0xb3a4: s390_format_RRF_UUFR(s390_irgen_CEGBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3a5: s390_format_RRF_UUFR(s390_irgen_CDGBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3a6: s390_format_RRF_UUFR(s390_irgen_CXGBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3a8: s390_format_RRF_UURF(s390_irgen_CGEBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3a9: s390_format_RRF_UURF(s390_irgen_CGDBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
+   case 0xb3aa: s390_format_RRF_UURF(s390_irgen_CGXBR, ovl.fmt.RRF2.m3,
+                                     ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
+                                     ovl.fmt.RRF2.r2);  goto ok;
    case 0xb3ac: s390_format_RRF_UURF(s390_irgen_CLGEBR, ovl.fmt.RRF2.m3,
                                      ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1,
                                      ovl.fmt.RRF2.r2);  goto ok;
@@ -14558,6 +14633,13 @@
          put_IA(mkaddr_expr(dres.continueAt));
          break;
       case Dis_StopHere:
+         if (dres.jk_StopHere == Ijk_EmWarn ||
+             dres.jk_StopHere == Ijk_EmFail) {
+            /* We assume here, that emulation warnings are not given for
+               insns that transfer control. There is no good way to
+               do that. */
+            put_IA(mkaddr_expr(guest_IA_next_instr));
+         }
          break;
       default:
          vassert(0);