Support the cu14 insn. That insn is very much like cu12 except the
converted value is always 4 byte wide. The only other difference is
the encoding of a 4-byte UTF-8 character.
Some code refactoring does the trick.
Part of fixing #289839.


git-svn-id: svn://svn.valgrind.org/vex/trunk@2459 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_defs.h b/priv/guest_s390_defs.h
index 7a05736..c6e7746 100644
--- a/priv/guest_s390_defs.h
+++ b/priv/guest_s390_defs.h
@@ -81,9 +81,11 @@
 ULong s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, ULong *addr);
 void  s390x_dirtyhelper_CUxy(UChar *addr, ULong data, ULong num_bytes);
 
-ULong s390_do_cu12_helper1(UInt byte1, UInt etf3_and_m3_is_1);
+ULong s390_do_cu12_cu14_helper1(UInt byte1, UInt etf3_and_m3_is_1);
 ULong s390_do_cu12_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
                            ULong stuff);
+ULong s390_do_cu14_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
+                           ULong stuff);
 ULong s390_do_cu21(UInt srcvalue, UInt low_surrogate);
 ULong s390_do_cu24(UInt srcvalue, UInt low_surrogate);
 ULong s390_do_cu42(UInt srcvalue);
diff --git a/priv/guest_s390_helpers.c b/priv/guest_s390_helpers.c
index 7969b27..120f697 100644
--- a/priv/guest_s390_helpers.c
+++ b/priv/guest_s390_helpers.c
@@ -546,7 +546,7 @@
     +-------+-----------+-------------------+
 */
 ULong
-s390_do_cu12_helper1(UInt byte, UInt etf3_and_m3_is_1)
+s390_do_cu12_cu14_helper1(UInt byte, UInt etf3_and_m3_is_1)
 {
    vassert(byte <= 0xff);
 
@@ -567,7 +567,7 @@
    return 4 << 8;  // 4 bytes
 }
 
-/* The function performs a CU12 operation. BYTE1, BYTE2, etc are the
+/* The function performs a CU12 or CU14 operation. BYTE1, BYTE2, etc are the
    bytes as read from the input stream, left to right. BYTE1 is a valid
    byte. The function returns three things encoded in an ULong value:
 
@@ -580,13 +580,13 @@
     |  0x0  | converted bytes | num_bytes | invalid_character |
     +-------+-----------------+-----------+-------------------+
 */
-ULong
-s390_do_cu12_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
-                     ULong stuff)
+static ULong
+s390_do_cu12_cu14_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
+                          ULong stuff, Bool is_cu12)
 {
+   UInt num_src_bytes = stuff >> 1, etf3_and_m3_is_1 = stuff & 0x1;
    UInt num_bytes = 0, invalid_character = 0;
    ULong retval = 0;
-   UInt num_src_bytes = stuff >> 1, etf3_and_m3_is_1 = stuff & 0x1;
 
    vassert(num_src_bytes <= 4);
 
@@ -684,26 +684,50 @@
       UInt uvw    = byte1 & 0x7;
       UInt xy     = (byte2 >> 4) & 0x3;
       UInt uvwxy  = (uvw << 2) | xy;
-      UInt abcd   = (uvwxy - 1) & 0xf;
       UInt efgh   = byte2 & 0xf;
       UInt ij     = (byte3 >> 4) & 0x3;
       UInt klmn   = byte3 & 0xf;
       UInt opqrst = byte4 & 0x3f;
       
-      UInt high_surrogate = (0xd8 << 8) | (abcd << 6) | (efgh << 2) | ij;
-      UInt low_surrogate  = (0xdc << 8) | (klmn << 6) | opqrst;
+      if (is_cu12) {
+         UInt abcd = (uvwxy - 1) & 0xf;
+         UInt high_surrogate = (0xd8 << 8) | (abcd << 6) | (efgh << 2) | ij;
+         UInt low_surrogate  = (0xdc << 8) | (klmn << 6) | opqrst;
 
-      num_bytes = 4;
-      retval = (high_surrogate << 16) | low_surrogate;
+         num_bytes = 4;
+         retval = (high_surrogate << 16) | low_surrogate;
+      } else {
+         num_bytes = 4;
+         retval =
+            (uvwxy << 16) | (efgh << 12) | (ij << 10) | (klmn << 6) | opqrst;
+      }
       break;
    }
    }
 
