From 4121dde9140afd6df17c1fadb01c3f14ffee3c38 Mon Sep 17 00:00:00 2001 From: Florian Krohm Date: Fri, 20 Jul 2012 00:06:35 +0000 Subject: [PATCH] Add support for the CU21 instruction (s390x). git-svn-id: svn://svn.valgrind.org/vex/trunk@2441 --- VEX/priv/guest_s390_defs.h | 2 + VEX/priv/guest_s390_helpers.c | 115 +++++++++++++++++++++++++++++ VEX/priv/guest_s390_toIR.c | 135 +++++++++++++++++++++++++++++++++- 3 files changed, 251 insertions(+), 1 deletion(-) diff --git a/VEX/priv/guest_s390_defs.h b/VEX/priv/guest_s390_defs.h index e319f315c4..634b91290e 100644 --- a/VEX/priv/guest_s390_defs.h +++ b/VEX/priv/guest_s390_defs.h @@ -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); diff --git a/VEX/priv/guest_s390_helpers.c b/VEX/priv/guest_s390_helpers.c index c898f2725c..0205644e67 100644 --- a/VEX/priv/guest_s390_helpers.c +++ b/VEX/priv/guest_s390_helpers.c @@ -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". ---*/ diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index 2f60d47bc6..3df2602be3 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -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; -- 2.47.2