Skip to content

Commit

Permalink
target/s390x: Implement CONVERT UNICODE insns
Browse files Browse the repository at this point in the history
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Richard Henderson <rth@twiddle.net>
  • Loading branch information
rth7680 committed Jul 17, 2017
1 parent c67ba30 commit 941ef3d
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 0 deletions.
6 changes: 6 additions & 0 deletions target/s390x/helper.h
Expand Up @@ -107,6 +107,12 @@ DEF_HELPER_2(stfle, i32, env, i64)
DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_4(stpq, TCG_CALL_NO_WG, void, env, i64, i64, i64)
DEF_HELPER_4(mvcos, i32, env, i64, i64, i64)
DEF_HELPER_4(cu12, i32, env, i32, i32, i32)
DEF_HELPER_4(cu14, i32, env, i32, i32, i32)
DEF_HELPER_4(cu21, i32, env, i32, i32, i32)
DEF_HELPER_4(cu24, i32, env, i32, i32, i32)
DEF_HELPER_4(cu41, i32, env, i32, i32, i32)
DEF_HELPER_4(cu42, i32, env, i32, i32, i32)

#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(servc, i32, env, i64, i64)
Expand Down
13 changes: 13 additions & 0 deletions target/s390x/insn-data.def
Expand Up @@ -313,6 +313,19 @@
C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0)
C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0)

/* CONVERT UTF-8 TO UTF-16 */
D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12)
/* CONVERT UTF-8 TO UTF-32 */
D(0xb9b0, CU14, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 14)
/* CONVERT UTF-16 to UTF-8 */
D(0xb2a6, CU21, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 21)
/* CONVERT UTF-16 to UTF-32 */
D(0xb9b1, CU24, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 24)
/* CONVERT UTF-32 to UTF-8 */
D(0xb9b2, CU41, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 41)
/* CONVERT UTF-32 to UTF-16 */
D(0xb9b3, CU42, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 42)

/* DIVIDE */
C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0)
C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0)
Expand Down
310 changes: 310 additions & 0 deletions target/s390x/mem_helper.c
Expand Up @@ -2196,3 +2196,313 @@ uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,

return cc;
}

/* Decode a Unicode character. A return value < 0 indicates success, storing
the UTF-32 result into OCHAR and the input length into OLEN. A return
value >= 0 indicates failure, and the CC value to be returned. */
typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr,
uint64_t ilen, bool enh_check, uintptr_t ra,
uint32_t *ochar, uint32_t *olen);

/* Encode a Unicode character. A return value < 0 indicates success, storing
the bytes into ADDR and the output length into OLEN. A return value >= 0
indicates failure, and the CC value to be returned. */
typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr,
uint64_t ilen, uintptr_t ra, uint32_t c,
uint32_t *olen);

static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
bool enh_check, uintptr_t ra,
uint32_t *ochar, uint32_t *olen)
{
uint8_t s0, s1, s2, s3;
uint32_t c, l;

if (ilen < 1) {
return 0;
}
s0 = cpu_ldub_data_ra(env, addr, ra);
if (s0 <= 0x7f) {
/* one byte character */
l = 1;
c = s0;
} else if (s0 <= (enh_check ? 0xc1 : 0xbf)) {
/* invalid character */
return 2;
} else if (s0 <= 0xdf) {
/* two byte character */
l = 2;
if (ilen < 2) {
return 0;
}
s1 = cpu_ldub_data_ra(env, addr + 1, ra);
c = s0 & 0x1f;
c = (c << 6) | (s1 & 0x3f);
if (enh_check && (s1 & 0xc0) != 0x80) {
return 2;
}
} else if (s0 <= 0xef) {
/* three byte character */
l = 3;
if (ilen < 3) {
return 0;
}
s1 = cpu_ldub_data_ra(env, addr + 1, ra);
s2 = cpu_ldub_data_ra(env, addr + 2, ra);
c = s0 & 0x0f;
c = (c << 6) | (s1 & 0x3f);
c = (c << 6) | (s2 & 0x3f);
/* Fold the byte-by-byte range descriptions in the PoO into
tests against the complete value. It disallows encodings
that could be smaller, and the UTF-16 surrogates. */
if (enh_check
&& ((s1 & 0xc0) != 0x80
|| (s2 & 0xc0) != 0x80
|| c < 0x1000
|| (c >= 0xd800 && c <= 0xdfff))) {
return 2;
}
} else if (s0 <= (enh_check ? 0xf4 : 0xf7)) {
/* four byte character */
l = 4;
if (ilen < 4) {
return 0;
}
s1 = cpu_ldub_data_ra(env, addr + 1, ra);
s2 = cpu_ldub_data_ra(env, addr + 2, ra);
s3 = cpu_ldub_data_ra(env, addr + 3, ra);
c = s0 & 0x07;
c = (c << 6) | (s1 & 0x3f);
c = (c << 6) | (s2 & 0x3f);
c = (c << 6) | (s3 & 0x3f);
/* See above. */
if (enh_check
&& ((s1 & 0xc0) != 0x80
|| (s2 & 0xc0) != 0x80
|| (s3 & 0xc0) != 0x80
|| c < 0x010000
|| c > 0x10ffff)) {
return 2;
}
} else {
/* invalid character */
return 2;
}

*ochar = c;
*olen = l;
return -1;
}