+   if (! is_cu12) num_bytes = 4;   // for CU14, by definition
+
    /* At this point RETVAL contains the converted bytes.
       Build up the final return value. */
    return (retval << 16) | (num_bytes << 8) | invalid_character;
 }
 
+ULong
+s390_do_cu12_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
+                     ULong stuff)
+{
+   return s390_do_cu12_cu14_helper2(byte1, byte2, byte3, byte4, stuff,
+                                    /* is_cu12 = */ 1);
+}
+
+ULong
+s390_do_cu14_helper2(UInt byte1, UInt byte2, UInt byte3, UInt byte4,
+                     ULong stuff)
+{
+   return s390_do_cu12_cu14_helper2(byte1, byte2, byte3, byte4, stuff,
+                                    /* is_cu12 = */ 0);
+}
+
 
 /*------------------------------------------------------------*/
 /*--- Clean helper for "convert to binary".                ---*/
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index 10fe3b0..3bc34a7 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -11472,12 +11472,12 @@
 }
 
 static IRExpr *
-s390_call_cu12_helper1(IRExpr *byte1, IRExpr *etf3_and_m3_is_1)
+s390_call_cu12_cu14_helper1(IRExpr *byte1, IRExpr *etf3_and_m3_is_1)
 {
    IRExpr **args, *call;
    args = mkIRExprVec_2(byte1, etf3_and_m3_is_1);
-   call = mkIRExprCCall(Ity_I64, 0 /*regparm*/,
-                        "s390_do_cu12_helper1", &s390_do_cu12_helper1, args);
+   call = mkIRExprCCall(Ity_I64, 0 /*regparm*/, "s390_do_cu12_cu14_helper1",
+                        &s390_do_cu12_cu14_helper1, args);
 
    /* Nothing is excluded from definedness checking. */
    call->Iex.CCall.cee->mcx_mask = 0;
@@ -11500,8 +11500,23 @@
    return call;
 }
 
-static HChar *
-s390_irgen_CU12(UChar m3, UChar r1, UChar r2)
+static IRExpr *
+s390_call_cu14_helper2(IRExpr *byte1, IRExpr *byte2, IRExpr *byte3,
+                       IRExpr *byte4, IRExpr *stuff)
+{
+   IRExpr **args, *call;
+   args = mkIRExprVec_5(byte1, byte2, byte3, byte4, stuff);
+   call = mkIRExprCCall(Ity_I64, 0 /*regparm*/,
+                        "s390_do_cu14_helper2", &s390_do_cu14_helper2, args);
+
+   /* Nothing is excluded from definedness checking. */
+   call->Iex.CCall.cee->mcx_mask = 0;
+
+   return call;
+}
+
+static void
+s390_irgen_cu12_cu14(UChar m3, UChar r1, UChar r2, Bool is_cu12)
 {
    IRTemp addr1 = newTemp(Ity_I64);
    IRTemp addr2 = newTemp(Ity_I64);
@@ -11527,8 +11542,8 @@
 
    /* Call the helper to get number of bytes and invalid byte indicator */
    IRTemp retval1 = newTemp(Ity_I64);
-   assign(retval1, s390_call_cu12_helper1(mkexpr(byte1),
-                                          mkU32(extended_checking)));
+   assign(retval1, s390_call_cu12_cu14_helper1(mkexpr(byte1),
+                                               mkU32(extended_checking)));
 
    /* Check for invalid 1st byte */
    IRExpr *is_invalid = unop(Iop_64to1, mkexpr(retval1));
@@ -11563,8 +11578,14 @@
                          binop(Iop_Shl64, mkexpr(num_src_bytes), mkU8(1)),
                          mkU64(extended_checking));
    IRTemp retval2 = newTemp(Ity_I64);
