]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add support for the CU21 instruction (s390x).
authorFlorian Krohm <florian@eich-krohm.de>
Fri, 20 Jul 2012 00:06:35 +0000 (00:06 +0000)
committerFlorian Krohm <florian@eich-krohm.de>
Fri, 20 Jul 2012 00:06:35 +0000 (00:06 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@2441

VEX/priv/guest_s390_defs.h
VEX/priv/guest_s390_helpers.c
VEX/priv/guest_s390_toIR.c

index e319f315c402ca642f710d12a9411242465f23b3..634b91290e32f73e0420ba812e04c91d83ed9b3a 100644 (file)
@@ -80,7 +80,9 @@ ULong s390x_dirtyhelper_STCK(ULong *addr);
 ULong s390x_dirtyhelper_STCKF(ULong *addr);
 ULong s390x_dirtyhelper_STCKE(ULong *addr);
 ULong s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, HWord addr);
+void  s390x_dirtyhelper_CUxy(UChar *addr, ULong data, ULong num_bytes);
 
+ULong s390_do_cu21(UInt srcvalue, UInt low_surrogate);
 UInt  s390_do_cvb(ULong decimal);
 ULong s390_do_cvd(ULong binary);
 
index c898f2725c2b31193e2a73eff2fc048f6a378886..0205644e672a0519000c9d930f698cf1d37175fe 100644 (file)
@@ -339,6 +339,121 @@ s390x_dirtyhelper_STFLE(VexGuestS390XState *guest_state, HWord addr)
 }
 #endif /* VGA_s390x */
 
