summary refs log tree commit diff stats
path: root/target/s390x/mem_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/s390x/mem_helper.c')
-rw-r--r--target/s390x/mem_helper.c585
1 files changed, 567 insertions, 18 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index ede84711d1..cdc78aa3d4 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -538,18 +538,21 @@ static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
 }
 
 /* search string (c is byte to search, r2 is string, r1 end of string) */
-uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
-                      uint64_t str)
+void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
 {
     uintptr_t ra = GETPC();
+    uint64_t end, str;
     uint32_t len;
-    uint8_t v, c = r0;
+    uint8_t v, c = env->regs[0];
 
-    str = wrap_address(env, str);
-    end = wrap_address(env, end);
+    /* Bits 32-55 must contain all 0.  */
+    if (env->regs[0] & 0xffffff00u) {
+        cpu_restore_state(ENV_GET_CPU(env), ra);
+        program_interrupt(env, PGM_SPECIFICATION, 6);
+    }
 
-    /* Assume for now that R2 is unmodified.  */
-    env->retxl = str;
+    str = get_address(env, r2);
+    end = get_address(env, r1);
 
     /* 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 8k.  */
@@ -557,20 +560,61 @@ uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
         if (str + len == end) {
             /* Character not found.  R1 & R2 are unmodified.  */
             env->cc_op = 2;
-            return end;
+            return;
         }
         v = cpu_ldub_data_ra(env, str + len, ra);
         if (v == c) {
             /* Character found.  Set R1 to the location; R2 is unmodified.  */
             env->cc_op = 1;
-            return str + len;
+            set_address(env, r1, str + len);
+            return;
+        }
+    }
+
+    /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
+    env->cc_op = 3;
+    set_address(env, r2, str + len);
+}
+
+void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+{
+    uintptr_t ra = GETPC();
+    uint32_t len;
+    uint16_t v, c = env->regs[0];
+    uint64_t end, str, adj_end;
+
+    /* Bits 32-47 of R0 must be zero.  */
+    if (env->regs[0] & 0xffff0000u) {
+        cpu_restore_state(ENV_GET_CPU(env), ra);
+        program_interrupt(env, PGM_SPECIFICATION, 6);
+    }
+
+    str = get_address(env, r2);
+    end = get_address(env, r1);
+
+    /* If the LSB of the two addresses differ, use one extra byte.  */
+    adj_end = end + ((str ^ end) & 1);
+
+    /* 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 8k.  */
+    for (len = 0; len < 0x2000; len += 2) {
+        if (str + len == adj_end) {
+            /* End of input found.  */
+            env->cc_op = 2;
+            return;
+        }
+        v = cpu_lduw_data_ra(env, str + len, ra);
+        if (v == c) {
+            /* Character found.  Set R1 to the location; R2 is unmodified.  */
+            env->cc_op = 1;
+            set_address(env, r1, str + len);
+            return;
         }
     }
 
     /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
-    env->retxl = str + len;
     env->cc_op = 3;
-    return end;
+    set_address(env, r2, str + len);
 }
 
 /* unsigned string compare (c is string terminator) */
@@ -1233,17 +1277,18 @@ uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
     return array + i;
 }
 
-static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
-                              uint64_t trans, uintptr_t ra)
+static inline uint32_t do_helper_trt(CPUS390XState *env, int len,
+                                     uint64_t array, uint64_t trans,
+                                     int inc, uintptr_t ra)
 {
-    uint32_t i;
+    int i;
 
     for (i = 0; i <= len; i++) {
-        uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
+        uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra);
         uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
 
         if (sbyte != 0) {
-            set_address(env, 1, array + i);
+            set_address(env, 1, array + i * inc);
             env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
             return (i == len) ? 2 : 1;
         }
@@ -1255,7 +1300,13 @@ static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
 uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
                      uint64_t trans)
 {
-    return do_helper_trt(env, len, array, trans, GETPC());
+    return do_helper_trt(env, len, array, trans, 1, GETPC());
+}
+
+uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array,
+                      uint64_t trans)
+{
+    return do_helper_trt(env, len, array, trans, -1, GETPC());
 }
 
 /* Translate one/two to one/two */
@@ -1353,6 +1404,195 @@ void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
     env->regs[r1 + 1] = int128_getlo(oldv);
 }
 
+uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
+{
+#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128)
+    uint32_t mem_idx = cpu_mmu_index(env, false);
+#endif
+    uintptr_t ra = GETPC();
+    uint32_t fc = extract32(env->regs[0], 0, 8);
+    uint32_t sc = extract32(env->regs[0], 8, 8);
+    uint64_t pl = get_address(env, 1) & -16;
+    uint64_t svh, svl;
+    uint32_t cc;
+
+    /* Sanity check the function code and storage characteristic.  */
+    if (fc > 1 || sc > 3) {
+        if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) {
+            goto spec_exception;
+        }
+        if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) {
+            goto spec_exception;
+        }
+    }
+
+    /* Sanity check the alignments.  */
+    if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) {
+        goto spec_exception;
+    }
+
+    /* Sanity check writability of the store address.  */
+#ifndef CONFIG_USER_ONLY
+    probe_write(env, a2, mem_idx, ra);
+#endif
+
+    /* Note that the compare-and-swap is atomic, and the store is atomic, but
+       the complete operation is not.  Therefore we do not need to assert serial
+       context in order to implement this.  That said, restart early if we can't
+       support either operation that is supposed to be atomic.  */
+    if (parallel_cpus) {
+        int mask = 0;
+#if !defined(CONFIG_ATOMIC64)
+        mask = -8;
+#elif !defined(CONFIG_ATOMIC128)
+        mask = -16;
+#endif
+        if (((4 << fc) | (1 << sc)) & mask) {
+            cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
+        }
+    }
+
+    /* All loads happen before all stores.  For simplicity, load the entire
+       store value area from the parameter list.  */
+    svh = cpu_ldq_data_ra(env, pl + 16, ra);
+    svl = cpu_ldq_data_ra(env, pl + 24, ra);
+
+    switch (fc) {
+    case 0:
+        {
+            uint32_t nv = cpu_ldl_data_ra(env, pl, ra);
+            uint32_t cv = env->regs[r3];
+            uint32_t ov;
+
+            if (parallel_cpus) {
+#ifdef CONFIG_USER_ONLY
+                uint32_t *haddr = g2h(a1);
+                ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+#else
+                TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx);
+                ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra);
+#endif
+            } else {
+                ov = cpu_ldl_data_ra(env, a1, ra);
+                cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+            }
+            cc = (ov != cv);
+            env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov);
+        }
+        break;
+
+    case 1:
+        {
+            uint64_t nv = cpu_ldq_data_ra(env, pl, ra);
+            uint64_t cv = env->regs[r3];
+            uint64_t ov;
+
+            if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC64
+# ifdef CONFIG_USER_ONLY
+                uint64_t *haddr = g2h(a1);
+                ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+# else
+                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx);
+                ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra);
+# endif
+#else
+                /* Note that we asserted !parallel_cpus above.  */
+                g_assert_not_reached();
+#endif
+            } else {
+                ov = cpu_ldq_data_ra(env, a1, ra);
+                cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+            }
+            cc = (ov != cv);
+            env->regs[r3] = ov;
+        }
+        break;
+
+    case 2:
+        {
+            uint64_t nvh = cpu_ldq_data_ra(env, pl, ra);
+            uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra);
+            Int128 nv = int128_make128(nvl, nvh);
+            Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
+            Int128 ov;
+
+            if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+                ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);
+                cc = !int128_eq(ov, cv);
+#else
+                /* Note that we asserted !parallel_cpus above.  */
+                g_assert_not_reached();
+#endif
+            } else {
+                uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra);
+                uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra);
+
+                ov = int128_make128(ol, oh);
+                cc = !int128_eq(ov, cv);
+                if (cc) {
+                    nv = ov;
+                }
+
+                cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra);
+                cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra);
+            }
+
+            env->regs[r3 + 0] = int128_gethi(ov);
+            env->regs[r3 + 1] = int128_getlo(ov);
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
+    }
+
+    /* Store only if the comparison succeeded.  Note that above we use a pair
+       of 64-bit big-endian loads, so for sc < 3 we must extract the value
+       from the most-significant bits of svh.  */
+    if (cc == 0) {
+        switch (sc) {
+        case 0:
+            cpu_stb_data_ra(env, a2, svh >> 56, ra);
+            break;
+        case 1:
+            cpu_stw_data_ra(env, a2, svh >> 48, ra);
+            break;
+        case 2:
+            cpu_stl_data_ra(env, a2, svh >> 32, ra);
+            break;
+        case 3:
+            cpu_stq_data_ra(env, a2, svh, ra);
+            break;
+        case 4:
+            if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+                Int128 sv = int128_make128(svl, svh);
+                helper_atomic_sto_be_mmu(env, a2, sv, oi, ra);
+#else
+                /* Note that we asserted !parallel_cpus above.  */
+                g_assert_not_reached();
+#endif
+            } else {
+                cpu_stq_data_ra(env, a2 + 0, svh, ra);
+                cpu_stq_data_ra(env, a2 + 8, svl, ra);
+            }
+        default:
+            g_assert_not_reached();
+        }
+    }
+
+    return cc;
+
+ spec_exception:
+    cpu_restore_state(ENV_GET_CPU(env), ra);
+    program_interrupt(env, PGM_SPECIFICATION, 6);
+    g_assert_not_reached();
+}
+
 #if !defined(CONFIG_USER_ONLY)
 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
 {
@@ -1886,7 +2126,6 @@ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr)
             [0x6] = do_helper_oc,
             [0x7] = do_helper_xc,
             [0xc] = do_helper_tr,
-            [0xd] = do_helper_trt,
         };
         dx_helper helper = dx[opc & 0xf];
 
@@ -2007,3 +2246,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);
+}