-   assign(retval2, s390_call_cu12_helper2(mkexpr(byte1), byte2, byte3, byte4,
-                                          stuff));
+
+   if (is_cu12) {
+      assign(retval2, s390_call_cu12_helper2(mkexpr(byte1), byte2, byte3,
+                                             byte4, stuff));
+   } else {
+      assign(retval2, s390_call_cu14_helper2(mkexpr(byte1), byte2, byte3,
+                                             byte4, stuff));
+   }
 
    /* Check for invalid character */
    s390_cc_set(2);
@@ -11583,25 +11604,30 @@
    IRTemp data = newTemp(Ity_I64);
    assign(data, binop(Iop_Shr64, mkexpr(retval2), mkU8(16)));
 
-   /* To store the bytes construct 2 dirty helper calls. The helper calls
-      are guarded (num_bytes == 2 and num_bytes == 4, respectively) such
-      that only one of them will be called at runtime. */
+   if (is_cu12) {
+      /* To store the bytes construct 2 dirty helper calls. The helper calls
+         are guarded (num_bytes == 2 and num_bytes == 4, respectively) such
+         that only one of them will be called at runtime. */
 
-   Int i;
-   for (i = 2; i <= 4; ++i) {
-      IRDirty *d;
+      Int i;
+      for (i = 2; i <= 4; ++i) {
+         IRDirty *d;
 
-      if (i == 3) continue;  // skip this one
+         if (i == 3) continue;  // skip this one
 
-      d = unsafeIRDirty_0_N(0 /* regparms */, "s390x_dirtyhelper_CUxy",
-                            &s390x_dirtyhelper_CUxy,
-                            mkIRExprVec_3(mkexpr(addr1), mkexpr(data),
-                                          mkexpr(num_bytes)));
-      d->guard = binop(Iop_CmpEQ64, mkexpr(num_bytes), mkU64(i));
-      d->mFx   = Ifx_Write;
-      d->mAddr = mkexpr(addr1);
-      d->mSize = i;
-      stmt(IRStmt_Dirty(d));
+         d = unsafeIRDirty_0_N(0 /* regparms */, "s390x_dirtyhelper_CUxy",
+                               &s390x_dirtyhelper_CUxy,
+                               mkIRExprVec_3(mkexpr(addr1), mkexpr(data),
+                                             mkexpr(num_bytes)));
+         d->guard = binop(Iop_CmpEQ64, mkexpr(num_bytes), mkU64(i));
+         d->mFx   = Ifx_Write;
+         d->mAddr = mkexpr(addr1);
+         d->mSize = i;
+         stmt(IRStmt_Dirty(d));
+      }
+   } else {
+      // cu14
+      store(mkexpr(addr1), unop(Iop_64to32, mkexpr(data)));
    }
 
    /* Update source address and length */
@@ -11613,10 +11639,24 @@
    put_gpr_dw0(r1 + 1, binop(Iop_Sub64, mkexpr(len1),  mkexpr(num_bytes)));
 
    iterate();
+}
+
+static HChar *
+s390_irgen_CU12(UChar m3, UChar r1, UChar r2)
+{
+   s390_irgen_cu12_cu14(m3, r1, r2, /* is_cu12 = */ 1);
 
    return "cu12";
 }
 
+static HChar *
+s390_irgen_CU14(UChar m3, UChar r1, UChar r2)
+{
+   s390_irgen_cu12_cu14(m3, r1, r2, /* is_cu12 = */ 0);
+
+   return "cu14";
+}
+
 /*------------------------------------------------------------*/
 /*--- Build IR for special instructions                    ---*/
 /*------------------------------------------------------------*/
@@ -12451,7 +12491,9 @@
    case 0xb9aa: /* LPTEA */ goto unimplemented;
    case 0xb9ae: /* RRBM */ goto unimplemented;
    case 0xb9af: /* PFMF */ goto unimplemented;
-   case 0xb9b0: /* CU14 */ goto unimplemented;
+   case 0xb9b0: s390_format_RRF_M0RERE(s390_irgen_CU14, ovl.fmt.RRF3.r3,
+                                       ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
+      goto ok;
    case 0xb9b1: s390_format_RRF_M0RERE(s390_irgen_CU24, ovl.fmt.RRF3.r3,
                                        ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
       goto ok;