+/*------------------------------------------------------------*/
+/*--- Dirty helper for the "convert unicode" insn family.  ---*/
+/*------------------------------------------------------------*/
+void
+s390x_dirtyhelper_CUxy(UChar *address, ULong data, ULong num_bytes)
+{
+   UInt i;
+
+   vassert(num_bytes >= 1 && num_bytes <= 4);
+
+   /* Store the least significant NUM_BYTES bytes in DATA left to right
+      at ADDRESS. */
+   for (i = 1; i <= num_bytes; ++i) {
+      address[num_bytes - i] = data & 0xff;
+      data >>= 8;
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*--- Clean helper for CU21.                               ---*/
+/*------------------------------------------------------------*/
+
+/* The function performs a CU21 operation. It returns three things
+   encoded in an ULong value:
+   - the converted bytes (at most 4)
+   - the number of converted bytes
+   - an indication whether LOW_SURROGATE, if any, is invalid
+
+   64      48                16           8                       0
+    +-------+-----------------+-----------+-----------------------+
+    |  0x0  | converted bytes | num_bytes | invalid_low_surrogate |
+    +-------+-----------------+-----------+-----------------------+
+*/
+ULong
+s390_do_cu21(UInt srcval, UInt low_surrogate)
+{
+   ULong retval = 0;   // shut up gcc
+   UInt b1, b2 , b3 , b4, num_bytes, invalid_low_surrogate = 0;
+
+   srcval &= 0xffff;
+
+   /* Determine the number of bytes in the converted value */
+   if (srcval <= 0x007f)
+      num_bytes = 1;
+   else if (srcval >= 0x0080 && srcval <= 0x07ff)
+      num_bytes = 2;
+   else if ((srcval >= 0x0800 && srcval <= 0xd7ff) ||
+            (srcval >= 0xdc00 && srcval <= 0xffff))
+      num_bytes = 3;
+   else
+      num_bytes = 4;
+
+   /* Determine UTF-8 bytes according to calculated num_bytes */
+   switch (num_bytes){
+   case 1:
+      retval = srcval;
+      break;
+
+   case 2:
+      /* order of bytes left to right: b1, b2 */
+      b1  = 0xc0;
+      b1 |= srcval >> 6;
+
+      b2  = 0x80;
+      b2 |= srcval & 0x3f;
+
+      retval = (b1 << 8) | b2;
+      break;
+
+   case 3:
+      /* order of bytes left to right: b1, b2, b3 */
+      b1  = 0xe0;
+      b1 |= srcval >> 12;
+
+      b2  = 0x80;
+      b2 |= (srcval >> 6) & 0x3f;
+
+      b3  = 0x80;
+      b3 |= srcval & 0x3f;
+
+      retval = (b1 << 16) | (b2 << 8) | b3;
+      break;
+
+   case 4: {
+      /* order of bytes left to right: b1, b2, b3, b4 */
+      UInt high_surrogate = srcval;
+      UInt uvwxy = ((high_surrogate >> 6) & 0xf) + 1;   // abcd + 1
+
+      b1  = 0xf0;
+      b1 |= uvwxy >> 2;     // uvw
+
+      b2  = 0x80;
+      b2 |= (uvwxy & 0x3) << 4;           // xy
+      b2 |= (high_surrogate >> 2) & 0xf;  // efgh
+
+      b3  = 0x80;
+      b3 |= (high_surrogate & 0x3) << 4;   // ij
+      b3 |= (low_surrogate >> 6) & 0xf;    // klmn
+
+      b4  = 0x80;
+      b4 |= low_surrogate & 0x3f;
+
+      retval = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
+
+      invalid_low_surrogate = (low_surrogate & 0xfc00) != 0xdc00;
+      break;
+   }
+   }
+
+   /* At this point RETVAL contains the converted bytes.
+      Build up the final return value. */
+   return (retval << 16) | (num_bytes << 8) | invalid_low_surrogate;
+}
+
 
 /*------------------------------------------------------------*/
 /*--- Clean helper for "convert to binary".                ---*/
index 2f60d47bc64fbfbc4c7857305fd556cd340141e2..3df2602be3f46b1e69ee2851e22c7da1899fe4c7 100644 (file)
@@ -11193,6 +11193,137 @@ s390_irgen_TRE(UChar r1,UChar r2)
    return "tre";
 }
 
+static IRExpr *
+s390_call_cu21(IRExpr *srcval, IRExpr *low_surrogate)
+{
+   IRExpr **args, *call;
+   args = mkIRExprVec_2(srcval, low_surrogate);
+   call = mkIRExprCCall(Ity_I64, 0 /*regparm*/,
+                       "s390_do_cu21", &s390_do_cu21, args);
+
+   /* Nothing is excluded from definedness checking. */
+   call->Iex.CCall.cee->mcx_mask = 0;
+
+   return call;
+}
+
+static HChar *
+s390_irgen_CU21(UChar m3, UChar r1, UChar r2)
+{
+   IRTemp addr1 = newTemp(Ity_I64);
+   IRTemp addr2 = newTemp(Ity_I64);
+   IRTemp len1 = newTemp(Ity_I64);
+   IRTemp len2 = newTemp(Ity_I64);
+
+   assign(addr1, get_gpr_dw0(r1));
+   assign(addr2, get_gpr_dw0(r2));
+   assign(len1, get_gpr_dw0(r1 + 1));
+   assign(len2, get_gpr_dw0(r2 + 1));
+
+   /* We're processing the 2nd operand 2 bytes at a time. Therefore, if
+      there are less than 2 bytes left, then the 2nd operand is exhausted
+      and we're done here. cc = 0 */
+   s390_cc_set(0);
+   if_condition_goto(binop(Iop_CmpLT64U, mkexpr(len2), mkU64(2)),
+                     guest_IA_next_instr);
+
+   /* There are at least two bytes there. Read them. */
+   IRTemp srcval = newTemp(Ity_I32);
+   assign(srcval, unop(Iop_16Uto32, load(Ity_I16, mkexpr(addr2))));
+
+   /* Find out whether this is a high surrogate. I.e. SRCVAL lies
+      inside the interval [0xd800 - 0xdbff] */
+   IRTemp  is_high_surrogate = newTemp(Ity_I32);
+   IRExpr *flag1 = mkite(binop(Iop_CmpLE32U, mkU32(0xd800), mkexpr(srcval)),
+                         mkU32(1), mkU32(0));
+   IRExpr *flag2 = mkite(binop(Iop_CmpLE32U, mkexpr(srcval), mkU32(0xdbff)),
+                         mkU32(1), mkU32(0));
+   assign(is_high_surrogate, binop(Iop_And32, flag1, flag2));
+
+   /* If SRCVAL is a high surrogate and there are less than 4 bytes left,
+      then the 2nd operand is exhausted and we're done here. cc = 0 */
+   IRExpr *not_enough_bytes =
+      mkite(binop(Iop_CmpLT64U, mkexpr(len2), mkU64(4)), mkU32(1), mkU32(0));
+
+   if_condition_goto(binop(Iop_CmpEQ32,
+                           binop(Iop_And32, mkexpr(is_high_surrogate),
+                                 not_enough_bytes),
+                           mkU32(1)), guest_IA_next_instr);
+
+   /* The 2nd operand is not exhausted. If the first 2 bytes are a high
+      surrogate, read the next two bytes (low surrogate). */
+   IRTemp  low_surrogate = newTemp(Ity_I32);
+   IRExpr *low_surrogate_addr = binop(Iop_Add64, mkexpr(addr2), mkU64(2));
+
+   assign(low_surrogate,
+          mkite(binop(Iop_CmpEQ32, mkexpr(is_high_surrogate), mkU32(1)),
+                unop(Iop_16Uto32, load(Ity_I16, low_surrogate_addr)),
+                mkU32(0)));  // any value is fine; it will not be used
+
+   /* Call the helper */
+   IRTemp retval = newTemp(Ity_I64);
+   assign(retval, s390_call_cu21(mkexpr(srcval), mkexpr(low_surrogate)));
+
+   /* Before we can test whether the 1st operand is exhausted we need to
+      test for an invalid low surrogate. Because cc=2 outranks cc=1. */
+   if (s390_host_has_etf3 && (m3 & 0x1) == 1) {
+      IRExpr *invalid_low_surrogate =
+         binop(Iop_And64, mkexpr(retval), mkU64(0xff));
+
+      s390_cc_set(2);
+      if_condition_goto(binop(Iop_CmpEQ64, invalid_low_surrogate, mkU64(1)),
+                        guest_IA_next_instr);
+   }
+
+   /* Now test whether the 1st operand is exhausted */
+   IRTemp num_bytes = newTemp(Ity_I64);
+   assign(num_bytes, binop(Iop_And64,
+                           binop(Iop_Shr64, mkexpr(retval), mkU8(8)),
+                           mkU64(0xff)));
+   s390_cc_set(1);
+   if_condition_goto(binop(Iop_CmpLT64U, mkexpr(len1), mkexpr(num_bytes)),
+                     guest_IA_next_instr);
+
+   /* Extract the bytes to be stored at addr1 */
+   IRTemp data = newTemp(Ity_I64);
+   assign(data, binop(Iop_Shr64, mkexpr(retval), mkU8(16)));
+
+   /* To store the bytes construct 4 dirty helper calls. The helper calls
+      are guarded (num_bytes == 1, num_bytes == 2, etc) such that only
+      one of them will be called at runtime. */
+   int i;
+   for (i = 1; i <= 4; ++i) {
+      IRDirty *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));
+   }
+
+   /* Update source address and length */
+   IRTemp num_src_bytes = newTemp(Ity_I64);
+   assign(num_src_bytes,
+          mkite(binop(Iop_CmpEQ32, mkexpr(is_high_surrogate), mkU32(1)),
+                mkU64(4), mkU64(2)));
+   put_gpr_dw0(r2,     binop(Iop_Add64, mkexpr(addr2), mkexpr(num_src_bytes)));
+   put_gpr_dw0(r2 + 1, binop(Iop_Sub64, mkexpr(len2),  mkexpr(num_src_bytes)));
+
+   /* Update destination address and length */
+   put_gpr_dw0(r1,     binop(Iop_Add64, mkexpr(addr1), mkexpr(num_bytes)));
+   put_gpr_dw0(r1 + 1, binop(Iop_Sub64, mkexpr(len1),  mkexpr(num_bytes)));
+
+   /* Iterate */
+   always_goto_and_chase(guest_IA_curr_instr);
+
+   return "cu21";
+}
+
 
 /*------------------------------------------------------------*/
 /*--- Build IR for special instructions                    ---*/
@@ -11635,7 +11766,9 @@ s390_decode_4byte_and_irgen(UChar *bytes)
    case 0xb29d: s390_format_S_RD(s390_irgen_LFPC, ovl.fmt.S.b2, ovl.fmt.S.d2);
                                  goto ok;
    case 0xb2a5: s390_format_RRE_FF(s390_irgen_TRE, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2);  goto ok;
-   case 0xb2a6: /* CU21 */ goto unimplemented;
+   case 0xb2a6: s390_format_RRF_M0RERE(s390_irgen_CU21, ovl.fmt.RRF3.r3,
+                                       ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2);
+      goto ok;
    case 0xb2a7: /* CU12 */ goto unimplemented;
    case 0xb2b0: s390_format_S_RD(s390_irgen_STFLE, ovl.fmt.S.b2, ovl.fmt.S.d2);
                                  goto ok;