This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[PATCH 4/4] S/390: Provide hardware accelerated iconv modules forUTF conversions
- From: "Andreas Krebbel" <krebbel at linux dot vnet dot ibm dot com>
- To: libc-alpha at sourceware dot org
- Date: Tue, 28 Apr 2009 16:44:22 +0200
- Subject: [PATCH 4/4] S/390: Provide hardware accelerated iconv modules forUTF conversions
Hi,
the attached patch introduces S/390 iconv modules using hardware
instructions for UTF conversions.
Bye,
-Andreas-
2009-04-28 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
* sysdeps/s390/s390-64/iconvdata/Makefile: New file.
* sysdeps/s390/s390-64/iconvdata/utf16-utf32-z9.c: New file.
* sysdeps/s390/s390-64/iconvdata/utf8-utf16-z9.c: New file.
* sysdeps/s390/s390-64/iconvdata/utf8-utf32-z9.c: New file.
Index: libc/sysdeps/s390/s390-64/iconvdata/Makefile
===================================================================
--- libc.orig/sysdeps/s390/s390-64/iconvdata/Makefile
+++ libc/sysdeps/s390/s390-64/iconvdata/Makefile
@@ -2,23 +2,74 @@ ifeq ($(subdir),iconvdata)
ISO-8859-1_CP037_Z900-routines := iso-8859-1_cp037_z900
ISO-8859-1_CP037_Z900-map := gconv.map
-extra-modules-left += ISO-8859-1_CP037_Z900
+ISO-8859-1_CP037_Z900-routines := iso-8859-1_cp037_z900
+ISO-8859-1_CP037_Z900-map := gconv.map
+
+UTF8_UTF32_Z9-routines := utf8-utf32-z9
+UTF8_UTF32_Z9-map := gconv.map
+
+UTF16_UTF32_Z9-routines := utf16-utf32-z9
+UTF16_UTF32_Z9-map := gconv.map
+
+UTF8_UTF16_Z9-routines := utf8-utf16-z9
+UTF8_UTF16_Z9-map := gconv.map
+
+extra-modules-left += ISO-8859-1_CP037_Z900 UTF8_UTF16_Z9 UTF16_UTF32_Z9 UTF8_UTF32_Z9
include extra-module.mk
-extra-objs += ISO-8859-1_CP037_Z900.so
-install-others += $(inst_gconvdir)/ISO-8859-1_CP037_Z900.so
+extra-objs += ISO-8859-1_CP037_Z900.so UTF8_UTF32_Z9.so UTF16_UTF32_Z9.so UTF8_UTF32_Z9.so
+install-others += $(inst_gconvdir)/ISO-8859-1_CP037_Z900.so \
+ $(inst_gconvdir)/UTF8_UTF32_Z9.so \
+ $(inst_gconvdir)/UTF16_UTF32_Z9.so \
+ $(inst_gconvdir)/UTF8_UTF16_Z9.so
-distribute += iso-8859-1_cp037_z900.c
+distribute += iso-8859-1_cp037_z900.c utf8-utf32-z9.c utf16-utf32-z9.c utf8-utf16-z9
$(inst_gconvdir)/ISO-8859-1_CP037_Z900.so: $(objpfx)ISO-8859-1_CP037_Z900.so $(+force)
$(do-install-program)
+$(inst_gconvdir)/ISO-8859-1_CP037_Z900.so: $(objpfx)ISO-8859-1_CP037_Z900.so $(+force)
+ $(do-install-program)
+$(inst_gconvdir)/UTF8_UTF32_Z9.so: $(objpfx)UTF8_UTF32_Z9.so $(+force)
+ $(do-install-program)
+$(inst_gconvdir)/UTF16_UTF32_Z9.so: $(objpfx)UTF16_UTF32_Z9.so $(+force)
+ $(do-install-program)
+$(inst_gconvdir)/UTF8_UTF16_Z9.so: $(objpfx)UTF8_UTF16_Z9.so $(+force)
+ $(do-install-program)
$(objpfx)gconv-modules-s390: gconv-modules $(+force)
cp $< $@
+ echo >> $@
+ echo "# S/390 hardware accelerated modules" >> $@
echo -n "module ISO-8859-1// IBM037// " >> $@
echo " ISO-8859-1_CP037_Z900 1" >> $@
echo -n "module IBM037// ISO-8859-1// " >> $@
echo " ISO-8859-1_CP037_Z900 1" >> $@
+ echo -n "module ISO-10646/UTF8/ UTF-32// " >> $@
+ echo " UTF8_UTF32_Z9 1" >> $@
+ echo -n "module UTF-32BE// ISO-10646/UTF8/ " >> $@
+ echo " UTF8_UTF32_Z9 1" >> $@
+ echo -n "module ISO-10646/UTF8/ UTF-32BE// " >> $@
+ echo " UTF8_UTF32_Z9 1" >> $@
+ echo -n "module UTF-16BE// UTF-32// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module UTF-32BE// UTF-16// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module INTERNAL UTF-16// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module UTF-32BE// UTF-16BE// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module INTERNAL UTF-16BE// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module UTF-16BE// UTF-32BE// " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module UTF-16BE// INTERNAL " >> $@
+ echo " UTF16_UTF32_Z9 1" >> $@
+ echo -n "module UTF-16BE// ISO-10646/UTF8/ " >> $@
+ echo " UTF8_UTF16_Z9 1" >> $@
+ echo -n "module ISO-10646/UTF8/ UTF-16// " >> $@
+ echo " UTF8_UTF16_Z9 1" >> $@
+ echo -n "module ISO-10646/UTF8/ UTF-16BE// " >> $@
+ echo " UTF8_UTF16_Z9 1" >> $@
$(inst_gconvdir)/gconv-modules: $(objpfx)gconv-modules-s390 $(+force)
$(do-install)
Index: libc/sysdeps/s390/s390-64/iconvdata/utf16-utf32-z9.c
===================================================================
--- /dev/null
+++ libc/sysdeps/s390/s390-64/iconvdata/utf16-utf32-z9.c
@@ -0,0 +1,301 @@
+/* Conversion between UTF-16 and UTF-32 BE/internal.
+
+ This module uses the Z9-109 variants of the Convert Unicode
+ instructions.
+ Copyright (C) 1997-2009 Free Software Foundation, Inc.
+
+ Author: Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
+ Based on the work by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ Thanks to Daniel Appich who covered the relevant performance work
+ in his diploma thesis.
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dl-procinfo.h>
+#include <gconv.h>
+
+/* UTF-32 big endian byte order mark. */
+#define BOM_UTF32 0x0000feffu
+
+/* UTF-16 big endian byte order mark. */
+#define BOM_UTF16 0xfeff
+
+#define DEFINE_INIT 0
+#define DEFINE_FINI 0
+#define MIN_NEEDED_FROM 2
+#define MAX_NEEDED_FROM 4
+#define MIN_NEEDED_TO 4
+#define FROM_LOOP from_utf16_loop
+#define TO_LOOP to_utf16_loop
+#define FROM_DIRECTION (dir == from_utf16)
+#define PREPARE_LOOP \
+ enum direction dir = ((struct utf16_data *) step->__data)->dir; \
+ int emit_bom = ((struct utf16_data *) step->__data)->emit_bom; \
+ \
+ if (emit_bom && !data->__internal_use \
+ && data->__invocation_counter == 0) \
+ { \
+ if (dir == to_utf16) \
+ { \
+ /* Emit the UTF-16 Byte Order Mark. */ \
+ if (__builtin_expect (outbuf + 2 > outend, 0)) \
+ return __GCONV_FULL_OUTPUT; \
+ \
+ put16u (outbuf, BOM_UTF16); \
+ outbuf += 2; \
+ } \
+ else \
+ { \
+ /* Emit the UTF-32 Byte Order Mark. */ \
+ if (__builtin_expect (outbuf + 4 > outend, 0)) \
+ return __GCONV_FULL_OUTPUT; \
+ \
+ put32u (outbuf, BOM_UTF32); \
+ outbuf += 4; \
+ } \
+ }
+
+/* Direction of the transformation. */
+enum direction
+{
+ illegal_dir,
+ to_utf16,
+ from_utf16
+};
+
+struct utf16_data
+{
+ enum direction dir;
+ int emit_bom;
+};
+
+
+extern int gconv_init (struct __gconv_step *step);
+int
+gconv_init (struct __gconv_step *step)
+{
+ /* Determine which direction. */
+ struct utf16_data *new_data;
+ enum direction dir = illegal_dir;
+ int emit_bom;
+ int result;
+
+ emit_bom = (__strcasecmp (step->__to_name, "UTF-32//") == 0
+ || __strcasecmp (step->__to_name, "UTF-16//") == 0);
+
+ if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0
+ && (__strcasecmp (step->__to_name, "UTF-32//") == 0
+ || __strcasecmp (step->__to_name, "UTF-32BE//") == 0
+ || __strcasecmp (step->__to_name, "INTERNAL") == 0))
+ {
+ dir = from_utf16;
+ }
+ else if ((__strcasecmp (step->__to_name, "UTF-16//") == 0
+ || __strcasecmp (step->__to_name, "UTF-16BE//") == 0)
+ && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0
+ || __strcasecmp (step->__from_name, "INTERNAL") == 0))
+ {
+ dir = to_utf16;
+ }
+
+ result = __GCONV_NOCONV;
+ if (dir != illegal_dir)
+ {
+ new_data = (struct utf16_data *) malloc (sizeof (struct utf16_data));
+
+ result = __GCONV_NOMEM;
+ if (new_data != NULL)
+ {
+ new_data->dir = dir;
+ new_data->emit_bom = emit_bom;
+ step->__data = new_data;
+
+ if (dir == from_utf16)
+ {
+ step->__min_needed_from = MIN_NEEDED_FROM;
+ step->__max_needed_from = MIN_NEEDED_FROM;
+ step->__min_needed_to = MIN_NEEDED_TO;
+ step->__max_needed_to = MIN_NEEDED_TO;
+ }
+ else
+ {
+ step->__min_needed_from = MIN_NEEDED_TO;
+ step->__max_needed_from = MIN_NEEDED_TO;
+ step->__min_needed_to = MIN_NEEDED_FROM;
+ step->__max_needed_to = MIN_NEEDED_FROM;
+ }
+
+ step->__stateful = 0;
+
+ result = __GCONV_OK;
+ }
+ }
+
+ return result;
+}
+
+
+extern void gconv_end (struct __gconv_step *data);
+void
+gconv_end (struct __gconv_step *data)
+{
+ free (data->__data);
+}
+
+/* The macro for the hardware loop. This is used for both
+ directions. */
+#define HARDWARE_CONVERT(INSTRUCTION) \
+ { \
+ register const unsigned char* pInput asm ("2") = inptr; \
+ register unsigned long long inlen asm ("3") = inend - inptr; \
+ register unsigned char* pOutput asm ("4") = outptr; \
+ register unsigned long long outlen asm("5") = outend - outptr; \
+ uint64_t cc = 0; \
+ \
+ asm volatile ("0: " INSTRUCTION " \n\t" \
+ " jo 0b \n\t" \
+ " ipm %2 \n" \
+ : "+a" (pOutput), "+a" (pInput), "=d" (cc), \
+ "+d" (outlen), "+d" (inlen) \
+ : \
+ : "cc"); \
+ \
+ inptr = pInput; \
+ outptr = pOutput; \
+ cc >>= 28; \
+ \
+ if (cc == 1) \
+ { \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ else if (cc == 2) \
+ { \
+ result = __GCONV_ILLEGAL_INPUT; \
+ break; \
+ } \
+ }
+
+/* Conversion function from UTF-16 to UTF-32 internal/BE. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_INPUT MAX_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
+#define LOOPFCT FROM_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu24 %0, %1, 1")
+/* The software routine is copied from utf-16.c (minus bytes
+ swapping). */
+#define BODY \
+ { \
+ uint16_t u1 = get16 (inptr); \
+ \
+ if (__builtin_expect (u1 < 0xd800, 1) || u1 > 0xdfff) \
+ { \
+ /* No surrogate. */ \
+ put32 (outptr, u1); \
+ inptr += 2; \
+ } \
+ else \
+ { \
+ /* It's a surrogate character. At least the first word says \
+ it is. */ \
+ if (__builtin_expect (inptr + 4 > inend, 0)) \
+ { \
+ /* We don't have enough input for another complete input \
+ character. */ \
+ result = __GCONV_INCOMPLETE_INPUT; \
+ break; \
+ } \
+ \
+ inptr += 2; \
+ uint16_t u2 = get16 (inptr); \
+ if (__builtin_expect (u2 < 0xdc00, 0) \
+ || __builtin_expect (u2 > 0xdfff, 0)) \
+ { \
+ /* This is no valid second word for a surrogate. */ \
+ inptr -= 2; \
+ STANDARD_FROM_LOOP_ERR_HANDLER (2); \
+ } \
+ \
+ put32 (outptr, ((u1 - 0xd7c0) << 10) + (u2 - 0xdc00)); \
+ inptr += 2; \
+ } \
+ outptr += 4; \
+ }
+#define LOOP_NEED_FLAGS
+#include <iconv/loop.c>
+
+/* Conversion from UTF-32 internal/BE to UTF-16. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_TO
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
+#define LOOPFCT TO_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu42 %0, %1")
+/* The software routine is copied from utf-16.c (minus bytes
+ swapping). */
+#define BODY \
+ { \
+ uint32_t c = get32 (inptr); \
+ \
+ if (__builtin_expect (c >= 0xd800 && c < 0xe000, 0)) \
+ { \
+ /* Surrogate characters in UCS-4 input are not valid. \
+ We must catch this. If we let surrogates pass through, \
+ attackers could make a security hole exploit by \
+ synthesizing any desired plane 1-16 character. */ \
+ result = __GCONV_ILLEGAL_INPUT; \
+ if (! ignore_errors_p ()) \
+ break; \
+ inptr += 4; \
+ ++*irreversible; \
+ continue; \
+ } \
+ \
+ if (__builtin_expect (c >= 0x10000, 0)) \
+ { \
+ if (__builtin_expect (c >= 0x110000, 0)) \
+ { \
+ STANDARD_TO_LOOP_ERR_HANDLER (4); \
+ } \
+ \
+ /* Generate a surrogate character. */ \
+ if (__builtin_expect (outptr + 4 > outend, 0)) \
+ { \
+ /* Overflow in the output buffer. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
+ put16 (outptr, 0xd7c0 + (c >> 10)); \
+ outptr += 2; \
+ put16 (outptr, 0xdc00 + (c & 0x3ff)); \
+ } \
+ else \
+ put16 (outptr, c); \
+ outptr += 2; \
+ inptr += 4; \
+ }
+#define LOOP_NEED_FLAGS
+#include <iconv/loop.c>
+
+#include <iconv/skeleton.c>
Index: libc/sysdeps/s390/s390-64/iconvdata/utf8-utf16-z9.c
===================================================================
--- /dev/null
+++ libc/sysdeps/s390/s390-64/iconvdata/utf8-utf16-z9.c
@@ -0,0 +1,436 @@
+/* Conversion between UTF-16 and UTF-32 BE/internal.
+
+ This module uses the Z9-109 variants of the Convert Unicode
+ instructions.
+ Copyright (C) 1997-2009 Free Software Foundation, Inc.
+
+ Author: Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
+ Based on the work by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ Thanks to Daniel Appich who covered the relevant performance work
+ in his diploma thesis.
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dl-procinfo.h>
+#include <gconv.h>
+
+/* UTF-16 big endian byte order mark. */
+#define BOM_UTF16 0xfeff
+
+#define DEFINE_INIT 0
+#define DEFINE_FINI 0
+#define MIN_NEEDED_FROM 1
+#define MAX_NEEDED_FROM 4
+#define MIN_NEEDED_TO 2
+#define MAX_NEEDED_TO 4
+#define FROM_LOOP from_utf8_loop
+#define TO_LOOP to_utf8_loop
+#define FROM_DIRECTION (dir == from_utf8)
+#define PREPARE_LOOP \
+ enum direction dir = ((struct utf8_data *) step->__data)->dir; \
+ int emit_bom = ((struct utf8_data *) step->__data)->emit_bom; \
+ \
+ if (emit_bom && !data->__internal_use \
+ && data->__invocation_counter == 0) \
+ { \
+ /* Emit the UTF-16 Byte Order Mark. */ \
+ if (__builtin_expect (outbuf + 2 > outend, 0)) \
+ return __GCONV_FULL_OUTPUT; \
+ \
+ put16u (outbuf, BOM_UTF16); \
+ outbuf += 2; \
+ }
+
+/* Direction of the transformation. */
+enum direction
+{
+ illegal_dir,
+ to_utf8,
+ from_utf8
+};
+
+struct utf8_data
+{
+ enum direction dir;
+ int emit_bom;
+};
+
+
+extern int gconv_init (struct __gconv_step *step);
+int
+gconv_init (struct __gconv_step *step)
+{
+ /* Determine which direction. */
+ struct utf8_data *new_data;
+ enum direction dir = illegal_dir;
+ int emit_bom;
+ int result;
+
+ emit_bom = (__strcasecmp (step->__to_name, "UTF-16//") == 0);
+
+ if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
+ && (__strcasecmp (step->__to_name, "UTF-16//") == 0
+ || __strcasecmp (step->__to_name, "UTF-16BE//") == 0))
+ {
+ dir = from_utf8;
+ }
+ else if (__strcasecmp (step->__from_name, "UTF-16BE//") == 0
+ && __strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0)
+ {
+ dir = to_utf8;
+ }
+
+ result = __GCONV_NOCONV;
+ if (dir != illegal_dir)
+ {
+ new_data = (struct utf8_data *) malloc (sizeof (struct utf8_data));
+
+ result = __GCONV_NOMEM;
+ if (new_data != NULL)
+ {
+ new_data->dir = dir;
+ new_data->emit_bom = emit_bom;
+ step->__data = new_data;
+
+ if (dir == from_utf8)
+ {
+ step->__min_needed_from = MIN_NEEDED_FROM;
+ step->__max_needed_from = MIN_NEEDED_FROM;
+ step->__min_needed_to = MIN_NEEDED_TO;
+ step->__max_needed_to = MIN_NEEDED_TO;
+ }
+ else
+ {
+ step->__min_needed_from = MIN_NEEDED_TO;
+ step->__max_needed_from = MIN_NEEDED_TO;
+ step->__min_needed_to = MIN_NEEDED_FROM;
+ step->__max_needed_to = MIN_NEEDED_FROM;
+ }
+
+ step->__stateful = 0;
+
+ result = __GCONV_OK;
+ }
+ }
+
+ return result;
+}
+
+
+extern void gconv_end (struct __gconv_step *data);
+void
+gconv_end (struct __gconv_step *data)
+{
+ free (data->__data);
+}
+
+/* The macro for the hardware loop. This is used for both
+ directions. */
+#define HARDWARE_CONVERT(INSTRUCTION) \
+ { \
+ register const unsigned char* pInput asm ("2") = inptr; \
+ register unsigned long long inlen asm ("3") = inend - inptr; \
+ register unsigned char* pOutput asm ("4") = outptr; \
+ register unsigned long long outlen asm("5") = outend - outptr; \
+ uint64_t cc = 0; \
+ \
+ asm volatile ("0: " INSTRUCTION " \n\t" \
+ " jo 0b \n\t" \
+ " ipm %2 \n" \
+ : "+a" (pOutput), "+a" (pInput), "=d" (cc), \
+ "+d" (outlen), "+d" (inlen) \
+ : \
+ : "cc"); \
+ \
+ inptr = pInput; \
+ outptr = pOutput; \
+ cc >>= 28; \
+ \
+ if (cc == 1) \
+ { \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ else if (cc == 2) \
+ { \
+ result = __GCONV_ILLEGAL_INPUT; \
+ break; \
+ } \
+ }
+
+/* Conversion function from UTF-8 to UTF-16. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_INPUT MAX_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
+#define LOOPFCT FROM_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu12 %0, %1, 1")
+/* The software implementation is based on the code in gconv_simple.c. */
+#define BODY \
+ { \
+ /* Next input byte. */ \
+ uint16_t ch = *inptr; \
+ \
+ if (__builtin_expect (ch < 0x80, 1)) \
+ { \
+ /* One byte sequence. */ \
+ ++inptr; \
+ } \
+ else \
+ { \
+ uint_fast32_t cnt; \
+ uint_fast32_t i; \
+ \
+ if (ch >= 0xc2 && ch < 0xe0) \
+ { \
+ /* We expect two bytes. The first byte cannot be 0xc0 or 0xc1, \
+ otherwise the wide character could have been represented \
+ using a single byte. */ \
+ cnt = 2; \
+ ch &= 0x1f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf0) == 0xe0, 1)) \
+ { \
+ /* We expect three bytes. */ \
+ cnt = 3; \
+ ch &= 0x0f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf8) == 0xf0, 1)) \
+ { \
+ /* We expect four bytes. */ \
+ cnt = 4; \
+ ch &= 0x07; \
+ } \
+ else \
+ { \
+ /* Search the end of this ill-formed UTF-8 character. This \
+ is the next byte with (x & 0xc0) != 0x80. */ \
+ i = 0; \
+ do \
+ ++i; \
+ while (inptr + i < inend \
+ && (*(inptr + i) & 0xc0) == 0x80 \
+ && i < 5); \
+ \
+ errout: \
+ STANDARD_FROM_LOOP_ERR_HANDLER (i); \
+ } \
+ \
+ if (__builtin_expect (inptr + cnt > inend, 0)) \
+ { \
+ /* We don't have enough input. But before we report that check \
+ that all the bytes are correct. */ \
+ for (i = 1; inptr + i < inend; ++i) \
+ if ((inptr[i] & 0xc0) != 0x80) \
+ break; \
+ \
+ if (__builtin_expect (inptr + i == inend, 1)) \
+ { \
+ result = __GCONV_INCOMPLETE_INPUT; \
+ break; \
+ } \
+ \
+ goto errout; \
+ } \
+ \
+ if (cnt == 4) \
+ { \
+ /* For 4 byte UTF-8 chars two UTF-16 chars (high and \
+ low) are needed. */ \
+ uint16_t zabcd, high, low; \
+ \
+ if (__builtin_expect (outptr + 4 > outend, 0)) \
+ { \
+ /* Overflow in the output buffer. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
+ /* See Principles of Operations cu12. */ \
+ zabcd = (((inptr[0] & 0x7) << 2) | ((inptr[1] & 0x30) >> 4)) - 1; \
+ \
+ /* z-bit must be zero after subtracting 1. */ \
+ if (zabcd & 0x10) \
+ goto errout; \
+ \
+ high = (uint16_t)(0xd8 << 8); /* high surrogate id */ \
+ high |= zabcd << 6; /* abcd bits */ \
+ high |= (inptr[1] & 0xf) << 2; /* efgh bits */ \
+ high |= (inptr[2] & 0x30) >> 4; /* ij bits */ \
+ \
+ low = (uint16_t)(0xdc << 8); /* low surrogate id */ \
+ low |= ((uint16_t)inptr[2] & 0xc) << 6; /* kl bits */ \
+ low |= (inptr[2] & 0x3) << 6; /* mn bits */ \
+ low |= inptr[3] & 0x3f; /* opqrst bits */ \
+ \
+ put16 (outptr, high); \
+ outptr += 2; \
+ put16 (outptr, low); \
+ outptr += 2; \
+ inptr += 4; \
+ } \
+ else \
+ { \
+ /* Read the possible remaining bytes. */ \
+ for (i = 1; i < cnt; ++i) \
+ { \
+ uint16_t byte = inptr[i]; \
+ \
+ if ((byte & 0xc0) != 0x80) \
+ /* This is an illegal encoding. */ \
+ break; \
+ \
+ ch <<= 6; \
+ ch |= byte & 0x3f; \
+ } \
+ inptr += cnt; \
+ \
+ /* Now adjust the pointers and store the result. */ \
+ *((uint16_t *) outptr) = ch; \
+ outptr += sizeof (uint16_t); \
+ } \
+ } \
+}
+
+#define LOOP_NEED_FLAGS
+#include <iconv/loop.c>
+
+/* Conversion from UTF-16 to UTF-8. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_TO
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
+#define LOOPFCT TO_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu21 %0, %1")
+/* The software routine is based on the functionality of the S/390
+ hardware instruction (cu21) as described in the Principles of
+ Operation. */
+#define BODY \
+ { \
+ uint16_t c = get16 (inptr); \
+ \
+ if (__builtin_expect (c >= 0x0000 && c <= 0x007f, 1)) \
+ { \
+ /* Single byte UTF-8 char. */ \
+ *outptr = c & 0xff; \
+ outptr++; \
+ } \
+ else if (c >= 0x0080 && c <= 0x07ff) \
+ { \
+ /* Two byte UTF-8 char. */ \
+ \
+ if (__builtin_expect (outptr + 2 > outend, 0)) \
+ { \
+ /* Overflow in the output buffer. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
+ outptr[0] = 0xc0; \
+ outptr[0] |= c >> 6; \
+ \
+ outptr[1] = 0x80; \
+ outptr[1] |= c & 0x3f; \
+ \
+ outptr += 2; \
+ } \
+ else if (c >= 0x0800 && c <= 0xd7ff) \
+ { \
+ /* Three byte UTF-8 char. */ \
+ \
+ if (__builtin_expect (outptr + 3 > outend, 0)) \
+ { \
+ /* Overflow in the output buffer. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ outptr[0] = 0xe0; \
+ outptr[0] |= c >> 12; \
+ \
+ outptr[1] = 0x80; \
+ outptr[1] |= (c >> 6) & 0x3f; \
+ \
+ outptr[2] = 0x80; \
+ outptr[2] = c & 0x3f; \
+ \
+ } \
+ else if (c >= 0xd800 && c <= 0xdbff) \
+ { \
+ /* Four byte UTF-8 char. */ \
+ uint16_t low, uvwxy; \
+ \
+ if (__builtin_expect (outptr + 4 > outend, 0)) \
+ { \
+ /* Overflow in the output buffer. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ inptr += 2; \
+ if (__builtin_expect (inptr + 2 > inend, 0)) \
+ { \
+ result = __GCONV_INCOMPLETE_INPUT; \
+ break; \
+ } \
+ \
+ low = get16 (inptr); \
+ \
+ if ((low & 0xfc) != 0xdc) \
+ { \
+ STANDARD_TO_LOOP_ERR_HANDLER (2); \
+ } \
+ uvwxy = ((c >> 6) & 0xf) + 1; \
+ outptr[0] = 0xf0; \
+ outptr[0] |= uvwxy >> 2; \
+ \
+ outptr[1] = 0x80; \
+ outptr[1] |= (uvwxy << 4) & 0x30; \
+ outptr[1] |= (c >> 2) & 0x0f; \
+ \
+ outptr[2] = 0x80; \
+ outptr[2] |= (c & 0x03) << 4; \
+ outptr[2] |= (low >> 6) & 0x0f; \
+ \
+ outptr[3] = 0x80; \
+ outptr[3] |= low & 0x3f; \
+ \
+ outptr += 4; \
+ } \
+ else \
+ { \
+ /* Search the end of this ill-formed UTF-8 character. This \
+ is the next byte with (x & 0xc0) != 0x80. */ \
+ int i = 0; \
+ do \
+ ++i; \
+ while (inptr + i < inend \
+ && (*(inptr + i) & 0xc0) == 0x80 \
+ && i < 5); \
+ \
+ errout: \
+ STANDARD_FROM_LOOP_ERR_HANDLER (i); \
+ } \
+ inptr += 2; \
+ }
+#define LOOP_NEED_FLAGS
+#include <iconv/loop.c>
+
+#include <iconv/skeleton.c>
Index: libc/sysdeps/s390/s390-64/iconvdata/utf8-utf32-z9.c
===================================================================
--- /dev/null
+++ libc/sysdeps/s390/s390-64/iconvdata/utf8-utf32-z9.c
@@ -0,0 +1,443 @@
+/* Conversion between UTF-8 and UTF-32 BE/internal.
+
+ This module uses the Z9-109 variants of the Convert Unicode
+ instructions.
+ Copyright (C) 1997-2009 Free Software Foundation, Inc.
+
+ Author: Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
+ Based on the work by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+ Thanks to Daniel Appich who covered the relevant performance work
+ in his diploma thesis.
+
+ This is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <dlfcn.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dl-procinfo.h>
+#include <gconv.h>
+
+/* UTF-32 big endian byte order mark. */
+#define BOM 0x0000feffu
+
+#define DEFINE_INIT 0
+#define DEFINE_FINI 0
+/* These definitions apply to the UTF-8 to UTF-32 direction. The
+ software implementation for UTF-8 still supports multibyte
+ characters up to 6 bytes whereas the hardware variant does not. */
+#define MIN_NEEDED_FROM 1
+#define MAX_NEEDED_FROM 6
+#define MIN_NEEDED_TO 4
+#define FROM_LOOP from_utf8_loop
+#define TO_LOOP to_utf8_loop
+#define FROM_DIRECTION (dir == from_utf8)
+#define PREPARE_LOOP \
+ enum direction dir = ((struct utf8_data *) step->__data)->dir; \
+ int emit_bom = ((struct utf8_data *) step->__data)->emit_bom; \
+ \
+ if (emit_bom && !data->__internal_use \
+ && data->__invocation_counter == 0) \
+ { \
+ /* Emit the Byte Order Mark. */ \
+ if (__builtin_expect (outbuf + 4 > outend, 0)) \
+ return __GCONV_FULL_OUTPUT; \
+ \
+ put32u (outbuf, BOM); \
+ outbuf += 4; \
+ }
+
+/* Direction of the transformation. */
+enum direction
+{
+ illegal_dir,
+ to_utf8,
+ from_utf8
+};
+
+struct utf8_data
+{
+ enum direction dir;
+ int emit_bom;
+};
+
+
+extern int gconv_init (struct __gconv_step *step);
+int
+gconv_init (struct __gconv_step *step)
+{
+ /* Determine which direction. */
+ struct utf8_data *new_data;
+ enum direction dir = illegal_dir;
+ int emit_bom;
+ int result;
+
+ emit_bom = (__strcasecmp (step->__to_name, "UTF-32//") == 0);
+
+ if (__strcasecmp (step->__from_name, "ISO-10646/UTF8/") == 0
+ && (__strcasecmp (step->__to_name, "UTF-32//") == 0
+ || __strcasecmp (step->__to_name, "UTF-32BE//") == 0
+ || __strcasecmp (step->__to_name, "INTERNAL") == 0))
+ {
+ dir = from_utf8;
+ }
+ else if (__strcasecmp (step->__to_name, "ISO-10646/UTF8/") == 0
+ && (__strcasecmp (step->__from_name, "UTF-32BE//") == 0
+ || __strcasecmp (step->__from_name, "INTERNAL") == 0))
+ {
+ dir = to_utf8;
+ }
+
+ result = __GCONV_NOCONV;
+ if (dir != illegal_dir)
+ {
+ new_data = (struct utf8_data *) malloc (sizeof (struct utf8_data));
+
+ result = __GCONV_NOMEM;
+ if (new_data != NULL)
+ {
+ new_data->dir = dir;
+ new_data->emit_bom = emit_bom;
+ step->__data = new_data;
+
+ if (dir == from_utf8)
+ {
+ step->__min_needed_from = MIN_NEEDED_FROM;
+ step->__max_needed_from = MIN_NEEDED_FROM;
+ step->__min_needed_to = MIN_NEEDED_TO;
+ step->__max_needed_to = MIN_NEEDED_TO;
+ }
+ else
+ {
+ step->__min_needed_from = MIN_NEEDED_TO;
+ step->__max_needed_from = MIN_NEEDED_TO;
+ step->__min_needed_to = MIN_NEEDED_FROM;
+ step->__max_needed_to = MIN_NEEDED_FROM;
+ }
+
+ step->__stateful = 0;
+
+ result = __GCONV_OK;
+ }
+ }
+
+ return result;
+}
+
+
+extern void gconv_end (struct __gconv_step *data);
+void
+gconv_end (struct __gconv_step *data)
+{
+ free (data->__data);
+}
+
+/* The macro for the hardware loop. This is used for both
+ directions. */
+#define HARDWARE_CONVERT(INSTRUCTION) \
+ { \
+ register const unsigned char* pInput asm ("2") = inptr; \
+ register unsigned long long inlen asm ("3") = inend - inptr; \
+ register unsigned char* pOutput asm ("4") = outptr; \
+ register unsigned long long outlen asm("5") = outend - outptr; \
+ uint64_t cc = 0; \
+ \
+ asm volatile ("0: " INSTRUCTION " \n\t" \
+ " jo 0b \n\t" \
+ " ipm %2 \n" \
+ : "+a" (pOutput), "+a" (pInput), "=d" (cc), \
+ "+d" (outlen), "+d" (inlen) \
+ : \
+ : "cc"); \
+ \
+ inptr = pInput; \
+ outptr = pOutput; \
+ cc >>= 28; \
+ \
+ if (cc == 1) \
+ { \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ else if (cc == 2) \
+ { \
+ result = __GCONV_ILLEGAL_INPUT; \
+ break; \
+ } \
+ }
+
+/* Conversion function from UTF-8 to UTF-32 internal/BE. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_INPUT MAX_NEEDED_FROM
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_TO
+#define LOOPFCT FROM_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu14 %0, %1, 1")
+/* The software routine is copied from gconv_simple.c. */
+#define BODY \
+ { \
+ /* Next input byte. */ \
+ uint32_t ch = *inptr; \
+ \
+ if (__builtin_expect (ch < 0x80, 1)) \
+ { \
+ /* One byte sequence. */ \
+ ++inptr; \
+ } \
+ else \
+ { \
+ uint_fast32_t cnt; \
+ uint_fast32_t i; \
+ \
+ if (ch >= 0xc2 && ch < 0xe0) \
+ { \
+ /* We expect two bytes. The first byte cannot be 0xc0 or 0xc1, \
+ otherwise the wide character could have been represented \
+ using a single byte. */ \
+ cnt = 2; \
+ ch &= 0x1f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf0) == 0xe0, 1)) \
+ { \
+ /* We expect three bytes. */ \
+ cnt = 3; \
+ ch &= 0x0f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf8) == 0xf0, 1)) \
+ { \
+ /* We expect four bytes. */ \
+ cnt = 4; \
+ ch &= 0x07; \
+ } \
+ else if (__builtin_expect ((ch & 0xfc) == 0xf8, 1)) \
+ { \
+ /* We expect five bytes. */ \
+ cnt = 5; \
+ ch &= 0x03; \
+ } \
+ else if (__builtin_expect ((ch & 0xfe) == 0xfc, 1)) \
+ { \
+ /* We expect six bytes. */ \
+ cnt = 6; \
+ ch &= 0x01; \
+ } \
+ else \
+ { \
+ /* Search the end of this ill-formed UTF-8 character. This \
+ is the next byte with (x & 0xc0) != 0x80. */ \
+ i = 0; \
+ do \
+ ++i; \
+ while (inptr + i < inend \
+ && (*(inptr + i) & 0xc0) == 0x80 \
+ && i < 5); \
+ \
+ errout: \
+ STANDARD_FROM_LOOP_ERR_HANDLER (i); \
+ } \
+ \
+ if (__builtin_expect (inptr + cnt > inend, 0)) \
+ { \
+ /* We don't have enough input. But before we report that check \
+ that all the bytes are correct. */ \
+ for (i = 1; inptr + i < inend; ++i) \
+ if ((inptr[i] & 0xc0) != 0x80) \
+ break; \
+ \
+ if (__builtin_expect (inptr + i == inend, 1)) \
+ { \
+ result = __GCONV_INCOMPLETE_INPUT; \
+ break; \
+ } \
+ \
+ goto errout; \
+ } \
+ \
+ /* Read the possible remaining bytes. */ \
+ for (i = 1; i < cnt; ++i) \
+ { \
+ uint32_t byte = inptr[i]; \
+ \
+ if ((byte & 0xc0) != 0x80) \
+ /* This is an illegal encoding. */ \
+ break; \
+ \
+ ch <<= 6; \
+ ch |= byte & 0x3f; \
+ } \
+ \
+ /* If i < cnt, some trail byte was not >= 0x80, < 0xc0. \
+ If cnt > 2 and ch < 2^(5*cnt-4), the wide character ch could \
+ have been represented with fewer than cnt bytes. */ \
+ if (i < cnt || (cnt > 2 && (ch >> (5 * cnt - 4)) == 0)) \
+ { \
+ /* This is an illegal encoding. */ \
+ goto errout; \
+ } \
+ \
+ inptr += cnt; \
+ } \
+ \
+ /* Now adjust the pointers and store the result. */ \
+ *((uint32_t *) outptr) = ch; \
+ outptr += sizeof (uint32_t); \
+ }
+#define LOOP_NEED_FLAGS
+
+#define STORE_REST \
+ { \
+ /* We store the remaining bytes while converting them into the UCS4 \
+ format. We can assume that the first byte in the buffer is \
+ correct and that it requires a larger number of bytes than there \
+ are in the input buffer. */ \
+ wint_t ch = **inptrp; \
+ size_t cnt, r; \
+ \
+ state->__count = inend - *inptrp; \
+ \
+ if (ch >= 0xc2 && ch < 0xe0) \
+ { \
+ /* We expect two bytes. The first byte cannot be 0xc0 or \
+ 0xc1, otherwise the wide character could have been \
+ represented using a single byte. */ \
+ cnt = 2; \
+ ch &= 0x1f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf0) == 0xe0, 1)) \
+ { \
+ /* We expect three bytes. */ \
+ cnt = 3; \
+ ch &= 0x0f; \
+ } \
+ else if (__builtin_expect ((ch & 0xf8) == 0xf0, 1)) \
+ { \
+ /* We expect four bytes. */ \
+ cnt = 4; \
+ ch &= 0x07; \
+ } \
+ else if (__builtin_expect ((ch & 0xfc) == 0xf8, 1)) \
+ { \
+ /* We expect five bytes. */ \
+ cnt = 5; \
+ ch &= 0x03; \
+ } \
+ else \
+ { \
+ /* We expect six bytes. */ \
+ cnt = 6; \
+ ch &= 0x01; \
+ } \
+ \
+ /* The first byte is already consumed. */ \
+ r = cnt - 1; \
+ while (++(*inptrp) < inend) \
+ { \
+ ch <<= 6; \
+ ch |= **inptrp & 0x3f; \
+ --r; \
+ } \
+ \
+ /* Shift for the so far missing bytes. */ \
+ ch <<= r * 6; \
+ \
+ /* Store the number of bytes expected for the entire sequence. */ \
+ state->__count |= cnt << 8; \
+ \
+ /* Store the value. */ \
+ state->__value.__wch = ch; \
+ }
+
+#define UNPACK_BYTES \
+ { \
+ static const unsigned char inmask[5] = { 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }; \
+ wint_t wch = state->__value.__wch; \
+ size_t ntotal = state->__count >> 8; \
+ \
+ inlen = state->__count & 255; \
+ \
+ bytebuf[0] = inmask[ntotal - 2]; \
+ \
+ do \
+ { \
+ if (--ntotal < inlen) \
+ bytebuf[ntotal] = 0x80 | (wch & 0x3f); \
+ wch >>= 6; \
+ } \
+ while (ntotal > 1); \
+ \
+ bytebuf[0] |= wch; \
+ }
+
+#define CLEAR_STATE \
+ state->__count = 0
+
+#include <iconv/loop.c>
+
+/* Conversion from UTF-32 internal/BE to UTF-8. */
+
+#define MIN_NEEDED_INPUT MIN_NEEDED_TO
+#define MIN_NEEDED_OUTPUT MIN_NEEDED_FROM
+#define MAX_NEEDED_OUTPUT MAX_NEEDED_FROM
+#define LOOPFCT TO_LOOP
+#define BODY_SELECT_ALTERNATE (GLRO (dl_hwcap) & HWCAP_S390_ETF3EH)
+#define BODY_ALTERNATE HARDWARE_CONVERT("cu41 %0, %1")
+/* The software routine is copied from gconv_simple.c. */
+#define BODY \
+ { \
+ uint32_t wc = *((const uint32_t *) inptr); \
+ \
+ if (__builtin_expect (wc < 0x80, 1)) \
+ /* It's an one byte sequence. */ \
+ *outptr++ = (unsigned char) wc; \
+ else if (__builtin_expect (wc <= 0x7fffffff, 1)) \
+ { \
+ size_t step; \
+ unsigned char *start; \
+ \
+ for (step = 2; step < 6; ++step) \
+ if ((wc & (~(uint32_t)0 << (5 * step + 1))) == 0) \
+ break; \
+ \
+ if (__builtin_expect (outptr + step > outend, 0)) \
+ { \
+ /* Too long. */ \
+ result = __GCONV_FULL_OUTPUT; \
+ break; \
+ } \
+ \
+ start = outptr; \
+ *outptr = (unsigned char) (~0xff >> step); \
+ outptr += step; \
+ do \
+ { \
+ start[--step] = 0x80 | (wc & 0x3f); \
+ wc >>= 6; \
+ } \
+ while (step > 1); \
+ start[0] |= wc; \
+ } \
+ else \
+ { \
+ STANDARD_TO_LOOP_ERR_HANDLER (4); \
+ } \
+ \
+ inptr += 4; \
+ }
+#define LOOP_NEED_FLAGS
+#include <iconv/loop.c>
+
+#include <iconv/skeleton.c>