static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
bool enh_check, uintptr_t ra,
uint32_t *ochar, uint32_t *olen)
{
uint16_t s0, s1;
uint32_t c, l;

if (ilen < 2) {
return 0;
}
s0 = cpu_lduw_data_ra(env, addr, ra);
if ((s0 & 0xfc00) != 0xd800) {
/* one word character */
l = 2;
c = s0;
} else {
/* two word character */
l = 4;
if (ilen < 4) {
return 0;
}
s1 = cpu_lduw_data_ra(env, addr + 2, ra);
c = extract32(s0, 6, 4) + 1;
c = (c << 6) | (s0 & 0x3f);
c = (c << 10) | (s1 & 0x3ff);
if (enh_check && (s1 & 0xfc00) != 0xdc00) {
/* invalid surrogate character */
return 2;
}
}

*ochar = c;
*olen = l;
return -1;
}

static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
bool enh_check, uintptr_t ra,
uint32_t *ochar, uint32_t *olen)
{
uint32_t c;

if (ilen < 4) {
return 0;
}
c = cpu_ldl_data_ra(env, addr, ra);
if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) {
/* invalid unicode character */
return 2;
}

*ochar = c;
*olen = 4;
return -1;
}

static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
uintptr_t ra, uint32_t c, uint32_t *olen)
{
uint8_t d[4];
uint32_t l, i;

if (c <= 0x7f) {
/* one byte character */
l = 1;
d[0] = c;
} else if (c <= 0x7ff) {
/* two byte character */
l = 2;
d[1] = 0x80 | extract32(c, 0, 6);
d[0] = 0xc0 | extract32(c, 6, 5);
} else if (c <= 0xffff) {
/* three byte character */
l = 3;
d[2] = 0x80 | extract32(c, 0, 6);
d[1] = 0x80 | extract32(c, 6, 6);
d[0] = 0xe0 | extract32(c, 12, 4);
} else {
/* four byte character */
l = 4;
d[3] = 0x80 | extract32(c, 0, 6);
d[2] = 0x80 | extract32(c, 6, 6);
d[1] = 0x80 | extract32(c, 12, 6);
d[0] = 0xf0 | extract32(c, 18, 3);
}

if (ilen < l) {
return 1;
}
for (i = 0; i < l; ++i) {
cpu_stb_data_ra(env, addr + i, d[i], ra);
}

*olen = l;
return -1;
}

static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
uintptr_t ra, uint32_t c, uint32_t *olen)
{
uint16_t d0, d1;

if (c <= 0xffff) {
/* one word character */
if (ilen < 2) {
return 1;
}
cpu_stw_data_ra(env, addr, c, ra);
*olen = 2;
} else {
/* two word character */
if (ilen < 4) {
return 1;
}
d1 = 0xdc00 | extract32(c, 0, 10);
d0 = 0xd800 | extract32(c, 10, 6);
d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1);
cpu_stw_data_ra(env, addr + 0, d0, ra);
cpu_stw_data_ra(env, addr + 2, d1, ra);
*olen = 4;
}

return -1;
}

static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
uintptr_t ra, uint32_t c, uint32_t *olen)
{
if (ilen < 4) {
return 1;
}
cpu_stl_data_ra(env, addr, c, ra);
*olen = 4;
return -1;
}

static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1,
uint32_t r2, uint32_t m3, uintptr_t ra,
decode_unicode_fn decode,
encode_unicode_fn encode)
{
uint64_t dst = get_address(env, r1);
uint64_t dlen = get_length(env, r1 + 1);
uint64_t src = get_address(env, r2);
uint64_t slen = get_length(env, r2 + 1);
bool enh_check = m3 & 1;
int cc, i;

/* Lest we fail to service interrupts in a timely manner, limit the
amount of work we're willing to do. For now, let's cap at 256. */
for (i = 0; i < 256; ++i) {
uint32_t c, ilen, olen;

cc = decode(env, src, slen, enh_check, ra, &c, &ilen);
if (unlikely(cc >= 0)) {
break;
}
cc = encode(env, dst, dlen, ra, c, &olen);
if (unlikely(cc >= 0)) {
break;
}

src += ilen;
slen -= ilen;
dst += olen;
dlen -= olen;
cc = 3;
}

set_address(env, r1, dst);
set_length(env, r1 + 1, dlen);
set_address(env, r2, src);
set_length(env, r2 + 1, slen);

return cc;
}

uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf8, encode_utf16);
}

uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf8, encode_utf32);
}

uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf16, encode_utf8);
}

uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf16, encode_utf32);
}

uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf32, encode_utf8);
}

uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
{
return convert_unicode(env, r1, r2, m3, GETPC(),
decode_utf32, encode_utf16);
}

0 comments on commit 941ef3d

Please sign in to comment.